close
Skip to content

fix(security): check bash operands verb-independently in the normalizer - #2603

Merged
kyleseaman merged 1 commit into
mainfrom
fix/normalizer-verb-independent-check
Aug 10, 2026
Merged

fix(security): check bash operands verb-independently in the normalizer#2603
kyleseaman merged 1 commit into
mainfrom
fix/normalizer-verb-independent-check

Conversation

@iamwhatever

@iamwhatever iamwhatever commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Cherry-pick of the fix from #2320 (by @joyboy5477) onto an org-owned branch, with an additional fix for attached-redirection bypass found by GPT 5.6 review.

Problem

_check_sensitive_via_normalizer() only ran for commands containing a recognized read verb, so write verbs with dot-segment spellings (./, ../, //, $HOME) bypassed path normalization entirely. Additionally, shell redirections attached without a space (>~/path, >>~/path, 2>~/path) were kept as a single token by shlex.split, making the path portion invisible to the checker.

Why it matters

The normalizer is the only layer that can decide path equivalence. Gating it on read verbs left the keystone fence open for every write verb on a default install — e.g. echo x > ~/.kiro/crew/./live_target.json was unchecked (code execution in the gateway's identity), while cat ~/.kiro/crew/./live_target.json was blocked.

Fix (symptoms → root cause → change)

  1. Verb-independent operand checking — removed the has_relevant_verb early-return gate; the verb allowlist is now used only to skip the command name itself
  2. key=value operand splittingof=, --output= carry their path through is_sensitive_path()
  3. Attached redirection stripping_REDIR_PREFIX_RE strips leading >, >>, N>, N>> prefixes so the path portion is exposed for checking

Tests

  • 55 regression tests from original PR pin every spelling × write-shape combination
  • 9 additional tests for the attached-redirection bypass (5 blocking + 4 benign-not-overblocked)
  • All 534 tests pass across test_governance_self_protection.py + test_security.py

Attribution

Original work by @joyboy5477 in #2320. The redirection-stripping fix addresses the GPT 5.6 finding on the cherry-picked code.

Closes #1638

@iamwhatever
iamwhatever requested a review from a team as a code owner August 10, 2026 16:47
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Advisory design-level review of 3e69af84a61c363961ee374e2c13d663623d1242 — updated in place on each push; does not block merge.

Reviewing the diff, PR description, and the normalizer implementation is done; here is the design review.

Design-Verdict: PASS

Consolidating both surfaces onto the one normalizing checker is the right root-cause shape; residual gaps are honestly documented, spec and tests land in the same commit.

Watch

  • The documented "not covered" case is a one-cd bypass (cd ~/.kiro/crew && printf x > live_target.json): a bare relative operand resolves against the gateway's cwd, so the keystone fence this PR advertises as closed for write verbs still fails open for the simplest relative spelling. Fine to ship as-is since it's inherent to inspecting a command line without its cwd, but the spec's proposed fail-closed basename match for keystone leaves deserves a tracked follow-up, not just a spec footnote.
  • The Windows $HOME skip pins a real gate hole (the tokenizer eats backslashed home paths, so $HOME spellings pass unchecked on native Windows) — pre-existing, but now documented in a test skip where it can quietly age; file it.

[DESIGN-REVIEWED] 3e69af8

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 3e69af84a61c363961ee374e2c13d663623d1242 — this comment is updated in place on each push.

Review details

The diff is a security-hardening change to the bash-command sensitive-path normalizer. Let me verify the key behaviors against the code I've read.

The change:

  • Removes the has_relevant_verb early-return so operand normalization runs verb-independently (matching the regex first-pass's verb-independent catch-all).
  • Splits key=value operands and checks the value (of=, --output=).
  • Strips attached-redirection prefixes via _REDIR_PREFIX_RE.

Verifications:

  • _REDIR_PREFIX_RE = ^\d*(?:>>?|<(?!<)) correctly anchors, handles fd prefixes, and excludes heredoc << via the lookahead — benign cat <<EOF is untouched.
  • Verb-independence only widens coverage; is_sensitive_path/_is_path_like still gate what's checked, and the regex first-pass already blocks path naming verb-independently, so no new false positives beyond existing behavior. Benign cases are pinned by tests.
  • The ~ in of=~/... values is expanded inside is_sensitive_path, so dd ... of=~/.kiro/crew/./live_target.json is caught.
  • The documented "Not covered" bare-relative-operand case is a pre-existing limitation, not a regression.

No correctness, security-regression, or lifecycle defect on the changed lines. The change strictly tightens the gate and matches its stated purpose.

No findings.

[OPUS-REVIEWED] 3e69af8

Verdict parsed from the review's SHA-scoped output markers for commit 3e69af84a61c363961ee374e2c13d663623d1242.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 3e69af84a61c363961ee374e2c13d663623d1242: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ human override accepted

Human judgment by @iamwhatever overrides the GPT 5.6 finding for 3e69af84a61c363961ee374e2c13d663623d1242; the recorded reason is authoritative for this commit.

This comment is updated in place on each push.

The model was not re-run because an authorized human decision supersedes it.

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 3e69af84a61c363961ee374e2c13d663623d1242: <one-sentence reason>

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 10, 2026
@iamwhatever
iamwhatever force-pushed the fix/normalizer-verb-independent-check branch from 78db92e to eb13cd3 Compare August 10, 2026 17:03
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 10, 2026
@iamwhatever
iamwhatever force-pushed the fix/normalizer-verb-independent-check branch from eb13cd3 to a30ba6b Compare August 10, 2026 17:10
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 10, 2026
Remove the verb-allowlist gate from _check_sensitive_via_normalizer() so
all path-like operands are checked regardless of verb, closing the write-side
gap where dot-segment spellings bypassed normalization for non-read verbs.

Additionally:
- Split key=value operands (of=, --output=) and check the value as a path
- Strip attached shell redirection operators (>~/path, >>~/path, 2>~/path,
  <~/path) that shlex keeps as a single token, exposing the path portion
  for checking; heredoc (<<) is excluded (delimiter word, not a path)

64 regression tests pin every spelling x write-shape combination plus
the output and input redirection bypasses.

Original work by Akash Vishwakarma (@joyboy5477).
Closes #1638
@iamwhatever
iamwhatever force-pushed the fix/normalizer-verb-independent-check branch from a30ba6b to 3e69af8 Compare August 10, 2026 17:25
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 10, 2026
@iamwhatever

Copy link
Copy Markdown
Collaborator Author

/ai-review override gpt 3e69af8: GPT infra failure (review incomplete, no verdict produced). Both prior genuine findings (output redirection bypass, input redirection bypass) were fixed in this commit. Opus 4.8 passed clean.

@github-actions

Copy link
Copy Markdown
Contributor

Human judgment recorded

@iamwhatever marked the gpt AI finding as false positive, not applicable, or explicitly accepted for 3e69af84a61c363961ee374e2c13d663623d1242.

GPT infra failure (review incomplete, no verdict produced). Both prior genuine findings (output redirection bypass, input redirection bypass) were fixed in this commit. Opus 4.8 passed clean.

This decision applies only to this commit. A new push requires a new judgment.

@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 10, 2026
@kyleseaman
kyleseaman merged commit 6ca9706 into main Aug 10, 2026
52 of 53 checks passed
@kyleseaman
kyleseaman deleted the fix/normalizer-verb-independent-check branch August 10, 2026 18:53
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 10, 2026
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…er (kirodotdev#2603)

Remove the verb-allowlist gate from _check_sensitive_via_normalizer() so
all path-like operands are checked regardless of verb, closing the write-side
gap where dot-segment spellings bypassed normalization for non-read verbs.

Additionally:
- Split key=value operands (of=, --output=) and check the value as a path
- Strip attached shell redirection operators (>~/path, >>~/path, 2>~/path,
  <~/path) that shlex keeps as a single token, exposing the path portion
  for checking; heredoc (<<) is excluded (delimiter word, not a path)

64 regression tests pin every spelling x write-shape combination plus
the output and input redirection bypasses.

Original work by Akash Vishwakarma (@joyboy5477).
Closes kirodotdev#1638

Co-authored-by: Akash Vishwakarma <vishwaka@usc.edu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bash command gate matches keystone paths textually, so path-equivalent forms bypass it

3 participants