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 internals
  • node_modules/ -- third-party dependencies
  • vendor/ -- 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

PatternService
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 author and contributors fields
  • README acknowledgments sections
  • License file headers
  • Git commit messages (note: these persist in .git/ and are excluded from file sweep, but warn the user about git log)

Sample Data Replacement Standards

When replacing PII, use these safe substitutes:

Data TypeReplacementRationale
Phone numbers555-XXX-XXXX (e.g., 555-012-3456)FCC reserved range
Email addresses[email protected], [email protected]IANA reserved domain
Business namesGeneric 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 IDsyour-sheet-id with note (set SHEET_ID in .env)Guides user to configure
API keysyour-api-key-here with note (set in .env)Guides user to configure
Internal URLshttps://your-app.example.comSafe 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_NAME or 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:

  1. Verify .env is listed in .gitignore
  2. Verify .env.example exists with all required variables (placeholder values only)
  3. Verify no .env file 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

RuleRationale
Never auto-replace without user confirmationAutomated replacement can break functionality or remove intentional data
Never delete content -- always replaceDeletions can break code; replacements keep the app functional
Exclude .git/, node_modules/, binariesThese are managed externally and would produce false positives
Warn about git historygit log and reflog retain PII even after file-level scrubbing
Run verification pass after all replacementsEnsures no PII was missed or reintroduced
Check .env is gitignoredPrevents 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:

  1. BFG Repo-Cleaner -- fastest option for removing specific strings from history
  2. git filter-repo -- more flexible, can rewrite paths and content
  3. 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.