KeiSeiKit-1.0/hooks/numeric-claims-guard.sh
Parfii-bot 85a61d7253 fix(hooks): post-audit hook chain hardening + 4 new defensive hooks
Hook chain repairs (Group A):
- alignment-check.sh: read .prompt (was .user_prompt) — hook was dead
- block-dangerous.sh: jq instead of inline interpreter (RULE 0.2 + fail-open fix)
- destructive-guard.sh: explicit INPUT=cat + jq guard + exit 0 — was silent no-op
- numeric-claims-guard.sh: exit 1 -> exit 2 (Claude Code spec — was non-blocking)
                          comments updated 0.17 -> 0.18 (env var name kept)
- no-downgrade.sh: removed (?i) PCRE syntax — POSIX ERE matched literal text
- task-timer.sh: jq -nc instead of bare printf — JSON injection on quotes/backslashes
                 in description was corrupting RULE 0.18 evidence journal
- check-error-patterns.sh: replaced with no-op stub — had hardcoded /Users/denis/...
                            PATH LEAK in public kit, plus inline interpreter use
- post-commit-audit.sh: added trailing exit 0 — grep return code was hook exit code
- citation-verify.sh: ALLOW_REGEX accepts HOOK-BYPASS marker — bypass was documented
                       but never matched
- settings-snippet.json: agent-stub-scan moved PreToolUse:Agent -> PostToolUse:Agent
                          (RULE 0.16 enforcement was firing before transcript existed)
- check-error-patterns hook removed from settings-snippet.json

New defensive hooks (Group H):
- no-github-push.sh: PreToolUse:Bash hard deny on github.com push/create/sync/remote-add
                      (RULE 0.1 — patent IP protection; was missing from public kit)
- secrets-pre-guard.sh: PreToolUse:Edit|Write — token-pattern scan with allowlist (RULE 0.8)
- chat-numeric-prewarn.sh: UserPromptSubmit reminder when prompt mentions time/cost
                            (RULE 0.18 chat extension)
- chat-numeric-postflag.sh: Stop event scans last assistant message for naked numerics
                             without REAL/FROM-JOURNAL/ESTIMATE-HTC markers

Source: full Sonnet test-retest audit 2026-05-02 (3 parallel waves of 6 agents each)
identified hook chain bugs as HIGH severity in all 3 runs independently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 21:38:47 +08:00

73 lines
2.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# RULE 0.18 — Numeric claim enforcement — block Edit/Write of numeric claims
# without evidence marker. Bypass: RULE_017_BYPASS=1 prefix (kept for compat).
#
# Reads tool-call JSON on stdin (Claude Code hook protocol).
set -euo pipefail
# Bypass check
if [[ "${RULE_017_BYPASS:-0}" = "1" ]]; then
exit 0
fi
# Read the tool input from stdin
INPUT="$(cat)"
# Extract the new content (Edit: new_string, Write: content)
NEW_CONTENT="$(printf '%s' "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)"
if [[ -z "$NEW_CONTENT" ]]; then
exit 0
fi
# Patterns that indicate a numeric claim
# - "~N min/hour/day/week"
# - "N MB/GB/LOC/tests/crates/atomars"
# - "~$N", "$N/mo"
# - "Nm Ns", "займёт N", "should take N"
NUMERIC_PATTERN='(~\s*[0-9]+(\.[0-9]+)?\s*(min|minute|hour|hr|day|week|month|sec|second|MB|GB|KB|LOC|line|test|crate|atomar|%|µs|ms|ns|TPS|req/s)|[0-9]+m\s*[0-9]+s|\$[0-9]+(\.[0-9]+)?(/(mo|hr|day|run))?|~\s*\$[0-9]+|should take|will take|takes about|займёт|за ~|estimated at|ETA[: ]|approximately\s+[0-9])'
# Markers that satisfy the rule
EVIDENCE_PATTERN='\[(REAL|FROM-JOURNAL|ESTIMATE-HTC)[: ]'
# Check if numeric pattern present
if ! echo "$NEW_CONTENT" | grep -iqE "$NUMERIC_PATTERN"; then
exit 0
fi
# Numeric pattern present — check for evidence marker
if echo "$NEW_CONTENT" | grep -qE "$EVIDENCE_PATTERN"; then
exit 0
fi
# Violation
MATCHED="$(echo "$NEW_CONTENT" | grep -iEo "$NUMERIC_PATTERN" | head -3 | tr '\n' '; ')"
cat >&2 <<EOF
═══════════════════════════════════════════════════════════════════
RULE 0.18 — Numeric claim without evidence marker.
═══════════════════════════════════════════════════════════════════
Found in Edit/Write content:
$MATCHED
Required: append ONE of these markers in the same paragraph:
[REAL: <file:line | commit | timestamp>]
[FROM-JOURNAL: ~/.claude/memory/time-metrics/<file>.jsonl#<id>]
[ESTIMATE-HTC: <one-sentence reason this can't be measured yet>]
Or write the actual measurement to a JSONL journal first:
echo '{"kind":"task","name":"...","duration_s":...}' \\
>> ~/.claude/memory/time-metrics/tasks.jsonl
Then cite that line.
Bypass (visible, per-call):
RULE_017_BYPASS=1 <command>
See: ~/.claude/rules/numeric-claims-evidence.md
═══════════════════════════════════════════════════════════════════
EOF
exit 2