@privacyscrubber/sdk 2.4.6 → 2.4.8
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 +32 -7
- package/cli.js +1 -1
- package/examples/python-bridge/fastapi_example.py +1 -1
- package/examples/python-bridge/privacyscrubber/__init__.py +1 -1
- package/examples/python-bridge/privacyscrubber/bridge_daemon.cjs +1 -1
- package/examples/python-bridge/privacyscrubber/cli.py +1 -1
- package/examples/python-bridge/pyproject.toml +1 -1
- package/examples/python-bridge/setup.py +1 -1
- package/fastmcp.cjs +56 -0
- package/fastmcp.d.ts +5 -0
- package/index.cjs +210 -18
- package/index.d.ts +9 -0
- package/index.js +210 -18
- package/package.json +1 -1
- package/ps-license-manager.js +1 -1
- package/ps-pii-engine.cjs +1 -1
- package/sarif.cjs +2 -2
- package/scrubber-core.cjs +1 -1
package/index.js
CHANGED
|
@@ -41,7 +41,7 @@ export const SDK_VERSION = (() => {
|
|
|
41
41
|
return pkgJson.version;
|
|
42
42
|
}
|
|
43
43
|
} catch (_) {}
|
|
44
|
-
return '2.4.
|
|
44
|
+
return '2.4.8';
|
|
45
45
|
})();
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -90,7 +90,10 @@ export class PrivacyScrubberLicenseError extends Error {
|
|
|
90
90
|
this.currentLength = currentLength;
|
|
91
91
|
this.limit = limit;
|
|
92
92
|
this.profile = profile;
|
|
93
|
-
|
|
93
|
+
const safeProf = encodeURIComponent(String(profile || 'General').toLowerCase());
|
|
94
|
+
this.upgradeUrl = `https://privacyscrubber.com/pricing?tier=sdk&profile=${safeProf}&utm_source=npm_sdk&utm_medium=sdk_error&utm_campaign=dev_upsell`;
|
|
95
|
+
this.trialUrl = `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${safeProf}&utm_source=npm_sdk&utm_medium=sdk_error&utm_campaign=dev_upsell`;
|
|
96
|
+
this.trialCommand = 'npx @privacyscrubber/sdk trial';
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -131,7 +134,7 @@ export function validateLicenseKey(key) {
|
|
|
131
134
|
* @param {Record<string, string>|string[]} currentTokenMap
|
|
132
135
|
* @returns {object}
|
|
133
136
|
*/
|
|
134
|
-
export function buildAuditTelemetry(currentTokenMap = {}) {
|
|
137
|
+
export function buildAuditTelemetry(currentTokenMap = {}, extraStats = {}) {
|
|
135
138
|
const entities = {};
|
|
136
139
|
let totalCount = 0;
|
|
137
140
|
|
|
@@ -197,11 +200,27 @@ export function buildAuditTelemetry(currentTokenMap = {}) {
|
|
|
197
200
|
trademark: 'ZTDS™ Reg. #182655957 (ILPO Cl 9 & 42)'
|
|
198
201
|
};
|
|
199
202
|
|
|
203
|
+
const agenticProtocol = {
|
|
204
|
+
protocolsSupported: ['MCP-1.0 (stdio/SSE)', 'A2A-1.0 (Agent2Agent)', 'ZTDS-RFC-v1.0'],
|
|
205
|
+
agentCardUrl: 'https://privacyscrubber.com/.well-known/agent-card.json',
|
|
206
|
+
ztdsStandardCardUrl: 'https://ztds.ai/.well-known/agent-card.json',
|
|
207
|
+
regulatorySafeHarbor: [
|
|
208
|
+
'GDPR-Art-4(5)-Pseudonymization',
|
|
209
|
+
'GDPR-Art-25-Privacy-by-Design',
|
|
210
|
+
'GDPR-Art-32-TOMs',
|
|
211
|
+
'EU-AI-Act-Art-50',
|
|
212
|
+
'EU-AI-Act-Art-17-EN-18286-QMS',
|
|
213
|
+
'CNIL-ICO-Agentic-Memory-Compartmentalization',
|
|
214
|
+
'IL-PPL-Amendment-13-Sec-17'
|
|
215
|
+
],
|
|
216
|
+
zeroCleartextEgress: true
|
|
217
|
+
};
|
|
218
|
+
|
|
200
219
|
const statutoryCitations = [
|
|
201
220
|
{
|
|
202
|
-
statute: 'EU AI Act (Art. 50)',
|
|
203
|
-
scope: 'AI Transparency &
|
|
204
|
-
rule: 'Sanitization runs locally by design; reduces the content sent to external AI models. Informational mapping, not a compliance determination.'
|
|
221
|
+
statute: 'EU AI Act (Art. 50 & Art. 17 / EN 18286:2026 QMS)',
|
|
222
|
+
scope: 'AI Transparency, Data Minimization & Harmonized QMS Governance',
|
|
223
|
+
rule: 'Sanitization runs locally by design; reduces the content sent to external AI models and supports Article 17 Quality Management System (EN 18286:2026) data governance controls. Informational mapping, not a compliance determination.'
|
|
205
224
|
},
|
|
206
225
|
{
|
|
207
226
|
statute: 'GDPR (Art. 4, 25, 28, 32)',
|
|
@@ -225,14 +244,34 @@ export function buildAuditTelemetry(currentTokenMap = {}) {
|
|
|
225
244
|
}
|
|
226
245
|
];
|
|
227
246
|
|
|
247
|
+
const rawLicenseTier = String(extraStats?.licenseTier || '').trim().toUpperCase();
|
|
248
|
+
const isOrganizationalScope = ['TEAMS', 'SDK', 'OEM', 'ENTERPRISE', 'ENTERPRISE_AIRGAP'].includes(rawLicenseTier);
|
|
249
|
+
const auditScope = isOrganizationalScope ? {
|
|
250
|
+
tier: rawLicenseTier,
|
|
251
|
+
isOrganizational: true,
|
|
252
|
+
badge: 'ORGANIZATIONAL MULTI-SEAT COMPLIANCE SCOPE',
|
|
253
|
+
coverage: 'Covers department, clinic, or firm personnel under unified ZTDS policy governance.',
|
|
254
|
+
upgradeRecommended: false,
|
|
255
|
+
upgradeUrl: null
|
|
256
|
+
} : {
|
|
257
|
+
tier: rawLicenseTier || 'SINGLE_OPERATOR',
|
|
258
|
+
isOrganizational: false,
|
|
259
|
+
badge: 'SINGLE-OPERATOR PERSONAL SCOPE',
|
|
260
|
+
coverage: 'Valid for individual workstation only. Multi-employee clinics, CPA firms, and departments require TEAMS ($99/mo flat, unlimited seats) for organizational audit coverage.',
|
|
261
|
+
upgradeRecommended: true,
|
|
262
|
+
upgradeUrl: 'https://privacyscrubber.com/pricing/?tier=teams&utm_source=audit_receipt&utm_medium=scope_upsell'
|
|
263
|
+
};
|
|
264
|
+
|
|
228
265
|
return {
|
|
229
266
|
totalCount,
|
|
230
267
|
entities,
|
|
231
268
|
types,
|
|
232
269
|
riskLevel,
|
|
233
270
|
frameworksList: Array.from(frameworksSet),
|
|
271
|
+
auditScope,
|
|
234
272
|
statutoryCitations,
|
|
235
273
|
ipHoldings,
|
|
274
|
+
agenticProtocol,
|
|
236
275
|
multimodalEgress: '0.00 Bytes (Solid Blackout & Burn-in Verified)'
|
|
237
276
|
};
|
|
238
277
|
}
|
|
@@ -283,6 +322,19 @@ export function _resetCommunityNoticeForTesting() {
|
|
|
283
322
|
communityNoticeLogged = false;
|
|
284
323
|
}
|
|
285
324
|
|
|
325
|
+
export const FREE_PROD_SPECIALIZED_CALL_LIMIT = 250;
|
|
326
|
+
let _freeProdSpecializedCalls = 0;
|
|
327
|
+
let _prodProfileFallbackNoticeLogged = false;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Resets the production specialized call quota state for testing purposes.
|
|
331
|
+
* @private
|
|
332
|
+
*/
|
|
333
|
+
export function _resetProdQuotaForTesting() {
|
|
334
|
+
_freeProdSpecializedCalls = 0;
|
|
335
|
+
_prodProfileFallbackNoticeLogged = false;
|
|
336
|
+
}
|
|
337
|
+
|
|
286
338
|
let devSecretNoticeLogged = false;
|
|
287
339
|
|
|
288
340
|
function emitDevSecretNoticeIfNeeded(isPro, tokenMap = {}, limit = 15000) {
|
|
@@ -463,6 +515,82 @@ We request approval to acquire a **Developer SDK License** (\$299/month flat or
|
|
|
463
515
|
* auditReceipt: string
|
|
464
516
|
* }}
|
|
465
517
|
*/
|
|
518
|
+
const SDK_VERTICAL_DETECTORS = [
|
|
519
|
+
{ profile: 'medical', label: 'Medical PHI (HIPAA)', regex: /\b(?:Patient\s*(?:Name|ID|DOB|Record)|MRN\b|Medical\s+Record|Attending\s+Physician|Chief\s+Complaint|Diagnosis\s*:|ICD-10|HIPAA|Clinical\s+Intake|Discharge\s+Summary|MedPod|NPI\s*(?:#|No|:)?\s*\d{10}|Medicare\s*(?:ID|Beneficiary|Claim)|Medicaid\s*ID|Prescription|Rx\s*#|Blood\s+Type|Vital\s+Signs|Radiology|Lab\s+Results|EHR\b|EMR\b)\b/gi },
|
|
520
|
+
{ profile: 'accounting', label: 'Tax & Accounting', regex: /\b(?:Form\s*1040|Form\s*W-2|Form\s*1099|Adjusted\s+Gross\s+Income|Employer\s+Identification\s+Number|Schedule\s+C\b|Steuer-ID|USt-IdNr|Tax\s+Return|General\s+Ledger|Withholding\s+Allowance|Total\s+Tax\s+Liability)\b/gi },
|
|
521
|
+
{ profile: 'wealthmgmt', label: 'Wealth Management', regex: /\b(?:Revocable\s+Living\s+Trust|Irrevocable\s+(?:Generation-Skipping\s+)?Trust|Grantor\s*:|Settlor\s*:|Trustee\s*:|Beneficiary\s+Designation|Roth\s+IRA|Asset\s+Allocation|FBO\b|CUSIP\s*:|Grantor\s+Retained\s+Annuity\s+Trust|AUM\s*:)\b/gi },
|
|
522
|
+
{ profile: 'legal', label: 'Legal & Contracts', regex: /\b(?:Non-Disclosure\s+Agreement|Attorney-Client\s+Privilege|Docket\s+No|Hereinafter\s+referred|Indemnification\s+Clause|Governing\s+Law\s+and\s+Jurisdiction|Plaintiff\b|Defendant\b|Subpoena\b|Deposition\b|State\s+Bar\s*#)\b/gi },
|
|
523
|
+
{ profile: 'finance', label: 'Finance & Banking', regex: /\b(?:Wire\s+Transfer\s+Authorization|SWIFT\s*\/?\s*BIC\s*:|IBAN\s*:\s*[A-Z]{2}\d{2}|ABA\s+Routing|ACH\s+Transfer|PCI-DSS|Primary\s+Account\s+Number)\b/gi },
|
|
524
|
+
{ profile: 'hr', label: 'HR & Resumes', regex: /\b(?:Employee\s*ID|Performance\s+Improvement\s+Plan|Payroll\s+Register|Gross\s+Pay\b|Net\s+Pay\b|Candidate\s+Evaluation|Compensation\s+Band|Sozialversicherungsnummer|Personalnummer|EEID\s*:)\b/gi },
|
|
525
|
+
{ profile: 'realestate', label: 'Real Estate', regex: /\b(?:Assessor\s+Parcel\s+Number|APN\s*:|MLS\s*#|Closing\s+Disclosure|Escrow\s+(?:Account|Number|Officer)|Property\s+Deed|Title\s+Insurance|Lease\s+Application)\b/gi },
|
|
526
|
+
{ profile: 'dev', label: 'API & Dev Logs', regex: /\b(?:BEGIN\s+(?:RSA|OPENSSH|EC|PRIVATE)\s+PRIVATE\s+KEY|AWS_SECRET_ACCESS_KEY|DATABASE_URL\s*=|GITHUB_TOKEN\s*=|Bearer\s+eyJ[A-Za-z0-9_-]+)\b/g },
|
|
527
|
+
{ profile: 'sales', label: 'Sales & CRM', regex: /\b(?:CRM\s+(?:Opportunity|Export)|HubSpot\b|Salesforce\b|MEDDPICC\b|Primary\s+Buyer\s*:|MSA\s+Redlines|Deal\s+Stage|Pipeline\s+Forecast|Opportunity\s+ID|Account\s+Executive)\b/gi },
|
|
528
|
+
{ profile: 'engineering', label: 'DevOps & Infra', regex: /\b(?:Incident\s+Postmortem|Kubeconfig|Kubernetes\s+Cluster|Helm\s+Chart|Terraform\s+State|DMAIC\b|Bill\s+of\s+Materials|BOM\s+Spec|CNC\s+Machining|Shop\s+Floor\s+Batch)\b/gi },
|
|
529
|
+
{ profile: 'insurance', label: 'Insurance & Claims', regex: /\b(?:Claim\s+Settlement|Policyholder\s*:|Policy\s*#?POL-|Claim\s+Number|Adjuster\s*:|Loss\s+Location|Loss\s+Run|Subrogation\b|FNOL\b|Deductible\s*:)\b/gi },
|
|
530
|
+
{ profile: 'pharma', label: 'Pharma & Trials', regex: /\b(?:Clinical\s+Trial(?:\s+Protocol)?|NCT\d{8}\b|IRB\s+Protocol|FDA\s+1572|Adverse\s+Event|Pharmacovigilance|Principal\s+Investigator|Cohort\s+[A-Z]\b|Blinded\s+Study|CDISC\b|IND\s*(?:#|Protocol))\b/gi },
|
|
531
|
+
{ profile: 'underwriting', label: 'Loan Underwriting & Income', regex: /\b(?:Loan\s+Underwriting|Mortgage\s+Appraisal|Loan\s+Application\s+#?LOS|FICO\s*:\s*\d{3}|Appraised\s+Value\s*:|80%\s+LTV|Monthly\s+Qualifying\s*:|YTD\s+Earnings|Borrower\s*:)\b/gi },
|
|
532
|
+
{ profile: 'security', label: 'Security & SIEM', regex: /\b(?:SIEM\s+Alert|CVE-\d{4}-\d{4,7}\b|Threat\s*:\s*CRITICAL|Unauthorized\s+S3\s+bucket|SOC\s+Incident|Firewall\s+Rule\s+Violation|Intrusion\s+Detection)\b/gi },
|
|
533
|
+
{ profile: 'support', label: 'Support & Tickets', regex: /\b(?:Support\s+Ticket|Ticket\s*#INC-|Zendesk\b|Intercom\b|Billing\s+Discrepancy|SLA\s+Escalation|CSAT\s+Score|Customer\s+Support\s+Escalation)\b/gi },
|
|
534
|
+
{ profile: 'compliance', label: 'Compliance Audit', regex: /\b(?:Suspicious\s+Activity\s+Report|FinCEN\s+SAR|BSA\s+Officer|31\s+CFR\b|ROPA\s+Register|Data\s+Protection\s+Impact\s+Assessment|SOC\s*2\s+Type\s+II|ISO\s*27001\s+Audit|CMMC\s+Level|ITAR\s+Controlled)\b/gi },
|
|
535
|
+
{ profile: 'ccpa', label: 'CCPA / CPRA Privacy', regex: /\b(?:CCPA\s*\/?\s*CPRA|1798\.105|Right\s+to\s+Delete|Opt-out\s+of\s+Sale|Ticket\s*#?DSAR-)\b/gi },
|
|
536
|
+
{ profile: 'bizops', label: 'Biz Ops & M&A', regex: /\b(?:Vendor\s+Master\s+Agreement|Bill\s+of\s+Lading|Customs\s+Declaration|Purchase\s+Order\s*#?PO-|Supply\s+Chain\s+Manifest)\b/gi },
|
|
537
|
+
{ profile: 'biotech', label: 'Biotech & Genomics', regex: /\b(?:Sequencing\s+Platform|Illumina\s+NovaSeq|Flowcell\s*:|FastQ\s+Sample|ClinVar\b|HGVS\s+Variant|Genomic\s+Sequencing|Bioinformatics\s+Intake|BRCA[12]\s+c\.)\b/gi },
|
|
538
|
+
{ profile: 'automotive', label: 'Automotive & EV', regex: /\b(?:Connected\s+Vehicle\s+Telematics|ECU\s+Serial|Telematics\s+Device|ASIL-D\b|DTC\s+P\d{4}\b|CAN-Bus\s+Log)\b/gi },
|
|
539
|
+
{ profile: 'energy', label: 'Energy & Smart Grid', regex: /\b(?:Critical\s+Infrastructure\s+SCADA|Substation\s+ID|Feeder\s+ID|Smart\s+Meter\s*:|ESI\s+ID\s*:|NERC\s+CIP|BES\s+Cyber\s+Asset)\b/gi },
|
|
540
|
+
{ profile: 'hospitality', label: 'Hospitality & Travel', regex: /\b(?:Airline\s+Passenger\s+Dossier|PNR\s*:\s*[A-Z0-9]{6}|e-Ticket\s*:|Boarding\s+Pass\s*:|Known\s+Traveler\s+KTN|Guest\s+Folio|Marriott\s+Bonvoy|SkyMiles)\b/gi },
|
|
541
|
+
{ profile: 'telecom', label: 'Telecom & Call Center', regex: /\b(?:Telecom\s+Subscriber\s+CDR|MSISDN\s*:|IMSI\s*:\s*\d{14,15}|IMEI\s*:\s*\d{14,16}|Cell\s+Tower\s+ID|SIP\s+Signaling)\b/gi },
|
|
542
|
+
{ profile: 'academic', label: 'Academic & Research', regex: /\b(?:FERPA\s+Student\s+Record|Student\s+ID\s*:\s*#?STU-|Disciplinary\s+Record|IEP\s+Evaluation|IRB\s+Approval)\b/gi },
|
|
543
|
+
{ profile: 'agents', label: 'AI Agents & Prompts', regex: /\b(?:Autonomous\s+Financial\s+Procurement\s+Agent|AgentID\s*:\s*agt_|Encrypted\s+Tool\s+Token|Agent\s+Memory\s*>)\b/gi }
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Automatically detects the most relevant industry compliance profile from text content.
|
|
548
|
+
* Executes in <0.05ms in local RAM with zero network egress.
|
|
549
|
+
*
|
|
550
|
+
* @param {string} text - Input document or prompt text.
|
|
551
|
+
* @returns {{
|
|
552
|
+
* detectedProfile: string,
|
|
553
|
+
* label: string,
|
|
554
|
+
* confidence: 'high'|'medium'|'low',
|
|
555
|
+
* score: number,
|
|
556
|
+
* alternatives: Array<{ profile: string, label: string, score: number }>
|
|
557
|
+
* }}
|
|
558
|
+
*/
|
|
559
|
+
export function detectProfile(text) {
|
|
560
|
+
if (!text || typeof text !== 'string') {
|
|
561
|
+
return { detectedProfile: 'general', label: 'General PII', confidence: 'low', score: 0, alternatives: [] };
|
|
562
|
+
}
|
|
563
|
+
const sampleSlice = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
564
|
+
const matches = [];
|
|
565
|
+
for (let i = 0; i < SDK_VERTICAL_DETECTORS.length; i++) {
|
|
566
|
+
const c = SDK_VERTICAL_DETECTORS[i];
|
|
567
|
+
c.regex.lastIndex = 0;
|
|
568
|
+
const found = sampleSlice.match(c.regex);
|
|
569
|
+
if (found && found.length > 0) {
|
|
570
|
+
matches.push({
|
|
571
|
+
profile: c.profile,
|
|
572
|
+
label: c.label,
|
|
573
|
+
score: found.length,
|
|
574
|
+
priority: i
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
if (matches.length === 0) {
|
|
579
|
+
return { detectedProfile: 'general', label: 'General PII', confidence: 'low', score: 0, alternatives: [] };
|
|
580
|
+
}
|
|
581
|
+
matches.sort((a, b) => (b.score !== a.score) ? (b.score - a.score) : (a.priority - b.priority));
|
|
582
|
+
const primary = matches[0];
|
|
583
|
+
const alternatives = matches.slice(1, 4).map(({ profile, label, score }) => ({ profile, label, score }));
|
|
584
|
+
const confidence = primary.score >= 3 ? 'high' : (primary.score >= 2 ? 'medium' : 'low');
|
|
585
|
+
return {
|
|
586
|
+
detectedProfile: primary.profile,
|
|
587
|
+
label: primary.label,
|
|
588
|
+
confidence,
|
|
589
|
+
score: primary.score,
|
|
590
|
+
alternatives
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
466
594
|
export function sanitize(text, options = {}) {
|
|
467
595
|
if (!text || typeof text !== 'string') {
|
|
468
596
|
return {
|
|
@@ -481,7 +609,7 @@ export function sanitize(text, options = {}) {
|
|
|
481
609
|
}
|
|
482
610
|
|
|
483
611
|
const {
|
|
484
|
-
profile = 'General',
|
|
612
|
+
profile: rawProfileOption = 'General',
|
|
485
613
|
licenseKey = (typeof process !== 'undefined' && (process.env?.PRIVACYSCRUBBER_KEY || process.env?.PRIVACYSCRUBBER_LICENSE_KEY)) || '',
|
|
486
614
|
customRules = [],
|
|
487
615
|
tokenLabelMap = {},
|
|
@@ -493,6 +621,12 @@ export function sanitize(text, options = {}) {
|
|
|
493
621
|
ignoreList = null
|
|
494
622
|
} = options;
|
|
495
623
|
|
|
624
|
+
const isAutoProfile = Boolean(
|
|
625
|
+
(typeof rawProfileOption === 'string' && rawProfileOption.trim().toLowerCase() === 'auto') ||
|
|
626
|
+
options.autoDetectProfile === true
|
|
627
|
+
);
|
|
628
|
+
const autoDetected = isAutoProfile ? detectProfile(text) : null;
|
|
629
|
+
|
|
496
630
|
const licenseCheck = validateLicense(licenseKey);
|
|
497
631
|
const rawTier = licenseCheck.valid ? (licenseCheck.tier || 'PRO') : 'FREE';
|
|
498
632
|
const isExpiredKey = !licenseCheck.valid && typeof licenseCheck.error === 'string' && /expired/i.test(licenseCheck.error);
|
|
@@ -514,6 +648,21 @@ export function sanitize(text, options = {}) {
|
|
|
514
648
|
const isSdkTier = licenseCheck.valid && ['SDK', 'OEM', 'ENTERPRISE'].includes(rawTier);
|
|
515
649
|
const isSeatTier = licenseCheck.valid && ['PRO', 'TEAMS'].includes(rawTier);
|
|
516
650
|
|
|
651
|
+
const isPro = isSdkTier;
|
|
652
|
+
const tier = isPro ? rawTier : (isSeatTier ? rawTier : 'FREE');
|
|
653
|
+
|
|
654
|
+
let autoSuggestedOnly = false;
|
|
655
|
+
let profile = rawProfileOption;
|
|
656
|
+
if (isAutoProfile && autoDetected) {
|
|
657
|
+
if (!isPro && text.length > 5000 && text.length <= 15000 && autoDetected.detectedProfile !== 'general') {
|
|
658
|
+
// Non-blocking PLG fallback: keep General for Free 5k-15k texts so auto-mode never breaks free calls
|
|
659
|
+
profile = 'General';
|
|
660
|
+
autoSuggestedOnly = true;
|
|
661
|
+
} else {
|
|
662
|
+
profile = autoDetected.detectedProfile;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
517
666
|
if (isSeatTier) {
|
|
518
667
|
const keyPreview = licenseKey ? `${licenseKey.slice(0, 12)}...` : '';
|
|
519
668
|
const errorMsg = `[PrivacyScrubber] License tier mismatch: ${rawTier} license (${keyPreview}) is licensed strictly for human seats (Web App & Chrome Extension). Backend automation, microservices, and CRM pipelines require a Developer SDK license ($299/mo). Upgrade: https://privacyscrubber.com/pricing?tier=sdk&src=seat_tier_mismatch`;
|
|
@@ -527,9 +676,6 @@ export function sanitize(text, options = {}) {
|
|
|
527
676
|
}
|
|
528
677
|
}
|
|
529
678
|
|
|
530
|
-
const isPro = isSdkTier;
|
|
531
|
-
const tier = isPro ? rawTier : (isSeatTier ? rawTier : 'FREE');
|
|
532
|
-
|
|
533
679
|
emitCommunityNoticeIfNeeded(isPro);
|
|
534
680
|
|
|
535
681
|
const isSpecialized = profile && profile.toLowerCase() !== 'general';
|
|
@@ -541,7 +687,8 @@ export function sanitize(text, options = {}) {
|
|
|
541
687
|
if (!isPro && text.length > limit) {
|
|
542
688
|
if (shouldThrow) {
|
|
543
689
|
const expiredPrefix = isExpiredKey ? `[PILOT/TRIAL EXPIRED: ${licenseCheck.error}] ` : '';
|
|
544
|
-
const
|
|
690
|
+
const safeProf = encodeURIComponent(String(profile || 'General').toLowerCase());
|
|
691
|
+
const errorMsg = `[PrivacyScrubber] ${expiredPrefix}Free Tier character limit exceeded (${limit.toLocaleString()} chars for profile '${profile}', received ${text.length.toLocaleString()} chars). Upgrade to Developer SDK ($299/mo) for unlimited throughput: https://privacyscrubber.com/pricing?tier=sdk&profile=${safeProf}&src=${isExpiredKey ? 'pilot_expired' : 'dev_limit'} (Start 7-day B2B trial: 'npx @privacyscrubber/sdk trial' or 'npx @privacyscrubber/sdk memo' for CISO memo)`;
|
|
545
692
|
throw new PrivacyScrubberLicenseError(errorMsg, {
|
|
546
693
|
tier: 'FREE',
|
|
547
694
|
currentLength: text.length,
|
|
@@ -562,8 +709,33 @@ export function sanitize(text, options = {}) {
|
|
|
562
709
|
});
|
|
563
710
|
|
|
564
711
|
// Auto-inject DevOps Secrets rules when in Dev profile or when detectSecrets is requested
|
|
565
|
-
const
|
|
566
|
-
|
|
712
|
+
const isProdEnv = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
|
|
713
|
+
const hasCustomEnterpriseConfig = isSpecialized || safeCustomRules.length > 0 || (Array.isArray(ignoreList) && ignoreList.length > 0);
|
|
714
|
+
let profileFallback = false;
|
|
715
|
+
let effectiveProfile = profile;
|
|
716
|
+
let effectiveCustomRules = safeCustomRules;
|
|
717
|
+
let effectiveIgnoreList = ignoreList;
|
|
718
|
+
|
|
719
|
+
if (!isPro && isProdEnv && hasCustomEnterpriseConfig) {
|
|
720
|
+
_freeProdSpecializedCalls++;
|
|
721
|
+
if (_freeProdSpecializedCalls > FREE_PROD_SPECIALIZED_CALL_LIMIT) {
|
|
722
|
+
profileFallback = true;
|
|
723
|
+
effectiveProfile = 'General';
|
|
724
|
+
effectiveCustomRules = [];
|
|
725
|
+
effectiveIgnoreList = null;
|
|
726
|
+
if (!_prodProfileFallbackNoticeLogged && typeof process !== 'undefined' && process.stderr && typeof process.stderr.write === 'function') {
|
|
727
|
+
_prodProfileFallbackNoticeLogged = true;
|
|
728
|
+
process.stderr.write(
|
|
729
|
+
`\n[@privacyscrubber/sdk] Free Production Quota (${FREE_PROD_SPECIALIZED_CALL_LIMIT} specialized calls) reached for profile '${profile}'.\n` +
|
|
730
|
+
`[@privacyscrubber/sdk] Gracefully falling back to 'General' profile (Core PII remains protected, 0 downtime).\n` +
|
|
731
|
+
`[@privacyscrubber/sdk] Unlock unlimited specialized profiles, custom rules & ignoreLists in production ($299/mo): https://privacyscrubber.com/pricing?tier=sdk&src=prod_profile_fallback (7-Day Trial: npx @privacyscrubber/sdk trial)\n\n`
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const activeRules = [...effectiveCustomRules];
|
|
738
|
+
if (effectiveProfile.toLowerCase() === 'dev' || detectSecrets === true) {
|
|
567
739
|
DEVOPS_SECRETS_DETECTOR.forEach(rule => {
|
|
568
740
|
activeRules.push({
|
|
569
741
|
name: rule.name,
|
|
@@ -584,10 +756,10 @@ export function sanitize(text, options = {}) {
|
|
|
584
756
|
textToScrub,
|
|
585
757
|
activeRules,
|
|
586
758
|
tokenLabelMap,
|
|
587
|
-
|
|
759
|
+
effectiveProfile,
|
|
588
760
|
existingSessionMap,
|
|
589
761
|
isPro,
|
|
590
|
-
|
|
762
|
+
effectiveIgnoreList
|
|
591
763
|
);
|
|
592
764
|
|
|
593
765
|
const endTime = (typeof performance !== 'undefined' && typeof performance.now === 'function')
|
|
@@ -595,7 +767,7 @@ export function sanitize(text, options = {}) {
|
|
|
595
767
|
: Date.now();
|
|
596
768
|
|
|
597
769
|
const executionMs = Math.round((endTime - startTime) * 100) / 100;
|
|
598
|
-
const telemetry = buildAuditTelemetry(rawResult.tokenMap || {});
|
|
770
|
+
const telemetry = buildAuditTelemetry(rawResult.tokenMap || {}, { licenseTier: tier });
|
|
599
771
|
const auditReceipt = generateReceipt ? formatAuditReceipt(telemetry) : '';
|
|
600
772
|
|
|
601
773
|
emitDevSecretNoticeIfNeeded(isPro, rawResult.tokenMap, limit);
|
|
@@ -622,15 +794,32 @@ export function sanitize(text, options = {}) {
|
|
|
622
794
|
tier: tier,
|
|
623
795
|
limit: limit,
|
|
624
796
|
wasTruncated: wasTruncated,
|
|
797
|
+
truncated: wasTruncated,
|
|
798
|
+
profileFallback: profileFallback,
|
|
799
|
+
effectiveProfile: effectiveProfile,
|
|
800
|
+
specializedCallsUsed: _freeProdSpecializedCalls,
|
|
801
|
+
specializedCallsLimit: isPro ? Infinity : FREE_PROD_SPECIALIZED_CALL_LIMIT,
|
|
625
802
|
...(isSeatTier && {
|
|
626
|
-
upgradeUrl:
|
|
803
|
+
upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=seat_tier_mismatch`,
|
|
804
|
+
trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=seat_tier_mismatch`,
|
|
805
|
+
trialCommand: 'npx @privacyscrubber/sdk trial',
|
|
627
806
|
recommendation: `Your ${rawTier} license is valid for interactive browser seats only. Upgrade to Developer SDK ($299/mo) for unlimited backend server throughput: https://privacyscrubber.com/pricing?tier=sdk`
|
|
628
807
|
}),
|
|
629
808
|
...(!isPro && !isSeatTier && {
|
|
630
|
-
upgradeUrl:
|
|
809
|
+
upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=${profileFallback ? 'prod_profile_fallback' : 'dev_upsell'}`,
|
|
810
|
+
trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=${profileFallback ? 'prod_profile_fallback' : 'dev_upsell'}`,
|
|
811
|
+
trialCommand: 'npx @privacyscrubber/sdk trial',
|
|
631
812
|
recommendation: 'Upgrade to Developer SDK ($299/mo) for unlimited backend text processing, custom regex rules, and specialized compliance profiles.'
|
|
632
813
|
})
|
|
633
814
|
},
|
|
815
|
+
profileDetection: {
|
|
816
|
+
autoDetected: isAutoProfile,
|
|
817
|
+
detectedProfile: autoDetected ? autoDetected.detectedProfile : String(effectiveProfile || 'General').toLowerCase(),
|
|
818
|
+
label: autoDetected ? autoDetected.label : effectiveProfile,
|
|
819
|
+
confidence: autoDetected ? autoDetected.confidence : 'high',
|
|
820
|
+
suggestedOnly: autoSuggestedOnly,
|
|
821
|
+
alternatives: autoDetected ? autoDetected.alternatives : []
|
|
822
|
+
},
|
|
634
823
|
telemetry: telemetry,
|
|
635
824
|
auditReceipt: auditReceipt
|
|
636
825
|
};
|
|
@@ -3862,6 +4051,7 @@ import {
|
|
|
3862
4051
|
|
|
3863
4052
|
export default {
|
|
3864
4053
|
SDK_VERSION,
|
|
4054
|
+
detectProfile,
|
|
3865
4055
|
sanitize,
|
|
3866
4056
|
sanitizeObject,
|
|
3867
4057
|
scrub,
|
|
@@ -3914,6 +4104,8 @@ export default {
|
|
|
3914
4104
|
scrubText,
|
|
3915
4105
|
unscrubText,
|
|
3916
4106
|
DEVOPS_SECRETS_DETECTOR,
|
|
4107
|
+
FREE_PROD_SPECIALIZED_CALL_LIMIT,
|
|
4108
|
+
_resetProdQuotaForTesting,
|
|
3917
4109
|
stripExifMetadata,
|
|
3918
4110
|
redactImageBoundingBoxes,
|
|
3919
4111
|
generateSvgRedactionOverlay
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@privacyscrubber/sdk",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
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.cjs",
|
|
6
6
|
"module": "index.js",
|