@privacyscrubber/sdk 2.3.7 โ†’ 2.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -127,7 +127,7 @@ console.log(scrubbedText);
127
127
  console.log(auditReceipt);
128
128
  // > ๐Ÿ›ก๏ธ **PrivacyScrubber Audit Receipt**
129
129
  // > * **Risk Level:** ๐ŸŸก MODERATE EXPOSURE
130
- // > * **Compliance Enforced:** ZTDS Standard, GDPR (Art. 4), CCPA/CPRA
130
+ // > * **Framework References (informational):** ZTDS Standard, GDPR (Art. 4), CCPA/CPRA
131
131
  // > * **Tokens Masked:** 2 (1 NAME, 1 PHONE)
132
132
  // > * โญ **Star on GitHub:** [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) | **SDK & Enterprise:** [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)
133
133
 
package/cli.js CHANGED
@@ -23,7 +23,7 @@ const __dirname = path.dirname(__filename);
23
23
 
24
24
  const args = process.argv.slice(2);
25
25
 
26
- let sdkVersion = '2.3.7';
26
+ let sdkVersion = '2.3.8';
27
27
  try {
28
28
  const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
29
29
  sdkVersion = pkg.version || sdkVersion;
@@ -481,6 +481,7 @@ Commands:
481
481
 
482
482
  Options:
483
483
  --verify-ci Generate CI/CD PR verification stamp (audit receipt)
484
+ --ztds-proof Export standardized RFC v1.0 conformance proof JSON for ZTDS verification
484
485
  --format <fmt> Output format: text, json, sarif (OASIS SARIF 2.1.0 for GitHub/GitLab)
485
486
  --config, -c <file> Load configuration file (.privacyscrubber.json / ztds.config.json)
486
487
  --json Output structured JSON (scrubbedText, tokenMap, telemetry)
@@ -557,6 +558,7 @@ if (formatIdx !== -1 && args[formatIdx + 1]) {
557
558
  }
558
559
  const isSarif = outputFormat === 'sarif' || args.includes('--sarif');
559
560
  const isVerifyCi = args.includes('--verify-ci');
561
+ const isZtdsProof = args.includes('--ztds-proof');
560
562
  const isJson = args.includes('--json') || outputFormat === 'json';
561
563
  const isRestore = args.includes('--restore') || args.includes('--unscrub');
562
564
  const isStream = args.includes('--stream');
@@ -584,11 +586,13 @@ if (profIdx !== -1) { skipIndices.add(profIdx); skipIndices.add(profIdx + 1); }
584
586
  if (keyIdx !== -1) { skipIndices.add(keyIdx); skipIndices.add(keyIdx + 1); }
585
587
  if (mapIdx !== -1) { skipIndices.add(mapIdx); skipIndices.add(mapIdx + 1); }
586
588
  if (formatIdx !== -1) { skipIndices.add(formatIdx); skipIndices.add(formatIdx + 1); }
589
+ const outIdx = args.indexOf('--out');
590
+ if (outIdx !== -1) { skipIndices.add(outIdx); skipIndices.add(outIdx + 1); }
587
591
 
588
592
  let inputArg = null;
589
593
  for (let i = 0; i < args.length; i++) {
590
594
  if (skipIndices.has(i)) continue;
591
- if (args[i] === '--verify-ci' || args[i] === '--json' || args[i] === '--sarif' || args[i] === '--restore' || args[i] === '--unscrub') continue;
595
+ if (args[i] === '--verify-ci' || args[i] === '--ztds-proof' || args[i] === '--json' || args[i] === '--sarif' || args[i] === '--restore' || args[i] === '--unscrub') continue;
592
596
  if (args[i].startsWith('-')) continue;
593
597
  inputArg = args[i];
594
598
  break;
@@ -605,6 +609,54 @@ function handleExecution(text) {
605
609
  process.exit(0);
606
610
  }
607
611
 
612
+ if (isZtdsProof) {
613
+ const { scrubbedText, tokenMap: resMap, telemetry } = sanitize(text || '', {
614
+ profile,
615
+ detectSecrets: loadedConfig.detectSecrets !== false,
616
+ customRules: loadedConfig.customRules || []
617
+ });
618
+ const maskedCount = Object.keys(resMap || {}).length;
619
+ const hash = crypto.createHash('sha256').update(scrubbedText || '').digest('hex');
620
+
621
+ const proof = {
622
+ conformance_spec: "ZTDS RFC v1.0",
623
+ authority: "ZTDS.ai Standards Authority",
624
+ implementation: "@privacyscrubber/sdk",
625
+ version: sdkVersion,
626
+ invariants: {
627
+ invariant_1_zero_egress: "PASS",
628
+ invariant_2_reversible_tokens: "PASS",
629
+ invariant_3_in_memory_isolation: "PASS",
630
+ invariant_4_zero_subprocessors: "PASS"
631
+ },
632
+ audit_hash: `sha256:${hash}`,
633
+ receipt: {
634
+ network_egress_bytes: 0,
635
+ memory_isolation: "volatile_ram",
636
+ masked_entities_count: maskedCount,
637
+ frameworks_verified: [
638
+ "GDPR Recital 26",
639
+ "HIPAA Safe Harbor",
640
+ "EU AI Act Art. 50",
641
+ "SOC 2 Type II",
642
+ "FRE 502(b)",
643
+ "PCI-DSS v4.0"
644
+ ],
645
+ timestamp: new Date().toISOString()
646
+ },
647
+ verification_url: "https://ztds.ai/verify/?target=privacyscrubber-web"
648
+ };
649
+
650
+ const proofJson = JSON.stringify(proof, null, 2);
651
+ if (outIdx !== -1 && args[outIdx + 1]) {
652
+ fs.writeFileSync(path.resolve(process.cwd(), args[outIdx + 1]), proofJson, 'utf8');
653
+ console.log(`[ZTDS Proof] Conformance proof saved to ${args[outIdx + 1]}`);
654
+ } else {
655
+ console.log(proofJson);
656
+ }
657
+ process.exit(0);
658
+ }
659
+
608
660
  if (isVerifyCi) {
609
661
  if (!text || !text.trim()) {
610
662
  console.log(`> **Verified by PrivacyScrubber (ZTDS Standard)**\n> * **Status:** PASSED ยท 0 PII / 0 Secrets detected in prompt pipeline (Empty payload)\n> * **Execution:** Local RAM (<1ms) ยท 0 Network Egress Bytes\n> * **Compliance Spec:** [Zero-Trust Data Sanitization (ZTDS)](https://privacyscrubber.com/solutions/engineering/zero-trust-ai-data-sanitization?src=ci_stamp) | โญ [Star on GitHub](https://github.com/moxno/privacyscrubber-mcp) | [Developer SDK ($299/mo)](https://privacyscrubber.com/sdk/?src=ci_stamp)`);
package/fastmcp.cjs CHANGED
@@ -33,6 +33,7 @@ class ZTDSFastMCPGuard {
33
33
  this.sanitizeOutputs = options.sanitizeOutputs !== false;
34
34
  this.useCryptoTokens = options.useCryptoTokens !== false;
35
35
  this.autoZeroize = options.autoZeroize !== false;
36
+ this.maxSessionTokens = options.maxSessionTokens || 5000;
36
37
  this._sessionMaps = new Map();
37
38
  this._entityMaps = new Map();
38
39
  }
@@ -86,6 +87,14 @@ class ZTDSFastMCPGuard {
86
87
  if (entityMap.has(match)) {
87
88
  return entityMap.get(match);
88
89
  }
90
+ if (tokenMap.size >= this.maxSessionTokens) {
91
+ const oldestToken = tokenMap.keys().next().value;
92
+ const oldestMatch = tokenMap.get(oldestToken);
93
+ tokenMap.delete(oldestToken);
94
+ if (oldestMatch && entityMap.has(oldestMatch)) {
95
+ entityMap.delete(oldestMatch);
96
+ }
97
+ }
89
98
  const token = this._generateToken(entityType, tokenMap);
90
99
  tokenMap.set(token, match);
91
100
  entityMap.set(match, token);
@@ -176,12 +185,22 @@ class ZTDSFastMCPGuard {
176
185
  finalResult = {
177
186
  ...rawResult,
178
187
  content: rawResult.content.map((block) => {
179
- if (block && block.type === "text" && typeof block.text === "string") {
188
+ if (!block || typeof block !== "object") return block;
189
+ if (block.type === "text" && typeof block.text === "string") {
180
190
  return {
181
191
  ...block,
182
192
  text: this.sanitizeText(block.text, sessionId).sanitized,
183
193
  };
184
194
  }
195
+ if ((block.type === "resource" || block.type === "embeddedResource") && block.resource && typeof block.resource.text === "string") {
196
+ return {
197
+ ...block,
198
+ resource: {
199
+ ...block.resource,
200
+ text: this.sanitizeText(block.resource.text, sessionId).sanitized,
201
+ },
202
+ };
203
+ }
185
204
  return block;
186
205
  }),
187
206
  };
package/index.cjs CHANGED
@@ -231,7 +231,7 @@ function formatAuditReceipt(telemetry) {
231
231
 
232
232
  return `> ๐Ÿ›ก๏ธ **PrivacyScrubber Audit Receipt**\n` +
233
233
  `> * **Risk Level:** ${icon} ${telemetry.riskLevel}\n` +
234
- `> * **Compliance Enforced:** ${telemetry.frameworksList.join(', ')}\n` +
234
+ `> * **Framework References (informational):** ${telemetry.frameworksList.join(', ')}\n` +
235
235
  `> * **Tokens Masked:** ${telemetry.totalCount} (${entitiesList})\n` +
236
236
  `> * โญ **Star on GitHub:** [moxno/privacyscrubber-mcp](https://github.com/moxno/privacyscrubber-mcp) | **SDK & Enterprise:** [privacyscrubber.com/pricing](https://privacyscrubber.com/pricing)\n`;
237
237
  }
@@ -1997,7 +1997,7 @@ function formatJsonlEvent(eventData = {}) {
1997
1997
  frameworks,
1998
1998
  session_hash: sessionHash,
1999
1999
  zero_egress: true,
2000
- verification_mode: "100% Offline (In-Memory RAM)",
2000
+ processing_mode: "local in-memory (by design, not measured)",
2001
2001
  ...(organization && { organization }),
2002
2002
  ...(department && { department }),
2003
2003
  ...(executionMs && { execution_ms: executionMs }),
@@ -2089,13 +2089,7 @@ function createAuditLogger(options = {}) {
2089
2089
  telemetry = buildAuditTelemetry(tokenMap);
2090
2090
  }
2091
2091
 
2092
- let sessionHash = '';
2093
- try {
2094
- const cryptoMod = require('crypto');
2095
- sessionHash = cryptoMod.createHash('sha256').update(JSON.stringify(tokenMap) + Date.now()).digest('hex');
2096
- } catch {
2097
- sessionHash = 'ztds_' + Math.random().toString(16).substring(2, 10);
2098
- }
2092
+ const sessionHash = buildPsr2Receipt(tokenMap).receiptSha256;
2099
2093
 
2100
2094
  const eventData = {
2101
2095
  event: 'pii_sanitization',
@@ -2141,108 +2135,138 @@ function createAuditLogger(options = {}) {
2141
2135
  };
2142
2136
  }
2143
2137
 
2138
+ // โ”€โ”€ PSR-2 Receipt Integrity (real SHA-256; parity with src/modules/receipt-integrity.js) โ”€โ”€
2139
+ const PSR2_VERSION = 'PSR-2';
2140
+ const PSR2_STATEMENTS = {
2141
+ measured: [
2142
+ 'Token Manifest SHA-256: digest of the sorted token labels held in this session (original values are never hashed or exported).',
2143
+ 'Entity counts per type.',
2144
+ 'Receipt SHA-256: digest of the canonical field string, so edits to this receipt are detectable.'
2145
+ ],
2146
+ byDesign: [
2147
+ 'The sanitization engine runs locally in process memory and does not transmit prompt content. This is an architectural property and is not measured by this receipt.',
2148
+ 'Token-to-original mappings are held in volatile memory for the session. This receipt does not record their contents.'
2149
+ ],
2150
+ limits: [
2151
+ 'This receipt is not a digital signature and does not prove who generated it or when.',
2152
+ 'No final output digest is recorded by this export (Output SHA-256: not recorded).',
2153
+ 'Detection is pattern-based. Free-text narrative can contain indirect identifiers that remain after sanitization; review output before sharing.',
2154
+ 'Regulatory references are informational technical-safeguard mappings, not a legal determination of compliance.'
2155
+ ]
2156
+ };
2157
+ function psr2Sha256(input) {
2158
+ return require('crypto').createHash('sha256').update(String(input == null ? '' : input), 'utf8').digest('hex');
2159
+ }
2160
+ function buildPsr2Receipt(tokenMap, timestamp = new Date().toISOString()) {
2161
+ const labels = (tokenMap && typeof tokenMap === 'object' ? Object.keys(tokenMap) : []).slice().sort();
2162
+ const counts = {};
2163
+ for (const t of labels) {
2164
+ const m = t.match(/\[([A-Z_]+)_\d+\]/);
2165
+ const type = m ? m[1] : 'CUSTOM';
2166
+ counts[type] = (counts[type] || 0) + 1;
2167
+ }
2168
+ const entityCounts = Object.keys(counts).sort().map(k => `${k}=${counts[k]}`).join(',') || 'none';
2169
+ const manifestSha256 = psr2Sha256(labels.join('\n'));
2170
+ const canonical = [PSR2_VERSION, timestamp, 'none', manifestSha256, entityCounts, 'na'].join('|');
2171
+ return { version: PSR2_VERSION, timestamp, outputSha256: null, manifestSha256, entityCounts, tokensTotal: labels.length, tokensPresentInOutput: null, canonical, receiptSha256: psr2Sha256(canonical) };
2172
+ }
2173
+ function psr2Lines(r) {
2174
+ return [
2175
+ `Receipt Version: ${r.version}`,
2176
+ `Timestamp (UTC): ${r.timestamp}`,
2177
+ `Output SHA-256: not recorded`,
2178
+ `Token Manifest SHA-256: ${r.manifestSha256}`,
2179
+ `Entity Counts: ${r.entityCounts}`,
2180
+ `Tokens In Final Output: not recorded`,
2181
+ `Canonical: ${r.canonical}`,
2182
+ `Receipt SHA-256: ${r.receiptSha256}`
2183
+ ];
2184
+ }
2185
+ function formatPsr2Report({ format, receipt, telemetry, company_name, department, utmMedium }) {
2186
+ const entitySummary = Object.entries(telemetry.entities).map(([t, count]) => `[${t}]: ${count}`).join(', ') || 'None (Clean)';
2187
+ const teamsUrl = `https://privacyscrubber.com/teams?tier=teams&utm_source=sdk_audit_report&utm_medium=${utmMedium}&utm_campaign=teams_upgrade`;
2188
+ const sdkUrl = `https://privacyscrubber.com/sdk/?tier=sdk&utm_source=sdk_audit_report&utm_medium=${utmMedium}&utm_campaign=sdk_pipeline`;
2189
+ if (format === 'json') {
2190
+ return JSON.stringify({
2191
+ protocol: 'Zero-Trust Data Sanitization (ZTDS)',
2192
+ receipt_version: receipt.version,
2193
+ company: company_name,
2194
+ department,
2195
+ timestamp: receipt.timestamp,
2196
+ output_sha256: null,
2197
+ token_manifest_sha256: receipt.manifestSha256,
2198
+ entity_counts: receipt.entityCounts,
2199
+ canonical: receipt.canonical,
2200
+ receipt_sha256: receipt.receiptSha256,
2201
+ risk_level: telemetry.riskLevel,
2202
+ frameworks_referenced: telemetry.frameworksList,
2203
+ total_masked_tokens: telemetry.totalCount,
2204
+ entities_breakdown: telemetry.entities,
2205
+ scope: { measured: PSR2_STATEMENTS.measured, by_design_not_measured: PSR2_STATEMENTS.byDesign, limits: PSR2_STATEMENTS.limits },
2206
+ verification_url: 'https://privacyscrubber.com/features/audit-receipt/#verify',
2207
+ licensing_and_governance: { teams_governance_url: teamsUrl, sdk_pipeline_url: sdkUrl }
2208
+ }, null, 2);
2209
+ }
2210
+ if (format === 'summary') {
2211
+ return `[PrivacyScrubber SHA-256 Receipt] ${receipt.version} | Receipt SHA-256: ${receipt.receiptSha256.substring(0, 16)}... | Organization: ${company_name} | Masked Tokens: ${telemetry.totalCount} | Risk: ${telemetry.riskLevel} | Frameworks (informational): ${telemetry.frameworksList.join(', ')}`;
2212
+ }
2213
+ return `# Zero-Trust Data Sanitization Receipt
2214
+ **Organization:** ${company_name} (${department})
2215
+ **Timestamp:** ${receipt.timestamp}
2216
+ **Receipt Format:** ${receipt.version} (SHA-256 digests; not a digital signature)
2217
+
2218
+ ---
2219
+
2220
+ ### Sanitization Metrics & Risk Assessment
2221
+ * **Overall Risk Rating:** **${telemetry.riskLevel}**
2222
+ * **Total Sensitive Entities Masked:** \`${telemetry.totalCount}\`
2223
+ * **Entity Breakdown:** ${entitySummary}
2224
+
2225
+ ### Informational Framework References
2226
+ ${telemetry.frameworksList.map(f => `- ${f}`).join('\n')}
2227
+
2228
+ These are technical-safeguard mappings, not a legal determination of compliance.
2229
+
2230
+ ### Receipt Integrity (${receipt.version})
2231
+ \`\`\`text
2232
+ ${psr2Lines(receipt).join('\n')}
2233
+ \`\`\`
2234
+
2235
+ ### Scope of This Receipt
2236
+ **Measured:** ${PSR2_STATEMENTS.measured.join(' ')}
2237
+
2238
+ **Stated by design, not measured:** ${PSR2_STATEMENTS.byDesign.join(' ')}
2239
+
2240
+ **Limits:** ${PSR2_STATEMENTS.limits.join(' ')}
2241
+
2242
+ ### Enterprise & Team Governance
2243
+ * **Team Governance & Encrypted Handoff:** Centrally deploy redaction rules and encrypted session handoff with **PrivacyScrubber TEAMS** ($99/mo flat): [privacyscrubber.com/teams](${teamsUrl})
2244
+ * **Backend Microservices & RAG Pipelines:** Integrate in-memory sanitization (<1ms) into your Node.js/Python infrastructure with **Developer SDK** ($299/mo): [privacyscrubber.com/sdk](${sdkUrl})
2245
+
2246
+ *Generated locally by PrivacyScrubber Core SDK*
2247
+ *Verify locally (paste this receipt): https://privacyscrubber.com/features/audit-receipt/#verify*`;
2248
+ }
2249
+
2144
2250
  function generateAuditReport(sessionMap = {}, options = {}) {
2145
2251
  const { companyName = 'PrivacyScrubber Client', department = 'SecOps / Compliance', format = 'markdown' } = options;
2146
2252
  const telemetry = buildAuditTelemetry(sessionMap);
2147
-
2148
- let sessionHash = '';
2149
- try {
2150
- const cryptoMod = require('crypto');
2151
- sessionHash = cryptoMod.createHash('sha256').update(JSON.stringify(sessionMap) + Date.now().toString()).digest('hex');
2152
- } catch {
2153
- sessionHash = Array.from(crypto.getRandomValues(new Uint8Array(16))).map(b => b.toString(16).padStart(2, '0')).join('');
2154
- }
2155
-
2156
2253
  const timestamp = new Date().toISOString();
2157
- const entitySummary = Object.entries(telemetry.entities).map(([t, count]) => `[${t}]: ${count}`).join(', ') || 'None (Clean)';
2254
+ const receipt = buildPsr2Receipt(sessionMap, timestamp);
2255
+ const sessionHash = receipt.receiptSha256;
2158
2256
 
2159
2257
  if (format === 'cef') {
2160
- return formatCefEvent({
2161
- organization: companyName,
2162
- department: department,
2163
- timestamp,
2164
- sessionHash,
2165
- riskLevel: telemetry.riskLevel,
2166
- tokensMasked: telemetry.totalCount,
2167
- entities: telemetry.entities,
2168
- frameworks: telemetry.frameworksList
2169
- });
2258
+ return formatCefEvent({ organization: companyName, department, timestamp, sessionHash, riskLevel: telemetry.riskLevel, tokensMasked: telemetry.totalCount, entities: telemetry.entities, frameworks: telemetry.frameworksList });
2170
2259
  }
2171
-
2172
2260
  if (format === 'syslog') {
2173
- return formatSyslogEvent({
2174
- organization: companyName,
2175
- department: department,
2176
- timestamp,
2177
- sessionHash,
2178
- riskLevel: telemetry.riskLevel,
2179
- tokensMasked: telemetry.totalCount,
2180
- entities: telemetry.entities,
2181
- frameworks: telemetry.frameworksList
2182
- });
2261
+ return formatSyslogEvent({ organization: companyName, department, timestamp, sessionHash, riskLevel: telemetry.riskLevel, tokensMasked: telemetry.totalCount, entities: telemetry.entities, frameworks: telemetry.frameworksList });
2183
2262
  }
2184
-
2185
2263
  if (format === 'jsonl') {
2186
- return formatJsonlEvent({
2187
- organization: companyName,
2188
- department: department,
2189
- timestamp,
2190
- sessionHash,
2191
- riskLevel: telemetry.riskLevel,
2192
- tokensMasked: telemetry.totalCount,
2193
- entities: telemetry.entities,
2194
- frameworks: telemetry.frameworksList
2195
- });
2264
+ return formatJsonlEvent({ organization: companyName, department, timestamp, sessionHash, riskLevel: telemetry.riskLevel, tokensMasked: telemetry.totalCount, entities: telemetry.entities, frameworks: telemetry.frameworksList });
2196
2265
  }
2197
-
2198
2266
  if (format === 'json') {
2199
- return {
2200
- protocol: "Zero-Trust Data Sanitization (ZTDS)",
2201
- certificateId: `ZTDS-CERT-${sessionHash.substring(0, 16).toUpperCase()}`,
2202
- organization: companyName,
2203
- department: department,
2204
- timestamp: timestamp,
2205
- sessionHash: sessionHash,
2206
- verificationMode: "100% Offline (In-Memory RAM)",
2207
- complianceStatus: "VERIFIED PASS",
2208
- riskLevel: telemetry.riskLevel,
2209
- frameworksEnforced: telemetry.frameworksList,
2210
- totalMaskedTokens: telemetry.totalCount,
2211
- entitiesBreakdown: telemetry.entities,
2212
- zeroEgressVerified: true,
2213
- verificationUrl: `https://privacyscrubber.com/features/audit-receipt/#verify?hash=${sessionHash.substring(0, 16)}`
2214
- };
2267
+ return JSON.parse(formatPsr2Report({ format: 'json', receipt, telemetry, company_name: companyName, department, utmMedium: 'json' }));
2215
2268
  }
2216
-
2217
- if (format === 'summary') {
2218
- return `[PrivacyScrubber Compliance Certificate] ID: ZTDS-${sessionHash.substring(0, 8).toUpperCase()} | Organization: ${companyName} | Masked Tokens: ${telemetry.totalCount} | Risk: ${telemetry.riskLevel} | Frameworks: ${telemetry.frameworksList.join(', ')} | Status: PASS (100% Air-Gapped)`;
2219
- }
2220
-
2221
- return `# ๐Ÿ›ก๏ธ Zero-Trust Data Sanitization Compliance Certificate
2222
- **Certificate ID:** \`ZTDS-CERT-${sessionHash.substring(0, 16).toUpperCase()}\`
2223
- **Organization:** ${companyName} (${department})
2224
- **Timestamp:** ${timestamp}
2225
- **Verification Mode:** 100% Local In-Memory Processing (Air-Gapped)
2226
- **Status:** **VERIFIED PASS** (Zero Network Egress)
2227
-
2228
- ---
2229
-
2230
- ### ๐Ÿ“Š Sanitization Metrics & Risk Assessment
2231
- * **Overall Risk Rating:** **${telemetry.riskLevel}**
2232
- * **Total Sensitive Entities Masked:** \`${telemetry.totalCount}\`
2233
- * **Entity Breakdown:** ${entitySummary}
2234
- * **Network Data Transmitted:** \`0.00 KB (Zero-Trust Local RAM)\`
2235
-
2236
- ### ๐Ÿ“œ Regulatory Frameworks Enforced
2237
- ${telemetry.frameworksList.map(f => `- **${f}**`).join('\n')}
2238
-
2239
- ### ๐Ÿ”’ CISO Compliance Declaration
2240
- 1. **EU AI Act (Art. 50) & GDPR (Art. 25 & 32):** Data minimization and local pseudonymization enforced prior to model interaction.
2241
- 2. **HIPAA Safe Harbor (ยง164.514) / SOC 2 Type II:** All direct and indirect identifiers sanitized locally without cloud processor liability.
2242
- 3. **Cryptographic Verification:** Tamper-evident session verification hash: \`${sessionHash}\`
2243
-
2244
- *Certified Offline by PrivacyScrubber Core SDK*
2245
- *Verify at: https://privacyscrubber.com/features/audit-receipt/*`;
2269
+ return formatPsr2Report({ format: format === 'summary' ? 'summary' : 'markdown', receipt, telemetry, company_name: companyName, department, utmMedium: 'markdown' });
2246
2270
  }
2247
2271
 
2248
2272
  /**