@privacyscrubber/sdk 2.2.9 → 2.3.1
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/browser.js +54 -5
- package/cli.js +64 -12
- package/index.d.ts +17 -0
- package/index.js +121 -8
- package/package.json +1 -1
- package/ps-license-manager.js +1 -1
- package/ps-pii-engine.cjs +1 -1
- package/scrubber-core.cjs +1 -1
package/browser.js
CHANGED
|
@@ -125,29 +125,77 @@ export function buildAuditTelemetry(currentTokenMap = {}) {
|
|
|
125
125
|
frameworksSet.add('GDPR (Art. 4)');
|
|
126
126
|
frameworksSet.add('CCPA/CPRA');
|
|
127
127
|
}
|
|
128
|
-
if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT')) {
|
|
128
|
+
if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT') || types.includes('NATIONAL_ID')) {
|
|
129
129
|
frameworksSet.add('SOC 2 Type II');
|
|
130
130
|
frameworksSet.add('ISO 27001 (A.8.11)');
|
|
131
131
|
}
|
|
132
|
-
if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD', 'STRIPE_KEY'].includes(t))) {
|
|
132
|
+
if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD', 'STRIPE_KEY', 'TAX_ID', 'EIN', 'W2', '1099', 'SALARY', 'WAGE'].includes(t))) {
|
|
133
133
|
frameworksSet.add('PCI DSS v4.0');
|
|
134
|
+
frameworksSet.add('IRC § 7216 & GLBA');
|
|
134
135
|
}
|
|
135
|
-
if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT'].includes(t))) {
|
|
136
|
+
if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT', 'DIAGNOSIS', 'NPI', 'DEA'].includes(t))) {
|
|
136
137
|
frameworksSet.add('HIPAA §164.514');
|
|
137
138
|
}
|
|
138
|
-
if (types.some(t => ['
|
|
139
|
+
if (types.some(t => ['CASE_NUMBER', 'DOCKET', 'PRIVILEGED', 'LEGAL', 'ATTORNEY', 'COURT', 'CONTRACT', 'NDA'].includes(t))) {
|
|
140
|
+
frameworksSet.add('FRE 502 (Privilege)');
|
|
141
|
+
}
|
|
142
|
+
if (types.some(t => ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'KEY', 'AWS_KEY', 'JWT_TOKEN', 'API_TOKEN', 'CREDENTIAL'].includes(t))) {
|
|
139
143
|
frameworksSet.add('NIST SP 800-53');
|
|
140
144
|
}
|
|
145
|
+
if (totalCount > 0) {
|
|
146
|
+
frameworksSet.add('EU AI Act (Art. 50)');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const ipHoldings = {
|
|
150
|
+
standard: 'IETF draft-sibiryakov-ztds-protocol-00',
|
|
151
|
+
standardUrl: 'https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/',
|
|
152
|
+
patent: 'IL 331905 (Tracking: 94221)',
|
|
153
|
+
wipoDas: 'B17B',
|
|
154
|
+
trademark: 'ZTDS™ Reg. #182655957 (ILPO Cl 9 & 42)'
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const statutoryCitations = [
|
|
158
|
+
{
|
|
159
|
+
statute: 'EU AI Act (Art. 50)',
|
|
160
|
+
scope: 'AI Transparency & Data Minimization',
|
|
161
|
+
rule: '0-byte external network transmission; prevents unauthorized ingestion into frontier models.'
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
statute: 'GDPR (Art. 4, 25, 28, 32)',
|
|
165
|
+
scope: 'Pseudonymization & Processor Liability',
|
|
166
|
+
rule: 'Volatile client-side tokenization eliminates GDPR Article 28 data processor liability.'
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
statute: 'HIPAA 45 CFR § 164.514(b)',
|
|
170
|
+
scope: 'Safe Harbor De-Identification',
|
|
171
|
+
rule: 'Deterministic removal of all 18 PHI identifiers prior to AI analysis.'
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
statute: 'IRC § 7216 & IRS Pub. 1075',
|
|
175
|
+
scope: 'Tax Return Information Confidentiality',
|
|
176
|
+
rule: 'Strict criminal penalty safeguard against unauthorized disclosure of tax records to cloud LLMs.'
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
statute: 'FRE 502',
|
|
180
|
+
scope: 'Attorney-Client Privilege Preservation',
|
|
181
|
+
rule: 'Automated in-memory redaction prevents inadvertent waiver of legal privilege in AI workflows.'
|
|
182
|
+
}
|
|
183
|
+
];
|
|
141
184
|
|
|
142
185
|
return {
|
|
143
186
|
totalCount,
|
|
144
187
|
entities,
|
|
145
188
|
types,
|
|
146
189
|
riskLevel,
|
|
147
|
-
frameworksList: Array.from(frameworksSet)
|
|
190
|
+
frameworksList: Array.from(frameworksSet),
|
|
191
|
+
statutoryCitations,
|
|
192
|
+
ipHoldings,
|
|
193
|
+
multimodalEgress: '0.00 Bytes (Solid Blackout & Burn-in Verified)'
|
|
148
194
|
};
|
|
149
195
|
}
|
|
150
196
|
|
|
197
|
+
export const buildCisoAuditTelemetry = buildAuditTelemetry;
|
|
198
|
+
|
|
151
199
|
export function formatAuditReceipt(telemetry) {
|
|
152
200
|
if (!telemetry || telemetry.totalCount === 0) {
|
|
153
201
|
return "> 🛡️ **PrivacyScrubber Audit Receipt**: CLEAN (ZERO PII DETECTED)\n" +
|
|
@@ -665,6 +713,7 @@ export default {
|
|
|
665
713
|
validateLicense,
|
|
666
714
|
validateLicenseKey,
|
|
667
715
|
buildAuditTelemetry,
|
|
716
|
+
buildCisoAuditTelemetry,
|
|
668
717
|
formatAuditReceipt,
|
|
669
718
|
PrivacyScrubberEngine,
|
|
670
719
|
PrivacyScrubberLicenseError,
|
package/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import path from 'node:path';
|
|
|
14
14
|
import crypto from 'node:crypto';
|
|
15
15
|
import http from 'node:http';
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
|
-
import { sanitize, restore, generateProcurementMemo, StreamingDetokenizer } from './index.js';
|
|
17
|
+
import { sanitize, restore, validateLicense, generateProcurementMemo, StreamingDetokenizer } from './index.js';
|
|
18
18
|
|
|
19
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
20
|
const __dirname = path.dirname(__filename);
|
|
@@ -61,17 +61,24 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
61
61
|
if (portIdx !== -1 && args[portIdx + 1]) {
|
|
62
62
|
port = parseInt(args[portIdx + 1], 10);
|
|
63
63
|
}
|
|
64
|
+
let host = process.env.HOST || '127.0.0.1';
|
|
65
|
+
const hostIdx = args.indexOf('--host');
|
|
66
|
+
if (hostIdx !== -1 && args[hostIdx + 1]) {
|
|
67
|
+
host = args[hostIdx + 1];
|
|
68
|
+
}
|
|
64
69
|
let socketPath = null;
|
|
65
70
|
const sockIdx = args.indexOf('--socket');
|
|
66
71
|
if (sockIdx !== -1 && args[sockIdx + 1]) {
|
|
67
72
|
socketPath = args[sockIdx + 1];
|
|
68
73
|
}
|
|
69
74
|
|
|
75
|
+
const defaultEnvKey = process.env.PRIVACYSCRUBBER_KEY || process.env.PRIVACYSCRUBBER_LICENSE_KEY || process.env.ZTDS_LICENSE_KEY || '';
|
|
76
|
+
|
|
70
77
|
const server = http.createServer((req, res) => {
|
|
71
|
-
// CORS headers for local loopback
|
|
78
|
+
// CORS headers for local loopback & container IPC
|
|
72
79
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
73
80
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
74
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
81
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-License-Key');
|
|
75
82
|
|
|
76
83
|
if (req.method === 'OPTIONS') {
|
|
77
84
|
res.writeHead(204);
|
|
@@ -79,9 +86,46 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
79
86
|
return;
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
const reqUrl = req.url ? req.url.split('?')[0] : '/';
|
|
90
|
+
|
|
91
|
+
// Extract authorization key from header if present
|
|
92
|
+
let headerKey = '';
|
|
93
|
+
const authHeader = req.headers['authorization'] || '';
|
|
94
|
+
if (authHeader.startsWith('Bearer ')) {
|
|
95
|
+
headerKey = authHeader.slice(7).trim();
|
|
96
|
+
} else if (authHeader) {
|
|
97
|
+
headerKey = authHeader.trim();
|
|
98
|
+
}
|
|
99
|
+
const xKey = req.headers['x-license-key'] ? String(req.headers['x-license-key']).trim() : '';
|
|
100
|
+
const effectiveServerKey = headerKey || xKey || defaultEnvKey;
|
|
101
|
+
|
|
102
|
+
if (req.method === 'GET' && (reqUrl === '/' || reqUrl === '/health' || reqUrl === '/v1/health')) {
|
|
103
|
+
const lic = validateLicense(effectiveServerKey);
|
|
83
104
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
84
|
-
res.end(JSON.stringify({
|
|
105
|
+
res.end(JSON.stringify({
|
|
106
|
+
status: 'ok',
|
|
107
|
+
engine: 'ZTDS',
|
|
108
|
+
tier: lic.valid ? (lic.tier || 'SDK') : 'Community (Free Tier)',
|
|
109
|
+
licensed: !!lic.valid,
|
|
110
|
+
version: '2.2.6',
|
|
111
|
+
networkEgress: '0 bytes (100% in-memory RAM)',
|
|
112
|
+
uptimeSec: Math.floor(process.uptime()),
|
|
113
|
+
memoryMb: (process.memoryUsage().heapUsed / (1024 * 1024)).toFixed(2)
|
|
114
|
+
}));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (req.method === 'GET' && (reqUrl === '/metrics' || reqUrl === '/v1/metrics')) {
|
|
119
|
+
const mem = process.memoryUsage();
|
|
120
|
+
res.writeHead(200, { 'Content-Type': 'text/plain; version=0.0.4' });
|
|
121
|
+
res.end(
|
|
122
|
+
`# HELP ztds_uptime_seconds Process uptime\n` +
|
|
123
|
+
`# TYPE ztds_uptime_seconds counter\n` +
|
|
124
|
+
`ztds_uptime_seconds ${Math.floor(process.uptime())}\n` +
|
|
125
|
+
`# HELP ztds_memory_heap_bytes Process heap usage\n` +
|
|
126
|
+
`# TYPE ztds_memory_heap_bytes gauge\n` +
|
|
127
|
+
`ztds_memory_heap_bytes ${mem.heapUsed}\n`
|
|
128
|
+
);
|
|
85
129
|
return;
|
|
86
130
|
}
|
|
87
131
|
|
|
@@ -99,9 +143,16 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
99
143
|
req.on('end', () => {
|
|
100
144
|
try {
|
|
101
145
|
const payload = bodyStr ? JSON.parse(bodyStr) : {};
|
|
102
|
-
|
|
146
|
+
const isScrub = reqUrl === '/scrub' || reqUrl === '/sanitize' || reqUrl === '/v1/scrub' || reqUrl === '/v1/sanitize';
|
|
147
|
+
const isUnscrub = reqUrl === '/unscrub' || reqUrl === '/restore' || reqUrl === '/v1/unscrub' || reqUrl === '/v1/restore';
|
|
148
|
+
|
|
149
|
+
if (isScrub) {
|
|
103
150
|
const text = payload.text || '';
|
|
104
151
|
const opts = payload.options || { profile: payload.profile || 'General', detectSecrets: true };
|
|
152
|
+
if (!opts.licenseKey) {
|
|
153
|
+
opts.licenseKey = payload.licenseKey || effectiveServerKey;
|
|
154
|
+
}
|
|
155
|
+
|
|
105
156
|
const start = process.hrtime.bigint();
|
|
106
157
|
const result = sanitize(text, opts);
|
|
107
158
|
const end = process.hrtime.bigint();
|
|
@@ -111,6 +162,7 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
111
162
|
res.end(JSON.stringify({
|
|
112
163
|
sanitizedText: result.scrubbedText,
|
|
113
164
|
tokenMap: result.tokenMap,
|
|
165
|
+
license: result.license,
|
|
114
166
|
telemetry: {
|
|
115
167
|
...result.telemetry,
|
|
116
168
|
latencyUs: durationUs,
|
|
@@ -120,7 +172,7 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
120
172
|
return;
|
|
121
173
|
}
|
|
122
174
|
|
|
123
|
-
if (
|
|
175
|
+
if (isUnscrub) {
|
|
124
176
|
const text = payload.text || '';
|
|
125
177
|
const tokenMap = payload.tokenMap || {};
|
|
126
178
|
const { restoredText, restoredCount } = restore(text, tokenMap);
|
|
@@ -130,10 +182,10 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
130
182
|
}
|
|
131
183
|
|
|
132
184
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
133
|
-
res.end(JSON.stringify({ error: 'Endpoint Not Found. Supported: /scrub, /unscrub, /health' }));
|
|
185
|
+
res.end(JSON.stringify({ error: 'Endpoint Not Found. Supported: /v1/scrub, /v1/unscrub, /v1/health, /v1/metrics' }));
|
|
134
186
|
} catch (err) {
|
|
135
187
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
136
|
-
res.end(JSON.stringify({ error: 'Invalid JSON payload', message: err.message }));
|
|
188
|
+
res.end(JSON.stringify({ error: 'Invalid JSON payload or processing error', message: err.message }));
|
|
137
189
|
}
|
|
138
190
|
});
|
|
139
191
|
return;
|
|
@@ -152,9 +204,9 @@ if (args.includes('sidecar') || args.includes('--sidecar') || args.includes('ser
|
|
|
152
204
|
console.log(`[ZTDS Sidecar] Ready for Python/Go local IPC with <1ms in-memory execution.`);
|
|
153
205
|
});
|
|
154
206
|
} else {
|
|
155
|
-
server.listen(port,
|
|
156
|
-
console.log(`[ZTDS Sidecar] HTTP Daemon listening on http
|
|
157
|
-
console.log(`[ZTDS Sidecar] Endpoints: POST /scrub | POST /unscrub | GET /health`);
|
|
207
|
+
server.listen(port, host, () => {
|
|
208
|
+
console.log(`[ZTDS Sidecar] HTTP Daemon listening on http://${host}:${port}`);
|
|
209
|
+
console.log(`[ZTDS Sidecar] Endpoints: POST /v1/scrub | POST /v1/unscrub | GET /v1/health | GET /v1/metrics`);
|
|
158
210
|
console.log(`[ZTDS Sidecar] Latency: <1.2ms (Zero network hops, local loopback)`);
|
|
159
211
|
});
|
|
160
212
|
}
|
package/index.d.ts
CHANGED
|
@@ -103,12 +103,28 @@ export interface LicenseMetadata {
|
|
|
103
103
|
recommendation?: string;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
export interface StatutoryCitation {
|
|
107
|
+
statute: string;
|
|
108
|
+
scope: string;
|
|
109
|
+
rule: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface IpHoldings {
|
|
113
|
+
standard: string;
|
|
114
|
+
standardUrl: string;
|
|
115
|
+
patent: string;
|
|
116
|
+
wipoDas: string;
|
|
117
|
+
trademark: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
106
120
|
export interface AuditTelemetry {
|
|
107
121
|
totalCount: number;
|
|
108
122
|
entities: Record<string, number>;
|
|
109
123
|
types: string[];
|
|
110
124
|
riskLevel: 'CLEAN (ZERO PII)' | 'LOW EXPOSURE' | 'MODERATE EXPOSURE' | 'CRITICAL (HIGH EXPOSURE)';
|
|
111
125
|
frameworksList: string[];
|
|
126
|
+
statutoryCitations?: StatutoryCitation[];
|
|
127
|
+
ipHoldings?: IpHoldings;
|
|
112
128
|
}
|
|
113
129
|
|
|
114
130
|
export interface SanitizeResult {
|
|
@@ -160,6 +176,7 @@ export function validateLicense(key: string): LicenseValidationResult;
|
|
|
160
176
|
export function validateLicenseKey(key: string): 'PRO' | 'TEAMS' | 'SDK' | 'ENTERPRISE' | 'OEM' | null;
|
|
161
177
|
|
|
162
178
|
export function buildAuditTelemetry(currentTokenMap?: Record<string, string> | string[]): AuditTelemetry;
|
|
179
|
+
export const buildCisoAuditTelemetry: typeof buildAuditTelemetry;
|
|
163
180
|
export function formatAuditReceipt(telemetry: AuditTelemetry): string;
|
|
164
181
|
|
|
165
182
|
export interface AuditEventData {
|
package/index.js
CHANGED
|
@@ -142,29 +142,80 @@ export function buildAuditTelemetry(currentTokenMap = {}) {
|
|
|
142
142
|
frameworksSet.add('GDPR (Art. 4)');
|
|
143
143
|
frameworksSet.add('CCPA/CPRA');
|
|
144
144
|
}
|
|
145
|
-
if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT')) {
|
|
145
|
+
if (types.includes('ID') || types.includes('SSN') || types.includes('PASSPORT') || types.includes('NATIONAL_ID')) {
|
|
146
146
|
frameworksSet.add('SOC 2 Type II');
|
|
147
147
|
frameworksSet.add('ISO 27001 (A.8.11)');
|
|
148
148
|
}
|
|
149
|
-
if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD', 'STRIPE_KEY'].includes(t))) {
|
|
149
|
+
if (types.some(t => ['CREDIT_CARD', 'BANK', 'IBAN', 'FINANCIAL', 'CARD', 'STRIPE_KEY', 'TAX_ID', 'EIN', 'W2', '1099', 'SALARY', 'WAGE'].includes(t))) {
|
|
150
150
|
frameworksSet.add('PCI DSS v4.0');
|
|
151
|
+
frameworksSet.add('IRC § 7216 & GLBA');
|
|
151
152
|
}
|
|
152
|
-
if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT'].includes(t))) {
|
|
153
|
+
if (types.some(t => ['MRN', 'HEALTH', 'MEDICAL', 'PATIENT', 'DIAGNOSIS', 'NPI', 'DEA'].includes(t))) {
|
|
153
154
|
frameworksSet.add('HIPAA §164.514');
|
|
154
155
|
}
|
|
155
|
-
if (types.some(t => ['
|
|
156
|
+
if (types.some(t => ['CASE_NUMBER', 'DOCKET', 'PRIVILEGED', 'LEGAL', 'ATTORNEY', 'COURT', 'CONTRACT', 'NDA'].includes(t))) {
|
|
157
|
+
frameworksSet.add('FRE 502 (Privilege)');
|
|
158
|
+
}
|
|
159
|
+
if (types.some(t => ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'KEY', 'AWS_KEY', 'JWT_TOKEN', 'API_TOKEN', 'CREDENTIAL'].includes(t))) {
|
|
156
160
|
frameworksSet.add('NIST SP 800-53');
|
|
157
161
|
}
|
|
162
|
+
if (totalCount > 0) {
|
|
163
|
+
frameworksSet.add('EU AI Act (Art. 50)');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const ipHoldings = {
|
|
167
|
+
standard: 'IETF draft-sibiryakov-ztds-protocol-00',
|
|
168
|
+
standardUrl: 'https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/',
|
|
169
|
+
patent: 'IL 331905 (Tracking: 94221)',
|
|
170
|
+
wipoDas: 'B17B',
|
|
171
|
+
trademark: 'ZTDS™ Reg. #182655957 (ILPO Cl 9 & 42)'
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const statutoryCitations = [
|
|
175
|
+
{
|
|
176
|
+
statute: 'EU AI Act (Art. 50)',
|
|
177
|
+
scope: 'AI Transparency & Data Minimization',
|
|
178
|
+
rule: '0-byte external network transmission; prevents unauthorized ingestion into frontier models.'
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
statute: 'GDPR (Art. 4, 25, 28, 32)',
|
|
182
|
+
scope: 'Pseudonymization & Processor Liability',
|
|
183
|
+
rule: 'Volatile client-side tokenization eliminates GDPR Article 28 data processor liability.'
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
statute: 'HIPAA 45 CFR § 164.514(b)',
|
|
187
|
+
scope: 'Safe Harbor De-Identification',
|
|
188
|
+
rule: 'Deterministic removal of all 18 PHI identifiers prior to AI analysis.'
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
statute: 'IRC § 7216 & IRS Pub. 1075',
|
|
192
|
+
scope: 'Tax Return Information Confidentiality',
|
|
193
|
+
rule: 'Strict criminal penalty safeguard against unauthorized disclosure of tax records to cloud LLMs.'
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
statute: 'FRE 502',
|
|
197
|
+
scope: 'Attorney-Client Privilege Preservation',
|
|
198
|
+
rule: 'Automated in-memory redaction prevents inadvertent waiver of legal privilege in AI workflows.'
|
|
199
|
+
}
|
|
200
|
+
];
|
|
158
201
|
|
|
159
202
|
return {
|
|
160
203
|
totalCount,
|
|
161
204
|
entities,
|
|
162
205
|
types,
|
|
163
206
|
riskLevel,
|
|
164
|
-
frameworksList: Array.from(frameworksSet)
|
|
207
|
+
frameworksList: Array.from(frameworksSet),
|
|
208
|
+
statutoryCitations,
|
|
209
|
+
ipHoldings,
|
|
210
|
+
multimodalEgress: '0.00 Bytes (Solid Blackout & Burn-in Verified)'
|
|
165
211
|
};
|
|
166
212
|
}
|
|
167
213
|
|
|
214
|
+
/**
|
|
215
|
+
* SSOT Alias for cross-plane parity with Web, Extension, and MCP.
|
|
216
|
+
*/
|
|
217
|
+
export const buildCisoAuditTelemetry = buildAuditTelemetry;
|
|
218
|
+
|
|
168
219
|
/**
|
|
169
220
|
* Formats structured audit telemetry into a human-readable Markdown Audit Receipt.
|
|
170
221
|
* @param {object} telemetry
|
|
@@ -716,6 +767,7 @@ export class PrivacyScrubberEngine {
|
|
|
716
767
|
this.generateReceipt = config.generateReceipt !== false;
|
|
717
768
|
this.sessionMap = { ...(config.initialSessionMap || {}) };
|
|
718
769
|
this.ignoreList = new Set(config.ignoreList || []);
|
|
770
|
+
this.maxSessionTokens = config.maxSessionTokens || 10000;
|
|
719
771
|
|
|
720
772
|
this.license = validateLicense(this.licenseKey);
|
|
721
773
|
}
|
|
@@ -758,6 +810,14 @@ export class PrivacyScrubberEngine {
|
|
|
758
810
|
const result = sanitize(text, mergedOptions);
|
|
759
811
|
if (options.persistSession !== false) {
|
|
760
812
|
Object.assign(this.sessionMap, result.tokenMap);
|
|
813
|
+
const maxTokens = options.maxSessionTokens || this.maxSessionTokens || 10000;
|
|
814
|
+
const keys = Object.keys(this.sessionMap);
|
|
815
|
+
if (keys.length > maxTokens) {
|
|
816
|
+
const pruneCount = keys.length - maxTokens;
|
|
817
|
+
for (let i = 0; i < pruneCount; i++) {
|
|
818
|
+
delete this.sessionMap[keys[i]];
|
|
819
|
+
}
|
|
820
|
+
}
|
|
761
821
|
}
|
|
762
822
|
return result;
|
|
763
823
|
}
|
|
@@ -1359,9 +1419,30 @@ export class StreamingDetokenizer {
|
|
|
1359
1419
|
this.replacer = buildStreamReplacer(this.tokenMap);
|
|
1360
1420
|
this.pendingBuffer = '';
|
|
1361
1421
|
this.hasTokens = Object.keys(this.tokenMap).length > 0;
|
|
1422
|
+
this._lastTokenCount = Object.keys(this.tokenMap).length;
|
|
1362
1423
|
this.decoder = new TextDecoder('utf-8', { fatal: false });
|
|
1363
1424
|
}
|
|
1364
1425
|
|
|
1426
|
+
/**
|
|
1427
|
+
* Dynamically updates the active token map for this streaming session.
|
|
1428
|
+
* @param {Record<string, string>} newMap
|
|
1429
|
+
*/
|
|
1430
|
+
updateTokenMap(newMap) {
|
|
1431
|
+
if (newMap && typeof newMap === 'object') {
|
|
1432
|
+
this.tokenMap = newMap;
|
|
1433
|
+
this.replacer = buildStreamReplacer(this.tokenMap);
|
|
1434
|
+
this.hasTokens = Object.keys(this.tokenMap).length > 0;
|
|
1435
|
+
this._lastTokenCount = Object.keys(this.tokenMap).length;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* Resets the internal sliding window buffer.
|
|
1441
|
+
*/
|
|
1442
|
+
reset() {
|
|
1443
|
+
this.pendingBuffer = '';
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1365
1446
|
/**
|
|
1366
1447
|
* Feeds an incoming chunk (string, Uint8Array, or Buffer) and returns de-tokenized text.
|
|
1367
1448
|
* Buffers any unclosed token candidates at chunk boundaries until closed or flushed.
|
|
@@ -1380,6 +1461,15 @@ export class StreamingDetokenizer {
|
|
|
1380
1461
|
}
|
|
1381
1462
|
|
|
1382
1463
|
if (!text) return '';
|
|
1464
|
+
|
|
1465
|
+
// Dynamically recompile if tokens were added to the referenced map
|
|
1466
|
+
const currentTokenCount = Object.keys(this.tokenMap).length;
|
|
1467
|
+
if (currentTokenCount !== this._lastTokenCount) {
|
|
1468
|
+
this.replacer = buildStreamReplacer(this.tokenMap);
|
|
1469
|
+
this.hasTokens = currentTokenCount > 0;
|
|
1470
|
+
this._lastTokenCount = currentTokenCount;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1383
1473
|
if (!this.hasTokens) return text;
|
|
1384
1474
|
|
|
1385
1475
|
this.pendingBuffer += text;
|
|
@@ -1431,7 +1521,7 @@ export class StreamingDetokenizer {
|
|
|
1431
1521
|
*/
|
|
1432
1522
|
toNodeTransform() {
|
|
1433
1523
|
const self = this;
|
|
1434
|
-
|
|
1524
|
+
const transformStream = new Transform({
|
|
1435
1525
|
transform(chunk, encoding, callback) {
|
|
1436
1526
|
try {
|
|
1437
1527
|
const out = self.feed(chunk);
|
|
@@ -1449,8 +1539,14 @@ export class StreamingDetokenizer {
|
|
|
1449
1539
|
} catch (err) {
|
|
1450
1540
|
callback(err);
|
|
1451
1541
|
}
|
|
1542
|
+
},
|
|
1543
|
+
destroy(err, callback) {
|
|
1544
|
+
self.reset();
|
|
1545
|
+
callback(err);
|
|
1452
1546
|
}
|
|
1453
1547
|
});
|
|
1548
|
+
transformStream.detokenizer = self;
|
|
1549
|
+
return transformStream;
|
|
1454
1550
|
}
|
|
1455
1551
|
|
|
1456
1552
|
/**
|
|
@@ -2014,6 +2110,8 @@ ${telemetry.frameworksList.map(f => `- **${f}**`).join('\n')}
|
|
|
2014
2110
|
*/
|
|
2015
2111
|
export function createSanitizeStream(options = {}) {
|
|
2016
2112
|
const engine = options.engine || new PrivacyScrubberEngine(options);
|
|
2113
|
+
const persistSession = options.persistSession !== false;
|
|
2114
|
+
const maxSessionTokens = options.maxSessionTokens || 10000;
|
|
2017
2115
|
let tail = '';
|
|
2018
2116
|
|
|
2019
2117
|
const stream = new Transform({
|
|
@@ -2024,7 +2122,12 @@ export function createSanitizeStream(options = {}) {
|
|
|
2024
2122
|
tail = lines.pop() || '';
|
|
2025
2123
|
|
|
2026
2124
|
for (let i = 0; i < lines.length; i++) {
|
|
2027
|
-
const sanitized = engine.sanitize(lines[i], {
|
|
2125
|
+
const sanitized = engine.sanitize(lines[i], {
|
|
2126
|
+
...options,
|
|
2127
|
+
persistSession,
|
|
2128
|
+
maxSessionTokens,
|
|
2129
|
+
generateReceipt: false
|
|
2130
|
+
});
|
|
2028
2131
|
this.push(sanitized.scrubbedText + '\n');
|
|
2029
2132
|
}
|
|
2030
2133
|
callback();
|
|
@@ -2035,7 +2138,12 @@ export function createSanitizeStream(options = {}) {
|
|
|
2035
2138
|
flush(callback) {
|
|
2036
2139
|
try {
|
|
2037
2140
|
if (tail.length > 0) {
|
|
2038
|
-
const sanitized = engine.sanitize(tail, {
|
|
2141
|
+
const sanitized = engine.sanitize(tail, {
|
|
2142
|
+
...options,
|
|
2143
|
+
persistSession,
|
|
2144
|
+
maxSessionTokens,
|
|
2145
|
+
generateReceipt: false
|
|
2146
|
+
});
|
|
2039
2147
|
this.push(sanitized.scrubbedText);
|
|
2040
2148
|
tail = '';
|
|
2041
2149
|
}
|
|
@@ -2043,6 +2151,10 @@ export function createSanitizeStream(options = {}) {
|
|
|
2043
2151
|
} catch (err) {
|
|
2044
2152
|
callback(err);
|
|
2045
2153
|
}
|
|
2154
|
+
},
|
|
2155
|
+
destroy(err, callback) {
|
|
2156
|
+
tail = '';
|
|
2157
|
+
callback(err);
|
|
2046
2158
|
}
|
|
2047
2159
|
});
|
|
2048
2160
|
|
|
@@ -2380,6 +2492,7 @@ export default {
|
|
|
2380
2492
|
validateLicense,
|
|
2381
2493
|
validateLicenseKey,
|
|
2382
2494
|
buildAuditTelemetry,
|
|
2495
|
+
buildCisoAuditTelemetry,
|
|
2383
2496
|
formatAuditReceipt,
|
|
2384
2497
|
generateAuditReport,
|
|
2385
2498
|
formatCefEvent,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@privacyscrubber/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "In-process consumer privacy defense and pre-emptive PII interception SDK for Node.js, Next.js, and AI pipelines. Zero data loss, zero third parties, sub-millisecond in-memory execution.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"browser": "browser.js",
|