pii-exposure-audit
Systematic repo-wide audit for personal information exposure — API keys, tokens, passwords, Windows paths, usernames, emails, Discord IDs, Nostr keys, personal domains, and business contact info. Use
SKILL.mdskills/github/pii-exposure-audit/SKILL.md
GitHubPII Exposure Audit
Systematic multi-pattern scanning to find personal information committed to a repo. Complements codebase-hardening (which covers app-security vulnerabilities, fork cleanup, and generic key removal) by focusing on what the repo reveals about its owner.
When to Use
When asked to:
- "Audit this repo for personal info before making it public"
- "Generate a sanitization checklist"
- "Find all instances of my name, email, or paths in the codebase"
- "Check for credentials committed to the repo"
- "What sensitive information is in this config?"
- "Prepare this repo for open-sourcing"
Workflow
Phase 1: Map the Repo
find . -not -path './.git/*' -type f | sort
Identify high-value targets: config files (.yaml, .json), scripts (.py, .sh), documentation (.md), profile/SOUL files, key stores.
Phase 2: Parallel Pattern Scan
Run all searches simultaneously — do NOT serialize them. Each searches the repo root (excluding .git).
| Category | Pattern | What It Finds |
|---|---|---|
| OpenAI/standard keys | sk-[a-zA-Z0-9]{20,} |
OpenAI-style API keys |
| GitHub tokens | gh[ps]_[a-zA-Z0-9]{10,} |
GH PATs/OAuth |
| HuggingFace tokens | hf_[a-zA-Z0-9]{10,} |
HF API tokens |
| GitLab tokens | glpat-[a-zA-Z0-9]{10,} |
GitLab PATs |
| Slack tokens | xox[bp]-[a-zA-Z0-9]{10,} |
Slack bot/user tokens |
| Bearer tokens | Bearer\s+[a-zA-Z0-9_-]{20,} |
Raw bearer auth headers |
| Generic key/secret | (Token|Secret|Password|Api[_-]?Key)\s*[:=]\s*['"][a-zA-Z0-9_\-\.]{8,} |
Catch-all credentials |
| Nostr nsec | nsec1[ac-z][a-z0-9]{50,} |
Nostr private keys |
| SHA-256 hex | [0-9a-f]{64} |
Potential private keys (verify context — also catches hashes) |
| Discord IDs | \b[0-9]{17,19}\b |
Discord channel/guild/user IDs |
| Emails | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} |
Email addresses |
| Windows paths | C:\\Users\\|C:/Users/ |
Reveals Windows username |
| Owner data folder | <detected_owner_string> |
Personal folder layout |
| IP addresses | \b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b |
Hardcoded IPs |
| Calendar URLs | cal\.com|calendly |
Personal booking links |
| Phone numbers | \(?[0-9]{3}\)?[-. ]?[0-9]{3}[-. ]?[0-9]{4} |
Contact numbers |
| Personal domains | <owner_domain>\.[a-z]+ |
Personal/business domain |
Phase 3: Read High-Value Files Directly
After pattern search, read the files most likely to carry credentials:
*config.yaml/*config.yml— MCP serverenv:sections often have plaintext credential fallbacks alongside${VAR_REF}markers*keys.json— Nostr key stores, API key directories.env.example— Reveals credential naming conventions- Scripts referencing
gmail,smtp,password,token,api_keyas Python constants - Profile/SOUL identity files — leak real names and contact info
Phase 4: Cross-Reference Env-Ref Credentials
When a config says ${VAR_NAME}, check:
.env.examplefor hardcoded fallback values for the same name- Python scripts for constants matching the env var name
- The real-secrets escape happens when config says
${SECRET_KEY}but a sibling script hardcodesSECRET_KEY = "plaintext-value"as a Python constant
Phase 5: Triage by Severity
| Tier | Label | Criteria | Example |
|---|---|---|---|
| 🔴 CRITICAL | Live credentials | Plaintext key/password that actually authenticates | GMAIL_SENDER_PASSWORD: ${GMAIL_APP_PASSWORD} |
| 🟠 HIGH | Credential-adjacent | Partially redacted keys, default passwords, internal auth keys | Truncated Nostr secrets, postgres:postgres@localhost |
| 🟡 MEDIUM | Personal info & paths | Absolute paths with owner name, emails, domains, calendar links | ${MY_REPOS}/Documents/..., your-domain.example |
| ⚪ LOW | Metadata & IDs | Channel UUIDs, Discord IDs, org GitHub handles | Discord channel IDs, Nostr channel UUIDs |
Phase 6: Produce Sanitization Checklist
Output as a grouped checklist. Each item has: file path, line number, what was found, what to replace it with.
CRITICAL (do first):
☐ config/config.yaml:431 — GMAIL_SENDER_PASSWORD in plaintext → replace with ${GMAIL_APP_PASSWORD}
☐ scripts/email_intelligence.py:23 — same Gmail app password as Python constant → use env var
HIGH:
☐ buzz/keys.json — 47 Nostr secret keys exposed → regenerate if repo goes public
...
MEDIUM:
☐ config/gateway.yaml:712 — Absolute path ${MY_REPOS}/... → use relative or $REPO_ROOT
...
See Also
codebase-hardeningskill — app security audits (auth, injection, SSRF, CORS) and general code quality fixesreferences/hermes-config-pii-audit.md— full worked example covering a Hermes config repo with 20+ findings across 151 files
Pitfalls
- Template files look safe but reveal naming conventions:
.env.examplefiles withKEY_PLACEHOLDERnames tell attackers what env vars you use. This is low severity but worth noting. - Discord IDs are not credentials but reveal infra: Channel/guild IDs in config let anyone who sees them know your Discord server structure. Obfuscate or remove before public sharing.
- Nostr key files with "...truncated" secrets: If the key is truncated with
...it's NOT safe — the full key exists somewhere or was pasted in full at some point. Regenerate before going public. Caveat: read the file's raw bytes first —...in TOOL OUTPUT may be display-side redaction of a full key on disk. Both cases are findings, but disk-truncation means the value is already gone while display-truncation means the full key is sitting in the file. - Tool output masks secret-adjacent strings:
process.env.OPENROUTER_API_KEYrenders asproces..._KEY, emails may render as[email protected], JWTs render truncated. Never declare a file clean (or corrupt) from displayed content — grep the real unmasked pattern (e.g.process.env) or read raw bytes. Usesecrets-safe-config-audit'sscripts/classify-secret.pyfor redaction-proof classification (length/prefix/sha evidence, never values). - 555 phone numbers feel fake but may be real: If a config has
OFFER_PHONE: (555) 123-4567, verify it's actually the 555-reserved prefix before declaring it safe. - Duplicated Gmail credentials in multiple config files: A real Gmail app password often gets copy-pasted into every MCP server config section. Search for the password string once, find it in every file.
- Don't conflate env-refs with plaintext:
${GITHUB_TOKEN}is fine (reads from environment).GITHUB_TOKEN: ghp_xxxxxis critical. Check the actual value, not just the variable name.