fix: redact stderr before the bound in themes and dev_fleet (#7374) - #7383
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Established redact-then-bound idiom applied to the three named sites, straddle-tested and structurally pinned, with the sibling class tracked in a follow-up issue. [DESIGN-REVIEWED] 87d0f27 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All claims verified. The fix sites are real and derived from issue #7374; First-Principles-Verdict: CONCERNS Every fix site earns its place; the new AST scanner is a second spelling of the regex sweep already pinning this class in auto_improvement. What this change shipsIntent: stop a credential that straddles a stderr truncation bound from escaping redaction into user-visible error text — a FIX.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 87d0f27 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
|
🤖 Kiro Crew Auto-Pipeline [operator: CrysisDeu#0c98c3a2] Disposition for First Principles CONCERNS (1 finding): Finding: deferred sibling set is mostly core-path, not auto_improvement — ownership claim inaccurate. ACCEPTED — the lane is right. Verified the sibling grep against this checkout: |
65f322f to
a126b2d
Compare
a126b2d to
f9f9cc5
Compare
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. |
Status: review-ready with two documented inherited redsAll checks owned by this PR are green on head 1. 2. No action is needed on this branch for either failure; both clear from upstream/external recovery alone. |
f9f9cc5 to
87d0f27
Compare
Disposition — First Principles CONCERNS (87d0f27)Finding: two structural scanners for one defect class (regex sweep in Disposition: accepted-and-deferred to #7390 — convergence is the promotion step, not this PR. Rationale:
No code change in this round; the concern is a sequencing decision, not a defect in the shipped diff. |
bolichen97
left a comment
There was a problem hiding this comment.
All three sites now feed the redactor the full stderr and bound afterwards: themes._clone_github switches to security.redact_and_truncate(proc.stderr.strip(), 200) (which runs both redactors over the whole text then slices, and was that module's only user of the two removed imports), and the two worktree_ops sites move the [:300] / [-200:] outside runtime._redact(...), so a credential straddling the boundary can no longer survive as an unmatched fragment. The straddle tests are premise-guarded rather than incidental, and the AST pin sanctions a slice only on a redact call's result, which makes reverting either site go red.
Summary
Three subprocess-stderr logging sites redacted credentials AFTER slicing to a fixed bound, so a credential straddling the slice was cut into fragments no redaction regex can match. This is the remainder of the defect class fixed by PR #7316 (
deps.py) and PR #7350 (auto_improvement app); the First Principles lane on #7350 named these three sites.The fix is the established mechanical reorder: feed the redactor the FULL text, apply the bound AFTER redaction.
src/kiro_crew/dashboard/handlers/themes.py(_clone_github, head cut[:200]): switched to the sharedsecurity.redact_and_truncate(text, 200)— the exact idiom PR fix(auto_improvement): redact pip stderr before bounding in install_deps (#7307) #7316 introduced. Both redactors (exfiltration URLs + credentials) still run, now over the full text; the two now-unused imports are swapped forredact_and_truncate.src/kiro_crew/apps/builtins/dev_fleet/worktree_ops.py(worktree-removal failure, head cut[:300]): slice moved outside theruntime._redact()call. (The issue named these sites indev_fleet/server.py; main has since refactored that module into a package, and the sites now live inworktree_ops.py— relocated by pattern per the issue's own instruction.)src/kiro_crew/apps/builtins/dev_fleet/worktree_ops.py(rebase-conflict tail,[-200:]): slice moved outsideruntime._redact(). A tail cut of already-redacted text can at worst split a redaction marker, never a secret.Regression tests
Two new test files follow the straddle pattern from #7316/#7350:
test/test_theme_clone_stderr_redact_before_bound.py— behavioral straddle test for_clone_github(premise-guarded: the secret provably straddles the 200 bound and the@hosttail lands beyond it) plus an AST-based structural pin for the module.test/test_dev_fleet_stderr_redact_before_bound.py— behavioral straddle test for the rebase-conflict tail cut (the tail window's left edge falls inside the secret, so a raw slice keeps the right half) plus the same structural pin covering both dev_fleet sites.The structural pin scans direct stderr/stdout slice expressions via AST: a bounded char slice is sanctioned only when applied to the RESULT of a redact call. Mutation-verified: reverting the tail-cut site makes both the behavioral test and the structural pin fail.
No
NON_EGRESS_REDACTION_MODULES/_REDACTION_SINKSposture change: all three modules already called a redactor —test/test_security_posture.pypasses unchanged.Testing
isort,flake8,mypy(1217 files), diff-scoped black gate: all green.pytest: green except failures reproduced identically at pristine base283625e16on this host (environmental: home-dir spelling, AF_UNIX path length), none in changed files' modules.Deferred follow-up (deliberately out of scope)
channel.py,dashboard/chat_runner.py,dashboard/handlers/artifacts.py,mcp_tools/control.py,vector_memory.py, …), not in auto_improvement. Only the auto_improvement subset is covered by open PR fix: redact git stderr before bounding in auto_improvement (#7333) #7350. A repo-wide sweep would red on all of them, so this PR ships only the three subprocess-stderr sites named by issue Slice-before-redact on subprocess stderr: 3 remaining sites outside auto_improvement (themes.py, dev_fleet server.py) #7374 plus straddle tests.auto_improvement/backend/clone_setup.pyreturns bounded raw stderr with no redaction at all; that file is covered by open PR fix: redact git stderr before bounding in auto_improvement (#7333) #7350.Pattern harvest
Rule candidate: AST/structural test (shipped in this PR as
_find_slice_before_redact_offenders, module-scoped)Pattern: a bounded char slice applied to a subprocess stderr/stdout expression BEFORE the redaction call (
redact*(text[:N])/redact*(text[-N:])or a rawstderr...[:N]) — redaction regexes cannot match a credential cut in half. Sanctioned form: slice the RESULT of the redact call, or usesecurity.redact_and_truncate(text, N). Repo-wide promotion deliberately deferred: ~15 core-path sibling sites remain (tracked in follow-up issue #7390) plus the auto_improvement subset on open PR #7350; promote once the baseline is empty.Closes #7374