secrets-hygiene-reviewer
The secrets surface that isn't at-commit-time. GHAS secret scanning catches the "secret pasted into a file and committed" case beautifully; this skill catches everything else.
When to use
- New service onboarding to GHAS — confirms hygiene beyond commit-scan coverage.
- After a near-miss (a secret got logged, almost got pushed, almost got into an error page).
- Container-build hardening review.
- Portfolio audit of high-blast-radius repos.
When NOT to use
- At-commit secret scanning → GHAS native (already covered by push protection per [[ghas-config-reviewer]] baseline).
- Secret rotation operations → not in scope; this skill flags rotation gaps, doesn't perform rotation.
- Cryptographic key review → that's design review (
secure-design-reviewerkey-management category).
Check categories
1. Runtime exposure
| Check | Severity |
|---|---|
Env var named *_SECRET, *_TOKEN, *_KEY, *_PASSWORD referenced in log.info/debug or printf-style output |
HIGH |
| Error pages / 500-handlers that include env vars in the response body | CRITICAL |
| Metrics labels include secret-named env vars (Prometheus / OpenTelemetry) | HIGH |
| Observability tags / span attributes include secrets | HIGH |
Shell-history leakage at entry points (echo $TOKEN, set -x enabled without scrub) |
MEDIUM |
2. Rotation gaps
| Check | Severity |
|---|---|
Secret used past its declared expires_at (per the secrets registry, if available) |
HIGH |
| No documented rotation schedule for a secret | MEDIUM |
| Rotation depends on a manual step with no calendar reminder | LOW |
| Service has hardcoded retry-on-secret-failure that masks rotation events | MEDIUM |
3. Config-file hygiene
| Check | Severity |
|---|---|
.env.example contains a real value (not a placeholder) |
CRITICAL |
| Dotfile in a dev-container template includes a real secret | CRITICAL |
Helm values.yaml or chart default includes a real secret |
HIGH |
Terraform .tfvars includes a real secret (vs sourced from KMS / SSM) |
CRITICAL |
4. Container-layer leakage
| Check | Severity |
|---|---|
Dockerfile ARG SECRET=... or ENV SECRET=... in a non-multi-stage build (secret persists in final image layer) |
CRITICAL |
Secret baked into image at build-time visible via docker history |
CRITICAL |
Secret in .dockerignore-bypassed path (e.g., .git history when source is COPYed) |
HIGH |
5. Cross-environment reuse
| Check | Severity |
|---|---|
Same secret value used in prod and non-prod (per the secrets registry) |
CRITICAL |
| Service config references the same secret name across environments without per-env override | HIGH |
| Test fixtures use real secret values (rather than synthetic) | HIGH |
Workflow
- Identify scope: repo / service / image.
- Walk each check category against the scope.
- Per finding: cite specific evidence (line / file / image layer).
- Produce findings list: structured per usual ops-skill shape.
- Summary: counts per severity + by category — presented in chat.
- Persist the review — after presenting findings, write the full review note via
vault-writer.write_researchtovault/research/appsec/YYYY-MM-DD-secrets-hygiene-{scope-slug}.md(frontmatter perresearch.yml,topic: appsec) so the next portfolio audit can query prior reviews.
Output structure
# Secrets Hygiene Review — {scope}
## Summary
- Findings: {N} ({critical/high/medium/low counts})
- By category: runtime exposure / rotation / config-file / container-layer / cross-env
## Findings
[per finding: id, category, severity, evidence, remediation]
## Cross-references
- Existing GHAS secret scanning posture: {covered / gap}
- vault notes referenced: {list}
Lands at vault/research/appsec/YYYY-MM-DD-secrets-hygiene-{scope-slug}.md (workflow step 6).
Composes with
ghas-config-reviewer— at-commit scan coverage; this skill picks up where that one ends.vault-writer.write_research— persists the review note (workflow step 6).secure-design-reviewer— key management category overlaps.actions-workflow-hardener— ATH-005 (long-lived cloud creds) overlaps.
Acceptance test (for step 26 done-criteria)
5 check categories documented with at least 18 named checks; severity levels assigned per the org's blast-radius model. Live exercise against real repo deferred to first invocation.