Skip to content

fix(responses): redact terminal reset-replacement errors - #6616

Merged
lidge-jun merged 11 commits into
lidge-jun:devfrom
luvs01:fix/reset-replacement-error-redaction
Oct 5, 2026
Merged

lidge-jun merged 11 commits into
lidge-jun:devfrom
luvs01:fix/reset-replacement-error-redaction

Conversation

@luvs01

@luvs01 luvs01 commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

When a connection-reset replacement (from #6555), or another send marked non-replayable, gets a real upstream error back, the generic/native Responses dispatch path returns it before the normal client error projection runs. Upstream diagnostic text on that path could echo the selected credential back to the client.

This PR makes that terminal path forward no upstream body text or headers. The client receives:

  • the real HTTP status and the non-replayable marker, so terminal and send-budget semantics are unchanged (no extra send, rebuild, or combo hop; the replacement grant stays spent);
  • an error type taken only from a closed allowlist (invalid_request_error, rate_limit_error, …), otherwise upstream_error, plus code when it exactly matches a closed allowlist of machine codes (context_length_exceeded, rate_limit_exceeded, …);
  • x-should-retry: false when upstream sent exactly that;
  • a fixed message: Provider error <status>: upstream diagnostic withheld for a non-replayable failure.

Synthetic replay refusals and ordinary (unmarked) error responses keep their existing delivery owners. The shared credential mask in terminal-error-redaction.ts is unchanged from dev. Owner docs updated in place: structure/transports/responses.md and structure/transports/responses-failover.md.

Design note: an earlier version of this PR tried to keep the diagnostic and redact the credential from it. Independent security review found five successive bypasses of that approach (unresolved and semicolon-less HTML references, JSON short escapes, truncated UTF-8 percent sequences, Unicode lookalikes, HTML markup splitting, JSON number normalization). Withholding the text is the only option on this rare terminal path that can be shown complete.

Verification

Head: 0ceb2fbdd8f2eb76e6cbd1da74ed195667cdb295, merged with dev (merge-tree clean against the current dev tip).

  • No local tests were run, per owner instruction. Behavioral evidence is hosted CI at this exact head: Cross-platform CI 37336122295 (all four Linux test shards, gates, structure, storage, API usage, Docker, npm-global and keyring smokes, aggregate ci: success) and React Doctor 37336122076: success.
  • Static checks at this head (exit 0): bun run typecheck, bun scripts/file-size-ratchet.ts, bun run structure:check, bun run privacy:scan, git diff --check. Both structure docs stay at 600 lines.
  • Regression coverage, both adapters (openai-chat, openai-responses), in tests/responses/responses-translated-reset.test.ts: a hostile-diagnostic matrix covering every bypass shape found in review (plus plain echo, scalars, JSON keys, arrays, non-2xx SSE, malformed and over-nested JSON) asserting the exact generic body, two physical sends, the spent grant, and that the credential is absent; allowlisted type and code preservation; unapproved and credential-valued type/code rejection; header allowlist; OAuth replacement; combo terminal consumption; marker provenance; bodyless and cancelled reads. Two existing tests that asserted relayed upstream text on marked responses were updated to the new contract with their send/replay assertions unchanged (responses-console-go-upload-retry.test.ts, ws-ambiguous-resend.test.ts).
  • Independent security review (gpt-6.1-sol, static) at this head: APPROVE-WITH-NOTES, notes pre-existing and outside this PR: fix(responses): redact terminal reset-replacement errors #6616 (comment)

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults (independent review linked above, bound to this head).

Review readiness checklist

  • Required validation passed with exact commands and scope recorded.
  • Current dev ancestry rechecked before readiness.
  • All correct review findings resolved.
  • Ready for review.

Summary by CodeRabbit

  • Bug Fixes

    • Non-replayable upstream errors now show a generic, sanitized message instead of potentially exposing provider diagnostics. Responses retain their status and may include allowlisted error details and retry guidance.
    • Cancellation-related failures are identified clearly, while successful and replayable responses remain unchanged.
  • Documentation

    • Updated response-handling documentation to describe sanitized errors and preserved response status.

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 224c98bd-6304-4c2d-98e2-4a17e692af52
📥 Commits

Reviewing files that changed from the base of the PR and between 37189ca and 0ceb2fb.

📒 Files selected for processing (9)
  • src/server/responses/adapter-dispatch.ts
  • src/server/responses/non-replayable-error.ts
  • src/server/responses/passthrough-delivery.ts
  • structure/transports/responses-failover.md
  • structure/transports/responses.md
  • tests/helpers/responses-core-source.ts
  • tests/responses/responses-console-go-upload-retry.test.ts
  • tests/responses/responses-translated-reset.test.ts
  • tests/responses/ws-ambiguous-resend.test.ts
 ____________________________________________________________________________________________________________________________________________________
< There are no final decisions. No decision is cast in stone. Instead, consider each as being written in the sand at the beach, and plan for change. >
 ----------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Oct 5, 2026
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

luvs01 and others added 9 commits October 5, 2026 13:37
Terminal reset-replacement diagnostics could still carry the selected
credential in two forms the bounded mask missed: an unresolved named HTML
reference (which folds to an opaque placeholder of unknown decoded width,
e.g. &plus; or &fjlig;) and a JSON number/boolean/null whose text equals the
credential. Withhold a diagnostic whose fold contains the placeholder, and
redact matching scalars while unmatched scalars keep their types.

Regression cases cover both adapters in the existing confidentiality block.
The bounded credential matcher decodes JSON \u escapes, percent escapes,
lowercase-x numeric references and semicolon-terminated named references.
Other forms (JSON short escapes such as \/, uppercase-X or unterminated
numeric references, legacy named references without a semicolon) stayed
literal, so a credential spelled through them could not be matched. When
such syntax remains in the final matching view, withhold the diagnostic.
Unrelated query strings (a=1&b=2) are preserved.
…gnostics

Enumerating semicolon-less HTML references left gaps (a legacy &amp before
letters or "=", or a long run). Withhold the terminal diagnostic whenever an
'&' followed by a letter or '#', or a JSON short escape, remains in the final
matching view. Over-withholding is accepted on this terminal-only path.

Adds both-adapter coverage for malformed and over-nested JSON, array bodies,
non-2xx SSE, and dropped upstream headers; records the policy in the
responses structure doc.
…ding

A bounded decoder cannot be proven equivalent to every client decoder; review
found a truncated UTF-8 percent sequence that folded differently from
decodeURIComponent. On the terminal non-replayable projection, withhold any
diagnostic string containing percent, JSON-escape or HTML reference syntax
(checked before and after a lookalike-only fold with escape decoding off).
Plain and lookalike echoes are still masked in place.

Reverts the NAMED_ENTITY_PLACEHOLDER export, which no longer has a consumer.
Review found Unicode lookalikes (uppercase Greek alpha, ligatures,
zero-width splits) that the confusable fold maps differently from the
credential, leaving a visually recoverable credential. No fold can be proven
equivalent to a reader, so the terminal projection now withholds any
diagnostic string containing encoding syntax or a character outside printable
ASCII, and otherwise relies on exact replacement of the selected credential.
Five review rounds each found another way a selected credential survived
redaction while upstream diagnostic text was preserved (encodings, Unicode
lookalikes, HTML markup, JSON number normalization, marker overlap). No
bounded redaction can be proven complete against a client's decoders, so the
terminal non-replayable projection now forwards no upstream body text: it
keeps the real status, non-replayability, an allowlisted error type and
x-should-retry: false, and returns a fixed message. The shared credential
mask in terminal-error-redaction.ts returns to its dev form.

The confidentiality matrix covers both adapters with every hostile
diagnostic shape found in review and asserts the exact generic body,
two physical sends, and the spent replacement grant.
…ojection

The combo owner reformats a consumed child failure, so the exact generic body
is not the combo contract; assert the generic phrase survives, the upstream
diagnostic and credential do not, and the terminal stop keeps two sends.
@lidge-jun

Copy link
Copy Markdown
Owner

Independent security review — head e7e71c0a9d0886abbdc9f457583c3b50f91e1306

Verdict: APPROVE (independent gpt-6.1-sol reviewer, static source review, bound to this exact head).

Scope reviewed: the full origin/dev...e7e71c0a9d delta (non-replayable terminal projection in src/server/responses/non-replayable-error.ts, its two callers in adapter-dispatch.ts and passthrough-delivery.ts, structure docs, and the confidentiality/accounting tests).

Summary:

  • The terminal non-replayable projection no longer forwards any upstream body text or headers. The client receives the real status, the non-replayable marker, an error type from a closed allowlist (otherwise upstream_error), x-should-retry: false when upstream sent exactly that, and a fixed message.
  • Terminal semantics are unchanged: no extra send, rebuild, or combo hop; the replacement grant stays spent; synthetic replay refusals and ordinary (unmarked) responses keep their existing owners. Combo formatting restates only the sanitized projection, so no upstream diagnostic is disclosed there.
  • Bounded reads keep their 64 KiB and timeout limits, with cancellation. No new logging was added.
  • Withholding upstream diagnostics on this rare terminal path is an accepted product trade-off; no docs-site contract requires relaying them.

How it got here: earlier review rounds on this branch at 25307c6, 7afd69b, 3592c8e, 74aebe7 and 243a5bf each found another way a credential could survive while upstream text was preserved. Rather than keep extending the redactor, the design now withholds upstream text entirely on this path, and terminal-error-redaction.ts returns to its dev form.

No local tests were run during review (owner instruction); behavioral evidence is hosted CI at this head.

…ror code

Responses are marked non-replayable by more than reset replacements (WS
socket death, compaction, unsafe run-turn activity), so the withheld-text
message now says "for a non-replayable failure". A closed allowlist of
machine error codes (context_length_exceeded, rate_limit_exceeded, ...) is
kept alongside the allowlisted type so clients retain an actionable signal.

Updates the two hosted-CI failures that asserted relayed upstream text on
marked responses (console-go upload boundary, WS pool-mode replacement 400).
@lidge-jun

lidge-jun commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Independent security review — head 0ceb2fbdd8f2eb76e6cbd1da74ed195667cdb295

Verdict: APPROVE-WITH-NOTES (independent gpt-6.1-sol reviewer, static source review, bound to this exact head). Supersedes the approval at e7e71c0.

The one new commit makes the withheld-text message accurate for every origin that marks a response non-replayable, and keeps error.code only when it exactly matches a closed allowlist of machine codes. Review found that allowlist closed and exact-match, so no upstream-controlled or credential bytes can pass through it. Both delivery gates (adapter-dispatch.ts, passthrough-delivery.ts) project marked errors before ordinary formatting. The updated tests still assert the one-send, one-replacement, and no-second-account invariants.

No local tests were run during review (owner instruction). Hosted CI at this head is green: Cross-platform CI 37336122295 and React Doctor 37336122076.

@lidge-jun
lidge-jun marked this pull request as ready for review October 5, 2026 16:44
@lidge-jun

Copy link
Copy Markdown
Owner

Maintainer integration (lidge-jun, dev only) — 2026-10-05 bug triage round 2, lane B4.

  • Head: 0ceb2fb
  • CI at this head: PR CI 37336122295, 37336122076; every executed job passed.
  • Review: non-replayable terminal reset-replacement errors return no upstream text (status, allowlisted type/code, x-should-retry false, fixed message) after five review rounds showed masking cannot be proven complete; security review APPROVE-WITH-NOTES at this head; readiness ticked by maintainer with hosted-CI-only note.
  • No local tests were run, per owner instruction; hosted CI is the evidence.
  • scripts/ci/assert-mergeable-review.sh --maintainer-integration: OK.

@lidge-jun
lidge-jun merged commit 7e3e37a into lidge-jun:dev Oct 5, 2026
41 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants