agentshield-sdk 7.0.0
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/CHANGELOG.md +191 -0
- package/LICENSE +21 -0
- package/README.md +975 -0
- package/bin/agent-shield.js +680 -0
- package/package.json +118 -0
- package/src/adaptive.js +330 -0
- package/src/agent-protocol.js +998 -0
- package/src/alert-tuning.js +480 -0
- package/src/allowlist.js +603 -0
- package/src/audit-immutable.js +914 -0
- package/src/audit-streaming.js +469 -0
- package/src/badges.js +196 -0
- package/src/behavior-profiling.js +289 -0
- package/src/benchmark-harness.js +804 -0
- package/src/canary.js +271 -0
- package/src/certification.js +563 -0
- package/src/circuit-breaker.js +321 -0
- package/src/compliance.js +617 -0
- package/src/confidence-tuning.js +324 -0
- package/src/confused-deputy.js +624 -0
- package/src/context-scoring.js +360 -0
- package/src/conversation.js +494 -0
- package/src/cost-optimizer.js +1024 -0
- package/src/ctf.js +462 -0
- package/src/detector-core.js +1999 -0
- package/src/distributed.js +359 -0
- package/src/document-scanner.js +795 -0
- package/src/embedding.js +307 -0
- package/src/encoding.js +429 -0
- package/src/enterprise.js +405 -0
- package/src/errors.js +100 -0
- package/src/eu-ai-act.js +523 -0
- package/src/fuzzer.js +764 -0
- package/src/honeypot.js +328 -0
- package/src/i18n-patterns.js +523 -0
- package/src/index.js +430 -0
- package/src/integrations.js +528 -0
- package/src/llm-redteam.js +670 -0
- package/src/main.js +741 -0
- package/src/main.mjs +38 -0
- package/src/mcp-bridge.js +542 -0
- package/src/mcp-certification.js +846 -0
- package/src/mcp-sdk-integration.js +355 -0
- package/src/mcp-security-runtime.js +741 -0
- package/src/mcp-server.js +740 -0
- package/src/middleware.js +208 -0
- package/src/model-finetuning.js +884 -0
- package/src/model-fingerprint.js +1042 -0
- package/src/multi-agent-trust.js +453 -0
- package/src/multi-agent.js +404 -0
- package/src/multimodal.js +296 -0
- package/src/nist-mapping.js +505 -0
- package/src/observability.js +330 -0
- package/src/openclaw.js +450 -0
- package/src/otel.js +544 -0
- package/src/owasp-2025.js +483 -0
- package/src/pii.js +390 -0
- package/src/plugin-marketplace.js +628 -0
- package/src/plugin-system.js +349 -0
- package/src/policy-dsl.js +775 -0
- package/src/policy-extended.js +635 -0
- package/src/policy.js +443 -0
- package/src/presets.js +409 -0
- package/src/production.js +557 -0
- package/src/prompt-leakage.js +321 -0
- package/src/rag-vulnerability.js +579 -0
- package/src/redteam.js +475 -0
- package/src/response-handler.js +429 -0
- package/src/scanners.js +357 -0
- package/src/self-healing.js +363 -0
- package/src/semantic.js +339 -0
- package/src/shield-score.js +250 -0
- package/src/sso-saml.js +897 -0
- package/src/stream-scanner.js +806 -0
- package/src/testing.js +505 -0
- package/src/threat-encyclopedia.js +629 -0
- package/src/threat-intel-network.js +1017 -0
- package/src/token-analysis.js +467 -0
- package/src/tool-guard.js +412 -0
- package/src/tool-output-validator.js +354 -0
- package/src/utils.js +83 -0
- package/src/watermark.js +235 -0
- package/src/worker-scanner.js +601 -0
- package/types/index.d.ts +2088 -0
package/src/pii.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PII Redaction (#43), Data Loss Prevention (#45), and Output Content Policies (#17)
|
|
5
|
+
*
|
|
6
|
+
* - PII Redaction: Automatically detect and redact personal information.
|
|
7
|
+
* - DLP: Define sensitive data patterns for your organization and block leaks.
|
|
8
|
+
* - Content Policies: Block agents from generating certain content categories.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =========================================================================
|
|
12
|
+
// PII PATTERNS
|
|
13
|
+
// =========================================================================
|
|
14
|
+
|
|
15
|
+
const PII_PATTERNS = {
|
|
16
|
+
email: {
|
|
17
|
+
regex: /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,
|
|
18
|
+
replacement: '[EMAIL REDACTED]',
|
|
19
|
+
category: 'email',
|
|
20
|
+
description: 'Email address'
|
|
21
|
+
},
|
|
22
|
+
phone_us: {
|
|
23
|
+
regex: /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
24
|
+
replacement: '[PHONE REDACTED]',
|
|
25
|
+
category: 'phone',
|
|
26
|
+
description: 'US phone number'
|
|
27
|
+
},
|
|
28
|
+
phone_intl: {
|
|
29
|
+
regex: /\b\+\d{1,3}[-.\s]?\d{2,4}[-.\s]?\d{3,4}[-.\s]?\d{3,4}\b/g,
|
|
30
|
+
replacement: '[PHONE REDACTED]',
|
|
31
|
+
category: 'phone',
|
|
32
|
+
description: 'International phone number'
|
|
33
|
+
},
|
|
34
|
+
ssn: {
|
|
35
|
+
regex: /\b\d{3}[-.\s]?\d{2}[-.\s]?\d{4}\b/g,
|
|
36
|
+
replacement: '[SSN REDACTED]',
|
|
37
|
+
category: 'ssn',
|
|
38
|
+
description: 'Social Security Number'
|
|
39
|
+
},
|
|
40
|
+
credit_card: {
|
|
41
|
+
regex: /\b(?:\d{4}[-.\s]?){3}\d{4}\b/g,
|
|
42
|
+
replacement: '[CREDIT CARD REDACTED]',
|
|
43
|
+
category: 'credit_card',
|
|
44
|
+
description: 'Credit card number'
|
|
45
|
+
},
|
|
46
|
+
ip_address: {
|
|
47
|
+
regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
48
|
+
replacement: '[IP REDACTED]',
|
|
49
|
+
category: 'ip_address',
|
|
50
|
+
description: 'IP address'
|
|
51
|
+
},
|
|
52
|
+
date_of_birth: {
|
|
53
|
+
regex: /\b(?:date\s+of\s+birth|DOB|born\s+on)\s*:?\s*\d{1,2}[/.-]\d{1,2}[/.-]\d{2,4}\b/gi,
|
|
54
|
+
replacement: '[DOB REDACTED]',
|
|
55
|
+
category: 'dob',
|
|
56
|
+
description: 'Date of birth'
|
|
57
|
+
},
|
|
58
|
+
street_address: {
|
|
59
|
+
regex: /\b\d{1,5}\s+[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\s+(?:St|Street|Ave|Avenue|Blvd|Boulevard|Dr|Drive|Ln|Lane|Rd|Road|Way|Ct|Court|Pl|Place)\b\.?\s*,?\s*(?:[A-Z][a-z]+\s*,?\s*)?(?:[A-Z]{2}\s*\d{5}(?:-\d{4})?)?/g,
|
|
60
|
+
replacement: '[ADDRESS REDACTED]',
|
|
61
|
+
category: 'address',
|
|
62
|
+
description: 'Street address'
|
|
63
|
+
},
|
|
64
|
+
passport: {
|
|
65
|
+
regex: /\b(?:passport)\s*(?:#|number|no\.?)\s*:?\s*[A-Z0-9]{6,9}\b/gi,
|
|
66
|
+
replacement: '[PASSPORT REDACTED]',
|
|
67
|
+
category: 'passport',
|
|
68
|
+
description: 'Passport number'
|
|
69
|
+
},
|
|
70
|
+
drivers_license: {
|
|
71
|
+
regex: /\b(?:driver'?s?\s*license|DL)\s*(?:#|number|no\.?)\s*:?\s*[A-Z0-9-]{5,15}\b/gi,
|
|
72
|
+
replacement: '[DRIVERS LICENSE REDACTED]',
|
|
73
|
+
category: 'drivers_license',
|
|
74
|
+
description: 'Driver\'s license number'
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
class PIIRedactor {
|
|
79
|
+
/**
|
|
80
|
+
* @param {object} [options]
|
|
81
|
+
* @param {Array<string>} [options.categories] - PII categories to redact. Defaults to all.
|
|
82
|
+
* @param {object} [options.customPatterns] - Additional custom PII patterns.
|
|
83
|
+
* @param {boolean} [options.logging=false] - Log redactions.
|
|
84
|
+
*/
|
|
85
|
+
constructor(options = {}) {
|
|
86
|
+
// Collect unique category values (not keys) so phone_us/phone_intl both map to 'phone'
|
|
87
|
+
const allCategories = [...new Set(Object.values(PII_PATTERNS).map(p => p.category))];
|
|
88
|
+
this.categories = options.categories || allCategories;
|
|
89
|
+
this.customPatterns = options.customPatterns || {};
|
|
90
|
+
this.logging = options.logging || false;
|
|
91
|
+
this.stats = { totalRedactions: 0, byCategory: {} };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Redacts PII from text.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} text - Text to redact.
|
|
98
|
+
* @returns {object} { redacted: string, findings: Array, count: number }
|
|
99
|
+
*/
|
|
100
|
+
redact(text) {
|
|
101
|
+
if (typeof text !== 'string' || !text) return { redacted: text || '', findings: [], count: 0 };
|
|
102
|
+
|
|
103
|
+
let redacted = text;
|
|
104
|
+
const findings = [];
|
|
105
|
+
|
|
106
|
+
// Apply built-in patterns
|
|
107
|
+
for (const [name, pattern] of Object.entries(PII_PATTERNS)) {
|
|
108
|
+
if (!this.categories.includes(pattern.category)) continue;
|
|
109
|
+
|
|
110
|
+
const matches = redacted.match(pattern.regex);
|
|
111
|
+
if (matches) {
|
|
112
|
+
for (const match of matches) {
|
|
113
|
+
findings.push({
|
|
114
|
+
type: name,
|
|
115
|
+
category: pattern.category,
|
|
116
|
+
description: pattern.description,
|
|
117
|
+
preview: match.substring(0, 4) + '...'
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
redacted = redacted.replace(pattern.regex, pattern.replacement);
|
|
121
|
+
this.stats.byCategory[pattern.category] = (this.stats.byCategory[pattern.category] || 0) + matches.length;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Apply custom patterns
|
|
126
|
+
for (const [name, pattern] of Object.entries(this.customPatterns)) {
|
|
127
|
+
const matches = redacted.match(pattern.regex);
|
|
128
|
+
if (matches) {
|
|
129
|
+
for (const match of matches) {
|
|
130
|
+
findings.push({
|
|
131
|
+
type: name,
|
|
132
|
+
category: 'custom',
|
|
133
|
+
description: pattern.description || name,
|
|
134
|
+
preview: match.substring(0, 4) + '...'
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
redacted = redacted.replace(pattern.regex, pattern.replacement || `[${name.toUpperCase()} REDACTED]`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.stats.totalRedactions += findings.length;
|
|
142
|
+
|
|
143
|
+
if (this.logging && findings.length > 0) {
|
|
144
|
+
console.warn(`[Agent Shield PII] Redacted ${findings.length} item(s):`, findings.map(f => f.description));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return { redacted, findings, count: findings.length };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Checks text for PII without redacting. Useful for output scanning.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} text
|
|
154
|
+
* @returns {object} { hasPII: boolean, findings: Array }
|
|
155
|
+
*/
|
|
156
|
+
detect(text) {
|
|
157
|
+
if (!text) return { hasPII: false, findings: [] };
|
|
158
|
+
|
|
159
|
+
const findings = [];
|
|
160
|
+
|
|
161
|
+
for (const [name, pattern] of Object.entries(PII_PATTERNS)) {
|
|
162
|
+
if (!this.categories.includes(pattern.category)) continue;
|
|
163
|
+
|
|
164
|
+
const matches = text.match(pattern.regex);
|
|
165
|
+
if (matches) {
|
|
166
|
+
for (const match of matches) {
|
|
167
|
+
findings.push({
|
|
168
|
+
type: name,
|
|
169
|
+
category: pattern.category,
|
|
170
|
+
description: pattern.description,
|
|
171
|
+
value: match
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { hasPII: findings.length > 0, findings };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
getStats() {
|
|
181
|
+
return { ...this.stats };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// =========================================================================
|
|
186
|
+
// DATA LOSS PREVENTION
|
|
187
|
+
// =========================================================================
|
|
188
|
+
|
|
189
|
+
class DLPEngine {
|
|
190
|
+
/**
|
|
191
|
+
* @param {object} [options]
|
|
192
|
+
* @param {Array<object>} [options.rules=[]] - DLP rules.
|
|
193
|
+
* @param {Function} [options.onViolation] - Callback when a rule is violated.
|
|
194
|
+
*/
|
|
195
|
+
constructor(options = {}) {
|
|
196
|
+
this.rules = options.rules || [];
|
|
197
|
+
this.onViolation = options.onViolation || null;
|
|
198
|
+
this.violations = [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Adds a DLP rule.
|
|
203
|
+
*
|
|
204
|
+
* @param {object} rule
|
|
205
|
+
* @param {string} rule.name - Rule name.
|
|
206
|
+
* @param {RegExp|string} rule.pattern - Pattern to match.
|
|
207
|
+
* @param {string} [rule.action='block'] - Action: 'block', 'redact', or 'warn'.
|
|
208
|
+
* @param {string} [rule.replacement] - Replacement text for 'redact' action.
|
|
209
|
+
* @param {string} [rule.severity='high'] - Severity level.
|
|
210
|
+
* @returns {DLPEngine} this (for chaining)
|
|
211
|
+
*/
|
|
212
|
+
addRule(rule) {
|
|
213
|
+
this.rules.push({
|
|
214
|
+
name: rule.name,
|
|
215
|
+
pattern: typeof rule.pattern === 'string' ? new RegExp(rule.pattern, 'gi') : rule.pattern,
|
|
216
|
+
action: rule.action || 'block',
|
|
217
|
+
replacement: rule.replacement || `[${rule.name.toUpperCase()} BLOCKED]`,
|
|
218
|
+
severity: rule.severity || 'high'
|
|
219
|
+
});
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Scans text against all DLP rules.
|
|
225
|
+
*
|
|
226
|
+
* @param {string} text
|
|
227
|
+
* @param {string} [source='unknown']
|
|
228
|
+
* @returns {object} { clean: boolean, violations: Array, redactedText: string }
|
|
229
|
+
*/
|
|
230
|
+
scan(text, source = 'unknown') {
|
|
231
|
+
if (!text) return { clean: true, violations: [], redactedText: text };
|
|
232
|
+
|
|
233
|
+
const violations = [];
|
|
234
|
+
let redactedText = text;
|
|
235
|
+
|
|
236
|
+
for (const rule of this.rules) {
|
|
237
|
+
// Reset regex lastIndex for global patterns
|
|
238
|
+
if (rule.pattern.global) rule.pattern.lastIndex = 0;
|
|
239
|
+
|
|
240
|
+
const matches = text.match(rule.pattern);
|
|
241
|
+
if (matches) {
|
|
242
|
+
for (const match of matches) {
|
|
243
|
+
const violation = {
|
|
244
|
+
rule: rule.name,
|
|
245
|
+
action: rule.action,
|
|
246
|
+
severity: rule.severity,
|
|
247
|
+
match: match.substring(0, 50),
|
|
248
|
+
source,
|
|
249
|
+
timestamp: Date.now()
|
|
250
|
+
};
|
|
251
|
+
violations.push(violation);
|
|
252
|
+
this.violations.push(violation);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (rule.action === 'redact') {
|
|
256
|
+
if (rule.pattern.global) rule.pattern.lastIndex = 0;
|
|
257
|
+
redactedText = redactedText.replace(rule.pattern, rule.replacement);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (violations.length > 0 && this.onViolation) {
|
|
263
|
+
this.onViolation({ violations, source });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const hasBlock = violations.some(v => v.action === 'block');
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
clean: violations.length === 0,
|
|
270
|
+
blocked: hasBlock,
|
|
271
|
+
violations,
|
|
272
|
+
redactedText
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Returns violation history.
|
|
278
|
+
* @returns {Array}
|
|
279
|
+
*/
|
|
280
|
+
getViolations() {
|
|
281
|
+
return [...this.violations];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
clearHistory() {
|
|
285
|
+
this.violations = [];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// =========================================================================
|
|
290
|
+
// OUTPUT CONTENT POLICIES
|
|
291
|
+
// =========================================================================
|
|
292
|
+
|
|
293
|
+
const CONTENT_CATEGORIES = {
|
|
294
|
+
medical_advice: {
|
|
295
|
+
patterns: [
|
|
296
|
+
/\b(?:you\s+should\s+(?:take|stop\s+taking|increase|decrease)\s+(?:your\s+)?(?:medication|dosage|prescription))\b/gi,
|
|
297
|
+
/\b(?:diagnos(?:e|is|ed)|prescrib(?:e|ed)|treatment\s+plan)\b.*\b(?:you|your|patient)\b/gi
|
|
298
|
+
],
|
|
299
|
+
description: 'Medical advice or diagnosis'
|
|
300
|
+
},
|
|
301
|
+
legal_advice: {
|
|
302
|
+
patterns: [
|
|
303
|
+
/\b(?:you\s+should\s+(?:sue|file\s+a\s+lawsuit|take\s+legal\s+action|hire\s+a\s+lawyer))\b/gi,
|
|
304
|
+
/\b(?:legal(?:ly)?\s+(?:binding|obligat|liable|entitled))\b.*\b(?:you|your)\b/gi
|
|
305
|
+
],
|
|
306
|
+
description: 'Legal advice or recommendations'
|
|
307
|
+
},
|
|
308
|
+
financial_advice: {
|
|
309
|
+
patterns: [
|
|
310
|
+
/\b(?:you\s+should\s+(?:invest|buy|sell|trade)\s+(?:in\s+)?(?:stocks?|bonds?|crypto|bitcoin|options?))\b/gi,
|
|
311
|
+
/\b(?:guaranteed\s+(?:return|profit|income))\b/gi
|
|
312
|
+
],
|
|
313
|
+
description: 'Financial or investment advice'
|
|
314
|
+
},
|
|
315
|
+
harmful_instructions: {
|
|
316
|
+
patterns: [
|
|
317
|
+
/\b(?:how\s+to\s+(?:make|build|create)\s+(?:a\s+)?(?:bomb|weapon|explosive|poison))\b/gi,
|
|
318
|
+
/\b(?:instructions\s+for\s+(?:making|building|creating)\s+(?:a\s+)?(?:bomb|weapon|explosive))\b/gi
|
|
319
|
+
],
|
|
320
|
+
description: 'Harmful or dangerous instructions'
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
class ContentPolicy {
|
|
325
|
+
/**
|
|
326
|
+
* @param {object} [options]
|
|
327
|
+
* @param {Array<string>} [options.blockedCategories=[]] - Categories to block.
|
|
328
|
+
* @param {Array<object>} [options.customCategories=[]] - Custom content categories.
|
|
329
|
+
* @param {Function} [options.onViolation] - Callback on policy violation.
|
|
330
|
+
*/
|
|
331
|
+
constructor(options = {}) {
|
|
332
|
+
this.blockedCategories = options.blockedCategories || [];
|
|
333
|
+
this.customCategories = options.customCategories || [];
|
|
334
|
+
this.onViolation = options.onViolation || null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Checks text against content policies.
|
|
339
|
+
*
|
|
340
|
+
* @param {string} text
|
|
341
|
+
* @returns {object} { allowed: boolean, violations: Array }
|
|
342
|
+
*/
|
|
343
|
+
check(text) {
|
|
344
|
+
if (!text) return { allowed: true, violations: [] };
|
|
345
|
+
|
|
346
|
+
const violations = [];
|
|
347
|
+
|
|
348
|
+
// Check built-in categories
|
|
349
|
+
for (const category of this.blockedCategories) {
|
|
350
|
+
const def = CONTENT_CATEGORIES[category];
|
|
351
|
+
if (!def) continue;
|
|
352
|
+
|
|
353
|
+
for (const pattern of def.patterns) {
|
|
354
|
+
if (pattern.global) pattern.lastIndex = 0;
|
|
355
|
+
if (pattern.test(text)) {
|
|
356
|
+
violations.push({
|
|
357
|
+
category,
|
|
358
|
+
description: def.description,
|
|
359
|
+
severity: 'high'
|
|
360
|
+
});
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Check custom categories
|
|
367
|
+
for (const custom of this.customCategories) {
|
|
368
|
+
for (const pattern of custom.patterns) {
|
|
369
|
+
const regex = typeof pattern === 'string' ? new RegExp(pattern, 'gi') : pattern;
|
|
370
|
+
if (regex.global) regex.lastIndex = 0;
|
|
371
|
+
if (regex.test(text)) {
|
|
372
|
+
violations.push({
|
|
373
|
+
category: custom.name,
|
|
374
|
+
description: custom.description || custom.name,
|
|
375
|
+
severity: custom.severity || 'medium'
|
|
376
|
+
});
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (violations.length > 0 && this.onViolation) {
|
|
383
|
+
this.onViolation({ violations, timestamp: Date.now() });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return { allowed: violations.length === 0, violations };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
module.exports = { PIIRedactor, DLPEngine, ContentPolicy, PII_PATTERNS, CONTENT_CATEGORIES };
|