Skip to content

Commit fc57377

Browse files
authored
fix(hooks): prevent false positives in block-cli-workarounds (#63)
1 parent 6892982 commit fc57377

2 files changed

Lines changed: 119 additions & 21 deletions

File tree

hooks/scripts/block-cli-workarounds.sh

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,29 @@ deny_with_cli_check() {
3838
esac
3939
}
4040

41-
# Block project creation via CLI
42-
if echo "$COMMAND" | grep -qE '(flutter|dart)\s+create'; then
43-
deny_with_cli_check "Do not use 'flutter create' or 'dart create'. Use the very_good_cli MCP 'create' tool instead."
44-
fi
45-
46-
if echo "$COMMAND" | grep -qE 'very_good\s+create'; then
47-
deny_with_cli_check "Do not use 'very_good create' via shell. Use the very_good_cli MCP 'create' tool instead."
48-
fi
49-
50-
# Block test runs via CLI
51-
if echo "$COMMAND" | grep -qE '(flutter|dart)\s+test'; then
52-
deny_with_cli_check "Do not use 'flutter test' or 'dart test'. Use the very_good_cli MCP 'test' tool instead."
53-
fi
54-
55-
if echo "$COMMAND" | grep -qE 'very_good\s+test'; then
56-
deny_with_cli_check "Do not use 'very_good test' via shell. Use the very_good_cli MCP 'test' tool instead."
57-
fi
41+
# Split on shell operators and check the first two tokens of each subcommand.
42+
# This avoids false positives from file paths (.dart) or quoted strings.
43+
BLOCKED=$(echo "$COMMAND" | awk '{
44+
n = split($0, parts, /[;&|]+/)
45+
for (i = 1; i <= n; i++) {
46+
gsub(/^[[:space:]]+/, "", parts[i])
47+
split(parts[i], w, /[[:space:]]+/)
48+
b = w[1]; s = w[2]
49+
if ((b == "flutter" || b == "dart") && s == "create") { print "create"; exit }
50+
if ((b == "flutter" || b == "dart") && s == "test") { print "test"; exit }
51+
if (b == "very_good" && s == "create") { print "vg_create"; exit }
52+
if (b == "very_good" && s == "test") { print "vg_test"; exit }
53+
if (b == "very_good" && s == "packages") { print "vg_packages"; exit }
54+
}
55+
}')
5856

59-
# Block license check via CLI
60-
if echo "$COMMAND" | grep -qE 'very_good\s+packages'; then
61-
deny_with_cli_check "Do not use 'very_good packages' via shell. Use the very_good_cli MCP 'packages_get' or 'packages_check_licenses' tool instead."
62-
fi
57+
case "$BLOCKED" in
58+
create) deny_with_cli_check "Do not use 'flutter create' or 'dart create'. Use the very_good_cli MCP 'create' tool instead." ;;
59+
test) deny_with_cli_check "Do not use 'flutter test' or 'dart test'. Use the very_good_cli MCP 'test' tool instead." ;;
60+
vg_create) deny_with_cli_check "Do not use 'very_good create' via shell. Use the very_good_cli MCP 'create' tool instead." ;;
61+
vg_test) deny_with_cli_check "Do not use 'very_good test' via shell. Use the very_good_cli MCP 'test' tool instead." ;;
62+
vg_packages) deny_with_cli_check "Do not use 'very_good packages' via shell. Use the very_good_cli MCP 'packages_get' or 'packages_check_licenses' tool instead." ;;
63+
esac
6364

6465
# Not a blocked command — allow
6566
exit 0
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/bin/bash
2+
# Tests for block-cli-workarounds.sh
3+
#
4+
# Usage: bash hooks/scripts/block-cli-workarounds_test.sh
5+
#
6+
# The hook reads a JSON payload from stdin containing tool_input.command,
7+
# then exits 0 with a deny JSON on stdout if blocked, or exits 0 silently
8+
# if allowed. We check stdout for the deny marker to determine the result.
9+
10+
set -euo pipefail
11+
12+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13+
HOOK="$SCRIPT_DIR/block-cli-workarounds.sh"
14+
15+
PASSED=0
16+
FAILED=0
17+
18+
# Run hook with a command and check if it was blocked or allowed.
19+
# Usage: run_hook "command string"
20+
# Returns: "blocked" or "allowed"
21+
run_hook() {
22+
local cmd="$1"
23+
local payload
24+
payload=$(jq -n --arg c "$cmd" '{"tool_input":{"command":$c}}')
25+
local output
26+
output=$(echo "$payload" | bash "$HOOK" 2>/dev/null) || true
27+
if echo "$output" | grep -q '"permissionDecision"'; then
28+
echo "blocked"
29+
else
30+
echo "allowed"
31+
fi
32+
}
33+
34+
assert_blocked() {
35+
local cmd="$1"
36+
local result
37+
result=$(run_hook "$cmd")
38+
if [ "$result" = "blocked" ]; then
39+
printf " \033[32mPASS\033[0m blocked: %s\n" "$cmd"
40+
PASSED=$((PASSED + 1))
41+
else
42+
printf " \033[31mFAIL\033[0m expected blocked but allowed: %s\n" "$cmd"
43+
FAILED=$((FAILED + 1))
44+
fi
45+
}
46+
47+
assert_allowed() {
48+
local cmd="$1"
49+
local result
50+
result=$(run_hook "$cmd")
51+
if [ "$result" = "allowed" ]; then
52+
printf " \033[32mPASS\033[0m allowed: %s\n" "$cmd"
53+
PASSED=$((PASSED + 1))
54+
else
55+
printf " \033[31mFAIL\033[0m expected allowed but blocked: %s\n" "$cmd"
56+
FAILED=$((FAILED + 1))
57+
fi
58+
}
59+
60+
echo "=== block-cli-workarounds tests ==="
61+
echo ""
62+
echo "--- Should be BLOCKED ---"
63+
assert_blocked "dart test"
64+
assert_blocked "flutter test"
65+
assert_blocked "dart test test/routing/foo_test.dart"
66+
assert_blocked "flutter test --coverage"
67+
assert_blocked "dart create my_app"
68+
assert_blocked "flutter create my_app"
69+
assert_blocked "very_good create flutter_app --project-name my_app"
70+
assert_blocked "very_good test --coverage --min-coverage 100"
71+
assert_blocked "very_good packages check licenses"
72+
assert_blocked "cd /path && dart test"
73+
assert_blocked "ENV=1 && flutter test --coverage"
74+
75+
echo ""
76+
echo "--- Should be ALLOWED ---"
77+
assert_allowed "dart analyze lib/foo.dart"
78+
assert_allowed "dart format lib/foo.dart"
79+
assert_allowed "dart pub get"
80+
assert_allowed "dart fix --apply"
81+
assert_allowed "flutter pub get"
82+
assert_allowed "flutter analyze"
83+
assert_allowed "git add lib/router.dart test/router_test.dart"
84+
assert_allowed "dart analyze lib/foo.dart test/bar_test.dart"
85+
assert_allowed "git commit -m 'fix dart test hook'"
86+
assert_allowed "echo 'flutter create is blocked'"
87+
assert_allowed "gh pr create --body 'use dart test instead'"
88+
assert_allowed "git log --grep='dart test'"
89+
assert_allowed "ls"
90+
assert_allowed "pwd"
91+
92+
echo ""
93+
echo "=== Results: $PASSED passed, $FAILED failed ==="
94+
95+
if [ "$FAILED" -gt 0 ]; then
96+
exit 1
97+
fi

0 commit comments

Comments
 (0)