Repository navigation
End the Guard's Stdin Redirect Scan at a Reserved Word - #2153
Conversation
The scan of the tokens after a loop's `done` stopped only at a separator, so a redirect on the next command inside an enclosing compound read as the loop's own input bound. It now also stops at a reserved word, which ends the loop's command the same way. A closing word is passed over, since a redirect after one binds a compound that encloses the loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A redirect after a closing word binds the compound it closes, and a pipe
inside that compound can still feed the loop, so the redirect is no bound
the scan can credit. `{ yes | while read l; do sleep 30; done } < f`
reads the pipe forever and was allowed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: ptr727/ProjectTemplate/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe loop stdin redirection scan now stops at shell reserved words as well as separators. Added self-tests cover redirects after ChangesLoop stdin redirection guard
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~8 minutes Merge Risk: ⚪ Minimal · up to The changed boundary prevents redirects in later commands from affecting the loop’s stdin classification. No actionable merge risk was identified in the reviewed change. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #2153 +/- ##
==========================================
Coverage ? 56.47%
==========================================
Files ? 16
Lines ? 7455
Branches ? 0
==========================================
Hits ? 4210
Misses ? 3245
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The change strictly tightens the guard in the safe direction, is well-reasoned, and is covered by four new selftest rows whose command shapes I verified as valid bash.
Review effort: Balanced
Findings: None
What changed in this PR
This PR fixes a false-allow in the agent-safety write guard's unbounded-wait-loop detector. _redirects_stdin in host-setup/agent-safety/claude/gh-write-guard.py scans the tokens after a loop's done to decide whether descriptor 0 is bound to a source that ends (which would make a read-driven loop terminate). Previously the scan only stopped at a shell separator, so a redirect belonging to a later command inside an enclosing compound (e.g. after then, else, fi, or }) was mis-credited as the loop's own input bound, letting an unbounded loop through. The scan now also ends at any bash reserved word, closing that gap.
Changes:
- Add a complete
_RESERVED_WORDSfrozenset (the 22 bash reserved words) and end the_redirects_stdinscan when a reserved word is reached, mirroring the existing separator stop. - Extend the
_redirects_stdindocstring to explain the reserved-word boundary, including the closing-word case where a pipe inside the compound can still feed the loop. - Add four
_WAIT_CASESselftest rows pinning thethen,else, closing-word, and piped-group shapes.
| File | Description |
|---|---|
| host-setup/agent-safety/claude/gh-write-guard.py | Adds _RESERVED_WORDS, ends the stdin-redirect scan at a reserved word, documents the behavior, and adds four selftest cases covering the newly denied shapes. |
I reviewed the logic and confirmed the change only returns early (adding deny conditions) and therefore cannot introduce a new false-allow: bound is only true when a valid loop-binding redirect already appeared immediately after done, before any reserved word. The reserved-word set matches bash's 22 reserved words exactly, and reserved words appearing as redirect targets are consumed as targets before the top-of-loop check, so done < do still binds correctly. I also verified with bash -n that all four new test-case command strings are valid bash. The two sibling defects (process-substitution scan and trailing-comment scan) are correctly scoped out with issues filed.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@coderabbitai review |
✅ Action performedReview finished.
|
… a Second Site's Verify Token Pair, With Fifteen More (#2175) ## Summary Promotes develop to main, carrying these pull requests: - [#2172](#2172) Accept Letters in Recorded Names in the Charset Rule and Prose Gate - [#2173](#2173) Forward a Second Site's Token Pair to the Deploy-Site Verify Hook - [#2169](#2169) Record HomeAutomation-Config's Merge-Bot and Gate Adoption in the Rollout Tracking - [#2164](#2164) Pass --no-project to the Pre-Commit Snippet's uv run Hooks - [#2161](#2161) Stop Crediting an until read Loop as Bounded in the Guard - [#2158](#2158) Drop the Path Argument From the Pre-Commit Snippet's Mypy Swap - [#2155](#2155) Reword the Canonical CRLF-Exception Comments for a Carrier's Own Pin - [#2153](#2153) End the Guard's Stdin Redirect Scan at a Reserved Word - [#2150](#2150) Describe the Pip Form Consistently Across python-codestyle - [#2144](#2144) Read the Run Id From the Runner's Environment in the Artifact-Cleanup Steps - [#2142](#2142) Diff a Merge Commit's Prose Against Its Merged-In Parent in the Pre-Commit Hook - [#2136](#2136) Qualify the Local Review Skill's Merge-Base Command to Match the Engine - [#2134](#2134) Quote the Bare Placeholder in skills_install.py's Usage Block - [#2132](#2132) Write the Hub-Checkout Reach Into the session-handoff Chain Commands - [#2130](#2130) Name the Missing build-system Condition in the Lint-Only Profile Bullet - [#2128](#2128) Skip a Blockquoted List Marker in the Prose Gate's Semicolon Rule - [#2119](#2119) State the Three Gaps D4.7's Supersede-and-Dispatch Step Leaves Open ## Closes Closes #2100 Closes #2031 Closes #1779 Closes #2148 Closes #1633 Closes #1188 Closes #1992 Closes #2032 Closes #2052 Closes #1481 Closes #2116 Closes #2107 Closes #2097 Closes #1512 Closes #2026 Closes #2101 Closes #2009 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Deployment verification can now check a second site using its own optional authentication token. * **Documentation** * Updated writing guidance to preserve the spelling and diacritics of recorded names. * Clarified Python project setup, formatting and testing guidance, and line-ending rules. * Expanded deployment and publishing guidance, including scenarios where publishing runs overlap. * **Bug Fixes** * Prose checks now handle quoted lists and tables more accurately, and merge checks avoid flagging comments brought in from the merged branch. * Improved checks for shell loops that read redirected input. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
_redirects_stdininhost-setup/agent-safety/claude/gh-write-guard.pyscanned the tokens after a loop'sdoneup to the first separator only. A redirect on the next command inside an enclosing compound therefore read as the loop's own input bound:The
< fbinds theecho, and the loop reads the caller's stdin with nothing bounding it, yet the guard allowed it. Theelseform did the same.The scan now ends at any bash reserved word, the same way it ends at a separator. That includes the closing words
},fi,done, andesac. A redirect after one of those binds the compound it closes, and a pipe inside that compound can still feed the loop, so{ yes | while read l; do sleep 30; done } < fruns forever. Develop allowed it, and this change denies it.One behavior change is in the safe direction:
{ while read l; do sleep 30; done } < fis now denied, a false deny of the kind_reads_its_input's docstring already accepts.while read l; do sleep 30; done < fstays allowed.Verification
python3 host-setup/agent-safety/claude/gh-write-guard.py --selftestpasses. Four new_WAIT_CASESrows pin thethen,else, closing-word, and piped-group forms.done.local-strict-reviewpasses ran. The first found the piped-group false allow described above, which the second commit fixes. The second found a pre-existing false allow from a redirect inside a trailing comment (done # < f). That one is filed as The Guard's Stdin Redirect Scan Credits a Redirect Inside a Trailing Comment #2152 and is out of this change's scope.Closes on promotion: #2032
🤖 Generated with Claude Code
Summary by CodeRabbit