skills/security-audit/SKILL.md
--- name: security-audit description: Pre-publish safety check for a git repo. Scans for secrets/tokens, PII, work identifiers, committed .env files, and risky git history. Use before making a private repo public, before pushing to a new remote, or before sharing a snapshot/zip. category: quality argument-hint: [path] [--quick] [--history-only] [--no-personal] allowed-tools: Bash Read Write Glob Grep --- # Security Audit Catches the things you'd be embarrassed (or fired) to ship publicly: API
npx skillsauth add RonanCodes/ronan-skills skills/security-auditInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Catches the things you'd be embarrassed (or fired) to ship publicly: API tokens, private keys, customer emails, internal hostnames, your real address in a CONTRIBUTORS file, the .env you accidentally committed in 2022 and forgot about.
/ro:security-audit # audit cwd, full scan (working tree + git history)
/ro:security-audit /path/to/repo # audit a specific repo
/ro:security-audit --quick # working tree only, skip git history (10× faster)
/ro:security-audit --history-only # only scan git history (use after fixing working tree)
/ro:security-audit --no-personal # skip the user's personal-patterns file
| Category | Detected by | Severity if found |
|----------|-------------|-------------------|
| Secrets / API tokens | gitleaks (100+ token patterns) + custom regex | 🔴 Critical |
| Private keys (SSH, PGP, X.509) | gitleaks | 🔴 Critical |
| Committed .env / .env.* files | git ls-files | 🔴 Critical |
| User's personal patterns | ~/.claude/security-audit-personal.txt | 🔴 Critical |
| Generic PII (emails, phone numbers) | ripgrep | 🟡 Warning |
| Internal hostnames (*.internal, *.local, *.corp) | ripgrep | 🟡 Warning |
| Suspicious commit messages (remove secret, delete password) | git log scan | 🟡 Warning |
| Large binary files (>5MB committed) | git ls-files | 🟡 Warning |
| .gitignore coverage gaps | template comparison | 🔵 Info |
| OS cruft (.DS_Store, Thumbs.db) | git ls-files | 🔵 Info |
| Auth posture (edge gate, phishing-resistant MFA, session hygiene) | Phase H heuristics | 🟡 Warning |
# gitleaks — primary secret scanner
which gitleaks >/dev/null 2>&1 || {
echo "Installing gitleaks..."
brew install gitleaks
}
If brew isn't available, fall back to manual pattern scanning (less coverage). Tell the user:
⚠️ gitleaks not installed. Falling back to regex-only scan (lower confidence).
Install: brew install gitleaks
cd into it. Otherwise use cwd.git rev-parse --git-dir — if not, abort with a clear message.git rev-parse --show-toplevel, current branch, git remote -v, total commits.Working tree:
gitleaks detect --no-banner --redact --report-format json --report-path /tmp/gitleaks-tree.json --source .
Git history (skip if --quick):
gitleaks detect --no-banner --redact --report-format json --report-path /tmp/gitleaks-history.json --source . --log-opts="--all"
Parse JSON: count findings, group by rule (e.g. "AWS Access Key", "GitHub PAT"), capture file + line + redacted snippet. Never print the actual secret — --redact keeps it safe.
git ls-files | grep -E '(^|/)\.env(\..+)?$' | grep -vE '\.env\.(example|template|sample)$'
Any hit = critical. Recommended fix:
git rm --cached .env
echo '.env' >> .gitignore
git commit -m "🔒 security: stop tracking .env"
# Then rotate any keys it contained, and use git-filter-repo to scrub from history
Look for the user's personal pattern file (in priority order):
./.security-audit-personal.txt (repo-local, gitignore it!)${CLAUDE_PLUGIN_DATA}/security-audit-personal.txt~/.claude/security-audit-personal.txtFormat: one pattern per line, # for comments. Each line is a ripgrep regex.
# Personal info — never share publicly
[email protected]
\bJane Doe\b
\b\+1[-\s]?555[-\s]?\d{3}[-\s]?\d{4}\b
123 Main Street
# Work info
acmecorp\.com
\bACME Internal\b
project-codename-falcon
If no file exists, skip this phase silently and add a tip to the final report:
💡 Tip: Create ~/.claude/security-audit-personal.txt with patterns specific to you
(real name, personal email, employer, internal project codenames) for stronger checks.
Run each pattern with ripgrep:
rg --no-heading --line-number --color=never -e "<pattern>" .
Two ripgrep passes against the working tree (skip node_modules, .git, dist, build, lockfiles):
Emails (excluding obvious examples):
rg --no-heading -n -e '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' \
--glob '!*.lock' --glob '!*.lockb' --glob '!node_modules' --glob '!.git' \
| grep -vE '@example\.(com|org)|@test\.com|@localhost|noreply@|@anthropic\.com'
Phone numbers (US/UK/EU formats):
rg --no-heading -n -e '\b(\+?\d{1,3}[-.\s]?)?\(?\d{3,4}\)?[-.\s]?\d{3,4}[-.\s]?\d{3,4}\b'
Group results by file. Phone false-positive rate is high (matches version numbers, IDs) — present as "review these" not "fix these."
rg --no-heading -n -e '\b[a-zA-Z0-9-]+\.(internal|local|corp|intranet|lan)\b'
Suspicious commit messages — these often are the leak (someone removed a secret in a later commit, but it's still in history):
git log --all --oneline --grep -E -i 'secret|password|token|api[_-]?key|credential|leak|oops|whoops|remove key|delete key' \
| head -30
Each match = "this commit may contain a secret in its diff — investigate with git show <sha>."
Tracked files that probably shouldn't be:
git ls-files | grep -E '(\.DS_Store|Thumbs\.db|\.idea/|\.vscode/settings\.json|\.swp$|\.pyc$|__pycache__|\.log$)$'
Large committed files (>5MB):
git ls-files | xargs -I{} sh -c 'if [ -f "{}" ]; then size=$(wc -c <"{}"); if [ "$size" -gt 5242880 ]; then echo "${size} {}"; fi; fi' 2>/dev/null
.gitignore coverage gaps — check if these common entries are missing:
.env
.env.*
.DS_Store
node_modules/
__pycache__/
*.pyc
.idea/
.vscode/
*.log
dist/
build/
Show only the missing ones, not the present ones.
For any app with a sign-in (look for clerk, better-auth, workos, an auth route, or a session cookie), check it against the authentication-hardening playbook (llm-wiki-security/wiki/playbooks/authentication-hardening.md). Auth is the main attack surface once data is encrypted, so flag gaps:
# Is there a public login route on what looks like a single-user / internal app?
git ls-files | grep -iE 'routes/.*(sign-in|login|auth)' | head
# Any phishing-resistant factor wired? (passkey / webauthn / FIDO)
grep -rilE 'passkey|webauthn|fido2?' src 2>/dev/null | head
# SMS / TOTP-only MFA (NOT phishing-resistant) used as the second factor?
grep -rilE 'sms|twilio|totp|otp' src 2>/dev/null | head
# Long-lived / never-expiring sessions?
grep -rinE 'maxAge|expiresIn|session.*(ttl|expir)' src 2>/dev/null | head
Warn (not block) when:
Point the user at the playbook for the full standard rather than restating it here.
Print a single structured report. Use this layout — terminal-friendly, no fluff:
🔒 Security Audit — <repo path>
Branch: <branch> | Commits scanned: <n> | Mode: <full|quick>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔴 CRITICAL — fix before sharing
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[1] AWS Access Key in .env.production:3
Detected by: gitleaks (rule: aws-access-token)
Also in history: yes (commits: a3f2b1c, 9c4e2a1)
Fix:
1. Rotate this key in AWS console NOW (treat as compromised)
2. git rm --cached .env.production && add to .gitignore
3. Scrub history: git filter-repo --path .env.production --invert-paths
4. Force-push (coordinate with team) and notify any forks
[2] Committed .env file: backend/.env
Fix:
1. Rotate any keys it contains
2. git rm --cached backend/.env
3. echo 'backend/.env' >> .gitignore
4. Commit + scrub history (see above)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🟡 WARNING — review
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[3] Personal email found (3 occurrences):
- README.md:42 ([email protected])
- CONTRIBUTORS:1 ([email protected])
- package.json:5 ([email protected])
Fix: replace with a public alias or no-reply address.
[4] Internal hostname references (2):
- docs/deploy.md:18 (db.acme.internal)
- scripts/sync.sh:4 (api.acme.internal)
Fix: replace with placeholders like <DB_HOST>.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔵 INFO — consider
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[5] .gitignore is missing common entries: .env, .DS_Store, dist/
[6] Large file committed: assets/demo.mov (47MB) — consider git-lfs
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Summary: 2 critical · 2 warning · 2 info
Verdict: 🚫 NOT SAFE TO PUBLISH — fix critical issues first.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If zero critical findings:
Verdict: ✅ Safe to publish.
(After addressing warnings if any contain info you don't want public.)
If critical findings exist, ask the user (via AskUserQuestion):
For history scrubbing, always recommend git-filter-repo over git filter-branch (deprecated). Warn that force-pushing rewritten history breaks anyone who has cloned/forked.
git rm.# gitleaks:allow comment to whitelist legitimate cases..security-audit-personal.txt to your global ~/.gitignore_global.git filter-repo. Force-pushing breaks open PRs and forks.First-time setup, suggest this to the user:
cat > ~/.claude/security-audit-personal.txt <<'EOF'
# Lines starting with # are comments. Each non-empty line is a ripgrep regex.
# Keep this file private — it itself contains the things you don't want public.
# Your personal contact info
[email protected]
\b<Your Real Name>\b
\b\+\d{1,3}[-\s]?\d{3,4}[-\s]?\d{3,4}[-\s]?\d{3,4}\b
# Your employer / venture identifiers
yourcompany\.com
\b<Internal Codename>\b
# Sensitive customer / partner names
\b<Customer Name>\b
EOF
chmod 600 ~/.claude/security-audit-personal.txt
If invoked with --json, write the full report to /tmp/security-audit-<timestamp>.json and print only the path. Schema:
{
"repo": "/abs/path",
"branch": "main",
"scanned_at": "2026-04-19T14:32:00Z",
"mode": "full",
"findings": [
{
"severity": "critical",
"category": "secret",
"rule": "aws-access-token",
"file": ".env.production",
"line": 3,
"in_history": true,
"history_commits": ["a3f2b1c", "9c4e2a1"],
"fix": ["Rotate key", "git rm --cached .env.production", "..."]
}
],
"summary": { "critical": 2, "warning": 2, "info": 2 },
"verdict": "not_safe"
}
development
--- name: worktree description: Coordinate multiple agents on one repo via a worktree-lock pool, so two agents never clobber each other's working tree. Acquire the first free slot (main, then beta/gamma… worktrees, created on demand), work there on your own branch, release when you've pushed. Use before modifying any repo that might be in use by another agent (factory, dataforce, etc.), or whenever you're told a repo is being worked on. Backed by `ro worktree`. category: development argument-hin
testing
--- name: ship description: Ship a feature branch the local-CI-first way — run the full local gate, push, open a PR, squash-merge, then deploy, without waiting on GitHub Actions. Use when a branch is ready for main and you want it merged and deployed now. Reads CI policy from `ro ci` (default skips remote CI because GitHub Actions billing keeps hitting limits). Sibling to /ro:gh-ship (waits on GitHub checks) and /ro:cf-ship (the deploy half). Triggers on "ship it", "ship this", "merge and deploy
testing
--- name: setup-logging description: Set up (or audit) the observability stack in a TanStack Start + Cloudflare Workers app so it is "diagnosable by default" — structured logging (logtape) with a request context carrying trace_id + userId + tenant/orgId, a trace_id propagated FE→BE→logs→Sentry→PostHog, Cloudflare Workers observability enabled, and Sentry + PostHog wired. Two modes: `setup` (wire it into an app) and `audit` (check an existing app + report gaps). Use when scaffolding a new app, wh
development
Manage credentials INSIDE the active ~/.claude/.env file — read which token/account to use for a given app (Simplicity vs Dataforce vs Ronan-personal), add or update a secret WITHOUT it passing through the chat (an interactive Terminal window prompts for it), and track secrets that were exposed in a transcript so they get rotated. Sibling to /ro:context (which switches WHICH env file is active). Use when the user wants to add an API key/token/secret, asks "which credential do I use for X", needs the env organized/labelled, or a secret was pasted into the chat and should be rotated.