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.
Files changed (84) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/LICENSE +21 -0
  3. package/README.md +975 -0
  4. package/bin/agent-shield.js +680 -0
  5. package/package.json +118 -0
  6. package/src/adaptive.js +330 -0
  7. package/src/agent-protocol.js +998 -0
  8. package/src/alert-tuning.js +480 -0
  9. package/src/allowlist.js +603 -0
  10. package/src/audit-immutable.js +914 -0
  11. package/src/audit-streaming.js +469 -0
  12. package/src/badges.js +196 -0
  13. package/src/behavior-profiling.js +289 -0
  14. package/src/benchmark-harness.js +804 -0
  15. package/src/canary.js +271 -0
  16. package/src/certification.js +563 -0
  17. package/src/circuit-breaker.js +321 -0
  18. package/src/compliance.js +617 -0
  19. package/src/confidence-tuning.js +324 -0
  20. package/src/confused-deputy.js +624 -0
  21. package/src/context-scoring.js +360 -0
  22. package/src/conversation.js +494 -0
  23. package/src/cost-optimizer.js +1024 -0
  24. package/src/ctf.js +462 -0
  25. package/src/detector-core.js +1999 -0
  26. package/src/distributed.js +359 -0
  27. package/src/document-scanner.js +795 -0
  28. package/src/embedding.js +307 -0
  29. package/src/encoding.js +429 -0
  30. package/src/enterprise.js +405 -0
  31. package/src/errors.js +100 -0
  32. package/src/eu-ai-act.js +523 -0
  33. package/src/fuzzer.js +764 -0
  34. package/src/honeypot.js +328 -0
  35. package/src/i18n-patterns.js +523 -0
  36. package/src/index.js +430 -0
  37. package/src/integrations.js +528 -0
  38. package/src/llm-redteam.js +670 -0
  39. package/src/main.js +741 -0
  40. package/src/main.mjs +38 -0
  41. package/src/mcp-bridge.js +542 -0
  42. package/src/mcp-certification.js +846 -0
  43. package/src/mcp-sdk-integration.js +355 -0
  44. package/src/mcp-security-runtime.js +741 -0
  45. package/src/mcp-server.js +740 -0
  46. package/src/middleware.js +208 -0
  47. package/src/model-finetuning.js +884 -0
  48. package/src/model-fingerprint.js +1042 -0
  49. package/src/multi-agent-trust.js +453 -0
  50. package/src/multi-agent.js +404 -0
  51. package/src/multimodal.js +296 -0
  52. package/src/nist-mapping.js +505 -0
  53. package/src/observability.js +330 -0
  54. package/src/openclaw.js +450 -0
  55. package/src/otel.js +544 -0
  56. package/src/owasp-2025.js +483 -0
  57. package/src/pii.js +390 -0
  58. package/src/plugin-marketplace.js +628 -0
  59. package/src/plugin-system.js +349 -0
  60. package/src/policy-dsl.js +775 -0
  61. package/src/policy-extended.js +635 -0
  62. package/src/policy.js +443 -0
  63. package/src/presets.js +409 -0
  64. package/src/production.js +557 -0
  65. package/src/prompt-leakage.js +321 -0
  66. package/src/rag-vulnerability.js +579 -0
  67. package/src/redteam.js +475 -0
  68. package/src/response-handler.js +429 -0
  69. package/src/scanners.js +357 -0
  70. package/src/self-healing.js +363 -0
  71. package/src/semantic.js +339 -0
  72. package/src/shield-score.js +250 -0
  73. package/src/sso-saml.js +897 -0
  74. package/src/stream-scanner.js +806 -0
  75. package/src/testing.js +505 -0
  76. package/src/threat-encyclopedia.js +629 -0
  77. package/src/threat-intel-network.js +1017 -0
  78. package/src/token-analysis.js +467 -0
  79. package/src/tool-guard.js +412 -0
  80. package/src/tool-output-validator.js +354 -0
  81. package/src/utils.js +83 -0
  82. package/src/watermark.js +235 -0
  83. package/src/worker-scanner.js +601 -0
  84. 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 };