pii-sweep
Automated PII and secrets detection for repositories being prepared for public release.
When to Trigger
Activate this skill when the user:
- Asks to scrub PII or remove personal data
- Wants to prepare a repo for open source or make a repo public
- Says "check for secrets" or "run a PII audit"
- Is about to publish or transfer ownership of a repository
- Asks to verify a repo is "clean" before release
Exclusions
Always skip these paths during scanning:
.git/-- repository internalsnode_modules/-- third-party dependenciesvendor/-- third-party dependencies*.min.js,*.min.css-- minified bundles- Binary files (images, fonts, compiled assets)
package-lock.json,bun.lockb,yarn.lock-- lockfiles
Sweep Patterns
Email Addresses
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
Exclude safe patterns: @example.com, @example.org, @your-domain.com, @users.noreply.github.com, noreply@
Flag everything else as a potential real email.
Phone Numbers
US formats:
\b\d{3}[-.]?\d{3}[-.]?\d{4}\b
International formats:
\+\d{1,3}[-.\s]?\(?\d{1,4}\)?[-.\s]?\d{1,4}[-.\s]?\d{1,9}
Exclude: Numbers that are clearly not phones (port numbers like 3000, 8080, 5432; hex values; timestamps).
API Keys and Tokens
| Pattern | Service |
|---|---|
sk_live_[a-zA-Z0-9]+ |
Stripe live key |
sk_test_[a-zA-Z0-9]+ |
Stripe test key |
ghp_[a-zA-Z0-9]+ |
GitHub PAT |
gho_[a-zA-Z0-9]+ |
GitHub OAuth token |
sntryu_[a-zA-Z0-9]+ |
Sentry user token |
xnd_[a-zA-Z0-9]+ |
Xendit API key |
Bearer [a-zA-Z0-9._-]+ |
Bearer tokens |
token:\s*"[^"]+" |
Generic token assignments |
AKIA[0-9A-Z]{16} |
AWS access key |
AIza[0-9A-Za-z_-]{35} |
Google API key |
xoxb-[0-9a-zA-Z-]+ |
Slack bot token |
xoxp-[0-9a-zA-Z-]+ |
Slack user token |
Spreadsheet and Document IDs
spreadsheetId.*['\"][a-zA-Z0-9_-]{20,}['\"]
Google Sheets URLs:
docs\.google\.com/spreadsheets/d/[a-zA-Z0-9_-]+
Also check for SHEET_ID, DOC_ID, SPREADSHEET_ID with hardcoded values.
Internal URLs
Flag domains that are NOT in the safe list:
Safe list: example.com, example.org, localhost, 127.0.0.1, 0.0.0.0, github.com, npmjs.com, vercel.app, shields.io, img.shields.io, creativecommons.org, opensource.org, keepachangelog.com, semver.org
Any other https?:// URL with a real-looking domain should be flagged for review.
Physical Addresses
Basic US address pattern:
\d{1,5}\s+[A-Z][a-z]+(\s+[A-Z][a-z]+)*\s+(St|Ave|Blvd|Dr|Rd|Ln|Way|Ct|Pl|Cir)\b
Also check for:
- ZIP codes near identifiable context:
\b\d{5}(-\d{4})?\b - City + State patterns:
[A-Z][a-z]+,\s*[A-Z]{2}\s+\d{5}
Known Team Member Names
Ask the user for a list of real names to search for. Search case-insensitively across all text files. Common locations where names hide:
- Code comments (
// Author: ...) - Package.json
authorandcontributorsfields - README acknowledgments sections
- License file headers
- Git commit messages (note: these persist in
.git/and are excluded from file sweep, but warn the user aboutgit log)
Sample Data Replacement Standards
When replacing PII, use these safe substitutes:
| Data Type | Replacement | Rationale |
|---|---|---|
| Phone numbers | 555-XXX-XXXX (e.g., 555-012-3456) |
FCC reserved range |
| Email addresses | user@example.com, admin@example.com |
IANA reserved domain |
| Business names | Generic descriptors: "Sample Cabinet Shop", "Metro Kitchen & Bath", "Acme Corp" | Clearly fictional |
| Person names | "Jane Doe", "John Smith", "Alex Johnson" | Obvious placeholders |
| Physical addresses | "123 Main St, Anytown, CA 90210" | Recognizable fake |
| Spreadsheet IDs | your-sheet-id with note (set SHEET_ID in .env) |
Guides user to configure |
| API keys | your-api-key-here with note (set in .env) |
Guides user to configure |
| Internal URLs | https://your-app.example.com |
Safe placeholder |
Workflow
Phase 1: First Pass Scan
Run all patterns across the repository. For each finding, report:
[CATEGORY] file/path:line_number
Matched: <the matched text, truncated if long>
Context: <surrounding line for review>
Group findings by category. Show a summary count:
Sweep Results:
Emails: 3 findings (2 files)
Phone numbers: 1 finding (1 file)
API keys: 2 findings (1 file)
Spreadsheet IDs: 0 findings
Internal URLs: 4 findings (3 files)
Addresses: 0 findings
Names: 1 finding (1 file)
--------------------------------
Total: 11 findings
Phase 2: User Review
Present each finding and ask the user to classify:
- True positive -- real PII, needs replacement
- False positive -- safe to ignore (e.g., test fixture, example data)
- Move to .env -- secret that should become an environment variable
Do NOT auto-replace anything. Wait for explicit user confirmation on each finding or batch.
Phase 3: Replace
For true positives:
- Replace with the appropriate sample data from the standards table above
- If moving to
.env, add the variable to.env(gitignored) and.env.example(with placeholder) - Update the code to read from
process.env.VARIABLE_NAMEor equivalent
For each replacement, show a before/after diff.
Phase 4: Verification Pass
Re-run all sweep patterns. The goal is zero findings (excluding acknowledged false positives).
If new findings appear (e.g., a replacement introduced a new pattern match), flag and resolve.
Phase 5: Environment File Check
If the project uses .env:
- Verify
.envis listed in.gitignore - Verify
.env.exampleexists with all required variables (placeholder values only) - Verify no
.envfile is tracked in git:git ls-files .env
Phase 6: Final Report
PII Sweep Complete
==================
Files scanned: 142
Patterns checked: 7 categories
Findings: 11 total
True positives: 8 (replaced)
False positives: 3 (acknowledged)
Moved to .env: 2
Replacements made:
- src/config.ts:14 email -> user@example.com
- src/config.ts:15 api_key -> moved to .env
- README.md:45 phone -> 555-012-3456
...
Remaining warnings:
- Git history contains real names in commit messages
(consider: git filter-branch or BFG Repo Cleaner)
- 2 false positives acknowledged by user
Status: READY FOR PUBLIC RELEASE
Safety Rules
| Rule | Rationale |
|---|---|
| Never auto-replace without user confirmation | Automated replacement can break functionality or remove intentional data |
| Never delete content -- always replace | Deletions can break code; replacements keep the app functional |
Exclude .git/, node_modules/, binaries |
These are managed externally and would produce false positives |
| Warn about git history | git log and reflog retain PII even after file-level scrubbing |
| Run verification pass after all replacements | Ensures no PII was missed or reintroduced |
Check .env is gitignored |
Prevents secrets from being committed in the future |
Git History Warning
File-level scrubbing does not clean git history. If the repository previously contained real PII in committed files, warn the user about these options:
- BFG Repo-Cleaner -- fastest option for removing specific strings from history
- git filter-repo -- more flexible, can rewrite paths and content
- Fresh repo -- if history is not important, create a new repo with a clean initial commit
This is outside the scope of pii-sweep (destructive git operations require explicit user action) but must always be mentioned in the final report if any true positives were found in tracked files.