Fast-follow to #596 (hide caller data from the model) and #595 (navigation/Tabstack destination allowlist). #596 is a strict improvement and safe to merge as-is; these are the residuals it leaves open for caller data, tracked here so they don't slip.
1. Snapshot re-injection (the main one)
#596 keeps the caller-data value out of the prompt and the fill_user_data result/events. But after a successful fill the value lives in element.value, and the next page snapshot serializes it straight back into the model context — packages/core/src/browser/ariaTree/ariaSnapshot.ts:256-257 exposes element.value for all inputs/textareas except a narrow credential list (password/cc-*/OTP). So a placed membership code / email / address / phone re-enters context on the following turn. This makes the "the model cannot leak what it never sees" property hold for the prompt/tool-result but not end-to-end.
Fix: redact known caller-data values from the snapshot string node-side (in WebAgent.addPageSnapshot, before compress), reusing a collectSensitiveValues(data) set. Robust to ARIA-ref churn (refs are regenerated every snapshot, so per-ref masking is not viable) and to React reconciliation, since it operates on the serialized string. Apply the same redaction to the extract tool's getMarkdown, which is the same re-injection class.
Known residual to document: sub-threshold-length values (very short secrets) won't be redacted.
2. Tighten the fill_user_data gate (pre-existing weakness, inherited)
fill_user_data reuses assessFill, which exempts operational fields (search boxes, comboboxes, spinbuttons, sliders) from the host-trust check. So a caller secret can be placed into an operational field on an untrusted host (reachable via a link click, which #595 does not gate), where the page's own JS can read element.value; and because it records operationalRefs, the submit gate then allows submitting it to that same host. This is not introduced by #596 — the normal fill tool has the same exemption — but caller data should not inherit the search-box carve-out.
Fix: gate fill_user_data more strictly than assessFill — allow only when the page host is trusted (start host + trusted_hostnames; unsafe_mode overriding), with no operational exemption, and do not add these refs to operationalRefs.
Guardrail
Do not report the indirect-prompt-injection → data-exfiltration finding as closed until item 1 lands: #595 + #596 substantially narrow it, but the snapshot re-injection path remains until the redaction is in.
Related / separate
Fast-follow to #596 (hide caller data from the model) and #595 (navigation/Tabstack destination allowlist). #596 is a strict improvement and safe to merge as-is; these are the residuals it leaves open for caller
data, tracked here so they don't slip.1. Snapshot re-injection (the main one)
#596keeps the caller-data value out of the prompt and thefill_user_dataresult/events. But after a successful fill the value lives inelement.value, and the next page snapshot serializes it straight back into the model context —packages/core/src/browser/ariaTree/ariaSnapshot.ts:256-257exposeselement.valuefor all inputs/textareas except a narrow credential list (password/cc-*/OTP). So a placed membership code / email / address / phone re-enters context on the following turn. This makes the "the model cannot leak what it never sees" property hold for the prompt/tool-result but not end-to-end.Fix: redact known caller-data values from the snapshot string node-side (in
WebAgent.addPageSnapshot, beforecompress), reusing acollectSensitiveValues(data)set. Robust to ARIA-ref churn (refs are regenerated every snapshot, so per-ref masking is not viable) and to React reconciliation, since it operates on the serialized string. Apply the same redaction to theextracttool'sgetMarkdown, which is the same re-injection class.Known residual to document: sub-threshold-length values (very short secrets) won't be redacted.
2. Tighten the
fill_user_datagate (pre-existing weakness, inherited)fill_user_datareusesassessFill, which exempts operational fields (search boxes, comboboxes, spinbuttons, sliders) from the host-trust check. So a caller secret can be placed into an operational field on an untrusted host (reachable via a linkclick, which#595does not gate), where the page's own JS can readelement.value; and because it recordsoperationalRefs, the submit gate then allows submitting it to that same host. This is not introduced by #596 — the normalfilltool has the same exemption — but caller data should not inherit the search-box carve-out.Fix: gate
fill_user_datamore strictly thanassessFill— allow only when the page host is trusted (start host +trusted_hostnames;unsafe_modeoverriding), with no operational exemption, and do not add these refs tooperationalRefs.Guardrail
Do not report the indirect-prompt-injection → data-exfiltration finding as closed until item 1 lands: #595 + #596 substantially narrow it, but the snapshot re-injection path remains until the redaction is in.
Related / separate
billing.card) — TAB-1104: hide caller data from the model (fill by key at the sink) #596 resolvesdata[key]flat; minor enhancement.