VeryGoodOpenSource / VeryGoodOpenSource/vgv-ai-flutter-plugin
fix: block-cli-workarounds denies commands with a quoted regex alternation
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:
- 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. - Keep the split, but skip any fragment whose originating position falls inside a quoted
region. - 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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