[DON'T MERGE] feat(pir): add state-aware ZIP generation#2689
Conversation
Build Branch
Static preview entry points
QR codes (mobile preview)
Integration commandsnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/feat/pir-state-aware-zip-implementation")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/feat/pir-state-aware-zip-implementation
git -C submodules/content-scope-scripts checkout origin/pr-releases/feat/pir-state-aware-zip-implementationPin to exact commitnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "c5f936f755a4f820b45cbdb569a8608a80e5f135")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/feat/pir-state-aware-zip-implementation
git -C submodules/content-scope-scripts checkout c5f936f755a4f820b45cbdb569a8608a80e5f135 |
[Beta] Generated file diffTime updated: Thu, 14 May 2026 09:07:42 GMT Android
File has changed AppleFile has changed IntegrationFile has changed WindowsFile has changed |
ff7fa93 to
235a41d
Compare
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js:35-40- warning: ZIP table lookups currently accept inherited properties. If the extracted/profile state is unknown, a page that has pollutedObject.prototypecan make the state appear present and turn the intended random-ZIP fallback into an exception or page-influenced ZIP data.Security Assessment
injected/src/features/broker-protection/actions/generators.js:35-40- warning:stateUpperCase in STATE_CITY_ZIPSand the unchecked city bracket read cross a hostile-page prototype boundary.STATE_CITY_ZIPSis a normal JSON object, so inherited properties from a tamperedObject.prototypeare visible during lookup.Risk Level
Medium Risk: this is scoped to broker-protection form-fill generation and does not touch API shims, messaging, captured globals, or bridge/origin checks, but it runs in injected code over page/extracted data.
Recommendations
- Replace inherited-property lookups with own-property checks, matching nearby patterns such as
Object.prototype.hasOwnProperty.call(...), for both the state table and city/default lookup.- Add a unit case that pollutes
Object.prototypefor an unknown state/city and verifiesgenerateZipCode()still returns a 5-digit random ZIP instead of throwing or using inherited data.Verification run:
npm run build --workspace=injected -- --platform android-broker-protection,npm run test-unit --workspace=injected -- --filter=generateZipCode, and ZIP JSON structure validation all passed.Sent by Cursor Automation: Web compat and sec
|
This PR requires a manual review and approval from a member of one of the following teams:
|
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js, lines 35-40 - warning: the state table check still usesstateUpperCase in STATE_CITY_ZIPS, followed by inherited bracket reads. In an injected page context, a pollutedObject.prototypecan make an unknown state appear present and bypass the intended random-ZIP fallback, causing throws or page-influenced ZIP selection.Security Assessment
injected/src/features/broker-protection/actions/generators.js, lines 35-40 - warning: the lookup crosses a hostile-page prototype boundary.STATE_CITY_ZIPSis imported JSON backed by ordinary objects, so inherited properties are visible toinand uncheckedstateCities[...]reads when state/city data is derived from broker-page extraction or native action state.Risk Level
Medium Risk: scoped to broker-protection form-fill generation and tests, with no API shim, messaging, captured-global, or bridge/origin changes, but the new lookup runs in injected code over potentially page-influenced profile fields.
Recommendations
- Replace inherited-property checks with own-property checks, e.g.
Object.prototype.hasOwnProperty.call(STATE_CITY_ZIPS, stateUpperCase)and own checks onstateCitiesbefore city/default lookup.- Add a regression test that pollutes
Object.prototypefor an unknown state/city and verifiesgenerateZipCode()still returns a random 5-digit ZIP without using inherited data or throwing.Verification:
npm run test-unit --workspace=injected -- --filter=generateZipCodeandnpm run build --workspace=injected -- --platform android-broker-protectionpassed on the current head.Sent by Cursor Automation: Web compat and sec
bd0e851 to
b4b4f3c
Compare
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js, lines 32-42 - info: the current head uses own-property checks for both state and city ZIP lookups, so the previous inherited-property/prototype-pollution compatibility concern is addressed. Unknown or missing state/city values still fall back to a random 5-digit ZIP.injected/unit-test/verify-artifacts.js, line 12 - info: the ZIP table raises the injected bundle-size guard from 860 KB to 890 KB. This is a scoped size/perf increase for bundles that includebrokerProtection, not an API-surface or DOM-compat regression.Security Assessment
- No warning/error findings in the current diff. The PR does not change API shims, captured globals, message bridge checks, origin validation,
postMessage, or native messaging payload construction.injected/src/features/broker-protection/actions/generators.js, lines 35-42 - info: the lookup now avoids inherited JSON-object properties viahasOwn(), addressing the stale security concern from the earlier review.Risk Level
Medium Risk: scoped to broker-protection form-fill generation over profile-derived city/state fields plus an eager ZIP dataset in injected bundles; no cross-platform API override or messaging security surface changes.
Recommendations
- Add a small regression unit that pollutes
Object.prototypeand verifiesgenerateZipCode({ state: 'ZZ', city: 'Injected' })still returns a random 5-digit ZIP without using inherited data.- If bundle size becomes a concern, consider a more compact ZIP representation or platform-specific loading for
brokerProtection-only contexts.Verification:
npm run test-unit --workspace injected -- --filter='generateZipCode'andnpm run build --workspace injectedpassed on the current head.Sent by Cursor Automation: Web compat and sec
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
No new web compatibility findings. The PR stays within the existing broker-protection form-fill path, does not add API overrides/shims, and preserves the existing form value-setting behavior while only changing generated ZIP selection when
useStateis explicitly set.Security Assessment
No new security findings. The previous inherited-property lookup concern is addressed by own-property checks before reading the ZIP table, and this diff does not change messaging, bridge secrets, origin checks, captured globals, or
postMessageusage.Risk Level
Medium Risk: injected broker-protection behavior now depends on a bundled state/city ZIP table and page/native-derived profile fields, but the change is scoped and avoids API surface, wrapper, messaging, or initialization changes.
Recommendations
- No blocking recommendations.
- Optional hardening: add a regression unit test that pollutes
Object.prototypeand verifies unknown state/city input still falls back to a random 5-digit ZIP.Verification:
npm run test-unit --workspace=injected -- --filter=generateZipCodeandnpm run build --workspace=injected -- --platform android-broker-protectionpassed.Sent by Cursor Automation: Web compat and sec
b4b4f3c to
98c3dbc
Compare
866ccb6 to
55577c2
Compare
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/utils/utils.js, lines 241-242 - warning: the sharedhasOwn()helper re-readsObject.prototype.hasOwnPropertyat action time. Broker pages run in the same hostile JS environment and can patch or delete that method after injection, which can turn expected missing-field paths into throws or incorrect matches.Security Assessment
injected/src/features/broker-protection/actions/generators.js, lines 35-48 /injected/src/features/broker-protection/utils/utils.js, lines 241-242 - warning: the state/city ZIP lookup now depends onhasOwn()for the prototype-pollution boundary, buthasOwn()itself uses an uncaptured global prototype method. I reproduced a page-style tamper where replacingObject.prototype.hasOwnPropertyand adding an inheritedZZentry makesgenerateZipCode({ state: 'ZZ' })use the inherited ZIP instead of the random fallback.Risk Level
Medium Risk: scoped to broker-protection form-fill generation and profile-derived ZIP selection, with no API shims, messaging, bridge, origin-validation, or
load()changes, but it runs in injected code against hostile pages.Recommendations
- Build
hasOwn()from the capturedhasOwnPropertyincaptured-globals.jsor a bound helper captured at module load, rather than readingObject.prototype.hasOwnPropertywhen actions execute.- Add a regression unit that imports
generateZipCode(), tampers withObject.prototype.hasOwnPropertyplus an inherited state/city entry, and verifies unknown states still fall back to a random 5-digit ZIP.Verification:
npm run test-unit --workspace=injected -- --filter=generateZipCodeandnpm run build --workspace=injected -- --platform android-broker-protectionpassed.Sent by Cursor Automation: Web compat and sec
55577c2 to
8e86270
Compare
8e86270 to
44c168d
Compare
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js, lines 34-40 - warning:state?.toUpperCase()/city?.toUpperCase()assume runtime strings. These values are carried through action/extracted-profile data, so malformed JSON/native/page-derived values such as numbers or arrays throw instead of taking the documented random/default ZIP fallback.Security Assessment
injected/src/features/broker-protection/utils/utils.js, lines 241-242 - warning: the newhasOwn()helper reads liveObject.prototype.hasOwnProperty. In page-world injected code, a hostile broker page can replace that prototype method after injection and make ZIP/action-data own-property checks throw or lie.captured-globals.jsalready snapshotshasOwnProperty; this helper should use that captured reference.Risk Level
Medium Risk: the change is scoped to broker-protection form filling and does not touch API shims, messaging, origin validation, or bridge secrets, but it runs over action/extracted profile data in injected code.
Recommendations
- Import
hasOwnPropertyfromcaptured-globals.jsinutils.jsand implementhasOwn()ashasOwnProperty.call(obj, key); add a regression that tampers withObject.prototype.hasOwnProperty.- Normalize
stateandcitywithtypeof value === 'string' ? value.toUpperCase() : undefinedbefore ZIP lookup; add cases for non-string state/city values verifying fallback behavior.Verification:
npm run test-unit --workspace=injected -- --filter=generateZipCodeandnpm run build --workspace=injected -- --platform android-broker-protectionpassed.Sent by Cursor Automation: Web compat and sec
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js, lines 34-40 - warning:state?.toUpperCase()andcity?.toUpperCase()assume runtime strings. If malformed native/action data or page-derived extracted profile data supplies a number/array/object, ZIP generation throws before reaching the documented random/default ZIP fallback, causing thefillFormaction to fail instead of preserving prior random ZIP behavior.Security Assessment
- No warning/error security findings in the current diff. The earlier inherited-property boundary is addressed by
hasOwn()using the capturedhasOwnProperty, and this PR does not change API shims, captured globals, messaging transports, bridge secrets, origin checks, orpostMessageusage.Risk Level
Medium Risk: scoped to broker-protection form filling and a bundled ZIP lookup table over profile-derived state/city fields; no API override or messaging security surface changes.
Recommendations
- Normalize only string values before lookup, e.g.
const stateUpperCase = typeof state === 'string' ? state.toUpperCase() : undefinedand the same forcity, so invalid values take the fallback path.- Add regression tests for non-string
stateandcityvalues confirminggenerateZipCode()returns a 5-digit fallback/default ZIP instead of throwing.Verification:
npm run test-unit --workspace=injected -- --filter=generateZipCodeandnpm run build --workspace=injected -- --platform android-broker-protectionpassed. A direct runtime probe with{ state: 5 }and{ state: 'IL', city: 5 }currently reproduces theTypeError.Sent by Cursor Automation: Web compat and sec
44c168d to
7aea0c2
Compare
|
@jonathanKingston do we need your review here or will we (in PIR) be able to merge on our own? |
7aea0c2 to
ff85d14
Compare
There was a problem hiding this comment.
Stale comment
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js, lines 34-40 - warning:state?.toUpperCase()andcity?.toUpperCase()still assume runtime strings. These values are ultimately profile/action data, so malformed native input or page-derived extraction such asstate: 5orcity: []throws before the intended random/default ZIP fallback and abortsfillForm.Security Assessment
- No warning/error security findings in the current diff. The previous inherited-property/captured-
hasOwnPropertyconcern is addressed, and this PR does not change API shims, wrapper utilities, messaging transports, bridge secret/origin checks, orpostMessageusage.Risk Level
Medium Risk: scoped to broker-protection form filling and a bundled ZIP lookup table over profile-derived state/city fields; no API override or messaging security surface changes.
Recommendations
- Guard uppercase normalization by type, e.g.
const stateUpperCase = typeof state === 'string' ? state.toUpperCase() : undefinedand the same forcity.- Add regression tests for non-string
stateandcityvalues confirminggenerateZipCode()returns a 5-digit random/default ZIP instead of throwing.Verification:
npm run test-unit --workspace=injected -- --filter=generateZipCodeandnpm run build --workspace=injected -- --platform android-broker-protectionpassed. A direct runtime probe with{ state: 5 }and{ state: 'IL', city: 5 }still reproduces theTypeError.Sent by Cursor Automation: Web compat and sec
| */ | ||
| export function generateZipCode(params) { | ||
| const { state, city } = params ?? {}; | ||
| const stateUpperCase = state?.toUpperCase(); |
There was a problem hiding this comment.
Optional chaining only handles null/undefined; it still calls .toUpperCase() on any other runtime value. Since params comes from profile/action data rather than a schema check at this boundary, malformed input like { state: 5 } or { state: 'IL', city: [] } throws and aborts fillForm instead of taking the random/default ZIP fallback. Please gate both conversions with typeof value === 'string' and add non-string regression cases.
ff85d14 to
2ffe3de
Compare
There was a problem hiding this comment.
Web Compatibility Assessment
injected/src/features/broker-protection/actions/generators.js, lines 32-42 - info: the current ZIP lookup type-checksstateandcitybefore case normalization and falls back for unknown/malformed inputs, so the prior non-stringtoUpperCase()fill-form abort is addressed. No API overrides, shims, prototype patches, DOM readiness changes, or event-listener timing changes are introduced.injected/unit-test/verify-artifacts.js, line 11 - info: the bundle-size guard is raised to 890 KB to account for the eager ZIP table. This is a scoped parse/size increase for bundles that includebrokerProtection, not an API-surface or DOM-compat regression.
Security Assessment
injected/src/features/broker-protection/utils/utils.js, lines 243-254 - info:hasOwn()now uses the capturedhasOwnProperty, andgenerateZipCode()uses own-property helpers for state/city table reads, addressing the earlier prototype-pollution boundary concern.- No warning/error security findings. This diff does not change captured globals, message bridge checks, messaging transports, origin validation,
postMessage, network requests, or native message payload construction.
Risk Level
Medium Risk: scoped to broker-protection form-fill generation over profile-derived state/city fields plus a bundled ZIP dataset; no cross-platform API override, wrapper, initialization, or messaging security surface changes.
Recommendations
- No blocking recommendations.
- Optional hardening: add a direct
generateZipCode()regression that pollutesObject.prototypewith inherited state/city ZIP entries and verifies unknown input still falls back. - If bundle size becomes material, consider a compact table representation or loading the ZIP data only in broker-protection-specific bundles.
Verification: npm run test-unit --workspace=injected -- --filter=generateZipCode and npm run build --workspace=injected -- --platform android-broker-protection passed on the current head.
Sent by Cursor Automation: Web compat and sec


Asana: https://app.asana.com/1/137249556945/task/1214636855527575
Summary
This PR adds state-aware ZIP generation for broker protection forms, using curated city ZIP data and a state default fallback.
Note
Medium Risk
Changes form-filling behavior by making
$generated_zip_code$optionally depend on extracted profilestate/city, and adds a large curated ZIP dataset which may impact bundle size and edge-case behavior across states/cities.Overview
Broker Protection form filling can now generate state/city-aware ZIP codes:
$generated_zip_code$supports an optionaluseStateflag to pick a real ZIP from a curatedstate-city-zips.jsonlist, falling back to a state default city or a random ZIP when inputs are missing/invalid.This also hardens property checks by introducing
utils.hasOwn/getOwn(using captured globals) and migrating severalhasOwnPropertyusages, adds unit/integration coverage for the new ZIP behavior, and bumps the bundle size threshold inverify-artifacts.jsto account for the added data.Reviewed by Cursor Bugbot for commit 2ffe3de. Bugbot is set up for automated code reviews on this repo. Configure here.