Skip to content

fix: flutter-reviewer read-only hook can be bypassed with a newline or git diff --output #161

Closed

Description

@HadiHassan22

Description

allow-readonly-git.sh is the agent-scoped hook that keeps flutter-reviewer read-only. The agent has no Edit/Write tools, so this hook is the only thing between it and a mutating shell command. Three inputs get past it:

  1. A newline. The operator check rejects ;, &, |, <, >, backticks and $(, but not a newline, which bash treats as a command separator just like ;. The allow check is a grep -Eq, which passes when any line matches, so a first line of git status is enough to let every following line through.
  2. git diff --output=<file> (or --output <file>) writes the diff to an arbitrary path, so the reviewer can create or overwrite files.
  3. git diff --ext-diff runs whatever external diff driver is configured.

When the hook does not deny, Claude Code's normal permission check still applies, so an interactive session in default mode will prompt. In bypass-permissions mode, auto-accept, headless/CI runs, or with a broad Bash allow rule, the hook is the only guard. The reviewer's input is code under review, which may be untrusted, so an instruction planted in that code is the realistic way to reach this.

Steps To Reproduce

H=hooks/scripts/allow-readonly-git.sh
t() { jq -nc --arg c "$1" '{tool_name:"Bash",tool_input:{command:$c}}' | bash "$H"; echo "exit=$?"; }

t $'git status\ntouch /tmp/reviewer-wrote-this'   # no deny
t 'git diff --output=/tmp/reviewer-wrote-this'    # no deny
t 'git diff --ext-diff'                           # no deny
t 'git status; touch /tmp/x'                      # denied (correct)

Verified on plugin 0.0.5 and on main at 5e8518c. The script is byte-identical between them, so the pending 0.0.6 release (#135) carries it too.

Expected Behavior

Only a single-line git diff / git status with no file-writing or program-running options is allowed. Newlines and carriage returns are denied, --output and --ext-diff (including any abbreviation git accepts) are denied.

Additional Context

None of these cases are covered by allow-readonly-git_test.sh. Fix in #162.

Separately, like the other hooks, this one exits 0 when jq is not installed, which allows every command. That follows the repo convention, so #162 leaves it alone, but it may be worth making this hook fail closed since it is the agent's only write guard.

Activity

  1. added a commit that references this issue on Sep 25, 2026
    788c40e
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions