Skip to content

End the Guard's Stdin Redirect Scan at a Reserved Word - #2153

Merged
ptr727 merged 2 commits into
developfrom
feature/auto-2032
Sep 30, 2026
Merged

ptr727 merged 2 commits into
developfrom
feature/auto-2032

Conversation

@ptr727

@ptr727 ptr727 commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

_redirects_stdin in host-setup/agent-safety/claude/gh-write-guard.py scanned the tokens after a loop's done up to the first separator only. A redirect on the next command inside an enclosing compound therefore read as the loop's own input bound:

if while read l; do sleep 30; done then echo x < f; fi

The < f binds the echo, and the loop reads the caller's stdin with nothing bounding it, yet the guard allowed it. The else form 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, and esac. 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 } < f runs forever. Develop allowed it, and this change denies it.

One behavior change is in the safe direction: { while read l; do sleep 30; done } < f is now denied, a false deny of the kind _reads_its_input's docstring already accepts. while read l; do sleep 30; done < f stays allowed.

Verification

  • python3 host-setup/agent-safety/claude/gh-write-guard.py --selftest passes. Four new _WAIT_CASES rows pin the then, else, closing-word, and piped-group forms.
  • With the reserved-word check disabled, all four new rows fail.
  • Each shape was checked in bash first. A reserved word after a redirect target is a bash syntax error, so a closing word can only come directly after done.
  • ruff format, ruff check, mypy, the prose gate, and the eol gate are clean.
  • Two local-strict-review passes 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

  • Bug Fixes
    • Corrected how shell input redirections are interpreted after control-flow boundaries, preventing later commands from being treated as changing a loop’s input.

ptr727 and others added 2 commits September 30, 2026 04:21
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>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 11:28
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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 configuration

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

Review profile: CHILL

Plan: Advanced

Run ID: 0b2a7dd5-f241-418c-aaff-01d163812f51

📥 Commits

Reviewing files that changed from the base of the PR and between 2a028d0 and 71d285f.

📒 Files selected for processing (1)
  • host-setup/agent-safety/claude/gh-write-guard.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The loop stdin redirection scan now stops at shell reserved words as well as separators. Added self-tests cover redirects after then, else, and a closing }.

Changes

Loop stdin redirection guard

Layer / File(s) Summary
Reserved-word boundary and validation
host-setup/agent-safety/claude/gh-write-guard.py
The scanner now stops at shell reserved words when checking loop stdin redirection. Self-tests cover redirects after then, else, and a closing }.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~8 minutes

Merge Risk: ⚪ Minimal · up to 71d28

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 Summary

Architecture risk: 🔵 Low · up to 71d28

The change affects 1 system.

Changed systems: host-setup

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — host-setup (service) was modified; 1 changed file maps to changed impact.

Before / after behavior

  • observed — Modified behavior in host-setup/agent-safety/claude/gh-write-guard.py: Adds _RESERVED_WORDS, a set of shell reserved words used to identify boundaries while scanning tokens after a loop.
  • observed — Modified behavior in host-setup/agent-safety/claude/gh-write-guard.py: Adds documentation that a loop command ends at reserved words, including closing words such as }, when determining whether a later input redirection bounds the loop.
  • observed — Modified behavior in host-setup/agent-safety/claude/gh-write-guard.py: _redirects_stdin now returns its accumulated result when it encounters a reserved word, in addition to its existing separator boundary. Later redirects therefore do not alter the loop’s stdin-bound determination.
  • observed — Modified behavior in host-setup/agent-safety/claude/gh-write-guard.py: Adds self-test cases expecting denial when apparent input redirects follow then, else, or a closing } after a sleeping read loop.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: ending the guard's stdin redirect scan at shell reserved words.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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 Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

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

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

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 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_WORDS frozenset (the 22 bash reserved words) and end the _redirects_stdin scan when a reserved word is reached, mirroring the existing separator stop.
  • Extend the _redirects_stdin docstring 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_CASES selftest rows pinning the then, 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.

@ptr727

ptr727 commented Sep 30, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ptr727
ptr727 merged commit e352f5d into develop Sep 30, 2026
11 checks passed
@ptr727
ptr727 deleted the feature/auto-2032 branch September 30, 2026 11:44
ptr727 added a commit that referenced this pull request Sep 30, 2026
… 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 -->
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