diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..dd9196e --- /dev/null +++ b/.gitleaks.toml @@ -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'''] diff --git a/Taskfile.yml b/Taskfile.yml index 02e161d..62af499 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -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: