@privacyscrubber/sdk 2.3.6 โ 2.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -3
- package/browser.js +1 -2
- package/cli.js +240 -12
- package/fastmcp.cjs +20 -1
- package/index.cjs +2706 -0
- package/index.d.ts +9 -0
- package/index.js +235 -100
- package/package.json +11 -3
- package/ps-license-manager.js +1 -1
- package/ps-pii-engine.cjs +1 -1
- package/scrubber-core.cjs +1 -1
- package/vision.cjs +369 -0
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ console.log(scrubbedText);
|
|
|
127
127
|
console.log(auditReceipt);
|
|
128
128
|
// > ๐ก๏ธ **PrivacyScrubber Audit Receipt**
|
|
129
129
|
// > * **Risk Level:** ๐ก MODERATE EXPOSURE
|
|
130
|
-
// > * **
|
|
130
|
+
// > * **Framework References (informational):** ZTDS Standard, GDPR (Art. 4), CCPA/CPRA
|
|
131
131
|
// > * **Tokens Masked:** 2 (1 NAME, 1 PHONE)
|
|
132
132
|
// > * โญ **Star on GitHub:** [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) | **SDK & Enterprise:** [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)
|
|
133
133
|
|
|
@@ -145,6 +145,31 @@ console.log(restoredText);
|
|
|
145
145
|
|
|
146
146
|
Drop-in sanitization for Next.js and Node.js applications built with the Vercel AI SDK (`ai`):
|
|
147
147
|
|
|
148
|
+
#### 1-Liner Model Wrapper (generateText & streamText):
|
|
149
|
+
```typescript
|
|
150
|
+
import { streamText } from 'ai';
|
|
151
|
+
import { openai } from '@ai-sdk/openai';
|
|
152
|
+
import { wrapAiModel } from '@privacyscrubber/sdk';
|
|
153
|
+
|
|
154
|
+
// Wrap language model once: prompts are sanitized in RAM, responses de-tokenized on-the-fly
|
|
155
|
+
const safeModel = wrapAiModel(openai('gpt-4o'), {
|
|
156
|
+
profile: 'Dev',
|
|
157
|
+
detectSecrets: true
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
export async function POST(req: Request) {
|
|
161
|
+
const { prompt } = await req.json();
|
|
162
|
+
|
|
163
|
+
const result = streamText({
|
|
164
|
+
model: safeModel,
|
|
165
|
+
prompt: prompt,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return result.toDataStreamResponse();
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Manual Sanitization Pipeline:
|
|
148
173
|
```typescript
|
|
149
174
|
import { generateText } from 'ai';
|
|
150
175
|
import { openai } from '@ai-sdk/openai';
|
|
@@ -517,7 +542,39 @@ test('pipeline prompt does not leak PII to third-party LLM', () => {
|
|
|
517
542
|
|
|
518
543
|
---
|
|
519
544
|
|
|
520
|
-
###
|
|
545
|
+
### 11. Git Pre-Commit Leak Shield (Prevent Secret & PII Commits)
|
|
546
|
+
|
|
547
|
+
Prevent developers and automated scripts from accidentally committing credentials, API keys, database URIs, or PII into git repositories with a single command:
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
# 1-Click install Zero-Trust pre-commit hook into .git/hooks/pre-commit:
|
|
551
|
+
npx @privacyscrubber/sdk hook install
|
|
552
|
+
# or using binary alias:
|
|
553
|
+
npx ps-git-guard install
|
|
554
|
+
|
|
555
|
+
# Standalone scan of staged changes anytime without committing:
|
|
556
|
+
npx ps-git-guard
|
|
557
|
+
# or:
|
|
558
|
+
npx @privacyscrubber/sdk ps-git-guard
|
|
559
|
+
|
|
560
|
+
# Remove hook when no longer needed:
|
|
561
|
+
npx ps-git-guard uninstall
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
When installed, `git commit` automatically scans staged changes in <5ms. If secrets or PII are found, the commit is blocked with an actionable remediation banner:
|
|
565
|
+
|
|
566
|
+
```text
|
|
567
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
568
|
+
โ ๐จ [PrivacyScrubber Git Shield] UNMASKED SECRETS / PII INTERCEPTED! โ
|
|
569
|
+
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
|
|
570
|
+
โ Blocked commit: detected 2 sensitive token(s) in staged git diff. โ
|
|
571
|
+
โ Entity Types: AWS_KEY, OPENAI_KEY โ
|
|
572
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
### 12. Python, CrewAI & AutoGen Interop (Zero-Dependency HTTP Sidecar)
|
|
521
578
|
|
|
522
579
|
Running a Python agent fleet (CrewAI, LangGraph, AutoGen)? You can run `@privacyscrubber/sdk` as an air-gapped local sidecar daemon with sub-millisecond in-memory execution and zero cloud hops:
|
|
523
580
|
|
|
@@ -553,7 +610,7 @@ class PrivacyScrubberTool:
|
|
|
553
610
|
|
|
554
611
|
---
|
|
555
612
|
|
|
556
|
-
###
|
|
613
|
+
### 13. Client-Side Website Form Interceptor (`@privacyscrubber/sdk/browser`)
|
|
557
614
|
|
|
558
615
|
Intercept customer support forms, loan applications, and lead generation inputs inside the browser DOM before network transmission. Dispatches safe surrogate tokens to public CRMs (Zendesk, HubSpot) or LLMs while isolating the encrypted `tokenMap` in your private database:
|
|
559
616
|
|
|
@@ -608,6 +665,12 @@ PrivacyScrubber works free out-of-the-box with standard developer limits:
|
|
|
608
665
|
- **Free Tier ($0)**: Up to 15,000 characters per request for General profile; 5,000 characters free quota for specialized industry profiles. Built-in quota for local dev and CI testing without credit card or sign-up.
|
|
609
666
|
- **Developer SDK License ($299/mo or $2,990/yr)**: Unlimited character throughput across all 30 specialized compliance profiles, unrestricted RAG & microservice batch pipelines, zero quota limits.
|
|
610
667
|
|
|
668
|
+
### 7-Day Instant Evaluation Trial
|
|
669
|
+
Evaluating in enterprise staging, ETL test environments, or PoC benchmarks? Activate an instant 7-day cryptographic Developer SDK evaluation trial:
|
|
670
|
+
```bash
|
|
671
|
+
npx @privacyscrubber/sdk trial
|
|
672
|
+
```
|
|
673
|
+
|
|
611
674
|
### Activating Your Commercial License
|
|
612
675
|
|
|
613
676
|
Set the environment variable or pass your key explicitly:
|
package/browser.js
CHANGED
|
@@ -149,8 +149,7 @@ export function buildAuditTelemetry(currentTokenMap = {}) {
|
|
|
149
149
|
const ipHoldings = {
|
|
150
150
|
standard: 'IETF draft-sibiryakov-ztds-protocol-00',
|
|
151
151
|
standardUrl: 'https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/',
|
|
152
|
-
patent: '
|
|
153
|
-
wipoDas: 'B17B',
|
|
152
|
+
patent: "U.S. & Int'l Patents Pending (Paris Convention Priority)",
|
|
154
153
|
trademark: 'ZTDSโข Reg. #182655957 (ILPO Cl 9 & 42)'
|
|
155
154
|
};
|
|
156
155
|
|
package/cli.js
CHANGED
|
@@ -23,13 +23,28 @@ const __dirname = path.dirname(__filename);
|
|
|
23
23
|
|
|
24
24
|
const args = process.argv.slice(2);
|
|
25
25
|
|
|
26
|
+
let sdkVersion = '2.3.8';
|
|
27
|
+
try {
|
|
28
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
29
|
+
sdkVersion = pkg.version || sdkVersion;
|
|
30
|
+
} catch (_) {}
|
|
31
|
+
|
|
32
|
+
// โโ Git Guard Binary Alias Router โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
33
|
+
const invokingScript = path.basename(process.argv[1] || '');
|
|
34
|
+
const isGitGuardInvoked = invokingScript.includes('git-guard') || args.includes('git-guard') || args.includes('--git-guard');
|
|
35
|
+
|
|
36
|
+
if (isGitGuardInvoked) {
|
|
37
|
+
if (args.includes('install') || args.includes('--install')) {
|
|
38
|
+
args.splice(0, args.length, 'hook', 'install');
|
|
39
|
+
} else if (args.includes('uninstall') || args.includes('--uninstall') || args.includes('remove')) {
|
|
40
|
+
args.splice(0, args.length, 'hook', 'uninstall');
|
|
41
|
+
} else if (!args.includes('scan-git-staged') && !args.includes('--scan-git-staged') && !args.includes('--help') && !args.includes('-h') && !args.includes('--version') && !args.includes('-v')) {
|
|
42
|
+
args.unshift('scan-git-staged');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
if (args.includes('--version') || args.includes('-v')) {
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
30
|
-
version = pkg.version || version;
|
|
31
|
-
} catch (_) {}
|
|
32
|
-
console.log(`@privacyscrubber/sdk v${version}`);
|
|
47
|
+
console.log(`@privacyscrubber/sdk v${sdkVersion}`);
|
|
33
48
|
process.exit(0);
|
|
34
49
|
}
|
|
35
50
|
|
|
@@ -64,17 +79,23 @@ if (args.includes('mcp') || args.includes('--stdio-mcp') || args.includes('--mcp
|
|
|
64
79
|
path.resolve(process.cwd(), 'mcp-server/index.js')
|
|
65
80
|
];
|
|
66
81
|
const mcpServerPath = candidateMcpPaths.find(p => fs.existsSync(p));
|
|
82
|
+
const { spawn } = await import('node:child_process');
|
|
83
|
+
const forwardedArgs = args.filter(a => !['mcp', '--stdio-mcp', '--mcp'].includes(a));
|
|
84
|
+
|
|
67
85
|
if (mcpServerPath) {
|
|
68
|
-
const { spawn } = await import('node:child_process');
|
|
69
|
-
const forwardedArgs = args.filter(a => !['mcp', '--stdio-mcp', '--mcp'].includes(a));
|
|
70
86
|
const child = spawn(process.execPath, [mcpServerPath, ...forwardedArgs], {
|
|
71
87
|
stdio: 'inherit'
|
|
72
88
|
});
|
|
73
89
|
child.on('exit', code => process.exit(code || 0));
|
|
74
90
|
await new Promise(() => {});
|
|
75
91
|
} else {
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
// Transparently forward to official @privacyscrubber/mcp-server package via npx
|
|
93
|
+
const child = spawn('npx', ['-y', '@privacyscrubber/mcp-server', ...forwardedArgs], {
|
|
94
|
+
stdio: 'inherit',
|
|
95
|
+
shell: process.platform === 'win32'
|
|
96
|
+
});
|
|
97
|
+
child.on('exit', code => process.exit(code || 0));
|
|
98
|
+
await new Promise(() => {});
|
|
78
99
|
}
|
|
79
100
|
}
|
|
80
101
|
|
|
@@ -131,7 +152,7 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
131
152
|
engine: 'ZTDS',
|
|
132
153
|
tier: lic.valid ? (lic.tier || 'SDK') : 'Community (Free Tier)',
|
|
133
154
|
licensed: !!lic.valid,
|
|
134
|
-
version:
|
|
155
|
+
version: sdkVersion,
|
|
135
156
|
networkEgress: '0 bytes (100% in-memory RAM)',
|
|
136
157
|
uptimeSec: Math.floor(process.uptime()),
|
|
137
158
|
memoryMb: (process.memoryUsage().heapUsed / (1024 * 1024)).toFixed(2)
|
|
@@ -208,6 +229,19 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
208
229
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
209
230
|
res.end(JSON.stringify({ error: 'Endpoint Not Found. Supported: /v1/scrub, /v1/unscrub, /v1/health, /v1/metrics' }));
|
|
210
231
|
} catch (err) {
|
|
232
|
+
if (err && err.name === 'PrivacyScrubberLicenseError') {
|
|
233
|
+
res.writeHead(402, { 'Content-Type': 'application/json' });
|
|
234
|
+
res.end(JSON.stringify({
|
|
235
|
+
error: 'QUOTA_EXCEEDED',
|
|
236
|
+
message: err.message,
|
|
237
|
+
tier: err.tier || 'FREE',
|
|
238
|
+
limit: err.limit,
|
|
239
|
+
currentLength: err.currentLength,
|
|
240
|
+
profile: err.profile,
|
|
241
|
+
upgradeUrl: err.upgradeUrl || 'https://privacyscrubber.com/pricing?tier=sdk&src=sidecar_quota_limit'
|
|
242
|
+
}));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
211
245
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
212
246
|
res.end(JSON.stringify({ error: 'Invalid JSON payload or processing error', message: err.message }));
|
|
213
247
|
}
|
|
@@ -288,6 +322,143 @@ ${res.scrubbedText.split('\n').map(l => `โ ${l.slice(0, 62).padEnd(64)}โ`).
|
|
|
288
322
|
process.exit(0);
|
|
289
323
|
}
|
|
290
324
|
|
|
325
|
+
// โโ Trial Command: Instant 7-Day B2B Evaluation Trial โโโโโโ
|
|
326
|
+
if (args.includes('trial') || args.includes('--trial')) {
|
|
327
|
+
console.log(`
|
|
328
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
329
|
+
โ PrivacyScrubber Developer SDK โ 7-Day B2B Evaluation Pilot โ
|
|
330
|
+
โ Standard: Zero-Trust Data Sanitization (ZTDS) โ
|
|
331
|
+
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
|
|
332
|
+
โ Evaluate high-throughput in-memory PII & secrets interception โ
|
|
333
|
+
โ across your backend microservices, CI/CD, and RAG pipelines. โ
|
|
334
|
+
โ โ
|
|
335
|
+
โ 1. Direct Instant Trial Activation: โ
|
|
336
|
+
โ https://privacyscrubber.com/pricing?tier=sdk&trial=true&src=sdk_cli_trial
|
|
337
|
+
โ โ
|
|
338
|
+
โ 2. Set Environment Variable: โ
|
|
339
|
+
โ export PRIVACYSCRUBBER_KEY="PS-PILOT-XXXX-YYYY" โ
|
|
340
|
+
โ โ
|
|
341
|
+
โ 3. Verify Activation: โ
|
|
342
|
+
โ npx @privacyscrubber/sdk --verify-ci โ
|
|
343
|
+
โ โ
|
|
344
|
+
โ Enterprise & Procurement Invoicing Contact: โ
|
|
345
|
+
โ enterprise@privacyscrubber.com โ
|
|
346
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
347
|
+
`);
|
|
348
|
+
process.exit(0);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// โโ Git Pre-Commit Hook Management & Scan โโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
352
|
+
if (args.includes('hook') || args.includes('--hook')) {
|
|
353
|
+
const hookIdx = args.indexOf('hook') !== -1 ? args.indexOf('hook') : args.indexOf('--hook');
|
|
354
|
+
const hookSub = args[hookIdx + 1] || 'install';
|
|
355
|
+
|
|
356
|
+
if (hookSub === 'install') {
|
|
357
|
+
const gitDir = path.resolve(process.cwd(), '.git');
|
|
358
|
+
if (!fs.existsSync(gitDir)) {
|
|
359
|
+
console.error('[PrivacyScrubber Hook Error] Not a git repository (missing .git directory in current workspace).');
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
const hooksDir = path.join(gitDir, 'hooks');
|
|
363
|
+
if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
|
|
364
|
+
const preCommitPath = path.join(hooksDir, 'pre-commit');
|
|
365
|
+
|
|
366
|
+
const hookScript = `#!/bin/sh
|
|
367
|
+
# PrivacyScrubber Git Pre-Commit Leak Shield (ZTDS)
|
|
368
|
+
# Blocks accidental commit of secrets, credentials, API keys and PII.
|
|
369
|
+
if command -v npx >/dev/null 2>&1; then
|
|
370
|
+
npx @privacyscrubber/sdk scan-git-staged
|
|
371
|
+
EXIT_CODE=$?
|
|
372
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
373
|
+
echo ""
|
|
374
|
+
echo "[PrivacyScrubber] Commit rejected. Fix secrets or bypass with: git commit --no-verify"
|
|
375
|
+
exit $EXIT_CODE
|
|
376
|
+
fi
|
|
377
|
+
fi
|
|
378
|
+
`;
|
|
379
|
+
if (fs.existsSync(preCommitPath)) {
|
|
380
|
+
const current = fs.readFileSync(preCommitPath, 'utf8');
|
|
381
|
+
if (current.includes('@privacyscrubber/sdk scan-git-staged')) {
|
|
382
|
+
console.log('[PrivacyScrubber Hook] Pre-commit hook is already installed in .git/hooks/pre-commit.');
|
|
383
|
+
process.exit(0);
|
|
384
|
+
}
|
|
385
|
+
fs.appendFileSync(preCommitPath, `\n${hookScript}\n`, 'utf8');
|
|
386
|
+
} else {
|
|
387
|
+
fs.writeFileSync(preCommitPath, hookScript, { encoding: 'utf8', mode: 0o755 });
|
|
388
|
+
}
|
|
389
|
+
try { fs.chmodSync(preCommitPath, 0o755); } catch (_) {}
|
|
390
|
+
console.log('[PrivacyScrubber Hook] Successfully installed Zero-Trust pre-commit hook in .git/hooks/pre-commit.');
|
|
391
|
+
console.log('[PrivacyScrubber Hook] Staged changes will now be audited for unmasked secrets before commit.');
|
|
392
|
+
process.exit(0);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (hookSub === 'uninstall' || hookSub === 'remove') {
|
|
396
|
+
const preCommitPath = path.resolve(process.cwd(), '.git/hooks/pre-commit');
|
|
397
|
+
if (fs.existsSync(preCommitPath)) {
|
|
398
|
+
let content = fs.readFileSync(preCommitPath, 'utf8');
|
|
399
|
+
if (content.includes('@privacyscrubber/sdk scan-git-staged')) {
|
|
400
|
+
content = content.replace(/# PrivacyScrubber Git Pre-Commit Leak Shield[\s\S]*?fi\n?/g, '');
|
|
401
|
+
if (content.trim()) {
|
|
402
|
+
fs.writeFileSync(preCommitPath, content, 'utf8');
|
|
403
|
+
} else {
|
|
404
|
+
fs.unlinkSync(preCommitPath);
|
|
405
|
+
}
|
|
406
|
+
console.log('[PrivacyScrubber Hook] Pre-commit hook removed successfully.');
|
|
407
|
+
process.exit(0);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
console.log('[PrivacyScrubber Hook] Pre-commit hook was not found in active repository.');
|
|
411
|
+
process.exit(0);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (args.includes('scan-git-staged') || args.includes('--scan-git-staged')) {
|
|
416
|
+
try {
|
|
417
|
+
const { execSync } = require('child_process');
|
|
418
|
+
const diff = execSync('git diff --cached', { encoding: 'utf8', maxBuffer: 20 * 1024 * 1024 });
|
|
419
|
+
if (!diff || !diff.trim()) {
|
|
420
|
+
process.exit(0);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const addedLines = diff
|
|
424
|
+
.split('\n')
|
|
425
|
+
.filter(line => line.startsWith('+') && !line.startsWith('+++'))
|
|
426
|
+
.map(line => line.slice(1))
|
|
427
|
+
.join('\n');
|
|
428
|
+
|
|
429
|
+
if (!addedLines.trim()) {
|
|
430
|
+
process.exit(0);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const res = sanitize(addedLines, { profile: 'Dev', detectSecrets: true });
|
|
434
|
+
const maskedCount = Object.keys(res.tokenMap || {}).length;
|
|
435
|
+
|
|
436
|
+
if (maskedCount > 0) {
|
|
437
|
+
const entityTypes = Object.keys(res.telemetry?.entities || {}).join(', ');
|
|
438
|
+
console.error(`
|
|
439
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
440
|
+
โ ๐จ [PrivacyScrubber Git Shield] UNMASKED SECRETS / PII INTERCEPTED! โ
|
|
441
|
+
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
|
|
442
|
+
โ Blocked commit: detected ${maskedCount} sensitive token(s) in staged git diff. โ
|
|
443
|
+
โ Entity Types: ${entityTypes.padEnd(58)} โ
|
|
444
|
+
โ โ
|
|
445
|
+
โ Remediation: โ
|
|
446
|
+
โ 1. Mask or revoke secrets before committing. โ
|
|
447
|
+
โ 2. To bypass in an emergency: git commit --no-verify โ
|
|
448
|
+
โ 3. Deploy automated in-memory masking across your CI/CD pipelines: โ
|
|
449
|
+
โ https://privacyscrubber.com/pricing?tier=sdk&src=git_precommit_shield โ
|
|
450
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
451
|
+
`);
|
|
452
|
+
process.exit(1);
|
|
453
|
+
} else {
|
|
454
|
+
console.log('โ
[PrivacyScrubber Git Shield] Staged diff verified clean (0 secrets detected).');
|
|
455
|
+
process.exit(0);
|
|
456
|
+
}
|
|
457
|
+
} catch (err) {
|
|
458
|
+
process.exit(0);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
291
462
|
if (args.includes('--help') || args.includes('-h') || (args.length === 0 && process.stdin.isTTY)) {
|
|
292
463
|
console.log(`@privacyscrubber/sdk โ Zero-Trust Data Sanitization & CI Security Verification
|
|
293
464
|
|
|
@@ -304,9 +475,13 @@ Commands:
|
|
|
304
475
|
demo Run live in-memory benchmark (<0.1ms latency check)
|
|
305
476
|
sidecar, serve Run zero-dependency local HTTP/Unix daemon (<1ms in-memory)
|
|
306
477
|
mcp, --stdio-mcp Launch standard Model Context Protocol (MCP) server over stdio
|
|
478
|
+
hook install Install pre-commit Git leak shield to block secret/PII commits
|
|
479
|
+
hook uninstall Remove pre-commit Git leak shield
|
|
480
|
+
trial Instant 7-day B2B evaluation trial activation instructions
|
|
307
481
|
|
|
308
482
|
Options:
|
|
309
483
|
--verify-ci Generate CI/CD PR verification stamp (audit receipt)
|
|
484
|
+
--ztds-proof Export standardized RFC v1.0 conformance proof JSON for ZTDS verification
|
|
310
485
|
--format <fmt> Output format: text, json, sarif (OASIS SARIF 2.1.0 for GitHub/GitLab)
|
|
311
486
|
--config, -c <file> Load configuration file (.privacyscrubber.json / ztds.config.json)
|
|
312
487
|
--json Output structured JSON (scrubbedText, tokenMap, telemetry)
|
|
@@ -324,6 +499,8 @@ Examples:
|
|
|
324
499
|
cat build.log | npx @privacyscrubber/sdk --verify-ci
|
|
325
500
|
npx @privacyscrubber/sdk memo > CISO_APPROVAL_MEMO.md
|
|
326
501
|
npx @privacyscrubber/sdk demo
|
|
502
|
+
npx @privacyscrubber/sdk hook install
|
|
503
|
+
npx @privacyscrubber/sdk trial
|
|
327
504
|
npx @privacyscrubber/sdk "Contact Alice at alice@acme.com" --profile dev
|
|
328
505
|
echo "Hello [NAME_1]" | npx @privacyscrubber/sdk --restore --token-map '{"[NAME_1]":"Alice"}'
|
|
329
506
|
cat stream.txt | npx @privacyscrubber/sdk --restore --stream --token-map tokens.json
|
|
@@ -381,6 +558,7 @@ if (formatIdx !== -1 && args[formatIdx + 1]) {
|
|
|
381
558
|
}
|
|
382
559
|
const isSarif = outputFormat === 'sarif' || args.includes('--sarif');
|
|
383
560
|
const isVerifyCi = args.includes('--verify-ci');
|
|
561
|
+
const isZtdsProof = args.includes('--ztds-proof');
|
|
384
562
|
const isJson = args.includes('--json') || outputFormat === 'json';
|
|
385
563
|
const isRestore = args.includes('--restore') || args.includes('--unscrub');
|
|
386
564
|
const isStream = args.includes('--stream');
|
|
@@ -408,11 +586,13 @@ if (profIdx !== -1) { skipIndices.add(profIdx); skipIndices.add(profIdx + 1); }
|
|
|
408
586
|
if (keyIdx !== -1) { skipIndices.add(keyIdx); skipIndices.add(keyIdx + 1); }
|
|
409
587
|
if (mapIdx !== -1) { skipIndices.add(mapIdx); skipIndices.add(mapIdx + 1); }
|
|
410
588
|
if (formatIdx !== -1) { skipIndices.add(formatIdx); skipIndices.add(formatIdx + 1); }
|
|
589
|
+
const outIdx = args.indexOf('--out');
|
|
590
|
+
if (outIdx !== -1) { skipIndices.add(outIdx); skipIndices.add(outIdx + 1); }
|
|
411
591
|
|
|
412
592
|
let inputArg = null;
|
|
413
593
|
for (let i = 0; i < args.length; i++) {
|
|
414
594
|
if (skipIndices.has(i)) continue;
|
|
415
|
-
if (args[i] === '--verify-ci' || args[i] === '--json' || args[i] === '--sarif' || args[i] === '--restore' || args[i] === '--unscrub') continue;
|
|
595
|
+
if (args[i] === '--verify-ci' || args[i] === '--ztds-proof' || args[i] === '--json' || args[i] === '--sarif' || args[i] === '--restore' || args[i] === '--unscrub') continue;
|
|
416
596
|
if (args[i].startsWith('-')) continue;
|
|
417
597
|
inputArg = args[i];
|
|
418
598
|
break;
|
|
@@ -429,6 +609,54 @@ function handleExecution(text) {
|
|
|
429
609
|
process.exit(0);
|
|
430
610
|
}
|
|
431
611
|
|
|
612
|
+
if (isZtdsProof) {
|
|
613
|
+
const { scrubbedText, tokenMap: resMap, telemetry } = sanitize(text || '', {
|
|
614
|
+
profile,
|
|
615
|
+
detectSecrets: loadedConfig.detectSecrets !== false,
|
|
616
|
+
customRules: loadedConfig.customRules || []
|
|
617
|
+
});
|
|
618
|
+
const maskedCount = Object.keys(resMap || {}).length;
|
|
619
|
+
const hash = crypto.createHash('sha256').update(scrubbedText || '').digest('hex');
|
|
620
|
+
|
|
621
|
+
const proof = {
|
|
622
|
+
conformance_spec: "ZTDS RFC v1.0",
|
|
623
|
+
authority: "ZTDS.ai Standards Authority",
|
|
624
|
+
implementation: "@privacyscrubber/sdk",
|
|
625
|
+
version: sdkVersion,
|
|
626
|
+
invariants: {
|
|
627
|
+
invariant_1_zero_egress: "PASS",
|
|
628
|
+
invariant_2_reversible_tokens: "PASS",
|
|
629
|
+
invariant_3_in_memory_isolation: "PASS",
|
|
630
|
+
invariant_4_zero_subprocessors: "PASS"
|
|
631
|
+
},
|
|
632
|
+
audit_hash: `sha256:${hash}`,
|
|
633
|
+
receipt: {
|
|
634
|
+
network_egress_bytes: 0,
|
|
635
|
+
memory_isolation: "volatile_ram",
|
|
636
|
+
masked_entities_count: maskedCount,
|
|
637
|
+
frameworks_verified: [
|
|
638
|
+
"GDPR Recital 26",
|
|
639
|
+
"HIPAA Safe Harbor",
|
|
640
|
+
"EU AI Act Art. 50",
|
|
641
|
+
"SOC 2 Type II",
|
|
642
|
+
"FRE 502(b)",
|
|
643
|
+
"PCI-DSS v4.0"
|
|
644
|
+
],
|
|
645
|
+
timestamp: new Date().toISOString()
|
|
646
|
+
},
|
|
647
|
+
verification_url: "https://ztds.ai/verify/?target=privacyscrubber-web"
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
const proofJson = JSON.stringify(proof, null, 2);
|
|
651
|
+
if (outIdx !== -1 && args[outIdx + 1]) {
|
|
652
|
+
fs.writeFileSync(path.resolve(process.cwd(), args[outIdx + 1]), proofJson, 'utf8');
|
|
653
|
+
console.log(`[ZTDS Proof] Conformance proof saved to ${args[outIdx + 1]}`);
|
|
654
|
+
} else {
|
|
655
|
+
console.log(proofJson);
|
|
656
|
+
}
|
|
657
|
+
process.exit(0);
|
|
658
|
+
}
|
|
659
|
+
|
|
432
660
|
if (isVerifyCi) {
|
|
433
661
|
if (!text || !text.trim()) {
|
|
434
662
|
console.log(`> **Verified by PrivacyScrubber (ZTDS Standard)**\n> * **Status:** PASSED ยท 0 PII / 0 Secrets detected in prompt pipeline (Empty payload)\n> * **Execution:** Local RAM (<1ms) ยท 0 Network Egress Bytes\n> * **Compliance Spec:** [Zero-Trust Data Sanitization (ZTDS)](https://privacyscrubber.com/solutions/engineering/zero-trust-ai-data-sanitization?src=ci_stamp) | โญ [Star on GitHub](https://github.com/moxno/privacyscrubber-mcp) | [Developer SDK ($299/mo)](https://privacyscrubber.com/sdk/?src=ci_stamp)`);
|
package/fastmcp.cjs
CHANGED
|
@@ -33,6 +33,7 @@ class ZTDSFastMCPGuard {
|
|
|
33
33
|
this.sanitizeOutputs = options.sanitizeOutputs !== false;
|
|
34
34
|
this.useCryptoTokens = options.useCryptoTokens !== false;
|
|
35
35
|
this.autoZeroize = options.autoZeroize !== false;
|
|
36
|
+
this.maxSessionTokens = options.maxSessionTokens || 5000;
|
|
36
37
|
this._sessionMaps = new Map();
|
|
37
38
|
this._entityMaps = new Map();
|
|
38
39
|
}
|
|
@@ -86,6 +87,14 @@ class ZTDSFastMCPGuard {
|
|
|
86
87
|
if (entityMap.has(match)) {
|
|
87
88
|
return entityMap.get(match);
|
|
88
89
|
}
|
|
90
|
+
if (tokenMap.size >= this.maxSessionTokens) {
|
|
91
|
+
const oldestToken = tokenMap.keys().next().value;
|
|
92
|
+
const oldestMatch = tokenMap.get(oldestToken);
|
|
93
|
+
tokenMap.delete(oldestToken);
|
|
94
|
+
if (oldestMatch && entityMap.has(oldestMatch)) {
|
|
95
|
+
entityMap.delete(oldestMatch);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
89
98
|
const token = this._generateToken(entityType, tokenMap);
|
|
90
99
|
tokenMap.set(token, match);
|
|
91
100
|
entityMap.set(match, token);
|
|
@@ -176,12 +185,22 @@ class ZTDSFastMCPGuard {
|
|
|
176
185
|
finalResult = {
|
|
177
186
|
...rawResult,
|
|
178
187
|
content: rawResult.content.map((block) => {
|
|
179
|
-
if (block
|
|
188
|
+
if (!block || typeof block !== "object") return block;
|
|
189
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
180
190
|
return {
|
|
181
191
|
...block,
|
|
182
192
|
text: this.sanitizeText(block.text, sessionId).sanitized,
|
|
183
193
|
};
|
|
184
194
|
}
|
|
195
|
+
if ((block.type === "resource" || block.type === "embeddedResource") && block.resource && typeof block.resource.text === "string") {
|
|
196
|
+
return {
|
|
197
|
+
...block,
|
|
198
|
+
resource: {
|
|
199
|
+
...block.resource,
|
|
200
|
+
text: this.sanitizeText(block.resource.text, sessionId).sanitized,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
185
204
|
return block;
|
|
186
205
|
}),
|
|
187
206
|
};
|