fix: model parameter expansion and comments in shell char walk - #9241
fix: model parameter expansion and comments in shell char walk#9241aniruddhaadak80 wants to merge 2 commits into
Conversation
Teach _iter_shell_chars the two bash constructs whose literal close-parens used to arrive active and truncate every substitution span: dollar-brace parameter expansion, whose interior is data, and hash comments, which run to the newline. Interior steps yield active=False so _matching_close_paren ignores them. Anything unmodeled errs toward scanning more, never less. Adds TestBuiltinDenyPatterns coverage for both reported cases plus the mid-word-hash guard.
68fd3a5 to
accef9a
Compare
First Principles Review (Fable 5, fork) — 🟡 CONCERNSPremise-level review of I've now verified the base state machine ( First-Principles-Verdict: CONCERNS Harvest claims "not generalizable," but heredoc bodies are a third data-interior construct the walker still truncates on — same root cause, left uncounted. What this change shipsIntent: stop Inventory (4 items)
Watch
[FIRST-PRINCIPLES-REVIEWED] 7173f92 |
GPT 5.6 Review (fork) —
|
Design Review (Fable 5, fork) — 🟡 CONCERNSDesign-level review of I have what I need. I traced the new Design-Verdict: CONCERNS The Watch
Suggestions
[DESIGN-REVIEWED] 7173f92 |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed Review detailsI've completed my falsification analysis. Let me summarize the key verification I did before finalizing. The candidate's core premise — that bash closes But the candidate's claimed observable wrong outcome (an allow-direction miss) does not hold up. I traced every consumer of
So the divergence at most causes over-blocking (the diff's stated, safe "scan MORE" direction) and is backstopped everywhere against an actual allow. I could not re-derive a concrete allow-direction failure at 80+ without a contrived, unverifiable combination — which the falsification bar forbids. No new grounded defect surfaced in the diff either. No findings. [OPUS-REVIEWED] 7173f92 |
|
@aniruddhaadak80 Thanks for staying on this. Notes from a repository-wide audit of open PRs, audited at 68fd3a5. Your branch has since moved to accef9a, so please treat anything already fixed as resolved. Already on main: merged #9183 deleted Still missing on main: Two defects we found in 68fd3a5, please confirm they are addressed in the new revision:
One description fix: #8830 is the Posted from the 2026-09-08 open-PR relationship audit (read-only, one auditor per PR); reply here if any of this is wrong. |
Design + Opus blocking findings: the interior loop froze quote state,
so a quoted } closed the expansion early and desynced the outer walk
from bash (allow-direction miss). The loop now tracks single/double
quotes and backslash escapes through the span, decrementing brace
depth only on an unquoted, unescaped }. Pins the :-"} separator case
and the single-quoted-brace full-body extraction. Spec accounting needs
no change: test_every_bash_metacharacter_is_accounted_for already rows
${ and # at their layers and passes.
|
All blocking findings addressed in the latest push (rebased onto current main, 2 commits total):
|
Problem / Motivation
_iter_shell_chars(src/kiro_crew/security/shell_normalizer.py) models shell quote andescape handling, but did not handle two additional bash syntax categories:
${...}parameter expansion#commentsA literal
)inside$(echo ${v:-})or after a#comment arrived withactive=True, so_matching_close_parencounted it as the substitutioncloser and truncated the extracted body. The nested-payload extractor then
handed every downstream scan a fragment while bash runs the whole body, so a
nested publish hides below the truncation point --
is_deniedallows a commandbash executes (same shape as the documented quoted-paren truncation behind
_matching_close_paren).Why it matters
The
_iter_shell_charsgenerator is the single source of truth for shellquote/escape state walked by every consumer: the boundary walk, the git-publish
border walk, the nested-payload extractor, and
is_denied/is_sensitive_bash_command.When the machine did not know about
${...}or#, those constructs wereinvisible to it, and a
)inside them was treated as a bare)-- afalse-allow of the class security reviews have caught before.
What changed (motivation → approach → change)
Extended
_iter_shell_charswith two new syntax branches:${...}parameter expansion: when$is followed by{in theunquoted state (
state == 0, unpaired$), the generator enters anexpansion closed by the matching
}. Interior steps yieldactive=Falseso
_matching_close_parenignores them, while quote state runs throughthe span per POSIX 2.6.2 -- only an unquoted, unescaped
}decrementsbrace depth, so a quoted
}(${v:-"}"},${v:-'}X)Y'}) cannot closeearly and desync the outer walk. Tracked quotes stay local to the span.
Nested
$(...)inside still resolves via the raw-text descent in_substitution_bodies.#comments: a#at word start (bash:echo a#bis one word) startsa comment running to the newline; interior steps yield
active=False, and abackslash-newline continues the comment as in bash.
Tests
test/test_security.py:test_substitution_span_survives_expansion_and_comment_parensand the new
test_expansion_close_ignores_quoted_braces(pins the tworeviewer repros) pass
test/test_push_branch_gate.py::test_every_bash_metacharacter_is_accounted_forpasses --
${and#keep their existing accounting rows, so no spec moveblack --target-version py310,flake8,isort: clean on both touched files;mypyonshell_normalizer.py: cleanPattern harvest
Not generalizable: one-off bash-fidelity gap in a single state machine plus its
regression pins; the review lesson (never freeze quote state across a span you
close by delimiter) is already stated in the code comment.
Related Issues
Fixes #9181. Note: #8830 (case-pattern terminators) is intentionally not cited --
that is #9164's scope, already merged.
Checklist
Contribution License Agreement