Skip to content

The Guard's while read Bound Credits a Condition That Never Exhausts Its Input #2160

Description

@ptr727

Requirement 7's read bound in host-setup/agent-safety/claude/gh-write-guard.py (_reads_its_input) credits a while loop whose condition's first word is read over a descriptor-0 redirect. It does not check that this read's exit status is the condition's status, or that the read actually consumes the loop's input. Each shape below is allowed by _unbounded_wait_loop and ran until killed (exit 124 under a short timeout) against a two-line regular file f:

The condition's status is not the read's:

  • while read l || true; do sleep 30; done < f
  • while read l; true; do sleep 30; done < f

The read is fed by its own redirect, not the loop's, so it never exhausts:

  • while read l < f; do sleep 30; done < f
  • while read l <<< x; do sleep 30; done < f
  • An exec < f in the body rebinds descriptor 0 on every pass in the same way.

The read succeeds without consuming anything (only -u is rejected today):

  • while read -t 0 l; do sleep 30; done < f, and the clustered read -rt0 spelling
  • while read -n 0 l; do sleep 30; done < f
  • while read -N 0 l; do sleep 30; done < f (bash 4 and later)

Root cause: the check asks whether a read comes first rather than whether the condition ends when that loop's own input is exhausted. Denying anything past a bare read [options] NAME... condition with no redirect of its own, and denying the non-consuming options, is the safe direction, as the piped-read false deny already is.

Found by a local strict review pass on the auto-1633 lane (#2159, fixing #1633) and recorded here rather than fixed, since it predates that change.

Activity

  1. added
    bugSomething isn't working
    agentsAgents instructions
    pre-existingReview finding classed pre-existing per local-strict-review Disposing of Findings
    on Sep 30, 2026
  2. ptr727 commented on Oct 8, 2026

    @ptr727
    OwnerAuthor

    Declined per #2517, approved by the maintainer, under GOVERNANCE.md "Trust Boundaries and Hardening Effort": The guard backstops an agent's own mistakes, and this is a command shape built to defeat it rather than one an agent session writes, so a miss does not realistically occur. See #2517 for the per-issue reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentsAgents instructionsbugSomething isn't workingpre-existingReview finding classed pre-existing per local-strict-review Disposing of Findings

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions