fix(redaction): close 3 residual value-shape gaps flagged on #943's final review - #1136
Conversation
…inal review Ally's review at #943's merge head (b7620db, still open when the PR was squash-merged) flagged 2 Critical + 1 Important findings that were never addressed before merge: - Critical: `promotesTier2ToTier1` capped promotion to <=2-token keys, so three-token-plus credential field names (`stripe_webhook_secret`, `database_client_secret`) stayed on the Tier-2 value gate and leaked short values with no recognizable prefix. Promotion is now keyed on the trigger word being the *trailing* token, which covers these while the sentence-shaped Tier-2 collisions this PR exists to fix (`secret_fields_must_stay_redacted`, `no_secret_values_in_this_report`) are untouched — the trigger word isn't trailing in any of them. - Critical: `looksLikeCredentialValue`'s URL branch only inspected `search`, so an OAuth2 implicit-flow fragment (`#access_token=...`) crossed the approval display boundary in plaintext. Now re-runs the same credential-param test against the fragment. - Important: `hasOpaqueUrlPathSegment` flagged every path segment >=20 chars regardless of shape, re-blanking benign long evidence links (commit SHAs, UUIDs, descriptive slugs) — the exact over-redaction #943 exists to remove, relocated into the URL branch. Exempts bare-hex/UUID identifiers and hyphen/underscore-joined readable slugs; a Slack-style unbroken opaque token still redacts (regression-tested). Verification: 4 new tests, mutation-checked (stashed only redaction.ts, kept tests — all 3 new assertions fail against pre-fix code, nothing else regresses). Full sweep: redaction + low-trust-red-team-routes + issue-comment-redaction + agent-secret-redaction + log-redaction — 89/89 green. tsc --noEmit clean. Refs BLO-20810, #943.
|
🔗 Paperclip issue: BLO-20810 |
1 similar comment
|
🔗 Paperclip issue: BLO-20810 |
…token backstop Live confirmation during BLO-20810's census re-run (post-#943-merge): a currently-pending approval card (id 1d47ba22) has its `authoritative_state` field blanked to ***REDACTED***. That key is Tier 2 ("auth" is a substring of "authoritative" — the same collision class this whole chain exists to fix), and its value was a whitespace-free status slug that tripped the generic `withoutScheme.length >= MIN_OPAQUE_TOKEN_LENGTH` backstop, which had no shape check at all (unlike the URL path-segment backstop this PR already patched). Reuse `looksLikeReadableSlug` (hyphen/underscore-joined, each part <=12 chars) for the non-URL fallback too: a status/state slug survives, while an equally long *unbroken* opaque token (no separators) still redacts. Verification: 1 new test, mutation-checked (stashed only redaction.ts, kept tests — the new assertion fails against pre-fix code, 43/44 pass). Full sweep after restoring the fix: redaction + low-trust-red-team-routes + issue-comment-redaction + agent-secret-redaction + log-redaction — 90/90 green. tsc --noEmit clean.
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: b78bb2e
Critical Issues (0)
Important Issues (1)
- [gstack/review]
server/src/redaction.ts:303— The readable-slug exemption is an allowlist for any three separator-delimited components of 12 characters or fewer, not for verified human-readable words. Consequently, a long unknown credential such asa1b2c3d4-e5f6g7h8-i9j0k1l2under an ambiguous Tier-2 key (author,authoritative_state, orbase_urlpath) now returns false fromlooksLikeCredentialValueand reaches the approval display unredacted. This regresses the fallback that intentionally redacted unknown long opaque tokens.- Keep the generic length backstop fail-closed for arbitrary delimited values; narrow only identifier formats that can be reliably recognized (for example, SHA and UUID), or add a defensible lexical validation with regression cases for delimiter-chunked secrets.
Suggestions (0)
Strengths
- The fragment check correctly extends credential-parameter detection to OAuth implicit-flow URLs.
- The focused test coverage captures the previously missed trailing
secret/authfield names and identifier URL paths.
Recommended Action
- Fix the Important issue before merge.
- Add a regression case for an unknown delimiter-chunked opaque token under a Tier-2 key.
…ased #1136's review (head b78bb2e) found the slug exemption fails open on a delimiter-chunked opaque token: `a1b2c3d4-e5f6g7h8-i9j0k1l2` is three parts of <=12 chars, so a pure arity+length test read it as human-readable and it escaped the generic length backstop that is meant to fail closed on unrecognized long values. Under an ambiguous Tier-2 key (`author`, `authoritative_state`, a `base_url` path) it reached the approval display unredacted. Judge the parts lexically instead: a slug's parts are whole words or bare numbers, while letters and digits interleaved *within* one part is the signature of an opaque chunk. Also require >=2 word-shaped parts so an all-numeric chunking stays fail-closed. Keeps the exemption this issue exists for — status slugs (`pending_human_merge_review`) and issue-numbered evidence links (`blo-20810-...`) stay readable, on both the value branch and the URL path-segment branch that shares the predicate. 3 regression tests, each pairing the newly-redacted secret with the benign value the exemption protects, so they cannot pass by deleting the exemption. Mutation-checked: with only the predicate reverted, exactly these 3 fail and the other 44 still pass. Full redaction suites 94/94, tsc --noEmit clean. Refs BLO-20810.
|
@ally please re-review at head This addresses the sole Important finding from your review at What changed: Review focus:
3 regression tests, each pairing the newly-redacted secret with the benign value the exemption protects, so they cannot pass by deleting the exemption. Mutation-checked: with only the predicate reverted, exactly those 3 fail and the other 44 pass. Redaction suites 94/94, |
|
@ally please re-review at head This is a re-request, not a new ask — the marker posted at 2026-08-14T14:25:48Z named this same head and has gone unanswered for 10h22m. Evidence that it was lost rather than queued: delivery funnel clean over the window ( What changed since your 2026-08-08 review at
Review focus:
Evidence: 3 regression tests, each pairing the newly-redacted secret with the benign value the exemption protects, so they cannot pass by deleting the exemption. Mutation-checked — with only the predicate reverted and tests kept, exactly those 3 fail and the other 44 pass. Redaction suites 94/94, |
Thinking Path
Linked Issues or Issue Description
Refs: BLO-20810
Refs: #943 (the PR this follows up on — see review comment #943 (comment) for the source findings)
What Changed
promotesTier2ToTier1: promotion to Tier 1 (unconditional redaction) for the ambiguousauth/secretstems is no longer capped at 2 key-tokens. A key now promotes if it has <=2 tokens (unchanged, any position) or ifauth/secretis the trailing token regardless of total length —stripe_webhook_secret,database_client_secret,my_webhook_secretnow redact unconditionally. Sentence-shaped Tier-2 collisions (secret_fields_must_stay_redacted,no_secret_values_in_this_report) are untouched because the trigger word isn't trailing in either.looksLikeCredentialValue: the URL branch now also tests the URL fragment (#...) against the same credential-param regex used for the query string, so{ base_url: "https://client.example/callback#access_token=abc123def456" }redacts instead of leaking.hasOpaqueUrlPathSegment: a path segment >=20 chars is no longer opaque-by-length alone. Bare hex identifiers (git SHAs, 20–64 hex chars), canonical UUIDs, and hyphen/underscore-joined readable slugs (>=3 parts, each <=12 chars) are exempted; an unbroken mixed-case opaque token (the Slack-webhook-secret shape) still redacts.looksLikeCredentialValue's non-URL fallback: the same readable-slug exemption now also applies to the genericlength >= 20opaque-token backstop, not just the URL path-segment case — fixes a live false positive found during verification (see below).Verification
cd server && npx vitest run src/__tests__/redaction.test.ts— 44/44 (39 existing + 5 new).redaction.tsper-commit, kept the tests — exactly the new assertions for that commit fail against the pre-fix code, nothing else regresses.redaction.test.ts+low-trust-red-team-routes.test.ts+issue-comment-redaction.test.ts+agent-secret-redaction.test.ts+log-redaction.test.ts— 90/90 green.npx tsc --noEmit -p server— clean.adapterConfig.env.*.value, unaffected, correct). Negative control: 4 non-redacted trigger-matching fields, all*.secretIdreferences (correct, unchanged from baseline). Found live: approval1d47ba22(currently pending, created 2026-08-07T13:12Z — after fix(redaction): stop blanking approval fields on key-name substring collisions #943 merged) hadauthoritative_stateblanked by the exact collision class BLO-20810 exists to fix; that's commit 2 above, fixed and regression-tested.7fccfc20,ecce2ee6) still show***REDACTED***on their originally-blanked fields — expected: redaction happens at create time and the original value is unrecoverable.c03d7b97(the specific card named in BLO-20810's AC) was independently resubmitted with renamed fields as a workaround before this fix landed, and is nowapprovedwith no blanked fields — satisfies BLO-20810's fallback AC ("restated ... if the card is decided first").Risks
server/src/redaction.tsand its test file only — no route or schema changes.hyphen/underscore-joined, each part <=12 chars, now applied in two places) is a heuristic, not a proof: an attacker-chosen secret shaped likeword-word-wordwould now survive under a Tier-2 key. This mirrors the tradeoff fix(redaction): stop blanking approval fields on key-name substring collisions #943 already accepted for the length-based gate (a security allowlist based on shape, not entropy) and is scoped to Tier-2 keys only — every Tier-1 stem (apiKey,password,authorization, etc.) is unconditional and unaffected by this change.secret_value(trigger word first, not sentence-shaped) would stay Tier 2. No such example was found in the review history or the live census; flagging for reviewer awareness rather than solving in this PR.Model Used
Claude Sonnet 5 (
claude-sonnet-5[1m]), Anthropic — 1M context window, extended thinking mode, agentic tool use (file read/edit/bash) via Claude Code.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template