VeryGoodOpenSource / VeryGoodOpenSource/vgv-ai-flutter-plugin

fix: block-cli-workarounds denies commands with a quoted regex alternation

Open
#147 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Shell
Stars
162
Forks
23
Avg merge
2d 22h
Merged PRs (30d)
4

Description

Description

block-cli-workarounds.sh denies read-only commands whose arguments merely contain the
blocked words — specifically, any command carrying a quoted regex alternation such as
grep -nE "very_good|flutter test|foo" CLAUDE.md.

This is the failure mode #60 was closed against, resurfacing through a different route. The
current implementation splits the command on shell operators to avoid exactly this, and its
comment states the intent (lines 41–42):

# Split on shell operators and check the first two tokens of each subcommand.
# This avoids false positives from file paths (.dart) or quoted strings.
BLOCKED=$(echo "$COMMAND" | awk '{
  n = split($0, parts, /[;&|]+/)

The split is unquoted-aware only in the sense that it ignores quoting entirely. A | inside
a quoted string is treated as a pipe, so the alternation is chopped into fragments and each
fragment is tested as if it were a command:

grep -nE "very_good|flutter test|foo" CLAUDE.md

splits into grep -nE "very_good, flutter test, foo" CLAUDE.md. The middle fragment is
literally flutter test — no adjacent quote character to spoil the match — so b == "flutter" && s == "test" fires and the command is denied.

Position inside the alternation decides the outcome, which is what makes this confusing in
practice: as the last alternative the closing quote attaches to the token (test"), the
comparison fails, and the identical search is allowed.

Steps To Reproduce

Feed the hook a PreToolUse payload directly:

H=hooks/scripts/block-cli-workarounds.sh
mk() { printf '{"tool_name":"Bash","tool_input":{"command":"%s"}}' "$1"; }

mk 'grep -nE \"very_good|flutter test|foo\" CLAUDE.md' | bash "$H"   # denied
mk 'grep -nE \"very_good|flutter test\" CLAUDE.md'     | bash "$H"   # allowed

Observed:

Command Result Correct?
grep -nE "very_good|flutter test|foo" CLAUDE.md denied no
grep -nE "very_good|flutter test" CLAUDE.md allowed yes
flutter test denied yes
very_good dart test --coverage allowed yes

The genuine-bypass cases still behave correctly — this is purely over-blocking.

Expected Behavior

A read-only command is not denied because one of its arguments contains the blocked words.
Only an actual invocation of flutter/dart/very_good as the command being run should be
blocked.

Additional Context

This is not hypothetical: it fired during an audit of a repo's own agent instructions. The
command was a grep over CLAUDE.md / AGENTS.md files looking for stale guidance about
flutter test and dart test — i.e. searching for references to the very strings the plugin
governs is one of the likelier ways to hit this, which makes it disproportionately annoying
for maintenance and documentation work.

The failure is also silent in the sense that the denial message ("Do not use 'flutter test'
or 'dart test'. Use the very_good_cli MCP 'test' tool instead.") describes an action the
operator did not attempt, so the natural reading is that the harness is broken rather than
that a guard misfired.

Possible directions, in rough order of robustness:

  1. Tokenise with quoting awareness rather than a regex split — e.g. have the hook ask a shell
    to do it, or only inspect the segment before the first unquoted quote character.
  2. Keep the split, but skip any fragment whose originating position falls inside a quoted
    region.
  3. Narrowest fix: require the matched fragment to be at a real command position — i.e. the
    start of the string or immediately after an unquoted ;, &&, ||, or |.

Option 3 preserves the current structure and closes this case, though it would still be
defeated by an unquoted alternation, which is why 1 is the durable answer.

Related: #60 (previous false positive with .dart / test/ paths), #109.

Verified against plugin 0.0.5; hooks/scripts/block-cli-workarounds.sh is byte-identical
to main as of this writing.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with hooks/scripts/block-cli-workarounds.sh, especially the awk split described around lines 41–42, and run the two grep reproductions from the issue through the hook. Compare those results with the genuine flutter test and very_good dart test cases. Done means quoted alternations in read-only commands are allowed while actual blocked command invocations remain denied.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, flutter, shell
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.