pii-guard-node-mini
v1.0.1
Published
Single-file Node.js/TypeScript PII masking engine (detect + mask + optional tokenization)
Downloads
290
Maintainers
Readme
PII Masker (single-file TypeScript utility)
A self-contained, dependency-free (runtime) PII/secret detection + masking engine designed to be copied directly into other Node.js/TypeScript projects.
What this is for
- Mask PII before sending text to LLMs, logs, telemetry, or analytics.
- Detect and transform common PII/secrets using regex + lightweight validation/heuristics.
- Provide configurable masking strategies per PII type.
- Optionally use tokenization for same-instance reversible masking (useful for round-tripping LLM responses).
Install / Add to your project
This asset is intentionally shipped as a single file.
Option A — copy the file
Copy
pii-masker.tsinto your project (e.g.src/utils/pii-masker.ts).If your project uses TypeScript, make sure Node types are available:
npm i -D @types/nodeThis file uses Node APIs (
crypto,Buffer). It’s meant for Node.js runtimes (or bundlers configured to polyfill Node APIs).
Option B — install from npm
npm i pii-guard-node-miniIf you copied the file instead of installing from npm, replace import paths like
"pii-guard-node-mini"with your local path (e.g."./utils/pii-masker").
Quick start
import createPIIMasker, { PIIType, MaskingStrategy } from "pii-guard-node-mini";
const masker = createPIIMasker({
preset: "balanced",
logLevel: "silent",
strategies: {
[PIIType.CREDIT_CARD]: MaskingStrategy.REDACT,
},
});
const input = "Email me at [email protected]. Card: 4111 1111 1111 1111";
const { maskedText, entities } = masker.mask(input);
console.log(maskedText);
console.log(entities);API overview
createPIIMasker(userConfig?) returns an object with:
mask(text)→{ maskedText, entities, maskMap }(may also includewarnings/truncated)maskObject(obj, fieldPaths?)→{ masked, entities, maskMap }(may includewarnings)detect(text)→DetectedEntity[]unmask(text)→string(only meaningful with tokenization + reversibility enabled)addDetector(type, fn)→ register custom detectoraddStrategy(name, fn)→ register custom strategyupdateConfig(partial)→ hot-update instance configgetReport()→ basic usage metricsclearVault()→ clears token vault (affects unmask)resetReport()→ clears countersgetConfig()→ resolved config snapshot
Presets
Presets pre-configure detectors + defaults.
const strict = createPIIMasker({ preset: "strict" });
const balanced = createPIIMasker({ preset: "balanced" });
const minimal = createPIIMasker({ preset: "minimal" });
const hipaa = createPIIMasker({ preset: "hipaa" });
const pci = createPIIMasker({ preset: "pci-dss" });Notes:
- Preset values can still be overridden by passing your own config fields.
- “HIPAA” / “PCI-DSS” preset names are practical bundles; you should still validate your usage for your environment.
Configuration (EngineConfig)
You pass a Partial<EngineConfig> to createPIIMasker().
Common knobs:
Production/enterprise recommended defaults
By default, results include entities (with original values) and maskMap (original → masked).
In production pipelines this can accidentally re-introduce sensitive data into logs.
Recommended configuration:
import createPIIMasker, { MaskingStrategy } from "pii-guard-node-mini";
const masker = createPIIMasker({
preset: "balanced",
// Output controls (recommended for production)
includeMaskMap: false,
includeEntities: true,
entityValueMode: "none", // don't return raw matches
includeMaskedValueInEntities: true, // safe to include the replacement
// Safety limits
maxTextLength: 200_000,
maxEntities: 2_000,
onLimitExceeded: "truncate", // or "throw" if you prefer hard-fail
// Recommended for LLM flows
defaultStrategy: MaskingStrategy.REDACT,
});Notes:
- If you set
entityValueMode: "none",DetectedEntity.valuebecomes an empty string. - If you need original values for debugging, enable them only in dev/test.
Output controls (data minimization)
These options are designed to prevent accidental re-introduction of sensitive data via outputs.
includeMaskMap: iftrue,maskMapcontains original → masked mappings.includeEntities: iffalse,entitieswill be an empty list.entityValueMode:"original"(default):DetectedEntity.valueis the raw match."masked":DetectedEntity.valuebecomes the replacement value."none":DetectedEntity.valuebecomes an empty string.
includeMaskedValueInEntities: includesDetectedEntity.masked(the replacement) in the entity.
Example (mask safely, keep entity locations/types only):
const masker = createPIIMasker({
includeMaskMap: false,
includeEntities: true,
entityValueMode: "none",
includeMaskedValueInEntities: false,
});confidenceThreshold
Higher means fewer matches (less false positives), lower means more aggressive masking.
const masker = createPIIMasker({ confidenceThreshold: 0.7 });defaultStrategy and strategies
Set the global default masking behavior, and override per type.
import { MaskingStrategy, PIIType } from "pii-guard-node-mini";
const masker = createPIIMasker({
defaultStrategy: MaskingStrategy.PARTIAL_MASK,
strategies: {
[PIIType.SSN]: MaskingStrategy.REDACT,
[PIIType.CREDIT_CARD]: MaskingStrategy.REDACT,
[PIIType.API_KEY]: MaskingStrategy.REDACT,
},
});allowList
Values that should not be masked even if they look like PII.
const masker = createPIIMasker({
allowList: ["example.com", "localhost"],
});denyList
Terms that must always be masked.
const masker = createPIIMasker({
denyList: [
{ term: "ProjectX", type: "CUSTOM" },
{ term: "InternalCodeWord", type: "CUSTOM" },
],
});detectorsEnabled
Run only a subset of detectors.
import { PIIType } from "pii-guard-node-mini";
const masker = createPIIMasker({
detectorsEnabled: new Set([PIIType.EMAIL, PIIType.PHONE]),
});locale
Affects some patterns/heuristics.
const maskerUS = createPIIMasker({ locale: "US" });
const maskerUK = createPIIMasker({ locale: "UK" });
const maskerIN = createPIIMasker({ locale: "IN" });hashSalt
Only used by HASH strategy.
const masker = createPIIMasker({
defaultStrategy: "HASH",
hashSalt: "your-app-specific-salt",
});
Limits: maxTextLength, maxEntities, onLimitExceeded
These controls are designed for untrusted inputs (logs, user text, LLM output) to prevent worst-case performance.
const masker = createPIIMasker({
maxTextLength: 100_000,
maxEntities: 1_000,
onLimitExceeded: "truncate", // or "throw"
});
const res = masker.mask(veryLargeText);
if (res.warnings?.length) {
// handle warnings in your telemetry
}Output controls: includeMaskMap, includeEntities, entityValueMode
const masker = createPIIMasker({
includeMaskMap: false,
includeEntities: true,
entityValueMode: "masked", // or "none" in production
includeMaskedValueInEntities: true,
});logLevel
const masker = createPIIMasker({ logLevel: "warn" });Masking strategies
Available MaskingStrategy values:
REDACT→[REDACTED_<TYPE>]PARTIAL_MASK→ keep some structure (e.g., last 4 digits)HASH→ stable salted hash token like[HASH:abcd1234...]TOKENIZE→<<PII_deadbeef>>+ store mapping in memory vaultREPLACE_FAKE→ replace with realistic fake values (best for demos)CUSTOM_FN→ call a registered custom function
Custom detectors
Add your own detector for domain-specific identifiers.
import { createPIIMasker, type DetectedEntity } from "pii-guard-node-mini";
const masker = createPIIMasker({});
masker.addDetector("EMPLOYEE_ID", (text) => {
const m = /E-\d{4,}/g.exec(text);
if (!m) return [];
const entity: DetectedEntity = {
type: "EMPLOYEE_ID",
value: m[0],
start: m.index,
end: m.index + m[0].length,
confidence: 1,
};
return [entity];
});
masker.updateConfig({
strategies: { EMPLOYEE_ID: "REDACT" },
});Custom strategies
Register a named strategy and assign it per type.
import { createPIIMasker } from "pii-guard-node-mini";
const masker = createPIIMasker({});
masker.addStrategy("KEEP_LAST_2", (entity) =>
entity.value.replace(/.(?=.{2})/g, "*"),
);
masker.updateConfig({
strategies: {
API_KEY: "KEEP_LAST_2",
},
});Masking objects (maskObject) and fieldPaths
By default, maskObject() deep-traverses and masks all string values.
If you pass fieldPaths, only matching paths are masked.
- Exact:
user.email - Wildcard segment:
users.*.email
const input = {
users: [
{ email: "[email protected]", note: "keep this" },
{ email: "[email protected]", note: "keep this" },
],
};
const out = masker.maskObject(input, ["users.*.email"]);Reversible masking (tokenization + unmask)
If you want to restore original values (e.g., when an LLM responds with tokens), use TOKENIZE and set enableReversibility: true.
import { createPIIMasker, MaskingStrategy } from "pii-guard-node-mini";
const masker = createPIIMasker({
enableReversibility: true,
defaultStrategy: MaskingStrategy.TOKENIZE,
});
const masked = masker.mask("Call me at +1 555 123 4567").maskedText;
const restored = masker.unmask(masked);Important:
- Reversal only works for values tokenized by the same masker instance.
- If you call
clearVault(), those mappings are lost.
Tokenization behavior note:
- If
enableReversibility: false, the engine will still produce token-looking placeholders, but it will not store mappings (sounmask()cannot restore, and tokens may not be deterministic). - If you need deterministic tokens, enable reversibility and keep the instance alive for the conversation/session.
Determinism: tokenizationDeterministic
tokenizationDeterministic: true means the same original value becomes the same token within the same instance.
Notes:
- Determinism requires storing mappings, so it only applies when
enableReversibility: true. - If you disable reversibility, tokens are generated but not stored.
Built-in detectors (high level)
This library uses pattern-based detectors. Built-in coverage includes:
- Email, phone, SSN, credit card (Luhn), IP, DOB, US address
- Person names (heuristic), passport (US/UK), IBAN
- AWS keys/secrets (heuristic), generic API keys (entropy), JWT, URLs with auth, MAC
- Bank account / routing (conservative: requires context and routing checksum where possible)
Reporting
const masker = createPIIMasker({ preset: "balanced" });
masker.mask("Email: [email protected]");
masker.mask("Card: 4111 1111 1111 1111");
console.log(masker.getReport());License
MIT License — see LICENSE.
