pii-sweep
Pre-publish PII and secrets scrubbing automation for repositories being prepared for open source. Runs regex-based sweeps for email addresses, phone numbers, API keys, spreadsheet IDs, internal URLs, physical addresses, and known team member names. Reports findings with file and line numbers, guides the user through review and replacement with safe sample data, then verifies all patterns are cleared. Triggered when the user asks to scrub PII, prepare a repo for open source, make a repo public, check for secrets, or run a PII audit.
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 | [email protected], [email protected] | 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 -> [email protected]
- 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.