chore(hooks): pre-push gate, secret scan, pr:evidence drafter
Phase A of the quality-gate rollout — local + pre-push gates that stop
slop before it leaves the laptop. Preventative controls, per the
architect playbook.
- .husky/pre-commit — adds scripts/secret-scan.sh as the first check
before the existing Biome format/lint. Blocks the commit when a
likely secret is found in staged additions. Existing Biome behaviour
is preserved.
- .husky/pre-push (new) — runs `pnpm check` (lint + typecheck + test)
before the branch leaves the laptop. Mirrors the `check` job in
.github/workflows/ci.yml so PR-blocking issues surface locally first.
Bypassable for genuine emergencies with `git push --no-verify`.
- scripts/secret-scan.sh (new) — regex scan over staged additions for
the common accidents: AWS keys, Anthropic/OpenAI/GitHub/Slack/Google/
Stripe tokens, JWTs, PEM private key blocks, hardcoded password
literals. Validated against synthetic leaks of each type. No external
tool dependency — pure bash + grep, runs in <100ms typical.
- scripts/pr-evidence.sh (new) — drafts a PR_BODY.md from the diff vs
origin/main: file lists (added/modified/deleted), commit summaries,
and the QA checklist. Flags screenshots as REQUIRED when the diff
touches packages/web/src/{components,routes} or packages/desktop.
Wired as `pnpm pr:evidence` so it runs from anywhere in the
workspace.
- .gitignore — adds PR_BODY.md so drafts don't get committed by
accident.
Pre-commit stays fast (<2s typical). Heavy checks (typecheck, full
test run) live in pre-push.
2026-05-24 18:29:13 +01:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Secret scan for staged files.
|
|
|
|
|
#
|
|
|
|
|
# Pre-commit hook calls this against staged additions. Fast (no external
|
|
|
|
|
# tool; just regex over the staged diff). Designed to catch the common
|
|
|
|
|
# accidents — API keys committed alongside code — not to replace a full
|
|
|
|
|
# secret-scanning service.
|
|
|
|
|
#
|
|
|
|
|
# Exits non-zero with a clear message if a likely secret is found.
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
# Only scan added/modified content (the `+` lines in the staged diff).
|
|
|
|
|
# This avoids false positives from existing committed strings.
|
|
|
|
|
STAGED_DIFF=$(git diff --cached --diff-filter=ACMR --unified=0 -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.json' '*.yml' '*.yaml' '*.toml' '*.env*' '*.sh' '*.md' 2>/dev/null || true)
|
|
|
|
|
|
|
|
|
|
if [ -z "$STAGED_DIFF" ]; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Only look at added lines (starting with `+`, excluding diff headers `+++`).
|
|
|
|
|
ADDED=$(printf '%s\n' "$STAGED_DIFF" | grep -E '^\+[^+]' || true)
|
|
|
|
|
|
|
|
|
|
if [ -z "$ADDED" ]; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
FOUND=0
|
|
|
|
|
FINDINGS=""
|
|
|
|
|
|
|
|
|
|
check_pattern() {
|
|
|
|
|
local name="$1"
|
|
|
|
|
local pattern="$2"
|
|
|
|
|
# Use `-e` to safely pass patterns that begin with `-` (e.g. PEM headers).
|
|
|
|
|
if printf '%s\n' "$ADDED" | grep -qE -e "$pattern"; then
|
|
|
|
|
FOUND=1
|
|
|
|
|
FINDINGS="${FINDINGS} - ${name}\n"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check_pattern "AWS access key" 'AKIA[0-9A-Z]{16}'
|
|
|
|
|
check_pattern "AWS secret key (high-entropy)" 'aws_secret_access_key[[:space:]]*[:=][[:space:]]*[A-Za-z0-9/+=]{40}'
|
|
|
|
|
check_pattern "Anthropic API key" 'sk-ant-[a-zA-Z0-9_-]{32,}'
|
|
|
|
|
check_pattern "OpenAI API key" 'sk-[a-zA-Z0-9]{20,}T3BlbkFJ[a-zA-Z0-9]{20,}'
|
|
|
|
|
check_pattern "OpenAI project key (newer)" 'sk-proj-[a-zA-Z0-9_-]{40,}'
|
|
|
|
|
check_pattern "GitHub personal access token" 'gh[psoru]_[A-Za-z0-9_]{36,}'
|
|
|
|
|
check_pattern "GitHub fine-grained PAT" 'github_pat_[A-Za-z0-9_]{82,}'
|
|
|
|
|
check_pattern "Slack token" 'xox[abprs]-[A-Za-z0-9-]{10,}'
|
|
|
|
|
check_pattern "Google API key" 'AIza[0-9A-Za-z_-]{35}'
|
|
|
|
|
check_pattern "Stripe live key" 'sk_live_[A-Za-z0-9]{24,}'
|
|
|
|
|
check_pattern "Honcho-style JWT (likely)" 'eyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}'
|
|
|
|
|
check_pattern "RSA/EC/DSA/OpenSSH private key block" '-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----'
|
|
|
|
|
check_pattern "Generic hardcoded password" '(password|passwd|pwd)[[:space:]]*[:=][[:space:]]*["'\'']\w{8,}["'\'']'
|
|
|
|
|
|
2026-06-02 15:12:33 -05:00
|
|
|
# Environment-specific values — keep live infra out of committed code/docs/PRs.
|
|
|
|
|
# Use examples instead (honcho.example.net; 192.0.2.x per RFC 5737 TEST-NET).
|
|
|
|
|
check_pattern "Tailnet hostname (env-specific; use example.net)" '[A-Za-z0-9-]+\.ts\.net'
|
|
|
|
|
check_pattern "Tailnet/CGNAT IP (env-specific; use 192.0.2.x)" '100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.[0-9]{1,3}\.[0-9]{1,3}'
|
|
|
|
|
|
chore(hooks): pre-push gate, secret scan, pr:evidence drafter
Phase A of the quality-gate rollout — local + pre-push gates that stop
slop before it leaves the laptop. Preventative controls, per the
architect playbook.
- .husky/pre-commit — adds scripts/secret-scan.sh as the first check
before the existing Biome format/lint. Blocks the commit when a
likely secret is found in staged additions. Existing Biome behaviour
is preserved.
- .husky/pre-push (new) — runs `pnpm check` (lint + typecheck + test)
before the branch leaves the laptop. Mirrors the `check` job in
.github/workflows/ci.yml so PR-blocking issues surface locally first.
Bypassable for genuine emergencies with `git push --no-verify`.
- scripts/secret-scan.sh (new) — regex scan over staged additions for
the common accidents: AWS keys, Anthropic/OpenAI/GitHub/Slack/Google/
Stripe tokens, JWTs, PEM private key blocks, hardcoded password
literals. Validated against synthetic leaks of each type. No external
tool dependency — pure bash + grep, runs in <100ms typical.
- scripts/pr-evidence.sh (new) — drafts a PR_BODY.md from the diff vs
origin/main: file lists (added/modified/deleted), commit summaries,
and the QA checklist. Flags screenshots as REQUIRED when the diff
touches packages/web/src/{components,routes} or packages/desktop.
Wired as `pnpm pr:evidence` so it runs from anywhere in the
workspace.
- .gitignore — adds PR_BODY.md so drafts don't get committed by
accident.
Pre-commit stays fast (<2s typical). Heavy checks (typecheck, full
test run) live in pre-push.
2026-05-24 18:29:13 +01:00
|
|
|
if [ $FOUND -eq 1 ]; then
|
2026-06-02 15:12:33 -05:00
|
|
|
printf '\n\033[31m✗ Secret scan: potential secrets or environment-specific values in staged changes\033[0m\n' >&2
|
chore(hooks): pre-push gate, secret scan, pr:evidence drafter
Phase A of the quality-gate rollout — local + pre-push gates that stop
slop before it leaves the laptop. Preventative controls, per the
architect playbook.
- .husky/pre-commit — adds scripts/secret-scan.sh as the first check
before the existing Biome format/lint. Blocks the commit when a
likely secret is found in staged additions. Existing Biome behaviour
is preserved.
- .husky/pre-push (new) — runs `pnpm check` (lint + typecheck + test)
before the branch leaves the laptop. Mirrors the `check` job in
.github/workflows/ci.yml so PR-blocking issues surface locally first.
Bypassable for genuine emergencies with `git push --no-verify`.
- scripts/secret-scan.sh (new) — regex scan over staged additions for
the common accidents: AWS keys, Anthropic/OpenAI/GitHub/Slack/Google/
Stripe tokens, JWTs, PEM private key blocks, hardcoded password
literals. Validated against synthetic leaks of each type. No external
tool dependency — pure bash + grep, runs in <100ms typical.
- scripts/pr-evidence.sh (new) — drafts a PR_BODY.md from the diff vs
origin/main: file lists (added/modified/deleted), commit summaries,
and the QA checklist. Flags screenshots as REQUIRED when the diff
touches packages/web/src/{components,routes} or packages/desktop.
Wired as `pnpm pr:evidence` so it runs from anywhere in the
workspace.
- .gitignore — adds PR_BODY.md so drafts don't get committed by
accident.
Pre-commit stays fast (<2s typical). Heavy checks (typecheck, full
test run) live in pre-push.
2026-05-24 18:29:13 +01:00
|
|
|
printf '%b' "$FINDINGS" >&2
|
|
|
|
|
printf '\n' >&2
|
|
|
|
|
printf 'If this is a false positive, bypass with: \033[33mgit commit --no-verify\033[0m\n' >&2
|
2026-06-02 15:12:33 -05:00
|
|
|
printf 'Otherwise: remove the secret/value (use an example), rotate if a credential, and re-stage.\n\n' >&2
|
chore(hooks): pre-push gate, secret scan, pr:evidence drafter
Phase A of the quality-gate rollout — local + pre-push gates that stop
slop before it leaves the laptop. Preventative controls, per the
architect playbook.
- .husky/pre-commit — adds scripts/secret-scan.sh as the first check
before the existing Biome format/lint. Blocks the commit when a
likely secret is found in staged additions. Existing Biome behaviour
is preserved.
- .husky/pre-push (new) — runs `pnpm check` (lint + typecheck + test)
before the branch leaves the laptop. Mirrors the `check` job in
.github/workflows/ci.yml so PR-blocking issues surface locally first.
Bypassable for genuine emergencies with `git push --no-verify`.
- scripts/secret-scan.sh (new) — regex scan over staged additions for
the common accidents: AWS keys, Anthropic/OpenAI/GitHub/Slack/Google/
Stripe tokens, JWTs, PEM private key blocks, hardcoded password
literals. Validated against synthetic leaks of each type. No external
tool dependency — pure bash + grep, runs in <100ms typical.
- scripts/pr-evidence.sh (new) — drafts a PR_BODY.md from the diff vs
origin/main: file lists (added/modified/deleted), commit summaries,
and the QA checklist. Flags screenshots as REQUIRED when the diff
touches packages/web/src/{components,routes} or packages/desktop.
Wired as `pnpm pr:evidence` so it runs from anywhere in the
workspace.
- .gitignore — adds PR_BODY.md so drafts don't get committed by
accident.
Pre-commit stays fast (<2s typical). Heavy checks (typecheck, full
test run) live in pre-push.
2026-05-24 18:29:13 +01:00
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
exit 0
|