manifest-dep-scan.sh 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. #!/bin/bash
  2. # hooks/manifest-dep-scan.sh
  3. # PostToolUse hook (matcher: Write|Edit) — the companion to pre-install-scan.sh.
  4. #
  5. # pre-install-scan covers `npm install` at the terminal. But in Claude Code the
  6. # dominant way a dependency enters is the agent EDITING a manifest (package.json,
  7. # requirements.txt, …) directly — no install command, so that hook never fires.
  8. # This hook closes that gap: when a dependency manifest is edited and the change
  9. # looks like it added/changed a version spec, it advises scoring the package via the
  10. # Socket depscore MCP and respecting the release-age cooldown BEFORE it gets installed.
  11. #
  12. # Advisory only (exit 0); never blocks an edit. Reads the tool call as JSON on stdin
  13. # (.tool_input.file_path / .new_string / .content), with a $1 fallback.
  14. #
  15. # Configuration in .claude/settings.json:
  16. # "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [
  17. # { "type": "command", "command": "bash \"$HOME/.claude/hooks/manifest-dep-scan.sh\"", "timeout": 5 } ]}]
  18. RAW=""; [[ ! -t 0 ]] && RAW="$(cat 2>/dev/null)"
  19. FILE=""; NEW=""
  20. if [[ -n "$RAW" ]] && command -v jq >/dev/null 2>&1; then
  21. FILE="$(printf '%s' "$RAW" | jq -r '.tool_input.file_path // empty' 2>/dev/null)"
  22. NEW="$(printf '%s' "$RAW" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)"
  23. fi
  24. [[ -z "$FILE" ]] && FILE="${1:-}"
  25. [[ -z "$FILE" ]] && exit 0
  26. case "$(basename "$FILE")" in
  27. package.json|composer.json|Cargo.toml|go.mod|Gemfile|pyproject.toml) ;;
  28. requirements*.txt) ;;
  29. *) exit 0 ;;
  30. esac
  31. # Only nudge when the change looks like a dependency version spec was added/changed —
  32. # avoids firing on unrelated manifest edits (scripts, version bumps, metadata).
  33. if [[ -n "$NEW" ]]; then
  34. # Find version-spec lines, then exclude the manifest's own metadata keys so a
  35. # `"version": "2.0.0"` bump or `name`/`description` edit doesn't false-fire.
  36. echo "$NEW" \
  37. | grep -E ':[[:space:]]*"[[:space:]~^><=v]*[0-9]|==[[:space:]]*[0-9]|=[[:space:]]*"[0-9]|[[:space:]]v?[0-9]+\.[0-9]' \
  38. | grep -qvE '"(version|name|description|license|homepage|repository|author|main|type)"[[:space:]]*:' \
  39. || exit 0
  40. fi
  41. echo "SUPPLY CHAIN: dependency manifest edited ($(basename "$FILE"))."
  42. echo "A dependency was added/changed by editing the manifest — it still has to be"
  43. echo "installed. Before that, score it and respect the release-age cooldown:"
  44. echo " - depscore (no auth): score the added package(s) via the socket MCP"
  45. echo " - cooldown/age: bash skills/supply-chain-defense/scripts/preinstall-check.sh <pkg>"
  46. echo " - never pull a day-zero version into anything that builds/runs."
  47. exit 0