Skip to content

Ignore a Quoted Separator When the Guard Finds a Loop's done - #2249

Merged
ptr727 merged 1 commit into
developfrom
feature/auto-2207
Oct 1, 2026
Merged

ptr727 merged 1 commit into
developfrom
feature/auto-2207

Conversation

@ptr727

@ptr727 ptr727 commented Oct 1, 2026

Copy link
Copy Markdown
Owner

The wait-loop rule found a loop's do and done by command position alone. The shell tokens drop their quoting, so a quoted ";" read as a separator and the word after it as the loop's close. while true; do echo ";" done; sleep 1; done therefore closed at its first done, the body it saw held no sleep, and the unbounded wait was allowed.

_loop_parts now takes the quote mask _unbounded_wait_loop already computes, and counts a do or done only when it opens a command and neither it nor the word before it is quoted. Two self-test rows pin the two halves (a done after a quoted ;, and a quoted done), and each fails when only its clause is removed.

The local strict review raised no finding. It noted one shape that is unreachable today: a backslash-newline continuation directly before do or done lexes as a quoted newline token, which the new previous-word test would reject. classify folds those continuations into a space before it calls the wait-loop check, its only caller, so the hook never sees that shape.

Closes on promotion: #2207

🤖 Generated with Claude Code

The wait-loop rule found a loop's `do` and `done` by command position
alone, and the shell tokens drop their quoting, so a quoted `";"` read as
a separator and the word after it as the loop's close.
`while true; do echo ";" done; sleep 1; done` therefore closed at its
first `done`, its body held no sleep, and the unbounded wait was allowed.

`_loop_parts` now takes the quote mask the caller already computes and
counts a `do` or `done` only when neither it nor the word before it is
quoted. Two self-test rows pin the two halves, each failing when its
clause is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 14:48
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: ptr727/ProjectTemplate/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 1c7e9691-dada-4b25-a4f6-ddb68573c769

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (develop@427b2d3). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #2249   +/-   ##
==========================================
  Coverage           ?   56.84%           
==========================================
  Files              ?       16           
  Lines              ?     7519           
  Branches           ?        0           
==========================================
  Hits               ?     4274           
  Misses             ?     3245           
  Partials           ?        0           
Flag Coverage Δ
python-3.13 56.84% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The change strictly tightens a security guard to close a verified bypass, is limited to one helper plus regression tests, and runtime checks confirm the new denials without over-denying bounded loops.

Review effort: Balanced
Findings: None

What changed in this PR

This PR fixes a bypass in the Claude Code gh-write-guard.py agent-safety hook's unbounded-wait-loop rule. Previously, _loop_parts located a loop's do/done by command position alone, ignoring the quote mask. A quoted ";" unquotes to the same token a real separator does, so a crafted command like while true; do echo ";" done; sleep 1; done closed the loop at its first (argument) done, hid the sleep body from the guard, and allowed an unbounded wait to run. The fix threads the already-computed per-token quote mask into _loop_parts so a do/done counts only when neither it nor the preceding token is quoted.

Changes:

  • Added an optional quoted mask parameter to _loop_parts and an inner keyword helper that rejects a quoted keyword or a keyword following a quoted separator.
  • Passed the existing mask from _unbounded_wait_loop into _loop_parts (works at both top level and the bash -c recursion depth).
  • Added two _WAIT_CASES self-test rows pinning a done after a quoted ; and a quoted done, both expected to deny.
File Description
host-setup/​agent-safety/​claude/​gh-write-guard.py Makes loop do/done detection quote-aware so a quoted separator or quoted keyword no longer closes a wait loop early, closing a guard bypass; adds two regression self-test cases.

I verified: _loop_parts has a single caller, which now passes the mask; the mask functions return a per-token list aligned to toks (or None, which safely preserves prior behavior); the backslash-newline-continuation edge noted in the description is unreachable because classify folds continuations to a space before tokenizing; and, by executing the module, that the two new patterns are denied while bounded/allowed loops (timeout ... bash -c, arithmetic/test-bounded, for, non-loop quoted done) remain allowed.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ptr727
ptr727 merged commit 7424d94 into develop Oct 1, 2026
11 checks passed
@ptr727
ptr727 deleted the feature/auto-2207 branch October 1, 2026 14:54
ptr727 added a commit that referenced this pull request Oct 1, 2026
## Summary

Promotes develop to main, carrying the pull requests below, each already
reviewed and merged into develop.

- [#2203](#2203): Strip
every heredoc body a line opens in the wait-loop rule.
- [#2216](#2216): Point
the charset-unknown message at where a tier is classified.
- [#2218](#2218): Name the
leftover old tree when the Bash loader cannot remove it.
- [#2227](#2227): End the
guard's stdin redirect scan at a trailing comment.
- [#2230](#2230): Tell
apart the causes a labels payload is refused for.
- [#2232](#2232): Correct
the task names and pointers in the Python tasks snippet header.
- [#2234](#2234): Drop the
stale private-repository note from PhotoCleaner's registry entry.
- [#2237](#2237): State
the bootstrap's archive live channel in two skills.
- [#2239](#2239): Stop the
promotion count on a failed fetch in backlog-burndown.
- [#2241](#2241): Render
the include walk once per `build_dist.py --check` run.
- [#2243](#2243): Have the
tree check call `escapes_repo_root` and refuse a symlinked component.
- [#2247](#2247): Refuse a
Windows drive component anywhere in a tree path.
- [#2222](#2222): Tick the
adopted repos in the merge-bot and gate rollout stages.
- [#2121](#2121): Refuse a
dangling remote-tracking target in `local_review`.
- [#2249](#2249): Ignore a
quoted separator when the guard finds a loop's `done`.

Closes #2112
Closes #2171
Closes #1790
Closes #2152
Closes #1372
Closes #2149
Closes #1082
Closes #1772
Closes #1310
Closes #1379
Closes #1452
Closes #2244
Closes #2168
Closes #2106
Closes #2207

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants