feat(security): add gitleaks pre-commit scan, wire into task check (infra#39)
Ported from mathias/infra's .gitleaks.toml (2026-08-04) -- same homelab bearer/MCP-token rules, plus a BRAIN_PG_DSN pattern for the hyperguild#20 leak class. Triaged 10 initial findings: 9 were claudewatcher's own scrubber test fixtures (deliberately fake), 1 was gitleaks matching the literal placeholder word "REDACTED" in a plan doc. Both allowlisted with rationale, clean scan confirmed before wiring into `task check`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016FQX4RwxyxuQY4yf4XaACL
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# gitleaks config for the hyperguild repo (infra#39 — leak prevention pass,
|
||||
# Phase 3 checklist item: "gitleaks pre-commit hook in infra AND hyperguild").
|
||||
#
|
||||
# Ported from mathias/infra's .gitleaks.toml (2026-08-04) — same homelab
|
||||
# token-shape rules, minus the SOPS/searxng allowlists infra needed (this
|
||||
# repo doesn't use SOPS).
|
||||
|
||||
title = "hyperguild gitleaks config"
|
||||
|
||||
[extend]
|
||||
useDefault = true
|
||||
|
||||
# --- Homelab-specific rules -------------------------------------------------
|
||||
|
||||
[[rules]]
|
||||
id = "homelab-static-bearer"
|
||||
description = "Homelab MCP/LLM static bearer or API key assigned a long literal value"
|
||||
regex = '''(?i)\b(DMABE_[A-Z0-9_]+|[A-Z0-9_]*MCP_TOKEN|ROUTING_MCP_TOKEN|INFRA_MCP_TOKEN|BRAIN_MCP_TOKEN|GITEA_MCP_TOKEN|LITELLM_MASTER_KEY|LITELLM_SALT_KEY|DMABE_LLMAPI_KEY|BRAIN_PG_DSN)\s*[:=]\s*['"]?([A-Za-z0-9/_+.\-]{16,})['"]?'''
|
||||
keywords = ["dmabe_", "mcp_token", "litellm_master_key", "litellm_salt_key", "llmapi_key", "brain_pg_dsn"]
|
||||
[[rules.allowlists]]
|
||||
description = "Env indirection is not a literal secret"
|
||||
regexes = [
|
||||
'''os\.environ''',
|
||||
'''valueFrom''',
|
||||
'''secretKeyRef''',
|
||||
'''\$\{?[A-Za-z_][A-Za-z0-9_]*\}?''',
|
||||
'''REDACTED''',
|
||||
'''<[A-Z_]+>''',
|
||||
]
|
||||
|
||||
[[rules]]
|
||||
id = "homelab-authorization-bearer"
|
||||
description = "Hardcoded Authorization: Bearer header"
|
||||
regex = '''(?i)authorization['"]?\s*[:=]\s*['"]?bearer\s+([A-Za-z0-9/_+.\-=]{16,})'''
|
||||
keywords = ["authorization", "bearer"]
|
||||
[[rules.allowlists]]
|
||||
description = "Env indirection is not a literal secret"
|
||||
regexes = [
|
||||
'''\$\{?[A-Za-z_][A-Za-z0-9_]*\}?''',
|
||||
'''os\.environ''',
|
||||
'''REDACTED''',
|
||||
'''<[A-Z_]+>''',
|
||||
]
|
||||
|
||||
# --- Global allowlist: claudewatcher's own scrubber test fixtures ------------
|
||||
# ingestion/internal/claudewatcher/{scrubber,watcher}_test.go deliberately
|
||||
# contain fake secret-shaped literals to test that the scrubber detects and
|
||||
# redacts them. Verified 2026-08-04: all 9 findings here are test fixtures
|
||||
# (github-pat, jwt, generic-api-key, homelab-authorization-bearer rules) plus
|
||||
# 1 doc finding that was gitleaks matching the literal placeholder word
|
||||
# "REDACTED" in a plan doc — not a real secret in either case.
|
||||
[[allowlists]]
|
||||
description = "claudewatcher scrubber test fixtures — deliberately fake secrets"
|
||||
paths = [
|
||||
'''ingestion/internal/claudewatcher/scrubber_test\.go$''',
|
||||
'''ingestion/internal/claudewatcher/watcher_test\.go$''',
|
||||
]
|
||||
|
||||
[[allowlists]]
|
||||
description = "Literal placeholder word REDACTED matched as if it were a token (verified 2026-08-04: extracted Secret == 'REDACTED' exactly, gitleaks' curl-auth-header rule matched the placeholder text itself, not a real credential)"
|
||||
condition = "AND"
|
||||
paths = ['''docs/superpowers/plans/2026-04-22-phase4-attempt-wiring\.md$''']
|
||||
regexes = ['''REDACTED''']
|
||||
@@ -101,6 +101,37 @@ tasks:
|
||||
- task: lint
|
||||
- task: test
|
||||
- task: vet
|
||||
- task: security:gitleaks
|
||||
|
||||
# ── Security ─────────────────────────────────────────────
|
||||
security:gitleaks:
|
||||
desc: Scan the working tree for secrets (gitleaks, fail-closed; skipped if gitleaks absent)
|
||||
dir: '{{.ROOT_DIR}}'
|
||||
cmds:
|
||||
- |
|
||||
GL="$(command -v gitleaks || true)"
|
||||
[ -z "$GL" ] && [ -x "$(go env GOPATH 2>/dev/null)/bin/gitleaks" ] && GL="$(go env GOPATH)/bin/gitleaks"
|
||||
if [ -z "$GL" ]; then
|
||||
echo "⚠ gitleaks not installed — skipping secret scan (CI enforces it)."
|
||||
echo " Install: go install github.com/zricethezav/gitleaks/v8@latest"
|
||||
exit 0
|
||||
fi
|
||||
"$GL" detect --no-git --redact --config .gitleaks.toml --source .
|
||||
|
||||
security:gitleaks:history:
|
||||
desc: "One-time FULL-HISTORY secret audit (infra#39 rotation pass; not a per-push gate)"
|
||||
dir: '{{.ROOT_DIR}}'
|
||||
cmds:
|
||||
- |
|
||||
GL="$(command -v gitleaks || true)"
|
||||
[ -z "$GL" ] && [ -x "$(go env GOPATH 2>/dev/null)/bin/gitleaks" ] && GL="$(go env GOPATH)/bin/gitleaks"
|
||||
if [ -z "$GL" ]; then
|
||||
echo "gitleaks not installed: go install github.com/zricethezav/gitleaks/v8@latest" >&2
|
||||
exit 2
|
||||
fi
|
||||
echo "Scanning FULL git history (redacted). Known historical leaks are expected"
|
||||
echo "until the infra#39 rotation pass completes — triage against the rotation list."
|
||||
"$GL" detect --redact --config .gitleaks.toml
|
||||
|
||||
lint:
|
||||
cmds:
|
||||
|
||||
Reference in New Issue
Block a user