Generate broker-protection types from JSON schemas#2789
Conversation
[Beta] Generated file diffTime updated: Tue, 23 Jun 2026 05:11:03 GMT Android
File has changed AppleFile has changed IntegrationFile has changed WindowsFile has changed |
|
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 & 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 ininjected/src/types/broker-protection.ts, and JSDoc@typedefwiring 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 missingchoicesarray no longer throws during iteration; it silently falls through todefaulthandling (or the "no default" error). Previously this would throw aTypeError. 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 === undefinedvs prior!('default' in action)is equivalent for JSON-native payloads. For JS objects with explicitdefault: 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/subscribewrappers (delegating toContentFeature), and an exhaustiveswitchdefault branch. No browser API surface changes.injected/src/messages/broker-protection/types/*.json— info Schema tightening (required retry.interval/maxAttempts, typedsuccessResponse.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 withnativeDatahygiene.injected/src/messages/broker-protection/types/input-data.json— info additionalProperties: trueonActionDatadocuments the existingdata()lookup-by-dataSourcepattern. Trust boundary remains native → content script (not page scripts).injected/src/features/broker-protection.js~94–96 info onActionReceivedsubscribe 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
- (warning) In
evaluateChoices, consider returning an explicit error whenaction.choicesis absent/empty on a choices-based click (rather than?? []), to preserve the prior fail-fast behavior for malformed actions.- (info) Add an integration test for a conditional click missing the
choicesarray to lock in intended behavior.- (info) Future hardening (out of scope here): runtime validation of
onActionReceivedpayloads against the new schemas beforeexecute().Sent by Cursor Automation: Web compat and sec
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/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-schemaPin to exact commitnpm (Android / Extension): 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 |
There was a problem hiding this comment.
Stale comment
Web Compatibility & Security Review (updated)
Re-reviewed after the latest push (
62753046e), which addresses prior feedback on theevaluateChoicesempty-array fallback.Web Compatibility Assessment
File Lines Severity Finding injected/src/features/broker-protection/actions/click.js154–156 info The fail-fast throw in evaluateChoicesis correct defensive coding. The caller already guards withaction.choices?.length, so this path is unreachable for well-formed native payloads;execute()converts it to anErrorResponseif triggered. No general web-compat impact — broker-protection only runs when native dispatchesonActionReceived.injected/src/features/broker-protection/actions/click.js173 info Switching from !('default' in action)toaction.default === undefinedaligns with JSON semantics (null= skip, absent = error). No change for valid native payloads.injected/src/messages/broker-protection/types/actions/click.json5–38 info Schema documents elements/choicesas mutually exclusive but does not enforce it viaoneOf/not. Runtime already rejects top-levelelementswhenchoicesis used (evaluateChoicesline 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, orload()-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.js20–50, 94 info this.messaging.notify→this.notify/this.subscribeuses the typedContentFeaturewrappers; underlying transport is unchanged. Outgoing payloads remain explicit field literals — nonativeDataforwarding risk.injected/src/messages/broker-protection/onActionReceived.subscribe.json1–18 info additionalProperties: falseon 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.json5, 16 info additionalProperties: trueonActionData/fetchedEmailis intentional (broker-specific keys, URL templating). Executor reads only knowndataSourcekeys viaObject.prototype.hasOwnProperty.callinexecute.js— no new bypass surface introduced.injected/src/features/broker-protection/actions/extract.js68–71 info meta.userDatainactionCompletedresponses is pre-existing PII on the native channel (now documented insuccess-response.json). Intentional for debugging/matching; not a page-exfiltration vector since notifications go to native, notpostMessage('*').(remainder of PR) — info No changes to captured-globals.js, message-bridge security checks, origin validation,shouldExemptMethod(), orpostMessageusage. Noeval()/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
evaluateChoicesand a clearerdefaultpresence check, neither affecting general page loads.Recommendations
- Resolved: Prior concern about
action.choices ?? []silently falling through todefaulthandling — the fail-fast throw addresses this.- Optional (schema hygiene): Add a
oneOftoclick.jsonrequiring eitherelementsorchoices(not both) so the generated types match runtime invariants.- Optional (schema hygiene): Consider
additionalProperties: falseon individual action schemas (extract.json,click.json, etc.) to catch typos at schema-validation time if runtime validation is added later.- No action needed for API-surface fidelity, captured-globals hygiene, or message-bridge trust boundaries — this PR does not touch those subsystems.
Sent by Cursor Automation: Web compat and sec
93f4a7f to
04db024
Compare
6275304 to
7e2522e
Compare
There was a problem hiding this comment.
Stale comment
Injected PR Evaluation: Web Compatibility & Security
PR: Generate broker-protection types from JSON schemas (#2789)
Scope: JSON schemas, generatedbroker-protection.ts, JSDoc alignment, two small runtime fixes inclick.js, typednotify/subscribewrappers.
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.js173–175 info defaultpresence check changed from'default' in actiontoaction.default === undefined. This is an intentional contract alignment: explicitdefault: undefined(or a polluted prototype key whose value isundefined) 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.js154–156 info evaluateChoicesnow throws whenchoicesis empty. Caller already gates onaction.choices?.length, so this is defensive; uncaught throw surfaces asactionErrorviaprocessActionAndNotifycatch.injected/src/features/broker-protection.js92–97 info this.messaging.subscribe→this.subscribe(typed alias). Behavior unchanged; sameContentFeaturewrapper.injected/src/features/broker-protection/actions/click.js76–83 info (pre-existing) Uses native elem.click()rather than syntheticMouseEvent. 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.js48–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 tamperedError.prototype.toStringcould affect the error string sent to native.injected/src/features/broker-protection.js17–50 info notify('actionCompleted' | 'actionError', …)passes explicit literal payloads — no object spread, nonativeDataforwarding. ✓injected/src/messages/broker-protection/onActionReceived.subscribe.json4–16 info Top-level additionalProperties: falseon subscription envelope andstate. ✓injected/src/messages/broker-protection/types/input-data.json5 info additionalProperties: trueonActionDatais intentional (broker-specific flat keys, email bundles). Trust boundary is native → injected, not hostile page → injected.injected/src/types/broker-protection.ts794–796 info SuccessResponse.metaallows arbitrary keys; extract attachesuserDatafor 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(), orpostMessageusage.
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
defaultedge-case fix and a defensive guard inevaluateChoices.
Recommendations
- (info) Consider capturing
Error/StringinprocessActionAndNotifycatch (broker-protection.js:50) in a follow-up strict-typing PR — consistent with other broker-protection hardening notes.- (info) Optional schema hygiene: add
additionalProperties: falseto per-action schemas (click.json,extract.json, etc.) andoneOfmutual-exclusivity forelementsvschoices— improves documentation; no runtime effect today.- (done) Integration tests already cover conditional-click
defaultsemantics; no additional compat tests required for this diff.Sent by Cursor Automation: Web compat and sec
| } catch (e) { | ||
| this.log.error('unhandled exception: ', e); | ||
| return this.messaging.notify('actionError', { error: e.toString() }); | ||
| return this.notify('actionError', { error: e.toString() }); |
There was a problem hiding this comment.
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.
04db024 to
292f471
Compare
7e2522e to
7d3c9e6
Compare
There was a problem hiding this comment.
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/, generatedbroker-protection.ts, JSDoc alignment across broker-protection action files, typednotify/subscribewrappers, and two smallclick.jsruntime contract fixes. No changes towrapper-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.js173–175 info 'default' in action→action.default === undefinedcorrectly treats an owndefault: undefinedas “no default”. Integration tests (conditional-clicks-hard-coded-undefined-default.json, null-default) cover the intended semantics.injected/src/features/broker-protection/actions/click.js154–156 info Fail-fast throw on empty choicesreplaces the prior?? []soft-fallthrough. Unreachable viaclick()'saction.choices?.lengthguard; if triggered,execute()converts toErrorResponse. Prior feedback fully addressed in7d3c9e6a7.injected/src/messages/broker-protection/types/actions/click.json5–18 info Schema documents elements/choicesmutual exclusivity but does not enforce it (oneOf/not). Runtime rejects top-levelelementson 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.js48–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 capturedError/Stringin a future hardening pass.injected/src/features/broker-protection/execute.js74–80 info data()dynamicdataSourcelookup viahasOwnProperty.callis pre-existing; extraActionDatakeys are intentional (additionalProperties: true). Trust boundary remains native → injected.injected/src/features/broker-protection.js20–50 info this.messaging.notify→this.notifyis a typed wrapper; outgoing payloads remain explicit ({ result },{ error }) with nonativeDataforwarding.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, orcaptured-globalschanges.
Risk Level
Low Risk — This is a broker-protection type/schema refactor with minimal runtime surface area; the only logic changes tighten
clickdefault/choices invariants on native-supplied configs, with no impact on general page API fidelity.
Recommendations
- (info) Consider adding
oneOf/nottoclick.jsonif schemas will ever be used for runtime validation.- (info) When broker-protection files enter strict-core, replace
e.toString()with capturedError/Stringhandling.- (info) No additional web-compat or security blockers identified for merge.
Sent by Cursor Automation: Web compat and sec
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>
292f471 to
b151165
Compare
7d3c9e6 to
c8bdc63
Compare
There was a problem hiding this comment.
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 ifevaluateChoicesis called directly with an emptychoicesarray. The caller guards withaction.choices?.length, so normal click flows are unchanged.injected/src/features/broker-protection/actions/click.js(~L173)info !('default' in action)→action.default === undefinedis semantically equivalent for JSON payloads from native (includingdefault: nullskip semantics). No expected behavior change for broker configs.injected/src/features/broker-protection.js, action modulesinfo this.messaging.notify/subscribe→this.notify/subscribeare typedContentFeaturewrappers 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/DDGProxychanges, or viewport/meta manipulation. Broker-protection DOM automation only runs when the feature is enabled and native pushesonActionReceived.Security Assessment
File Severity Finding injected/src/messages/broker-protection/types/input-data.json,user-profile.jsoninfo additionalProperties: trueonActionData/UserProfiledocuments 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/actionErrornotifications use explicit literal payloads ({ result },{ error }). No object spreading of inbound params; nonativeDataleakage risk introduced.injected/src/features/broker-protection/execute.js(~L77)info data()lookup usesObject.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.jsoninfo Subscription schema sets additionalProperties: falseon the envelope andstateobject — 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, orpostMessageusage. No neweval/Functionexecution 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
- (info) Consider a unit test for
evaluateChoiceswithdefault: nullto lock in the skip-without-error contract now codified inclick.json.- (info) If stricter native-payload validation is desired in future, schemas would need a runtime validator — current
types-generatorpipeline is compile-time only.- (info) Pre-existing:
SuccessResponse.metacan carryuserData(PII) back to native on extract matches — unchanged by this PR, but worth noting for native-side logging policies.Sent by Cursor Automation: Web compat and sec
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>
c8bdc63 to
de9d056
Compare
There was a problem hiding this comment.
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.notify → this.notify and this.messaging.subscribe → this.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
- (info) Consider expressing the
elementsXORchoicesconstraint inclick.jsonviaoneOfso the schema matches the executor's mutual-exclusivity check — improves contract accuracy for native consumers, not a security fix. - (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. - (info) No additional web-compat or security tests are required for this diff; existing broker-protection integration tests cover the executor paths.
Sent by Cursor Automation: Web compat and sec
| } | ||
|
|
||
| if (!action.choices?.length) { | ||
| throw new Error('evaluateChoices requires a non-empty `choices` array'); |
There was a problem hiding this comment.
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.' }); |
There was a problem hiding this comment.
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).


Asana Task/Github Issue: N/A
Description
Stacked on #2778.
Adds JSON Schema types to PIR. Two reasons:
broker-protection.ts, replacing theRecord<string, any>/anytypedefs 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):Schema corrections applied (now reflect native reality)
Each one checked against the native implementations and the executor:
condition.json–actionsnow required (native always sends it: 29/29 broker JSON, Android models it non-null).input-data.json– addedfetchedEmail+emailData(Apple sends both,execute.jsresolves them viadataSource); clarified thedataSourcedefaults are fallbacks.success-response.json–nexttyped asPirAction[](wasunknown[]), the follow-up actions the executor runs locally and that no nativeActionResultmodel decodes;metare-described as an action-specific bag (Android decodesextract'smeta.userData), not "debug".retry-config.json–interval+maxAttemptsnow required (the web retry helper dereferences both with no fallback);environment→enum: ["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 thecaptchaTypedescription (real values areimage/cloudFlareTurnstile, not the citedrecaptcha/hcaptcha, which a broker-JSON census found 0 of); removedinjectCaptchaHandler(onlynavigatereads it, never the captcha actions).scroll.json– dropped deaddataSource(the handler never reads it andexecute.jspasses no data bag to scroll).user-profile.json– addedid,city,state,street,zip,birthYear,fullName,suffix(heavily template-referenced ProfileQuery keys);extracted-profile.json– addedage,birthYear,profileUrl(FormElementtypekeys the fill-form reads).click-element.json–parent.profileMatchnow required (selectRootElementthrows ifparentis present without it).Testing Steps
cd injected && npm run build-types– regeneratesbroker-protection.tswith 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:
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 generateinjected/src/types/broker-protection.ts, which becomes the source of truth forPirAction, 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 onexecute, actions, captcha code, and integration mocks now references concrete types likeExtractAction,UserProfile, andGetCaptchaInfoAction.BrokerProtectionuses typednotify/subscribeinstead of generic messaging helpers.Small runtime/type-alignment tweaks:
executenarrowsActionDataper action with explicit casts; click choice evaluation guards emptychoicesand usesdefault === undefined; some captcha test helpers require an explicitselector.check-strict-corewhitelists the generated TS file.Reviewed by Cursor Bugbot for commit de9d056. Bugbot is set up for automated code reviews on this repo. Configure here.