@privacyscrubber/sdk 2.4.7 → 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/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/index.cjs +179 -13
- package/index.js +179 -13
- 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/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "privacyscrubber"
|
|
7
|
-
version = "2.4.
|
|
7
|
+
version = "2.4.8"
|
|
8
8
|
description = "Zero-Trust Data Sanitization & In-Memory PII Shield for Python AI Pipelines (ZTDS RFC v1.0)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -13,7 +13,7 @@ with open(os.path.join(here, "README.md"), encoding="utf-8") as f:
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="privacyscrubber",
|
|
16
|
-
version="2.4.
|
|
16
|
+
version="2.4.8",
|
|
17
17
|
description="Zero-Trust Data Sanitization & In-Memory PII Shield for Python AI Pipelines (ZTDS RFC v1.0)",
|
|
18
18
|
long_description=long_description,
|
|
19
19
|
long_description_content_type="text/markdown",
|
package/index.cjs
CHANGED
|
@@ -39,7 +39,7 @@ const SDK_VERSION = (() => {
|
|
|
39
39
|
return pkgJson.version;
|
|
40
40
|
}
|
|
41
41
|
} catch (_) {}
|
|
42
|
-
return '2.4.
|
|
42
|
+
return '2.4.8';
|
|
43
43
|
})();
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -132,7 +132,7 @@ function validateLicenseKey(key) {
|
|
|
132
132
|
* @param {Record<string, string>|string[]} currentTokenMap
|
|
133
133
|
* @returns {object}
|
|
134
134
|
*/
|
|
135
|
-
function buildAuditTelemetry(currentTokenMap = {}) {
|
|
135
|
+
function buildAuditTelemetry(currentTokenMap = {}, extraStats = {}) {
|
|
136
136
|
const entities = {};
|
|
137
137
|
let totalCount = 0;
|
|
138
138
|
|
|
@@ -242,12 +242,31 @@ function buildAuditTelemetry(currentTokenMap = {}) {
|
|
|
242
242
|
}
|
|
243
243
|
];
|
|
244
244
|
|
|
245
|
+
const rawLicenseTier = String(extraStats?.licenseTier || '').trim().toUpperCase();
|
|
246
|
+
const isOrganizationalScope = ['TEAMS', 'SDK', 'OEM', 'ENTERPRISE', 'ENTERPRISE_AIRGAP'].includes(rawLicenseTier);
|
|
247
|
+
const auditScope = isOrganizationalScope ? {
|
|
248
|
+
tier: rawLicenseTier,
|
|
249
|
+
isOrganizational: true,
|
|
250
|
+
badge: 'ORGANIZATIONAL MULTI-SEAT COMPLIANCE SCOPE',
|
|
251
|
+
coverage: 'Covers department, clinic, or firm personnel under unified ZTDS policy governance.',
|
|
252
|
+
upgradeRecommended: false,
|
|
253
|
+
upgradeUrl: null
|
|
254
|
+
} : {
|
|
255
|
+
tier: rawLicenseTier || 'SINGLE_OPERATOR',
|
|
256
|
+
isOrganizational: false,
|
|
257
|
+
badge: 'SINGLE-OPERATOR PERSONAL SCOPE',
|
|
258
|
+
coverage: 'Valid for individual workstation only. Multi-employee clinics, CPA firms, and departments require TEAMS ($99/mo flat, unlimited seats) for organizational audit coverage.',
|
|
259
|
+
upgradeRecommended: true,
|
|
260
|
+
upgradeUrl: 'https://privacyscrubber.com/pricing/?tier=teams&utm_source=audit_receipt&utm_medium=scope_upsell'
|
|
261
|
+
};
|
|
262
|
+
|
|
245
263
|
return {
|
|
246
264
|
totalCount,
|
|
247
265
|
entities,
|
|
248
266
|
types,
|
|
249
267
|
riskLevel,
|
|
250
268
|
frameworksList: Array.from(frameworksSet),
|
|
269
|
+
auditScope,
|
|
251
270
|
statutoryCitations,
|
|
252
271
|
ipHoldings,
|
|
253
272
|
agenticProtocol,
|
|
@@ -301,6 +320,19 @@ function _resetCommunityNoticeForTesting() {
|
|
|
301
320
|
communityNoticeLogged = false;
|
|
302
321
|
}
|
|
303
322
|
|
|
323
|
+
const FREE_PROD_SPECIALIZED_CALL_LIMIT = 250;
|
|
324
|
+
let _freeProdSpecializedCalls = 0;
|
|
325
|
+
let _prodProfileFallbackNoticeLogged = false;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Resets the production specialized call quota state for testing purposes.
|
|
329
|
+
* @private
|
|
330
|
+
*/
|
|
331
|
+
function _resetProdQuotaForTesting() {
|
|
332
|
+
_freeProdSpecializedCalls = 0;
|
|
333
|
+
_prodProfileFallbackNoticeLogged = false;
|
|
334
|
+
}
|
|
335
|
+
|
|
304
336
|
let devSecretNoticeLogged = false;
|
|
305
337
|
|
|
306
338
|
function emitDevSecretNoticeIfNeeded(isPro, tokenMap = {}, limit = 15000) {
|
|
@@ -481,6 +513,82 @@ We request approval to acquire a **Developer SDK License** (\$299/month flat or
|
|
|
481
513
|
* auditReceipt: string
|
|
482
514
|
* }}
|
|
483
515
|
*/
|
|
516
|
+
const SDK_VERTICAL_DETECTORS = [
|
|
517
|
+
{ 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 },
|
|
518
|
+
{ 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 },
|
|
519
|
+
{ 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 },
|
|
520
|
+
{ 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 },
|
|
521
|
+
{ 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 },
|
|
522
|
+
{ 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 },
|
|
523
|
+
{ 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 },
|
|
524
|
+
{ 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 },
|
|
525
|
+
{ 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 },
|
|
526
|
+
{ 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 },
|
|
527
|
+
{ 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 },
|
|
528
|
+
{ 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 },
|
|
529
|
+
{ 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 },
|
|
530
|
+
{ 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 },
|
|
531
|
+
{ 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 },
|
|
532
|
+
{ 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 },
|
|
533
|
+
{ 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 },
|
|
534
|
+
{ 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 },
|
|
535
|
+
{ 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 },
|
|
536
|
+
{ 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 },
|
|
537
|
+
{ 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 },
|
|
538
|
+
{ 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 },
|
|
539
|
+
{ 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 },
|
|
540
|
+
{ 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 },
|
|
541
|
+
{ 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 }
|
|
542
|
+
];
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Automatically detects the most relevant industry compliance profile from text content.
|
|
546
|
+
* Executes in <0.05ms in local RAM with zero network egress.
|
|
547
|
+
*
|
|
548
|
+
* @param {string} text - Input document or prompt text.
|
|
549
|
+
* @returns {{
|
|
550
|
+
* detectedProfile: string,
|
|
551
|
+
* label: string,
|
|
552
|
+
* confidence: 'high'|'medium'|'low',
|
|
553
|
+
* score: number,
|
|
554
|
+
* alternatives: Array<{ profile: string, label: string, score: number }>
|
|
555
|
+
* }}
|
|
556
|
+
*/
|
|
557
|
+
function detectProfile(text) {
|
|
558
|
+
if (!text || typeof text !== 'string') {
|
|
559
|
+
return { detectedProfile: 'general', label: 'General PII', confidence: 'low', score: 0, alternatives: [] };
|
|
560
|
+
}
|
|
561
|
+
const sampleSlice = text.length > 16384 ? text.slice(0, 16384) : text;
|
|
562
|
+
const matches = [];
|
|
563
|
+
for (let i = 0; i < SDK_VERTICAL_DETECTORS.length; i++) {
|
|
564
|
+
const c = SDK_VERTICAL_DETECTORS[i];
|
|
565
|
+
c.regex.lastIndex = 0;
|
|
566
|
+
const found = sampleSlice.match(c.regex);
|
|
567
|
+
if (found && found.length > 0) {
|
|
568
|
+
matches.push({
|
|
569
|
+
profile: c.profile,
|
|
570
|
+
label: c.label,
|
|
571
|
+
score: found.length,
|
|
572
|
+
priority: i
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (matches.length === 0) {
|
|
577
|
+
return { detectedProfile: 'general', label: 'General PII', confidence: 'low', score: 0, alternatives: [] };
|
|
578
|
+
}
|
|
579
|
+
matches.sort((a, b) => (b.score !== a.score) ? (b.score - a.score) : (a.priority - b.priority));
|
|
580
|
+
const primary = matches[0];
|
|
581
|
+
const alternatives = matches.slice(1, 4).map(({ profile, label, score }) => ({ profile, label, score }));
|
|
582
|
+
const confidence = primary.score >= 3 ? 'high' : (primary.score >= 2 ? 'medium' : 'low');
|
|
583
|
+
return {
|
|
584
|
+
detectedProfile: primary.profile,
|
|
585
|
+
label: primary.label,
|
|
586
|
+
confidence,
|
|
587
|
+
score: primary.score,
|
|
588
|
+
alternatives
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
484
592
|
function sanitize(text, options = {}) {
|
|
485
593
|
if (!text || typeof text !== 'string') {
|
|
486
594
|
return {
|
|
@@ -499,7 +607,7 @@ function sanitize(text, options = {}) {
|
|
|
499
607
|
}
|
|
500
608
|
|
|
501
609
|
const {
|
|
502
|
-
profile = 'General',
|
|
610
|
+
profile: rawProfileOption = 'General',
|
|
503
611
|
licenseKey = (typeof process !== 'undefined' && (process.env?.PRIVACYSCRUBBER_KEY || process.env?.PRIVACYSCRUBBER_LICENSE_KEY)) || '',
|
|
504
612
|
customRules = [],
|
|
505
613
|
tokenLabelMap = {},
|
|
@@ -511,6 +619,12 @@ function sanitize(text, options = {}) {
|
|
|
511
619
|
ignoreList = null
|
|
512
620
|
} = options;
|
|
513
621
|
|
|
622
|
+
const isAutoProfile = Boolean(
|
|
623
|
+
(typeof rawProfileOption === 'string' && rawProfileOption.trim().toLowerCase() === 'auto') ||
|
|
624
|
+
options.autoDetectProfile === true
|
|
625
|
+
);
|
|
626
|
+
const autoDetected = isAutoProfile ? detectProfile(text) : null;
|
|
627
|
+
|
|
514
628
|
const licenseCheck = validateLicense(licenseKey);
|
|
515
629
|
const rawTier = licenseCheck.valid ? (licenseCheck.tier || 'PRO') : 'FREE';
|
|
516
630
|
const isExpiredKey = !licenseCheck.valid && typeof licenseCheck.error === 'string' && /expired/i.test(licenseCheck.error);
|
|
@@ -532,6 +646,21 @@ function sanitize(text, options = {}) {
|
|
|
532
646
|
const isSdkTier = licenseCheck.valid && ['SDK', 'OEM', 'ENTERPRISE'].includes(rawTier);
|
|
533
647
|
const isSeatTier = licenseCheck.valid && ['PRO', 'TEAMS'].includes(rawTier);
|
|
534
648
|
|
|
649
|
+
const isPro = isSdkTier;
|
|
650
|
+
const tier = isPro ? rawTier : (isSeatTier ? rawTier : 'FREE');
|
|
651
|
+
|
|
652
|
+
let autoSuggestedOnly = false;
|
|
653
|
+
let profile = rawProfileOption;
|
|
654
|
+
if (isAutoProfile && autoDetected) {
|
|
655
|
+
if (!isPro && text.length > 5000 && text.length <= 15000 && autoDetected.detectedProfile !== 'general') {
|
|
656
|
+
// Non-blocking PLG fallback: keep General for Free 5k-15k texts so auto-mode never breaks free calls
|
|
657
|
+
profile = 'General';
|
|
658
|
+
autoSuggestedOnly = true;
|
|
659
|
+
} else {
|
|
660
|
+
profile = autoDetected.detectedProfile;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
535
664
|
if (isSeatTier) {
|
|
536
665
|
const keyPreview = licenseKey ? `${licenseKey.slice(0, 12)}...` : '';
|
|
537
666
|
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`;
|
|
@@ -545,9 +674,6 @@ function sanitize(text, options = {}) {
|
|
|
545
674
|
}
|
|
546
675
|
}
|
|
547
676
|
|
|
548
|
-
const isPro = isSdkTier;
|
|
549
|
-
const tier = isPro ? rawTier : (isSeatTier ? rawTier : 'FREE');
|
|
550
|
-
|
|
551
677
|
emitCommunityNoticeIfNeeded(isPro);
|
|
552
678
|
|
|
553
679
|
const isSpecialized = profile && profile.toLowerCase() !== 'general';
|
|
@@ -581,8 +707,33 @@ function sanitize(text, options = {}) {
|
|
|
581
707
|
});
|
|
582
708
|
|
|
583
709
|
// Auto-inject DevOps Secrets rules when in Dev profile or when detectSecrets is requested
|
|
584
|
-
const
|
|
585
|
-
|
|
710
|
+
const isProdEnv = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
|
|
711
|
+
const hasCustomEnterpriseConfig = isSpecialized || safeCustomRules.length > 0 || (Array.isArray(ignoreList) && ignoreList.length > 0);
|
|
712
|
+
let profileFallback = false;
|
|
713
|
+
let effectiveProfile = profile;
|
|
714
|
+
let effectiveCustomRules = safeCustomRules;
|
|
715
|
+
let effectiveIgnoreList = ignoreList;
|
|
716
|
+
|
|
717
|
+
if (!isPro && isProdEnv && hasCustomEnterpriseConfig) {
|
|
718
|
+
_freeProdSpecializedCalls++;
|
|
719
|
+
if (_freeProdSpecializedCalls > FREE_PROD_SPECIALIZED_CALL_LIMIT) {
|
|
720
|
+
profileFallback = true;
|
|
721
|
+
effectiveProfile = 'General';
|
|
722
|
+
effectiveCustomRules = [];
|
|
723
|
+
effectiveIgnoreList = null;
|
|
724
|
+
if (!_prodProfileFallbackNoticeLogged && typeof process !== 'undefined' && process.stderr && typeof process.stderr.write === 'function') {
|
|
725
|
+
_prodProfileFallbackNoticeLogged = true;
|
|
726
|
+
process.stderr.write(
|
|
727
|
+
`\n[@privacyscrubber/sdk] Free Production Quota (${FREE_PROD_SPECIALIZED_CALL_LIMIT} specialized calls) reached for profile '${profile}'.\n` +
|
|
728
|
+
`[@privacyscrubber/sdk] Gracefully falling back to 'General' profile (Core PII remains protected, 0 downtime).\n` +
|
|
729
|
+
`[@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`
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const activeRules = [...effectiveCustomRules];
|
|
736
|
+
if (effectiveProfile.toLowerCase() === 'dev' || detectSecrets === true) {
|
|
586
737
|
DEVOPS_SECRETS_DETECTOR.forEach(rule => {
|
|
587
738
|
activeRules.push({
|
|
588
739
|
name: rule.name,
|
|
@@ -603,10 +754,10 @@ function sanitize(text, options = {}) {
|
|
|
603
754
|
textToScrub,
|
|
604
755
|
activeRules,
|
|
605
756
|
tokenLabelMap,
|
|
606
|
-
|
|
757
|
+
effectiveProfile,
|
|
607
758
|
existingSessionMap,
|
|
608
759
|
isPro,
|
|
609
|
-
|
|
760
|
+
effectiveIgnoreList
|
|
610
761
|
);
|
|
611
762
|
|
|
612
763
|
const endTime = (typeof performance !== 'undefined' && typeof performance.now === 'function')
|
|
@@ -614,7 +765,7 @@ function sanitize(text, options = {}) {
|
|
|
614
765
|
: Date.now();
|
|
615
766
|
|
|
616
767
|
const executionMs = Math.round((endTime - startTime) * 100) / 100;
|
|
617
|
-
const telemetry = buildAuditTelemetry(rawResult.tokenMap || {});
|
|
768
|
+
const telemetry = buildAuditTelemetry(rawResult.tokenMap || {}, { licenseTier: tier });
|
|
618
769
|
const auditReceipt = generateReceipt ? formatAuditReceipt(telemetry) : '';
|
|
619
770
|
|
|
620
771
|
emitDevSecretNoticeIfNeeded(isPro, rawResult.tokenMap, limit);
|
|
@@ -642,6 +793,10 @@ function sanitize(text, options = {}) {
|
|
|
642
793
|
limit: limit,
|
|
643
794
|
wasTruncated: wasTruncated,
|
|
644
795
|
truncated: wasTruncated,
|
|
796
|
+
profileFallback: profileFallback,
|
|
797
|
+
effectiveProfile: effectiveProfile,
|
|
798
|
+
specializedCallsUsed: _freeProdSpecializedCalls,
|
|
799
|
+
specializedCallsLimit: isPro ? Infinity : FREE_PROD_SPECIALIZED_CALL_LIMIT,
|
|
645
800
|
...(isSeatTier && {
|
|
646
801
|
upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=seat_tier_mismatch`,
|
|
647
802
|
trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=seat_tier_mismatch`,
|
|
@@ -649,12 +804,20 @@ function sanitize(text, options = {}) {
|
|
|
649
804
|
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`
|
|
650
805
|
}),
|
|
651
806
|
...(!isPro && !isSeatTier && {
|
|
652
|
-
upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src
|
|
653
|
-
trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src
|
|
807
|
+
upgradeUrl: `https://privacyscrubber.com/pricing?tier=sdk&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=${profileFallback ? 'prod_profile_fallback' : 'dev_upsell'}`,
|
|
808
|
+
trialUrl: `https://privacyscrubber.com/pricing?tier=sdk&trial=true&profile=${encodeURIComponent(String(profile || 'General').toLowerCase())}&src=${profileFallback ? 'prod_profile_fallback' : 'dev_upsell'}`,
|
|
654
809
|
trialCommand: 'npx @privacyscrubber/sdk trial',
|
|
655
810
|
recommendation: 'Upgrade to Developer SDK ($299/mo) for unlimited backend text processing, custom regex rules, and specialized compliance profiles.'
|
|
656
811
|
})
|
|
657
812
|
},
|
|
813
|
+
profileDetection: {
|
|
814
|
+
autoDetected: isAutoProfile,
|
|
815
|
+
detectedProfile: autoDetected ? autoDetected.detectedProfile : String(effectiveProfile || 'General').toLowerCase(),
|
|
816
|
+
label: autoDetected ? autoDetected.label : effectiveProfile,
|
|
817
|
+
confidence: autoDetected ? autoDetected.confidence : 'high',
|
|
818
|
+
suggestedOnly: autoSuggestedOnly,
|
|
819
|
+
alternatives: autoDetected ? autoDetected.alternatives : []
|
|
820
|
+
},
|
|
658
821
|
telemetry: telemetry,
|
|
659
822
|
auditReceipt: auditReceipt
|
|
660
823
|
};
|
|
@@ -3881,6 +4044,7 @@ const {
|
|
|
3881
4044
|
|
|
3882
4045
|
const _defaultExport = {
|
|
3883
4046
|
SDK_VERSION,
|
|
4047
|
+
detectProfile,
|
|
3884
4048
|
sanitize,
|
|
3885
4049
|
sanitizeObject,
|
|
3886
4050
|
scrub,
|
|
@@ -3933,6 +4097,8 @@ const _defaultExport = {
|
|
|
3933
4097
|
scrubText,
|
|
3934
4098
|
unscrubText,
|
|
3935
4099
|
DEVOPS_SECRETS_DETECTOR,
|
|
4100
|
+
FREE_PROD_SPECIALIZED_CALL_LIMIT,
|
|
4101
|
+
_resetProdQuotaForTesting,
|
|
3936
4102
|
stripExifMetadata,
|
|
3937
4103
|
redactImageBoundingBoxes,
|
|
3938
4104
|
generateSvgRedactionOverlay
|