fix(dashboard): redact known-path listings segment-wise so deep paths survive - #8055
fix(dashboard): redact known-path listings segment-wise so deep paths survive#8055jeeshofone wants to merge 1 commit into
Conversation
Design Review (Fable 5, fork) — ✅ PASSDesign-level review of Design-Verdict: PASS Provenance-aware segment redaction at the call sites that prove path-ness is the right layer; the general matcher's whole-run amplification correctly stays intact. Suggestions
[DESIGN-REVIEWED] fe20f96 |
First Principles Review (Fable 5, fork) — ✅ PASSPremise-level review of All checks complete. The premise is verified in the base ( First-Principles-Verdict: PASS A measured defect (~79 files silently missing) fixed at its cause — provenance-aware boundary at path-typed call sites — covering every sibling display site, adding only one private helper. What this change shipsIntent: stop the file tree silently omitting real files whose deep slash-only paths the credential redactor swallows whole (#8042) — a FIX.
The general matcher is untouched, which is the correct level: weakening Watch
[FIRST-PRINCIPLES-REVIEWED] fe20f96 |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed |
05ef24c to
1d5f518
Compare
Advisory round — head
|
GPT 5.6 Review (fork) — 🔴 changes requested (blocking)Reviewed BLOCKING -- src/kiro_crew/dashboard/handlers/files.py:4609 -- Segment recombination exposes slash-bearing credentials |
1d5f518 to
487a424
Compare
GPT round — head
|
GPT round on
|
|
You asked for a maintainer pick, so here is an independent measurement against your head Your direction is the right oneWorth saying first, because it is the part the review lanes have not told you. The issue's wording does not settle whether the collapse causes over-redaction (distinct non-secrets share a mask, information lost) or under-redaction (a real secret missed because a sibling path already claimed the key). Those need different fixes. I settled it on
Under-redaction does not reproduce on
So The hole, measured against the shipped helperThese rows come from importing
Three things about this table:
Why your own guard cannot see thisThis is the part I think is genuinely hard to get from a review lane, and it is an easy mistake rather than an oversight.
The class at risk is the 40-character bare secret that A same-class site the diff leaves behindSeparate item, small. Still plain Why this is a pick and not a review nitA splitter cannot distinguish a Which is what makes it a genuine security-sensitivity trade rather than something a reviewer can just tell you to correct: the approach buys exact path fidelity and pays in bare-secret coverage on the values it special-cases. Whether that trade is the right one is the call you asked for, and it is not mine to make. I am offering no alternative shape here on purpose. Measurement notes: every token above is synthetic, generated locally from a seeded PRNG, never a real credential, and no token value appears in this comment or in any fixture. The |
487a424 to
3dba6fd
Compare
Acknowledging the measurement — arbitration stance updated, head
|
… survive (kirodotdev#8042) redact()'s bare-secret matcher has '/' in its payload class (base64 alphabet), so a deep slash-only path chains into ONE >=40-char candidate run; the whole-run amplification in redact_credentials then replaces the entire path with a single placeholder. Two genuinely different files collapse to a byte-identical key, and the kirodotdev#7678 de-dup then silently drops the later entries from the workspace tree. The listing endpoints (api_project_tree, api_project_git_status) KNOW their values are project-relative POSIX paths, so a separator is a hard boundary there: _redact_path_display() redacts per segment on the slow path (fast path: unchanged values return after one redact() call). A genuine >=40-char secret as a single segment still redacts; the general redact() behaviour for unknown-provenance text is untouched.
3dba6fd to
fe20f96
Compare
Design PASS suggestion taken — head
|
Record-keeping: GPT round 3 re-asserts the arbitration axis, now on both surfacesNo new mechanism this round — noting it for the arbitration record:
Code stays held on the contested axis. The three options (per-segment / fail-closed |
Open PR relationship auditThis is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion. Relationship findings
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
Problem / Motivation
redact()'s bare-secret matcher (_BARE_SECRET_RUN_RE) has/inside its payload character class (it is a base64 alphabet character), so a deep slash-separated path whose directory chain carries no./-/_scans as one candidate run of 40+ characters.redact_credentialspass 3's whole-run amplification — correct for a real secret glued to adjacent base64 characters — then replaces the entire path with a single[REDACTED: credential]placeholder.Two genuinely different files (
.../libeay32.dlland.../ssleay32.dllunder a stock vendoredBinaries/ThirdParty/OpenSSL/Win64/VS2015/chain) collapse to a byte-identical key. #7678's de-duplication then resolves the collision by dropping the later entries — so post-#7678 the failure mode is no longer a loud render crash but the workspace tree silently omitting real files (the reporter measured 237 mangled paths → 158 duplicate keys → ~79 files invisible on one large vendored tree). Fixes #8042.Why it matters
Files that exist on disk vanish from the file picker with no error, no log line, and no visual hint. The threshold behaviour (exactly one extra directory level flips a path from intact to destroyed) makes it look intermittent and unreportable to most users.
What changed (motivation → approach → change)
/from its class — splitting a real slash-carrying base64 secret into sub-40-char pieces would leak it. The fix has to live where the value is provably a path.api_project_tree,api_project_git_status) know they hold project-relative POSIX path lists, so a separator is a hard boundary there, and only there.src/kiro_crew/dashboard/handlers/files.py): new module helper_redact_path_display(path)— fast path returns after a singleredact()call when the value is unchanged (the overwhelmingly common case, so the 10k-entry tree does not pay per-segment scanning); slow path redacts each/-separated segment independently. Both endpoints' path mutations now route through it. The generalredact()and_BARE_SECRET_RUN_REare untouched.Tests
test_deep_paths_are_not_collapsed_into_one_key— reproduces the redact() treats a deep path as one secret run, collapsing distinct paths to one key #8042 shape end-to-end throughGET /api/project/tree: two vendored-binary paths under a deep slash-only chain both survive intact and distinct. Includes a premise assert that the generalredact()still mangles the raw path, so the test provably exercises the slow path (and flags fixture rot if the matcher ever changes).test_redact_path_display_still_redacts_a_secret_segment— coverage-hole guard: a credential-shaped single segment still redacts; the ordinary neighbouring segment survives.test/test_project_tree.pysuite: 10 passed (including fix(dashboard): de-duplicate redacted path listings so a collision can't crash the tree (#7671) #7678's existing collision-dedup test, which still passes — an in-segment credential collision still collapses and dedups exactly as before).Manual verification
Ran the reporter's repro semantics via the new premise assert:
redact_via_context("Binaries/ThirdParty/OpenSSL/Win64/VS2015/libeay32.dll")mangles the path on this branch's base (confirming the live bug), while_redact_path_displayreturns both example paths intact and distinct.Screenshots / video
Not applicable — backend response content; the observable effect is files no longer disappearing from the tree, covered by the endpoint-level test.
Related Issues
Fixes #8042. Context: #7671 (original render crash), #7678 (crash fix via de-dup; explicitly scoped away from this matcher), #6350 (prior separator-related fence issue in the same module, different function).
Pattern harvest
Rule candidate: a redaction/sanitization heuristic tuned for unknown-provenance text should get a provenance-aware wrapper at call sites that KNOW the value's type (here: paths), rather than weakening the general matcher — the type knowledge is what makes a separator a safe hard boundary. Second occurrence of this shape in
security.py-adjacent code (#7912's shell-vs-source-body context split is the same lesson).Checklist
Contribution License Agreement
By submitting this pull request, I confirm that my contribution is made under the terms of the project's contribution license agreement.