# 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.

- Skill: `0xjitsu/pii-sweep` (Agent Skill)
- Install (CLI): `npx skillmds@latest add 0xjitsu/pii-sweep`
- Raw SKILL.md: https://api.skillmd.com/api/skills/0xjitsu/pii-sweep/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- License: AGPL-3.0-or-later
- Author: 0xjitsu (https://skillmd.com/u/0xjitsu)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/0xjitsu/pii-sweep

---


# 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

```regex
[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:
```regex
\b\d{3}[-.]?\d{3}[-.]?\d{4}\b
```

International formats:
```regex
\+\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

```regex
spreadsheetId.*['\"][a-zA-Z0-9_-]{20,}['\"]
```

Google Sheets URLs:
```regex
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:
```regex
\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 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_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    -> 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:

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.

