You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: session-scoped echo protection for parameterized recorded inputs (#2013)
* feat: session-scoped echo protection for parameterized recorded inputs
Extends ADR 0017's fill-step-scoped guarantee to the whole recording
session (#1398). After #1349, a later read-only action (`wait`, `is`,
`get`) can independently observe and record an app-rendered echo of an
already-parameterized `fill --record-as` value in its own result or
target-v1 identity evidence, re-leaking the literal even though the
originating fill was protected.
- SessionState gains a small, ephemeral, never-serialized
literal->placeholder registry populated only from explicit
`--record-as` pairs, owned by session-action-recorder.ts.
- Result/event payload fields get content-aware substring redaction
(reusing the fill boundary's recursive scrub) for every literal
registered so far in the session, longest-literal-first.
- target-v1/targets-v1 identity evidence is never silently
text-substituted while still claiming a trustworthy identity (replay
compares against the live tree, which re-renders the real value).
A landmark-mode (wait) echo is dropped to no annotation, exactly like
#1349's existing identity-empty case, so an echoing landmark can no
longer serve as an ADR 0016 destination guard. Action-mode evidence
(get/is/mutating actions) redacts the label and downgrades
verification to "unverifiable" instead, since ADR 0012/0016 forbid
dropping required identity evidence.
- Amends ADR 0017 (new mechanism), ADR 0012 (#1349/writer-invariant
cross-references), and ADR 0016 (destination guard cross-reference).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RarRVX34ZW25TJejBZJ2Ui
* fix: placeholder-safe single-pass multi-literal redaction
Addresses review feedback on #2013: sequential single-literal
replacement (register somethinglong -> ${ABC}, then ABC -> ${OTHER})
could rewrite a placeholder produced by an earlier pass, corrupting it
to ${${OTHER}}.
Replaces the per-pair sequential loop with one placeholder-safe
left-to-right multi-literal pass (parameterizeAgainstLiteralMap): it
never re-scans text it has already emitted, so no literal can be
matched inside another pair's placeholder token in either direction.
A registered literal is matched before checking for an existing
placeholder token, so a value that itself happens to look like
${SOMETHING} is still redacted correctly. The scan uses a sticky regex
instead of slicing per character, and literal pairs are sorted once
per payload/evidence walk instead of once per string leaf.
parameterizeRecordedFillPayload/parameterizeBackendOutput are
generalized to take injected leaf-transform/carries callbacks so the
single-pair fill-boundary path (with its existing whitespace-collapse
behavior) and the new multi-pair session-wide path share one
structural traversal.
Adds regression coverage for both result payloads and action-mode
target evidence, plus the placeholder-shaped-literal edge case.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RarRVX34ZW25TJejBZJ2Ui
* fix: unexport parameterizeAgainstLiteralMap (CI: fallow dead-code gate)
Only used internally within this file (by parameterizeRecordedResultEcho
and parameterizeTargetEvidenceEcho); the export had no consumer outside
the module, which the fallow audit correctly flags as dead code.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RarRVX34ZW25TJejBZJ2Ui
---------
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments