@privacyscrubber/sdk 2.2.0 → 2.2.2
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 +178 -6
- package/cli.js +166 -19
- package/index.d.ts +122 -0
- package/index.js +626 -17
- package/package.json +1 -1
- package/ps-license-manager.js +1 -1
- package/ps-pii-engine.cjs +1 -1
- package/scrubber-core.cjs +1 -21
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
[](https://privacyscrubber.com)
|
|
7
7
|
[](https://www.npmjs.com/package/@privacyscrubber/sdk)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[-06b6d4.svg)](https://privacyscrubber.com/patents/)
|
|
10
|
+
[](https://github.com/moxno/privacyscrubber-mcp)
|
|
9
11
|
|
|
10
12
|
> **Zero-Trust Data Sanitization (ZTDS) & PII Redaction Engine**
|
|
11
13
|
> 100% In-Memory Execution. Zero Remote Network Calls. Zero Disk Writes. Sub-millisecond Latency.
|
|
@@ -26,7 +28,7 @@ The official Node.js / TypeScript programmatic SDK for [PrivacyScrubber.com](htt
|
|
|
26
28
|
| **DevOps Secrets Scanning** | **Built-in (AWS, JWT, DBs)** | Custom regex rules | Custom detectors | Custom classifiers |
|
|
27
29
|
| **Cost Predictability** | **Free Tier / Flat $199/mo** | DevOps server maintenance | Pay-per-GB cloud fees | Pay-per-unit API fees |
|
|
28
30
|
|
|
29
|
-
> 📊 **Full Technical Benchmark**: Read the in-depth latency analysis, cold-start profiles, and architecture deep-dive in [docs/benchmarks/sdk-vs-presidio.md](https://github.com/moxno/
|
|
31
|
+
> 📊 **Full Technical Benchmark**: Read the in-depth latency analysis, cold-start profiles, and architecture deep-dive in [docs/benchmarks/sdk-vs-presidio.md](https://github.com/moxno/privacyscrubber-mcp/blob/main/docs/benchmarks/sdk-vs-presidio.md).
|
|
30
32
|
|
|
31
33
|
### 💡 The Breach Math vs SDK Math
|
|
32
34
|
|
|
@@ -92,6 +94,7 @@ console.log(auditReceipt);
|
|
|
92
94
|
// > * **Risk Level:** 🟡 MODERATE EXPOSURE
|
|
93
95
|
// > * **Compliance Enforced:** ZTDS Standard, GDPR (Art. 4), CCPA/CPRA
|
|
94
96
|
// > * **Tokens Masked:** 2 (1 NAME, 1 PHONE)
|
|
97
|
+
// > * ⭐ **Star on GitHub:** [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) | **SDK & Enterprise:** [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)
|
|
95
98
|
|
|
96
99
|
// 2. Deterministically restore LLM response
|
|
97
100
|
const aiResponse = "I have queued an SMS notification for [NAME_1] at [PHONE_1].";
|
|
@@ -103,7 +106,157 @@ console.log(restoredText);
|
|
|
103
106
|
|
|
104
107
|
---
|
|
105
108
|
|
|
106
|
-
### 3.
|
|
109
|
+
### 3. Vercel AI SDK Integration (`generateText` & `streamText`)
|
|
110
|
+
|
|
111
|
+
Drop-in sanitization for Next.js and Node.js applications built with the Vercel AI SDK (`ai`):
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { generateText } from 'ai';
|
|
115
|
+
import { openai } from '@ai-sdk/openai';
|
|
116
|
+
import { sanitize, restore } from '@privacyscrubber/sdk';
|
|
117
|
+
|
|
118
|
+
export async function POST(req: Request) {
|
|
119
|
+
const { prompt } = await req.json();
|
|
120
|
+
|
|
121
|
+
// 1. Sanitize user prompt in local RAM (<1ms)
|
|
122
|
+
const { scrubbedText, tokenMap } = sanitize(prompt, {
|
|
123
|
+
profile: 'General',
|
|
124
|
+
detectSecrets: true
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// 2. Outbound call to LLM contains zero PII or credentials
|
|
128
|
+
const { text } = await generateText({
|
|
129
|
+
model: openai('gpt-4o'),
|
|
130
|
+
prompt: scrubbedText,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// 3. Deterministically restore authentic tokens locally
|
|
134
|
+
const { restoredText } = restore(text, tokenMap);
|
|
135
|
+
|
|
136
|
+
return Response.json({ response: restoredText });
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### 3b. Real-Time LLM Streaming De-tokenization (`wrapAiStream`, `createStreamTokenRestorer`, `createNodeStreamTokenRestorer`)
|
|
143
|
+
|
|
144
|
+
When LLMs stream responses via Server-Sent Events (SSE) or WebSockets, tokens can be split across arbitrary chunk boundaries (e.g. Chunk 1: `"Contact [EM"`, Chunk 2: `"AIL_1] at once"`). `StreamingDetokenizer` uses a zero-latency sliding window buffer to stitch and restore tokens on the fly without delaying stream output:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { streamText } from 'ai';
|
|
148
|
+
import { openai } from '@ai-sdk/openai';
|
|
149
|
+
import { sanitize, wrapAiStream } from '@privacyscrubber/sdk';
|
|
150
|
+
|
|
151
|
+
export async function POST(req: Request) {
|
|
152
|
+
const { prompt } = await req.json();
|
|
153
|
+
|
|
154
|
+
// 1. Sanitize prompt in RAM
|
|
155
|
+
const { scrubbedText, tokenMap } = sanitize(prompt, { profile: 'General' });
|
|
156
|
+
|
|
157
|
+
// 2. Stream from LLM
|
|
158
|
+
const { textStream } = streamText({
|
|
159
|
+
model: openai('gpt-4o'),
|
|
160
|
+
prompt: scrubbedText,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// 3. Wrap stream — split tokens ([EM + AIL_1]) are rehydrated in real-time!
|
|
164
|
+
const restoredStream = wrapAiStream(textStream, tokenMap);
|
|
165
|
+
|
|
166
|
+
// 4. Return SSE stream to client with authentic values restored
|
|
167
|
+
return new Response(restoredStream);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### Transparent OpenAI Streaming (`wrapOpenAI` with `stream: true`):
|
|
172
|
+
```typescript
|
|
173
|
+
import OpenAI from 'openai';
|
|
174
|
+
import { wrapOpenAI } from '@privacyscrubber/sdk';
|
|
175
|
+
|
|
176
|
+
const client = wrapOpenAI(new OpenAI(), { profile: 'Dev' });
|
|
177
|
+
|
|
178
|
+
const stream = await client.chat.completions.create({
|
|
179
|
+
model: 'gpt-4o',
|
|
180
|
+
messages: [{ role: 'user', content: 'Debug AWS_KEY=AKIAIOSFODNN7EXAMPLE for user alice@acme.com' }],
|
|
181
|
+
stream: true, // Transparent real-time chunk de-tokenization!
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
for await (const chunk of stream) {
|
|
185
|
+
process.stdout.write(chunk.choices[0]?.delta?.content || '');
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Node.js Pipeline Streaming (`createNodeStreamTokenRestorer`):
|
|
190
|
+
```javascript
|
|
191
|
+
import { pipeline } from 'node:stream/promises';
|
|
192
|
+
import { createNodeStreamTokenRestorer } from '@privacyscrubber/sdk';
|
|
193
|
+
|
|
194
|
+
const restorer = createNodeStreamTokenRestorer(tokenMap);
|
|
195
|
+
await pipeline(readableLlmStream, restorer, clientResponseStream);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 4. LangChain (LCEL Pipelines & `createLangChainTransform`)
|
|
201
|
+
|
|
202
|
+
Use the official middleware helper or compose inside LangChain Expression Language (LCEL) chains:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
206
|
+
import { createLangChainTransform, PrivacyScrubberEngine } from '@privacyscrubber/sdk';
|
|
207
|
+
|
|
208
|
+
// Option A: 1-line middleware helper
|
|
209
|
+
const transform = createLangChainTransform({ defaultProfile: 'Healthcare' });
|
|
210
|
+
const { scrubbedText, tokenMap } = transform.preprocess("Patient John Doe MRN-99120 prescribed Lipitor.");
|
|
211
|
+
// ... invoke LLM with scrubbedText ...
|
|
212
|
+
const restoredOutput = transform.postprocess(llmOutput, tokenMap);
|
|
213
|
+
|
|
214
|
+
// Option B: Composable LCEL Runnable chain
|
|
215
|
+
const engine = new PrivacyScrubberEngine({ defaultProfile: 'Dev' });
|
|
216
|
+
const model = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });
|
|
217
|
+
|
|
218
|
+
const secureChain = async (rawQuestion: string) => {
|
|
219
|
+
const { scrubbedText, tokenMap } = engine.sanitize(rawQuestion);
|
|
220
|
+
const response = await model.invoke(scrubbedText);
|
|
221
|
+
return engine.restore(response.content as string, tokenMap);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const result = await secureChain("Debug AWS_KEY=AKIAIOSFODNN7EXAMPLE for user alice@acme.com");
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### 5. LlamaIndex.TS (RAG Node Sanitizer)
|
|
230
|
+
|
|
231
|
+
Prevent PII poisoning in vector stores by sanitizing documents prior to indexing:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { Document } from 'llamaindex';
|
|
235
|
+
import { sanitize } from '@privacyscrubber/sdk';
|
|
236
|
+
|
|
237
|
+
// Sanitize documents before vector index ingestion (HIPAA & GDPR safe)
|
|
238
|
+
export function sanitizeDocumentsForIndex(docs: Document[]): Document[] {
|
|
239
|
+
return docs.map(doc => {
|
|
240
|
+
const { scrubbedText, telemetry } = sanitize(doc.text, {
|
|
241
|
+
profile: 'Healthcare', // Masks 18 HIPAA identifiers
|
|
242
|
+
detectSecrets: true
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
return new Document({
|
|
246
|
+
text: scrubbedText,
|
|
247
|
+
metadata: {
|
|
248
|
+
...doc.metadata,
|
|
249
|
+
psRiskLevel: telemetry.riskLevel,
|
|
250
|
+
psCompliant: telemetry.frameworksList.join(',')
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### 6. RAG & Vector Database Ingestion (Pinecone, Chroma, pgvector)
|
|
107
260
|
|
|
108
261
|
Storing raw PII inside vector embeddings is irreversible and violates GDPR Article 17 (Right to be Forgotten). Sanitize document chunks prior to vectorization:
|
|
109
262
|
|
|
@@ -127,7 +280,7 @@ function prepareVectorChunk(rawDocumentChunk) {
|
|
|
127
280
|
|
|
128
281
|
---
|
|
129
282
|
|
|
130
|
-
###
|
|
283
|
+
### 7. Stateful Multi-Turn Agent Engine (`PrivacyScrubberEngine`)
|
|
131
284
|
|
|
132
285
|
Maintain token bindings across multi-step autonomous agent loops (LangChain, AutoGen, CrewAI):
|
|
133
286
|
|
|
@@ -149,7 +302,7 @@ const restored = engine.restore(agentOutputText);
|
|
|
149
302
|
|
|
150
303
|
---
|
|
151
304
|
|
|
152
|
-
###
|
|
305
|
+
### 8. High-Throughput Stream Sanitization (`createSanitizeStream`)
|
|
153
306
|
|
|
154
307
|
Sanitize massive log files, database dumps, and SSE payloads on-the-fly with <1MB heap memory. Automatically handles split chunk boundaries:
|
|
155
308
|
|
|
@@ -173,7 +326,7 @@ sanitizeStream.on('finish', () => {
|
|
|
173
326
|
|
|
174
327
|
---
|
|
175
328
|
|
|
176
|
-
###
|
|
329
|
+
### 9. AI Agent Guard & Function Calling (`createGuardedTools`, `applyPatch`)
|
|
177
330
|
|
|
178
331
|
Equip autonomous agents (Cursor, Vercel AI SDK, OpenAI, LangChain) with safe tools that intercept secrets and restore authentic data on disk:
|
|
179
332
|
|
|
@@ -202,7 +355,7 @@ const patchResult = await guardApplyPatch.execute({
|
|
|
202
355
|
|
|
203
356
|
---
|
|
204
357
|
|
|
205
|
-
###
|
|
358
|
+
### 10. Automated PR Security Verification Stamp (CI/CD)
|
|
206
359
|
|
|
207
360
|
Enforce zero plaintext PII and credential leaks in your CI/CD pipeline (GitHub Actions, GitLab CI, Jenkins) before pull requests are merged into production.
|
|
208
361
|
|
|
@@ -312,10 +465,29 @@ To acquire a commercial Developer SDK license:
|
|
|
312
465
|
- 🔌 **MCP Server**: [@privacyscrubber/mcp-server](https://www.npmjs.com/package/@privacyscrubber/mcp-server)
|
|
313
466
|
- 🧩 **Chrome Extension**: [Chrome Web Store](https://chromewebstore.google.com/detail/privacyscrubber-%E2%80%94-zero-tr/pimoejgefeilajmmbpghifdmhdlkgjol)
|
|
314
467
|
- 📖 **Developer SDK Specs**: [https://privacyscrubber.com/features/developer-sdk/](https://privacyscrubber.com/features/developer-sdk/)
|
|
468
|
+
- ⭐ **GitHub Repository**: [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) (Star us on GitHub!)
|
|
315
469
|
- 🧪 **DLP Latency Benchmark**: [https://privacyscrubber.com/dlp-speed-test/](https://privacyscrubber.com/dlp-speed-test/)
|
|
316
470
|
|
|
471
|
+
### 📚 Production Architecture Guides
|
|
472
|
+
- 🛡️ **RAG & Vector Databases**: [Sanitizing PII Before Vector DB Ingestion (Pinecone, Chroma, Qdrant)](https://privacyscrubber.com/solutions/dev/rag-vector-database-pii-masking/)
|
|
473
|
+
- 🤖 **LangChain & LlamaIndex**: [In-Memory PII Middleware for AI Agent Pipelines](https://privacyscrubber.com/solutions/agents/langchain-llamaindex-pii-anonymizer/)
|
|
474
|
+
- ⚡ **AWS Comprehend Alternative**: [In-Memory Redaction Without Egress or Cloud Overhead](https://privacyscrubber.com/solutions/dev/aws-comprehend-pii-alternative/)
|
|
475
|
+
|
|
317
476
|
---
|
|
318
477
|
|
|
319
478
|
## ⚖️ License
|
|
320
479
|
|
|
321
480
|
MIT License © 2026 PrivacyScrubber. Commercial production usage without quota limits requires an active Developer SDK or Enterprise license.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## 🏛️ Intellectual Property & Virtual Patent Marking
|
|
485
|
+
|
|
486
|
+
The Zero-Trust Data Sanitization (ZTDS) architecture, in-memory deterministic tokenization, cryptographic session handoff, and headless execution methods implemented in this SDK are proprietary technology of Ilya Sibiryakov (BrandMeWeb) and are protected under **Patent Pending** status:
|
|
487
|
+
|
|
488
|
+
- **Patent Office:** State of Israel Ministry of Justice, Patent Office (ILPO)
|
|
489
|
+
- **Application Number:** `331905` (Tracking ID: `94221`)
|
|
490
|
+
- **Filing / Priority Date:** September 14, 2026 (Paris Convention Art. 4 & 35 U.S.C. § 119 Priority)
|
|
491
|
+
- **Official Title:** *SYSTEM AND METHOD FOR CLIENT-SIDE ZERO-TRUST DATA SANITIZATION AND CRYPTOGRAPHIC SESSION HANDOFF IN ARTIFICIAL INTELLIGENCE WORKFLOWS*
|
|
492
|
+
- **Virtual Patent Marking:** [privacyscrubber.com/patents/](https://privacyscrubber.com/patents/) in accordance with 35 U.S.C. § 287(a).
|
|
493
|
+
|
package/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import fs from 'node:fs';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import crypto from 'node:crypto';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
-
import { sanitize } from './index.js';
|
|
16
|
+
import { sanitize, restore, generateProcurementMemo, StreamingDetokenizer } from './index.js';
|
|
17
17
|
|
|
18
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
19
|
const __dirname = path.dirname(__filename);
|
|
@@ -21,7 +21,7 @@ const __dirname = path.dirname(__filename);
|
|
|
21
21
|
const args = process.argv.slice(2);
|
|
22
22
|
|
|
23
23
|
if (args.includes('--version') || args.includes('-v')) {
|
|
24
|
-
let version = '2.2.
|
|
24
|
+
let version = '2.2.2';
|
|
25
25
|
try {
|
|
26
26
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
27
27
|
version = pkg.version || version;
|
|
@@ -30,6 +30,76 @@ if (args.includes('--version') || args.includes('-v')) {
|
|
|
30
30
|
process.exit(0);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// ── Memo Command: Generate CISO / DPO Procurement Memorandum ──────
|
|
34
|
+
if (args.includes('memo') || args.includes('--memo') || args.includes('--procurement-memo')) {
|
|
35
|
+
let format = 'markdown';
|
|
36
|
+
const formatIdx = args.indexOf('--format');
|
|
37
|
+
if (formatIdx !== -1 && args[formatIdx + 1]) {
|
|
38
|
+
format = args[formatIdx + 1];
|
|
39
|
+
}
|
|
40
|
+
let appName = 'Internal AI Applications & LLM Pipelines';
|
|
41
|
+
const appIdx = args.indexOf('--app');
|
|
42
|
+
if (appIdx !== -1 && args[appIdx + 1]) {
|
|
43
|
+
appName = args[appIdx + 1];
|
|
44
|
+
}
|
|
45
|
+
let developer = 'Engineering / AI Platform Team';
|
|
46
|
+
const devIdx = args.indexOf('--developer') !== -1 ? args.indexOf('--developer') : args.indexOf('--dev');
|
|
47
|
+
if (devIdx !== -1 && args[devIdx + 1]) {
|
|
48
|
+
developer = args[devIdx + 1];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const memo = generateProcurementMemo({ appName, developer, format });
|
|
52
|
+
process.stdout.write(memo.endsWith('\n') ? memo : memo + '\n');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Demo Command: Live In-Memory Benchmark & Zero-Trust Inspection ──
|
|
57
|
+
if (args.includes('demo') || args.includes('--demo')) {
|
|
58
|
+
const sampleInput = `DevOps Incident Report #4829
|
|
59
|
+
Reporter: Sarah Connor (sarah.connor@cyberdyne.io, +1-415-555-0199, SSN 987-65-4321)
|
|
60
|
+
Host: prod-api-04.us-east-1.internal (10.0.42.15)
|
|
61
|
+
AWS Credentials: AKIAIOSFODNN7EXAMPLE / wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
|
|
62
|
+
Stripe API Key: sk_live_51OzTestDummyStripeKey998877665544332211
|
|
63
|
+
Database URI: postgresql://admin:SuperSecretPassword123@db-prod.internal:5432/production
|
|
64
|
+
Status: Database connection timeout during batch export.`;
|
|
65
|
+
|
|
66
|
+
const start = process.hrtime.bigint();
|
|
67
|
+
const res = sanitize(sampleInput, { profile: 'Dev', detectSecrets: true });
|
|
68
|
+
const end = process.hrtime.bigint();
|
|
69
|
+
const durationUs = Number(end - start) / 1000;
|
|
70
|
+
const durationMs = (durationUs / 1000).toFixed(3);
|
|
71
|
+
|
|
72
|
+
const maskedCount = Object.keys(res.tokenMap || {}).length;
|
|
73
|
+
const border = '═'.repeat(66);
|
|
74
|
+
const thin = '─'.repeat(66);
|
|
75
|
+
|
|
76
|
+
console.log(`
|
|
77
|
+
╔${border}╗
|
|
78
|
+
║ PrivacyScrubber Developer SDK — Live In-Memory Benchmark ║
|
|
79
|
+
║ Standard: Zero-Trust Data Sanitization (ZTDS) ║
|
|
80
|
+
╠${border}╣
|
|
81
|
+
║ Input Characters : ${sampleInput.length.toString().padEnd(44)} ║
|
|
82
|
+
║ Protected Tokens : ${maskedCount.toString().padEnd(44)} ║
|
|
83
|
+
║ Execution Latency : ${(durationMs + ' ms (' + durationUs.toFixed(0) + ' µs)').padEnd(44)} ║
|
|
84
|
+
║ Network Egress : 0 bytes (Pure in-memory process RAM) ║
|
|
85
|
+
║ Risk Level : ${(res.telemetry?.riskLevel || 'CLEAN').padEnd(44)} ║
|
|
86
|
+
║ Frameworks : ${(res.telemetry?.frameworksList?.slice(0, 3).join(', ') || 'GDPR, SOC 2, NIST').padEnd(44)} ║
|
|
87
|
+
╟${thin}╢
|
|
88
|
+
║ RAW INPUT PREVIEW: ║
|
|
89
|
+
${sampleInput.split('\n').map(l => `║ ${l.slice(0, 62).padEnd(64)}║`).join('\n')}
|
|
90
|
+
╟${thin}╢
|
|
91
|
+
║ SANITIZED OUTPUT (Safe for LLM Egress): ║
|
|
92
|
+
${res.scrubbedText.split('\n').map(l => `║ ${l.slice(0, 62).padEnd(64)}║`).join('\n')}
|
|
93
|
+
╟${thin}╢
|
|
94
|
+
║ NEXT STEPS & PROCUREMENT: ║
|
|
95
|
+
║ * Copy CISO Approval Memo : npx @privacyscrubber/sdk memo ║
|
|
96
|
+
║ * CI/CD PR Stamp : cat prompt.txt | sdk --verify-ci ║
|
|
97
|
+
║ * Unlimited SDK ($199/mo) : https://privacyscrubber.com/pricing ║
|
|
98
|
+
╚${border}╝
|
|
99
|
+
`);
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
33
103
|
if (args.includes('--help') || args.includes('-h') || (args.length === 0 && process.stdin.isTTY)) {
|
|
34
104
|
console.log(`@privacyscrubber/sdk — Zero-Trust Data Sanitization & CI Security Verification
|
|
35
105
|
|
|
@@ -37,18 +107,37 @@ Usage:
|
|
|
37
107
|
npx @privacyscrubber/sdk <fileOrText> [options]
|
|
38
108
|
cat prompt.txt | npx @privacyscrubber/sdk [options]
|
|
39
109
|
npx @privacyscrubber/sdk --verify-ci <fileOrText> [options]
|
|
110
|
+
npx @privacyscrubber/sdk memo [options]
|
|
111
|
+
npx @privacyscrubber/sdk demo
|
|
112
|
+
|
|
113
|
+
Commands:
|
|
114
|
+
memo Generate executive CISO & DPO procurement memo
|
|
115
|
+
demo Run live in-memory benchmark (<0.1ms latency check)
|
|
40
116
|
|
|
41
117
|
Options:
|
|
42
|
-
--verify-ci Generate CI/CD PR verification stamp
|
|
43
|
-
--
|
|
118
|
+
--verify-ci Generate CI/CD PR verification stamp (audit receipt)
|
|
119
|
+
--json Output structured JSON (scrubbedText, tokenMap, telemetry)
|
|
120
|
+
--restore, --unscrub Restore masked tokens in text back to original values
|
|
121
|
+
--stream Stream de-tokenization chunks in real-time from stdin
|
|
122
|
+
--token-map <path> Path to JSON file or inline JSON string of token mappings
|
|
123
|
+
--profile <name> Specify detection profile (default: Dev, 30 profiles available)
|
|
44
124
|
--key <licKey> PrivacyScrubber PRO/TEAMS/SDK license key
|
|
45
125
|
-v, --version Show SDK version
|
|
46
126
|
-h, --help Show help screen
|
|
47
127
|
|
|
48
128
|
Examples:
|
|
49
129
|
cat prompt.txt | npx @privacyscrubber/sdk
|
|
130
|
+
cat prompt.txt | npx @privacyscrubber/sdk --json
|
|
50
131
|
cat build.log | npx @privacyscrubber/sdk --verify-ci
|
|
132
|
+
npx @privacyscrubber/sdk memo > CISO_APPROVAL_MEMO.md
|
|
133
|
+
npx @privacyscrubber/sdk demo
|
|
51
134
|
npx @privacyscrubber/sdk "Contact Alice at alice@acme.com" --profile dev
|
|
135
|
+
echo "Hello [NAME_1]" | npx @privacyscrubber/sdk --restore --token-map '{"[NAME_1]":"Alice"}'
|
|
136
|
+
cat stream.txt | npx @privacyscrubber/sdk --restore --stream --token-map tokens.json
|
|
137
|
+
|
|
138
|
+
Commercial Licensing:
|
|
139
|
+
Developer SDK ($199/mo flat): https://privacyscrubber.com/pricing?tier=sdk&src=cli_help
|
|
140
|
+
Teams ($99/mo flat): https://privacyscrubber.com/pricing?tier=teams&src=cli_help
|
|
52
141
|
`);
|
|
53
142
|
process.exit(0);
|
|
54
143
|
}
|
|
@@ -66,30 +155,60 @@ if (keyIdx !== -1 && args[keyIdx + 1]) {
|
|
|
66
155
|
}
|
|
67
156
|
|
|
68
157
|
const isVerifyCi = args.includes('--verify-ci');
|
|
158
|
+
const isJson = args.includes('--json');
|
|
159
|
+
const isRestore = args.includes('--restore') || args.includes('--unscrub');
|
|
160
|
+
const isStream = args.includes('--stream');
|
|
161
|
+
|
|
162
|
+
let tokenMap = {};
|
|
163
|
+
const mapIdx = args.indexOf('--token-map');
|
|
164
|
+
if (mapIdx !== -1 && args[mapIdx + 1]) {
|
|
165
|
+
const mapSource = args[mapIdx + 1];
|
|
166
|
+
try {
|
|
167
|
+
if (fs.existsSync(mapSource)) {
|
|
168
|
+
tokenMap = JSON.parse(fs.readFileSync(mapSource, 'utf8'));
|
|
169
|
+
} else {
|
|
170
|
+
tokenMap = JSON.parse(mapSource);
|
|
171
|
+
}
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.error(`[PrivacyScrubber CLI Error] Failed to parse --token-map: ${err.message}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
69
177
|
|
|
70
178
|
// Locate input argument (first positional parameter that is not a flag or flag value)
|
|
71
179
|
const skipIndices = new Set();
|
|
72
180
|
if (profIdx !== -1) { skipIndices.add(profIdx); skipIndices.add(profIdx + 1); }
|
|
73
181
|
if (keyIdx !== -1) { skipIndices.add(keyIdx); skipIndices.add(keyIdx + 1); }
|
|
182
|
+
if (mapIdx !== -1) { skipIndices.add(mapIdx); skipIndices.add(mapIdx + 1); }
|
|
74
183
|
|
|
75
184
|
let inputArg = null;
|
|
76
185
|
for (let i = 0; i < args.length; i++) {
|
|
77
186
|
if (skipIndices.has(i)) continue;
|
|
78
|
-
if (args[i] === '--verify-ci') continue;
|
|
187
|
+
if (args[i] === '--verify-ci' || args[i] === '--json' || args[i] === '--restore' || args[i] === '--unscrub') continue;
|
|
79
188
|
if (args[i].startsWith('-')) continue;
|
|
80
189
|
inputArg = args[i];
|
|
81
190
|
break;
|
|
82
191
|
}
|
|
83
192
|
|
|
84
193
|
function handleExecution(text) {
|
|
194
|
+
if (isRestore) {
|
|
195
|
+
const { restoredText, restoredCount } = restore(text, tokenMap);
|
|
196
|
+
if (isJson) {
|
|
197
|
+
console.log(JSON.stringify({ restoredText, restoredCount }, null, 2));
|
|
198
|
+
} else {
|
|
199
|
+
process.stdout.write(restoredText.endsWith('\n') ? restoredText : restoredText + '\n');
|
|
200
|
+
}
|
|
201
|
+
process.exit(0);
|
|
202
|
+
}
|
|
203
|
+
|
|
85
204
|
if (isVerifyCi) {
|
|
86
205
|
if (!text || !text.trim()) {
|
|
87
|
-
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`);
|
|
206
|
+
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) | [Developer SDK ($199/mo)](https://privacyscrubber.com/pricing?tier=sdk&src=ci_stamp)`);
|
|
88
207
|
process.exit(0);
|
|
89
208
|
}
|
|
90
209
|
|
|
91
|
-
const { scrubbedText, tokenMap, telemetry } = sanitize(text, { profile, detectSecrets: true });
|
|
92
|
-
const maskedCount = Object.keys(
|
|
210
|
+
const { scrubbedText, tokenMap: resMap, telemetry } = sanitize(text, { profile, detectSecrets: true });
|
|
211
|
+
const maskedCount = Object.keys(resMap || {}).length;
|
|
93
212
|
const hash = crypto.createHash('sha256').update(scrubbedText).digest('hex').slice(0, 16);
|
|
94
213
|
|
|
95
214
|
const stamp = `
|
|
@@ -97,17 +216,31 @@ function handleExecution(text) {
|
|
|
97
216
|
> * **Status:** ✅ PASSED · ${maskedCount} item(s) protected (${telemetry?.riskLevel || 'CLEAN'})
|
|
98
217
|
> * **Execution:** Local RAM (<1ms) · 0 Network Egress Bytes
|
|
99
218
|
> * **Audit Hash:** \`${hash}\` (Zero-Trust Verified)
|
|
219
|
+
> * **Compliance Spec:** [Zero-Trust Data Sanitization (ZTDS)](https://privacyscrubber.com/solutions/engineering/zero-trust-ai-data-sanitization?src=ci_stamp) | [Developer SDK ($199/mo)](https://privacyscrubber.com/pricing?tier=sdk&src=ci_stamp)
|
|
100
220
|
`;
|
|
101
221
|
console.log(stamp.trim());
|
|
102
222
|
process.exit(0);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!text) {
|
|
226
|
+
if (isJson) {
|
|
227
|
+
console.log(JSON.stringify({ scrubbedText: '', tokenMap: {}, count: 0 }, null, 2));
|
|
106
228
|
}
|
|
107
|
-
const { scrubbedText } = sanitize(text, { profile, detectSecrets: true });
|
|
108
|
-
process.stdout.write(scrubbedText.endsWith('\n') ? scrubbedText : scrubbedText + '\n');
|
|
109
229
|
process.exit(0);
|
|
110
230
|
}
|
|
231
|
+
|
|
232
|
+
const res = sanitize(text, { profile, detectSecrets: true });
|
|
233
|
+
if (isJson) {
|
|
234
|
+
console.log(JSON.stringify({
|
|
235
|
+
scrubbedText: res.scrubbedText,
|
|
236
|
+
tokenMap: res.tokenMap,
|
|
237
|
+
count: res.count,
|
|
238
|
+
telemetry: res.telemetry
|
|
239
|
+
}, null, 2));
|
|
240
|
+
} else {
|
|
241
|
+
process.stdout.write(res.scrubbedText.endsWith('\n') ? res.scrubbedText : res.scrubbedText + '\n');
|
|
242
|
+
}
|
|
243
|
+
process.exit(0);
|
|
111
244
|
}
|
|
112
245
|
|
|
113
246
|
if (inputArg) {
|
|
@@ -118,12 +251,26 @@ if (inputArg) {
|
|
|
118
251
|
handleExecution(inputArg);
|
|
119
252
|
}
|
|
120
253
|
} else if (!process.stdin.isTTY) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
254
|
+
if (isStream && isRestore) {
|
|
255
|
+
const detok = new StreamingDetokenizer(tokenMap);
|
|
256
|
+
process.stdin.setEncoding('utf8');
|
|
257
|
+
process.stdin.on('data', chunk => {
|
|
258
|
+
const out = detok.feed(chunk);
|
|
259
|
+
if (out) process.stdout.write(out);
|
|
260
|
+
});
|
|
261
|
+
process.stdin.on('end', () => {
|
|
262
|
+
const finalOut = detok.flush();
|
|
263
|
+
if (finalOut) process.stdout.write(finalOut);
|
|
264
|
+
process.exit(0);
|
|
265
|
+
});
|
|
266
|
+
} else {
|
|
267
|
+
const chunks = [];
|
|
268
|
+
process.stdin.on('data', c => chunks.push(c));
|
|
269
|
+
process.stdin.on('end', () => {
|
|
270
|
+
const content = Buffer.concat(chunks).toString('utf8');
|
|
271
|
+
handleExecution(content);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
127
274
|
} else {
|
|
128
275
|
console.error(`Error: No input provided. Use: npx @privacyscrubber/sdk <fileOrText> or pipe stdin.`);
|
|
129
276
|
process.exit(1);
|