thermograph/.claude/hooks/secrets-guard.sh
emi fbdfebf873
All checks were successful
shell-lint / shellcheck (push) Successful in 14s
secrets-guard / encrypted (push) Successful in 15s
PR build (required check) / changes (pull_request) Successful in 18s
secrets-guard / encrypted (pull_request) Successful in 15s
shell-lint / shellcheck (pull_request) Successful in 18s
PR build (required check) / validate-observability (pull_request) Successful in 37s
PR build (required check) / build-frontend (pull_request) Successful in 3m1s
PR build (required check) / build-backend (pull_request) Successful in 3m32s
PR build (required check) / gate (pull_request) Successful in 4s
guardrails: enforce live-host and secrets policy with hooks (#82)
2026-07-25 07:09:11 +00:00

34 lines
1.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# PreToolUse guard: never write infra/deploy/secrets/*.yaml directly.
#
# Every file matching that glob is SOPS-encrypted, and secrets-guard.yml fails the
# build if one is not. This is the same rule enforced one step earlier: a direct
# Write or Edit produces plaintext, and by the time CI catches it the secret has
# already been written to disk and probably committed.
#
# The supported path is `sops edit <file>`, which decrypts to a temp file, opens an
# editor and re-encrypts on save — the plaintext never touches the working tree.
#
# Denies rather than asks: there is no legitimate reason to hand-write one of these,
# so a prompt would only be an invitation to click through.
set -uo pipefail
payload=$(cat)
command -v jq >/dev/null 2>&1 || exit 0
path=$(printf '%s' "$payload" | jq -r '.tool_input.file_path // ""')
[ -n "$path" ] || exit 0
case "$path" in
*/infra/deploy/secrets/*.yaml)
jq -n --arg p "$path" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: ("Refusing to write \($p) directly — every file under infra/deploy/secrets/ is SOPS-encrypted and a direct write would land plaintext. Use `sops edit \($p)` instead, which re-encrypts on save. (secrets-guard.yml would fail the build on this anyway, but only after the secret was already on disk.)")
}
}'
exit 0
;;
*) exit 0 ;;
esac