pii-masker
v1.0.1
Published
Mask PII before sending content to AI chatbots (Cursor hooks, CLI, VS Code)
Downloads
206
Maintainers
Readme
PII Masker
Mask personally identifiable information (PII) before content is sent to AI chatbots. Use it in Cursor, VS Code (or any VS Code–based IDE), or as a CLI in your pipeline.
What gets masked
- Email addresses
- Phone numbers (US and international formats)
- Social Security Numbers (US)
- Credit card–like numbers (4×4 digits)
- IPv4 addresses
- AWS access keys (
AKIA...) - API keys / secrets (common
key=patterns) - Bearer tokens
Replacements are placeholders like [EMAIL_REDACTED], [PHONE_REDACTED], etc. Use scrambled mode for compact IDs like [E_a7f3b2], [P_9c2e1d] that stay consistent across the file.
Installation
Via npm:
npm install -g pii-masker # CLI globally: pii-mask
npm install pii-masker # Or as a project dependencyFrom source (clone this repo):
git clone https://github.com/your-username/pii-masker.git
cd pii-maskerFor Cursor hooks, use the path where pii-masker lives:
- Global install:
$(npm root -g)/pii-masker - Local install:
./node_modules/pii-masker - Cloned repo: your local path, e.g.
/Users/you/Projects/pii-masker
1. Cursor (IDE)
Cursor’s hooks run before a prompt is sent or a file is read. This repo includes two hooks:
- beforeSubmitPrompt – If the prompt (or attached context) contains PII, submission is blocked and the user is asked to remove or mask it.
- beforeReadFile – PII in file contents is masked before being sent as context to the AI (when Cursor supports returning modified content).
Project-level (this repo only)
If your project is this repo (or you copied it into your repo), Cursor will use the hooks from .cursor/hooks.json when you open the project. Commands are run from the project root with node hooks/mask-pii-prompt.js and node hooks/mask-pii-read-file.js.
Global (all projects in Cursor)
To run the same hooks in every Cursor workspace, point Cursor at your pii-masker repo with absolute paths (no need to copy files). The scripts load ../src/pii-patterns.js from their own directory.
- Create or edit
~/.cursor/hooks.jsonand point to thepii-maskerinstall path:
{
"version": 1,
"hooks": {
"beforeSubmitPrompt": [
{ "command": "env PII_MASKER_ASK_ON_PII=1 PII_MASKER_SCRAMBLED=1 node /path/to/pii-masker/hooks/mask-pii-prompt.js", "timeout": 10 }
],
"beforeReadFile": [
{ "command": "node /path/to/pii-masker/hooks/mask-pii-read-file.js", "timeout": 10 }
]
}
}Replace /path/to/pii-masker with:
- npm global:
$(npm root -g)/pii-masker - npm local:
./node_modules/pii-masker(use absolute path in production) - Cloned repo: e.g.
/Users/you/Projects/pii-masker
Cursor runs the command from its own context, so absolute paths are more reliable than ~.
Note: beforeSubmitPrompt cannot substitute a masked prompt; Cursor shows a dialog or blocks. The user can approve to send anyway or edit and resend.
Default behavior (PII detected): Shows an Approve/Deny dialog. Approve = send anyway (unmasked). Deny = cancel. Does not block completely—user can choose to proceed.
Warn-only mode: Set PII_MASKER_WARN_ONLY=1 to allow the prompt through with a warning (no dialog).
Ask mode: Set PII_MASKER_ASK_ON_PII=1 to create masked copies of attached files when PII is detected. User still sees Approve/Deny; on Deny, masked files (e.g. testfile.pii-masked.md) are available to attach instead.
Strict block mode: Set PII_MASKER_BLOCK_ON_PII=1 to block completely when PII is detected (legacy behavior).
Masked files for attachments (ask mode)
In ask mode (PII_MASKER_ASK_ON_PII=1), when you [Deny] the dialog, the hook writes masked copies of attached files next to the originals (e.g. testfile.md → testfile.pii-masked.md). Attach those instead and resend; the model will only see redacted content. Combine with PII_MASKER_USE_IDS=1 and PII_MASKER_MAPPING_FILE=1 for ID substitution and lookup.
ID substitution and mapping
PII_MASKER_USE_IDS=1– Replace PII with[EMAIL_1],[PHONE_1],[SSN_1], etc., instead of[EMAIL_REDACTED]. Same value gets the same ID everywhere (e.g. in prompt + all attachments).PII_MASKER_SCRAMBLED=1– Replace PII with deterministic hex IDs like[E_a7f3b2],[P_9c2e1d],[K_4b8d12]instead of*_REDACTED. Same value → same code everywhere. Use--mappingin the CLI to write a lookup file.PII_MASKER_MAPPING_FILE=1– Write the ID → real value mapping to.cursor/pii-mapping.jsonin the project root so you can see what[EMAIL_1]referred to. Useful for ID mode in both beforeSubmitPrompt (attachments) and beforeReadFile (file content when the agent reads a file).
Example .cursor/pii-mapping.json:
{
"mappings": {
"conv-123": {
"[EMAIL_1]": "[email protected]",
"[PHONE_1]": "555-123-4567"
}
},
"updated": "2025-03-08T12:00:00.000Z"
}2. VS Code / Cursor (extension)
The extension masks PII automatically when you paste (default: on). You can also run commands to mask selection or clipboard.
Install: See vscode-extension/INSTALL.md for:
- Marketplace – Install from the VS Code Extensions Marketplace (when published).
- From .vsix – Install from a
.vsixfile (Extensions → … → Install from VSIX). - From source – Run with F5 for development.
Publish to the marketplace: See vscode-extension/PUBLISHING.md for packaging (vsce package), creating a publisher, and publishing with vsce publish.
Behavior after install
- Automatic (default): When you paste text in an editor, PII in that text is replaced with placeholders (e.g.
[EMAIL_REDACTED]) before insertion. Turn off in Settings: search PII Masker and uncheck Mask on paste. - Commands: “Mask PII in selection” (replace selection with masked text), “Mask PII in clipboard (paste masked)” (next paste will be masked).
3. CLI / pipeline
Use the masker in scripts or CI so any content you send to a chatbot (or log) is masked.
Requirements: Node.js (no extra npm install; the script uses the local src/pii-patterns.js).
From the pii-masker directory:
# Stdin → stdout
cat file.txt | node cli.js
# With verbose (report findings to stderr)
echo "Contact me at [email protected]" | node cli.js -v
# File in, stdout out
node cli.js --file input.txt
# File in, file out
node cli.js --file input.txt --out masked.txt
# Substitute with IDs and write mapping (for pipelines or manual lookup)
node cli.js --file input.txt --out masked.txt --ids --mapping pii-mapping.json
# Scrambled mode: [E_a7f3b2], [P_9c2e1d] etc. (same value → same code)
node cli.js --file input.txt --out masked.txt --scrambled
# Scrambled + mapping for lookup
node cli.js --file input.txt --out masked.txt --scrambled --mapping pii-mapping.jsonYou can call pii-mask if installed globally, or node /path/to/pii-masker/cli.js in your pipeline.
Tests and proof of concept
Run all tests:
npm testThis runs unit tests for PII patterns (tests/pii-patterns.test.js) and integration tests for the Cursor hooks (tests/hooks.test.js).
Run only unit or hook tests:
npm run test:unit
npm run test:hooksProof of concept (see masking on sample data):
npm run demoOr node demo/proof-of-concept.js. This prints before/after for sample strings (emails, phones, API keys, allowlisted values, and ID substitution with mapping), plus a quick CLI check.
Layout
pii-masker/
├── README.md # This file
├── package.json
├── cli.js # CLI entry
├── src/
│ └── pii-patterns.js # Shared PII detection and masking
├── tests/
│ ├── run.js # Test runner (npm test)
│ ├── pii-patterns.test.js # Unit tests for masking
│ └── hooks.test.js # Hook integration tests
├── demo/
│ └── proof-of-concept.js # Demo script (npm run demo)
├── hooks/
│ ├── mask-pii-prompt.js # Cursor beforeSubmitPrompt
│ └── mask-pii-read-file.js # Cursor beforeReadFile
├── .cursor/
│ └── hooks.json # Cursor project-level hooks
└── vscode-extension/
├── package.json
├── extension.js # VS Code commands
└── pii-patterns.js # Bundled copy for VSIXCustomization
- Patterns: Edit
src/pii-patterns.jsto add or change regexes and replacement strings. Sync or copy the same logic intovscode-extension/pii-patterns.jsif you use the extension. - Allowlist: The same file defines an
ALLOWLISTso values like*@example.com,123-45-6789, and127.0.0.1are not masked. Adjust as needed. - Cursor env (hooks):
PII_MASKER_ASK_ON_PII=1– when PII detected, create masked copies of attachments; user sees Approve/Deny.PII_MASKER_BLOCK_ON_PII=1– block completely when PII detected (legacy strict mode).PII_MASKER_SCRAMBLED=1– use[E_a7f3b2],[P_9c2e1d], etc., instead of*_REDACTED.PII_MASKER_WARN_ONLY=1– allow prompt through with warning (no dialog).PII_MASKER_USE_IDS=1– use[EMAIL_1],[PHONE_1], etc. (for mapping file).PII_MASKER_MAPPING_FILE=1– write ID → value mapping to.cursor/pii-mapping.json.
- Cursor: Use
failClosed: trueinhooks.jsonif you want hook failures (e.g. script not found) to block the action instead of allowing it.
Limitations
- Client-side only: This does not protect you if you intentionally send PII or use another client. For organization-wide policy, consider server-side or proxy masking.
- Cursor: Prompt substitution is not supported yet (block or warn-only only). File-content masking in
beforeReadFilemay not be applied by Cursor in all cases. - Detection: Regex-based; false positives and false negatives are possible. Allowlisting and warn-only mode help reduce friction.
See DESIGN.md for a fuller discussion of tradeoffs and alternatives.
Publishing to npm
- Replace
your-usernameinpackage.jsonwith your GitHub username (or removerepository/homepageif not using GitHub). - Log in:
npm login - Publish:
npm publish(for scoped packages:npm publish --access public)
License
MIT.
