Skip to content

Generate broker-protection types from JSON schemas#2789

Open
noisysocks wants to merge 3 commits into
mainfrom
randerson/broker-protection-json-schema
Open

Generate broker-protection types from JSON schemas#2789
noisysocks wants to merge 3 commits into
mainfrom
randerson/broker-protection-json-schema

Conversation

@noisysocks

@noisysocks noisysocks commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Asana Task/Github Issue: N/A

Description

Stacked on #2778.

Adds JSON Schema types to PIR. Two reasons:

  • Docs eventually. Same as special-pages and web-compat, the native↔web contract lives in JSON Schema now, so we can generate HTML docs off the schemas later.
  • Precise types now. They generate broker-protection.ts, replacing the Record<string, any> / any typedefs scattered through the feature with real action types (PirAction, ExtractAction, ActionData, …).

How this was built

I used Claude Code's dynamic workflows for the audit: fan agents out over the native PIR implementations (Apple, Android, Windows) and the real broker JSON to work out what each action actually carries, cross-check every schema against both that and our executor, then run an adversarial pass to bin the false positives.

The run, as it looked in /workflows (31 agents, each phase fanning out in parallel):

broker-protection-schema-audit
│
├─ Ground truth
│  ├─ ground-truth:apple
│  ├─ ground-truth:android
│  └─ ground-truth:windows
├─ Schema audit
│  ├─ audit:action-union-and-dispatch
│  ├─ audit:extract-action
│  ├─ audit:extract-specs
│  ├─ audit:navigate-action
│  ├─ audit:click-action
│  ├─ audit:fillform-action
│  ├─ audit:expectation-condition
│  ├─ audit:scroll-action
│  ├─ audit:captcha-actions
│  ├─ audit:responses
│  ├─ audit:input-data-and-profiles
│  ├─ audit:retry-config
│  └─ audit:messages-envelope
├─ Verify
│  ├─ verify:extract-specs
│  ├─ verify:captcha-actions
│  ├─ verify:responses
│  └─ … one per area with findings
├─ Loose types
│  ├─ loose:actions
│  ├─ loose:captcha-services
│  ├─ loose:extractors+comparisons
│  └─ loose:core
└─ Synthesize
   └─ synthesize

Schema corrections applied (now reflect native reality)

Each one checked against the native implementations and the executor:

  • condition.jsonactions now required (native always sends it: 29/29 broker JSON, Android models it non-null).
  • input-data.json – added fetchedEmail + emailData (Apple sends both, execute.js resolves them via dataSource); clarified the dataSource defaults are fallbacks.
  • success-response.jsonnext typed as PirAction[] (was unknown[]), the follow-up actions the executor runs locally and that no native ActionResult model decodes; meta re-described as an action-specific bag (Android decodes extract's meta.userData), not "debug".
  • retry-config.jsoninterval + maxAttempts now required (the web retry helper dereferences both with no fallback); environmentenum: ["web"]; a web-only field, since no native model or broker JSON carries a per-action retry.
  • get-captcha-info.json / solve-captcha.json – fixed the captchaType description (real values are image/cloudFlareTurnstile, not the cited recaptcha/hcaptcha, which a broker-JSON census found 0 of); removed injectCaptchaHandler (only navigate reads it, never the captcha actions).
  • scroll.json – dropped dead dataSource (the handler never reads it and execute.js passes no data bag to scroll).
  • user-profile.json – added id, city, state, street, zip, birthYear, fullName, suffix (heavily template-referenced ProfileQuery keys); extracted-profile.json – added age, birthYear, profileUrl (FormElement type keys the fill-form reads).
  • click-element.jsonparent.profileMatch now required (selectRootElement throws if parent is present without it).

Testing Steps

  • cd injected && npm run build-types – regenerates broker-protection.ts with no diff.
  • npm run tsc-strict-core + npm run tsc – clean.
  • cd injected && npm run test-unit – broker-protection specs green (114).

Checklist

Please tick all that apply:

  • I have tested this change locally
  • I have tested this change locally in all supported browsers
  • This change will be visible to users
  • I have added automated tests that cover this change
  • I have ensured the change is gated by config
  • This change was covered by a ship review
  • This change was covered by a tech design
  • Any dependent config has been merged

Note

Medium Risk
Touches the native↔web PIR contract and action dispatch typing across many broker-protection paths; schema mismatches could surface at compile time or reject valid broker JSON if schemas are too strict.

Overview
Introduces a JSON Schema contract for broker-protection (PIR): per-action shapes, profile/extract specs, ActionData, and messaging envelopes (onActionReceived, actionCompleted, actionError). Schemas generate injected/src/types/broker-protection.ts, which becomes the source of truth for PirAction, discriminated action types, and native↔web message typing.

The executor and action handlers are rewired to those types: inline PirAction / extract typedefs are removed in favor of imports from the generated module; JSDoc on execute, actions, captcha code, and integration mocks now references concrete types like ExtractAction, UserProfile, and GetCaptchaInfoAction. BrokerProtection uses typed notify / subscribe instead of generic messaging helpers.

Small runtime/type-alignment tweaks: execute narrows ActionData per action with explicit casts; click choice evaluation guards empty choices and uses default === undefined; some captcha test helpers require an explicit selector. check-strict-core whitelists the generated TS file.

Reviewed by Cursor Bugbot for commit de9d056. Bugbot is set up for automated code reviews on this repo. Configure here.

@noisysocks noisysocks requested review from a team as code owners June 22, 2026 11:31
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

[Beta] Generated file diff

Time updated: Tue, 23 Jun 2026 05:11:03 GMT

Android
    - android/autofillImport.js
  • android/brokerProtection.js

File has changed

Apple
    - apple/contentScopeIsolated.js

File has changed

Integration
    - integration/contentScope.js

File has changed

Windows
    - windows/contentScope.js

File has changed

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Cursor assessed this PR as Medium Risk (only Low Risk is auto-approved).

This PR requires a manual review and approval from a member of one of the following teams:

  • @duckduckgo/content-scope-scripts-owners
  • @duckduckgo/apple-devs
  • @duckduckgo/android-devs
  • @duckduckgo/team-windows-development
  • @duckduckgo/extension-owners
  • @duckduckgo/config-aor
  • @duckduckgo/breakage-aor
  • @duckduckgo/breakage

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility & Security Review

Automation assessment for PR #2789 (feat(broker-protection): generate types from JSON schemas).

This PR is primarily a type-system and schema contract change: JSON Schema definitions under injected/src/messages/broker-protection/, generated TypeScript in injected/src/types/broker-protection.ts, and JSDoc @typedef wiring across broker-protection action files. It does not touch browser API shims, wrapper-utils.js, captured-globals.js, the message bridge, or platform entry points.


Web Compatibility Assessment

File Lines Severity Finding
injected/src/features/broker-protection/actions/click.js ~154 warning action.choices ?? [] changes runtime behavior: a conditional click with a missing choices array no longer throws during iteration; it silently falls through to default handling (or the "no default" error). Previously this would throw a TypeError. Low practical risk since actions originate from native, but it can mask misconfigured payloads.
injected/src/features/broker-protection/actions/click.js ~169 info action.default === undefined vs prior !('default' in action) is equivalent for JSON-native payloads. For JS objects with explicit default: undefined, the new check is actually safer (avoids 'elements' in undefined).
injected/src/features/broker-protection.js, execute.js, action files info Remaining diffs are JSDoc/type annotations, typed notify/subscribe wrappers (delegating to ContentFeature), and an exhaustive switch default branch. No browser API surface changes.
injected/src/messages/broker-protection/types/*.json info Schema tightening (required retry.interval/maxAttempts, typed successResponse.next, etc.) is compile-time contract documentation only — no runtime validation is added.

No API fidelity, prototype-chain, DOM-timing, or platform-specific compatibility regressions identified.


Security Assessment

File Lines Severity Finding
injected/src/features/broker-protection.js ~20–50, ~94 info Migration to this.notify() / this.subscribe() passes explicit object literals ({ error }, { result }) — no spreading of inbound params. Compliant with nativeData hygiene.
injected/src/messages/broker-protection/types/input-data.json info additionalProperties: true on ActionData documents the existing data() lookup-by-dataSource pattern. Trust boundary remains native → content script (not page scripts).
injected/src/features/broker-protection.js ~94–96 info onActionReceived subscribe callback still has no runtime JSON Schema validation. Pre-existing; schemas are type-generation only. Page scripts cannot inject actions without spoofing native messaging.
No changes to message-bridge trust boundaries, origin validation, captured globals, postMessage, or config gating.

No critical or high-severity security findings.


Risk Level

Low Risk — Schema/type-generation refactor within the broker-protection feature; one minor runtime behavior softening in conditional click evaluation; no injected API overrides or messaging security boundary changes.


Recommendations

  1. (warning) In evaluateChoices, consider returning an explicit error when action.choices is absent/empty on a choices-based click (rather than ?? []), to preserve the prior fail-fast behavior for malformed actions.
  2. (info) Add an integration test for a conditional click missing the choices array to lock in intended behavior.
  3. (info) Future hardening (out of scope here): runtime validation of onActionReceived payloads against the new schemas before execute().
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread injected/src/features/broker-protection/actions/click.js Outdated
@noisysocks noisysocks changed the title feat(broker-protection): generate types from JSON schemas broker-protection: generate types from JSON schemas Jun 22, 2026
@noisysocks noisysocks changed the title broker-protection: generate types from JSON schemas Generate broker-protection types from JSON schemas Jun 22, 2026
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Build Branch

Branch pr-releases/randerson/broker-protection-json-schema
Commit f634af3851
Updated June 23, 2026 at 5:10:18 AM UTC

Static preview entry points

QR codes (mobile preview)
Entry point QR code
Docs QR for docs preview
Static pages QR for static pages preview
Integration pages QR for integration pages preview

Integration commands

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#pr-releases/randerson/broker-protection-json-schema

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/randerson/broker-protection-json-schema")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/randerson/broker-protection-json-schema
git -C submodules/content-scope-scripts checkout origin/pr-releases/randerson/broker-protection-json-schema
Pin to exact commit

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#f634af385147a6a80025bc94ba14487897a8a3c1

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "f634af385147a6a80025bc94ba14487897a8a3c1")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/randerson/broker-protection-json-schema
git -C submodules/content-scope-scripts checkout f634af385147a6a80025bc94ba14487897a8a3c1

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility & Security Review (updated)

Re-reviewed after the latest push (62753046e), which addresses prior feedback on the evaluateChoices empty-array fallback.

Web Compatibility Assessment

File Lines Severity Finding
injected/src/features/broker-protection/actions/click.js 154–156 info The fail-fast throw in evaluateChoices is correct defensive coding. The caller already guards with action.choices?.length, so this path is unreachable for well-formed native payloads; execute() converts it to an ErrorResponse if triggered. No general web-compat impact — broker-protection only runs when native dispatches onActionReceived.
injected/src/features/broker-protection/actions/click.js 173 info Switching from !('default' in action) to action.default === undefined aligns with JSON semantics (null = skip, absent = error). No change for valid native payloads.
injected/src/messages/broker-protection/types/actions/click.json 5–38 info Schema documents elements/choices as mutually exclusive but does not enforce it via oneOf/not. Runtime already rejects top-level elements when choices is used (evaluateChoices line 150). Schemas are compile-time only (no runtime validation), so this is a contract-documentation gap, not a site-breakage risk.
(remainder of PR) info No changes to browser API shims, wrapper-utils.js, DDGProxy, prototype patching, viewport/meta manipulation, or load()-time hooks. DOM interactions (click, extract, fillForm) are pre-existing, native-orchestrated broker-removal behavior.

Security Assessment

File Lines Severity Finding
injected/src/features/broker-protection.js 20–50, 94 info this.messaging.notifythis.notify / this.subscribe uses the typed ContentFeature wrappers; underlying transport is unchanged. Outgoing payloads remain explicit field literals — no nativeData forwarding risk.
injected/src/messages/broker-protection/onActionReceived.subscribe.json 1–18 info additionalProperties: false on the subscription envelope is good. Trust boundary remains native → web; page scripts cannot inject actions without the platform messaging channel.
injected/src/messages/broker-protection/types/input-data.json 5, 16 info additionalProperties: true on ActionData / fetchedEmail is intentional (broker-specific keys, URL templating). Executor reads only known dataSource keys via Object.prototype.hasOwnProperty.call in execute.js — no new bypass surface introduced.
injected/src/features/broker-protection/actions/extract.js 68–71 info meta.userData in actionCompleted responses is pre-existing PII on the native channel (now documented in success-response.json). Intentional for debugging/matching; not a page-exfiltration vector since notifications go to native, not postMessage('*').
(remainder of PR) info No changes to captured-globals.js, message-bridge security checks, origin validation, shouldExemptMethod(), or postMessage usage. No eval()/Function() additions.

Risk Level

Low Risk — This PR is predominantly JSON-schema definitions, generated TypeScript types, and JSDoc alignment; runtime behavior changes are limited to a defensive invariant in evaluateChoices and a clearer default presence check, neither affecting general page loads.

Recommendations

  1. Resolved: Prior concern about action.choices ?? [] silently falling through to default handling — the fail-fast throw addresses this.
  2. Optional (schema hygiene): Add a oneOf to click.json requiring either elements or choices (not both) so the generated types match runtime invariants.
  3. Optional (schema hygiene): Consider additionalProperties: false on individual action schemas (extract.json, click.json, etc.) to catch typos at schema-validation time if runtime validation is added later.
  4. No action needed for API-surface fidelity, captured-globals hygiene, or message-bridge trust boundaries — this PR does not touch those subsystems.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread injected/src/features/broker-protection/actions/click.js
Comment thread injected/src/messages/broker-protection/types/actions/click.json
Comment thread injected/src/messages/broker-protection/types/input-data.json
@noisysocks noisysocks force-pushed the randerson/broker-extract-refactor branch from 93f4a7f to 04db024 Compare June 23, 2026 02:46
@noisysocks noisysocks force-pushed the randerson/broker-protection-json-schema branch from 6275304 to 7e2522e Compare June 23, 2026 02:46
@github-actions github-actions Bot added the semver-patch Bug fix / internal — no release needed label Jun 23, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Injected PR Evaluation: Web Compatibility & Security

PR: Generate broker-protection types from JSON schemas (#2789)
Scope: JSON schemas, generated broker-protection.ts, JSDoc alignment, two small runtime fixes in click.js, typed notify/subscribe wrappers.


Web Compatibility Assessment

File Lines Severity Finding
(none — API shims) No changes to wrapper-utils.js, DDGProxy, captured-globals.js, or browser API overrides. General web-compat risk from this PR is negligible.
injected/src/features/broker-protection/actions/click.js 173–175 info default presence check changed from 'default' in action to action.default === undefined. This is an intentional contract alignment: explicit default: undefined (or a polluted prototype key whose value is undefined) no longer falls through to 'elements' in undefined (TypeError). Covered by integration tests (conditional-clicks-hard-coded-undefined-default.json, null-default case).
injected/src/features/broker-protection/actions/click.js 154–156 info evaluateChoices now throws when choices is empty. Caller already gates on action.choices?.length, so this is defensive; uncaught throw surfaces as actionError via processActionAndNotify catch.
injected/src/features/broker-protection.js 92–97 info this.messaging.subscribethis.subscribe (typed alias). Behavior unchanged; same ContentFeature wrapper.
injected/src/features/broker-protection/actions/click.js 76–83 info (pre-existing) Uses native elem.click() rather than synthetic MouseEvent. Unchanged by this PR; acceptable for broker automation context.

Broker-protection DOM actions only run when the feature is remote-config enabled and native delivers onActionReceived; this PR does not broaden that surface.


Security Assessment

File Lines Severity Finding
injected/src/features/broker-protection.js 48–50 info Catch path uses e.toString() (uncaptured). Pre-existing pattern; low practical risk here because the handler is native-driven, not page-invoked, but a tampered Error.prototype.toString could affect the error string sent to native.
injected/src/features/broker-protection.js 17–50 info notify('actionCompleted' | 'actionError', …) passes explicit literal payloads — no object spread, no nativeData forwarding. ✓
injected/src/messages/broker-protection/onActionReceived.subscribe.json 4–16 info Top-level additionalProperties: false on subscription envelope and state. ✓
injected/src/messages/broker-protection/types/input-data.json 5 info additionalProperties: true on ActionData is intentional (broker-specific flat keys, email bundles). Trust boundary is native → injected, not hostile page → injected.
injected/src/types/broker-protection.ts 794–796 info SuccessResponse.meta allows arbitrary keys; extract attaches userData for native matching. PII over the native channel by design; unchanged behavior, now documented in schema/types.
(schemas) info JSON schemas are compile-time/typegen only — not runtime validators. Malformed native payloads are still handled by existing executor error paths, not rejected at the messaging layer.

No changes to message-bridge, origin validation, captured-globals.js, shouldExemptMethod(), or postMessage usage.


Risk Level

Low Risk — Schema/type-generation refactor with no API shims or messaging security-boundary changes; the only runtime logic edits are a tested default edge-case fix and a defensive guard in evaluateChoices.


Recommendations

  1. (info) Consider capturing Error/String in processActionAndNotify catch (broker-protection.js:50) in a follow-up strict-typing PR — consistent with other broker-protection hardening notes.
  2. (info) Optional schema hygiene: add additionalProperties: false to per-action schemas (click.json, extract.json, etc.) and oneOf mutual-exclusivity for elements vs choices — improves documentation; no runtime effect today.
  3. (done) Integration tests already cover conditional-click default semantics; no additional compat tests required for this diff.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Comment thread injected/src/features/broker-protection/actions/click.js
Comment thread injected/src/features/broker-protection/actions/click.js
} catch (e) {
this.log.error('unhandled exception: ', e);
return this.messaging.notify('actionError', { error: e.toString() });
return this.notify('actionError', { error: e.toString() });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security (info): e.toString() uses an uncaptured global. Low risk in this native-driven path, but worth aligning with captured Error/String in a future strict-typing pass so a page-tampered Error.prototype.toString cannot influence the native error payload.

@noisysocks noisysocks force-pushed the randerson/broker-extract-refactor branch from 04db024 to 292f471 Compare June 23, 2026 04:13
@noisysocks noisysocks force-pushed the randerson/broker-protection-json-schema branch from 7e2522e to 7d3c9e6 Compare June 23, 2026 04:13

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Injected PR Evaluation: Web Compatibility & Security

Re-reviewed after sync to 7d3c9e6a7 (fix(broker-protection): throw on empty choices in evaluateChoices), which lands the fail-fast invariant called out in prior automation feedback.

Scope: JSON schemas under injected/src/messages/broker-protection/, generated broker-protection.ts, JSDoc alignment across broker-protection action files, typed notify/subscribe wrappers, and two small click.js runtime contract fixes. No changes to wrapper-utils.js, captured-globals.js, message-bridge security, or general browser API shims.


Web Compatibility Assessment

File Lines Severity Finding
injected/src/features/broker-protection/actions/click.js 173–175 info 'default' in actionaction.default === undefined correctly treats an own default: undefined as “no default”. Integration tests (conditional-clicks-hard-coded-undefined-default.json, null-default) cover the intended semantics.
injected/src/features/broker-protection/actions/click.js 154–156 info Fail-fast throw on empty choices replaces the prior ?? [] soft-fallthrough. Unreachable via click()'s action.choices?.length guard; if triggered, execute() converts to ErrorResponse. Prior feedback fully addressed in 7d3c9e6a7.
injected/src/messages/broker-protection/types/actions/click.json 5–18 info Schema documents elements/choices mutual exclusivity but does not enforce it (oneOf/not). Runtime rejects top-level elements on the choices path; schemas are compile-time only today.
(remainder of PR) info No browser API overrides, prototype patches, or DOM timing changes. Broker-protection is feature-scoped and native-driven; general third-party site compat risk is negligible.

Security Assessment

File Lines Severity Finding
injected/src/features/broker-protection.js 48–50 info e.toString() in the outer catch uses an uncaptured global. Low risk (native-driven payloads, explicit { error } objects, no spreading), but align with captured Error/String in a future hardening pass.
injected/src/features/broker-protection/execute.js 74–80 info data() dynamic dataSource lookup via hasOwnProperty.call is pre-existing; extra ActionData keys are intentional (additionalProperties: true). Trust boundary remains native → injected.
injected/src/features/broker-protection.js 20–50 info this.messaging.notifythis.notify is a typed wrapper; outgoing payloads remain explicit ({ result }, { error }) with no nativeData forwarding.
injected/src/messages/broker-protection/*.json info New schemas document the wire contract but are not runtime-validated. Top-level notify/subscribe envelopes use additionalProperties: false; nested profile/data objects allow extra keys by design.
(remainder of PR) info No message-bridge, origin-validation, postMessage, eval, or captured-globals changes.

Risk Level

Low Risk — This is a broker-protection type/schema refactor with minimal runtime surface area; the only logic changes tighten click default/choices invariants on native-supplied configs, with no impact on general page API fidelity.


Recommendations

  1. (info) Consider adding oneOf/not to click.json if schemas will ever be used for runtime validation.
  2. (info) When broker-protection files enter strict-core, replace e.toString() with captured Error/String handling.
  3. (info) No additional web-compat or security blockers identified for merge.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

noisysocks added a commit that referenced this pull request Jun 23, 2026
The nested city/state extraction pushes apple-isolated
(contentScopeIsolated.js) to 860,496 bytes, 496 over the current
860,000 cap, failing the verify-artifacts size check.

Bump the cap by 10k to 870,000. This is the binding bundle for
broker-protection (the only one PIR is co-bundled into); the stacked
follow-ups (#2778 refactor, #2789 schema types) net smaller, so 870k
covers the whole stack with ~9.5k of headroom. Pinging code owners
per the size-test convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@noisysocks noisysocks force-pushed the randerson/broker-extract-refactor branch from 292f471 to b151165 Compare June 23, 2026 04:56
@noisysocks noisysocks force-pushed the randerson/broker-protection-json-schema branch from 7d3c9e6 to c8bdc63 Compare June 23, 2026 04:56

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Web Compatibility & Security Review

Web Compatibility Assessment

File Severity Finding
injected/src/features/broker-protection/actions/click.js (~L154–156) info New throw new Error('evaluateChoices requires a non-empty choices array') is only reachable if evaluateChoices is called directly with an empty choices array. The caller guards with action.choices?.length, so normal click flows are unchanged.
injected/src/features/broker-protection/actions/click.js (~L173) info !('default' in action)action.default === undefined is semantically equivalent for JSON payloads from native (including default: null skip semantics). No expected behavior change for broker configs.
injected/src/features/broker-protection.js, action modules info this.messaging.notify/subscribethis.notify/subscribe are typed ContentFeature wrappers that delegate to the same messaging transport. No API-surface or timing change.
All changed runtime files info No browser API overrides, prototype patches, wrapper-utils / DDGProxy changes, or viewport/meta manipulation. Broker-protection DOM automation only runs when the feature is enabled and native pushes onActionReceived.

Security Assessment

File Severity Finding
injected/src/messages/broker-protection/types/input-data.json, user-profile.json info additionalProperties: true on ActionData / UserProfile documents existing permissive contracts (broker-specific flat keys). Trust boundary remains native → injected; schemas are compile-time only and do not add runtime validation.
injected/src/features/broker-protection.js (~L20–50) info Outgoing actionCompleted / actionError notifications use explicit literal payloads ({ result }, { error }). No object spreading of inbound params; no nativeData leakage risk introduced.
injected/src/features/broker-protection/execute.js (~L77) info data() lookup uses Object.prototype.hasOwnProperty.call(data, source) — appropriate for dynamic key resolution on objects that may traverse the native boundary.
injected/src/messages/broker-protection/onActionReceived.subscribe.json info Subscription schema sets additionalProperties: false on the envelope and state object — good boundary documentation. Runtime validation still depends on the messaging transport, not these schemas.
PR overall info No changes to captured-globals.js, message-bridge security checks, shouldExemptMethod(), origin validation, or postMessage usage. No new eval/Function execution paths beyond pre-existing captcha handler serialization.

Risk Level

Low Risk — This PR is primarily JSON Schema definitions, generated TypeScript types, and JSDoc alignment. Runtime behavior is effectively unchanged; no injected privacy-shim or messaging-security surfaces are modified.

Recommendations

  1. (info) Consider a unit test for evaluateChoices with default: null to lock in the skip-without-error contract now codified in click.json.
  2. (info) If stricter native-payload validation is desired in future, schemas would need a runtime validator — current types-generator pipeline is compile-time only.
  3. (info) Pre-existing: SuccessResponse.meta can carry userData (PII) back to native on extract matches — unchanged by this PR, but worth noting for native-side logging policies.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

Base automatically changed from randerson/broker-extract-refactor to main June 23, 2026 05:06
noisysocks and others added 3 commits June 23, 2026 15:09
Adopt the existing injected messages->types pipeline for PIR/
broker-protection (as special-pages and web-compat already do), making
JSON Schema the single source of truth for the native<->web messaging
contract and the extract specs.

- Add injected/src/messages/broker-protection/ schemas: the three
  messages (onActionReceived, actionCompleted, actionError), the
  PirAction discriminated union (9 action arms), the extract profile/
  field specs, the input-data bundle, and the response shapes.
- Generate injected/src/types/broker-protection.ts and gate it in
  CORE_FILES.
- Replace the hand-written spec typedefs in actions/extract.js and the
  PirAction/Expectation typedefs in types.js with the generated types.
- Use the generated module augmentation: export BrokerProtection as a
  named export and route the onActionReceived handler + notifies through
  the typed this.subscribe/this.notify wrappers.
- Narrow the captcha/navigate helpers to their specific action arms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tract and drop loose types

Audit the generated schemas against the native PIR models (Windows/Apple/
Android) and our web executor, and adjust them to describe what the executor
actually supports:

- condition.actions, retry.{interval,maxAttempts} and
  clickElement.parent.profileMatch are now required; retry.environment is
  constrained to "web"
- successResponse.next is typed as the PirAction union
- ActionData gains fetchedEmail/emailData; UserProfile and ExtractedProfile
  gain the flat keys the URL templater and form-fill actually read
- captcha actions drop injectCaptchaHandler (navigate-only) and document the
  captchaType values the executor handles
- drop fields the executor ignores: scroll.dataSource, click.failSilently,
  the extract profile.email spec

Replace the remaining Record<string, any>/any typedefs across the feature with
the generated action/data types, and alias type imports via top-of-file
@typedef blocks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
evaluateChoices is only ever called from click() inside an
`if (action.choices?.length)` guard, so reaching it with an absent or
empty `choices` array is a broken invariant. Replace the `?? []`
fallback (which silently iterated nothing and fell through to default
handling) with a fail-fast throw; execute() already converts it to an
ErrorResponse. Addresses Cursor review feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@noisysocks noisysocks force-pushed the randerson/broker-protection-json-schema branch from c8bdc63 to de9d056 Compare June 23, 2026 05:09

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Injected PR Evaluation: Web Compatibility & Security

Reviewed the full diff (57790702…de9d056c). This PR introduces JSON Schema definitions and generated TypeScript types for broker-protection messaging, replaces hand-written JSDoc typedefs with imports from the generated types, and makes two small runtime tweaks (this.notify/this.subscribe wrappers, evaluateChoices guard).


Web Compatibility Assessment

File Lines Severity Finding
(entire PR) info No browser API overrides, prototype patches, or wrapper/DDGProxy changes. Web-compat risk from injected script tampering detection is unchanged.
injected/src/features/broker-protection.js 20–50, 94 info this.messaging.notifythis.notify and this.messaging.subscribethis.subscribe are thin ContentFeature wrappers that delegate to the same underlying transport. No messaging semantics change.
injected/src/features/broker-protection/actions/*.js various info Action handlers (click, extract, fill-form, navigate, captcha, etc.) receive JSDoc type tightening only; DOM interaction logic is unchanged.
injected/src/features/broker-protection/actions/click.js 154–156 info New throw on empty choices is unreachable from click() because the caller already gates on action.choices?.length. No production behavior change; aids TS narrowing.
injected/src/features/broker-protection/actions/click.js 173 info action.default === undefined vs prior 'default' in action is equivalent when the property is absent. Explicit default: undefined now returns the "no default provided" error slightly earlier — still an error path, not a silent success.
injected/src/features/broker-protection/execute.js 673–720 info Exhaustive switch with never cast in the default branch is a type-safety refactor; runtime error message for unknown actionType is unchanged.

Security Assessment

File Lines Severity Finding
(entire PR) info No changes to captured-globals.js, wrapper-utils.js, message-bridge.js, shouldExemptMethod(), or platform entry points.
injected/src/features/broker-protection.js 20–50 info Outbound notify('actionCompleted' | 'actionError', …) passes explicit { result } / { error } literals — no spreading of inbound params, so nativeData cannot be forwarded back to native.
injected/src/messages/broker-protection/onActionReceived.subscribe.json 1–18 info Subscribe envelope uses additionalProperties: false on the top-level and state object — good boundary typing. Schemas are used for type generation, not runtime validation (same trust model as other features: native is the sender).
injected/src/messages/broker-protection/types/input-data.json 8 info additionalProperties: true on ActionData preserves flexibility for broker-specific data keys. Matches pre-existing executor behavior; data still originates from native, not page scripts.
injected/src/messages/broker-protection/types/actions/*.json various info Per-action schemas omit additionalProperties: false, so extra fields are schema-valid. Executor ignores unknown keys today; acceptable given native is the trusted producer.
injected/src/messages/broker-protection/types/action.json 13–15 info Recursive oneOf for nested actions/next arrays (condition/expectation) is native-controlled depth — not a new page-controlled attack surface.

Risk Level

Low Risk — This is a schema/type-generation refactor with equivalent messaging calls and no injected API surface changes; the only runtime logic delta is a defensive invariant in evaluateChoices that is unreachable from the public click() entry point.


Recommendations

  1. (info) Consider expressing the elements XOR choices constraint in click.json via oneOf so the schema matches the executor's mutual-exclusivity check — improves contract accuracy for native consumers, not a security fix.
  2. (info) If runtime payload validation is desired for onActionReceived, that would be a separate follow-up in the messaging layer; this PR correctly scopes schemas to compile-time type safety.
  3. (info) No additional web-compat or security tests are required for this diff; existing broker-protection integration tests cover the executor paths.
Open in Web View Automation 

Sent by Cursor Automation: Web compat and sec

}

if (!action.choices?.length) {
throw new Error('evaluateChoices requires a non-empty `choices` array');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info (web compat): This throw is unreachable from click() because the caller already checks action.choices?.length before calling evaluateChoices. It does not change production behavior — any thrown error would still be caught by processActionAndNotify and emitted as actionError, but that path cannot be hit today.

try {
if (!action) {
return this.messaging.notify('actionError', { error: 'No action found.' });
return this.notify('actionError', { error: 'No action found.' });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info (security): Switching to this.notify() is equivalent to the prior this.messaging.notify() call (thin ContentFeature wrapper). Outbound params remain explicit literals { result } / { error } with no spreading — nativeData forwarding risk is unchanged (none).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver-patch Bug fix / internal — no release needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant