agentshield-sdk 7.3.0 → 8.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 (50) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +63 -7
  3. package/package.json +8 -3
  4. package/src/agent-intent.js +807 -0
  5. package/src/agent-protocol.js +4 -0
  6. package/src/allowlist.js +605 -603
  7. package/src/audit-streaming.js +486 -469
  8. package/src/audit.js +1 -1
  9. package/src/behavior-profiling.js +299 -289
  10. package/src/behavioral-dna.js +4 -9
  11. package/src/canary.js +273 -271
  12. package/src/compliance.js +619 -617
  13. package/src/confidence-tuning.js +328 -324
  14. package/src/context-scoring.js +362 -360
  15. package/src/cost-optimizer.js +1024 -1024
  16. package/src/cross-turn.js +663 -0
  17. package/src/detector-core.js +186 -0
  18. package/src/distributed.js +5 -1
  19. package/src/embedding.js +310 -307
  20. package/src/ensemble.js +523 -0
  21. package/src/herd-immunity.js +12 -12
  22. package/src/honeypot.js +332 -328
  23. package/src/integrations.js +1 -2
  24. package/src/intent-firewall.js +14 -14
  25. package/src/llm-redteam.js +678 -670
  26. package/src/main.js +63 -0
  27. package/src/middleware.js +5 -2
  28. package/src/model-fingerprint.js +1059 -1042
  29. package/src/multi-agent-trust.js +459 -453
  30. package/src/multi-agent.js +1 -1
  31. package/src/normalizer.js +734 -0
  32. package/src/persistent-learning.js +677 -0
  33. package/src/pii.js +4 -0
  34. package/src/policy-dsl.js +775 -775
  35. package/src/presets.js +409 -409
  36. package/src/production.js +22 -9
  37. package/src/redteam.js +475 -475
  38. package/src/response-handler.js +436 -429
  39. package/src/scanners.js +358 -357
  40. package/src/self-healing.js +368 -363
  41. package/src/self-training.js +772 -0
  42. package/src/semantic.js +339 -339
  43. package/src/shield-score.js +250 -250
  44. package/src/smart-config.js +812 -0
  45. package/src/sso-saml.js +8 -4
  46. package/src/testing.js +24 -2
  47. package/src/tool-guard.js +412 -412
  48. package/src/watermark.js +242 -235
  49. package/src/worker-scanner.js +608 -601
  50. package/types/index.d.ts +660 -0
package/src/watermark.js CHANGED
@@ -1,235 +1,242 @@
1
- 'use strict';
2
-
3
- /**
4
- * Output Watermarking (#44) and Differential Privacy (#46)
5
- *
6
- * - Watermarking: Embed invisible watermarks in agent outputs for tracing.
7
- * - Differential Privacy: Add noise to stored conversation data.
8
- */
9
-
10
- const crypto = require('crypto');
11
-
12
- // =========================================================================
13
- // OUTPUT WATERMARKING
14
- // =========================================================================
15
-
16
- /**
17
- * Zero-width characters used for binary watermark encoding.
18
- */
19
- const WM_ZERO = '\u200B'; // zero-width space = 0
20
- const WM_ONE = '\u200C'; // zero-width non-joiner = 1
21
- const WM_START = '\u200D'; // zero-width joiner = start marker
22
- const WM_END = '\uFEFF'; // byte order mark = end marker
23
-
24
- class OutputWatermark {
25
- /**
26
- * @param {object} [options]
27
- * @param {string} [options.secret] - Secret key for HMAC signing.
28
- * @param {boolean} [options.includeTimestamp=true] - Include timestamp in watermark.
29
- */
30
- constructor(options = {}) {
31
- this.secret = options.secret || crypto.randomBytes(16).toString('hex');
32
- this.includeTimestamp = options.includeTimestamp !== undefined ? options.includeTimestamp : true;
33
- }
34
-
35
- /**
36
- * Embeds an invisible watermark in text.
37
- *
38
- * @param {string} text - The text to watermark.
39
- * @param {object} metadata - Data to encode in the watermark.
40
- * @param {string} [metadata.agentId] - Agent identifier.
41
- * @param {string} [metadata.sessionId] - Session identifier.
42
- * @returns {string} Watermarked text.
43
- */
44
- embed(text, metadata = {}) {
45
- if (!text) return text;
46
-
47
- const payload = {
48
- ...metadata,
49
- ts: this.includeTimestamp ? Date.now() : undefined
50
- };
51
-
52
- // Create signed payload
53
- const payloadStr = JSON.stringify(payload);
54
- const signature = crypto.createHmac('sha256', this.secret)
55
- .update(payloadStr)
56
- .digest('hex')
57
- .substring(0, 8);
58
-
59
- const data = `${payloadStr}|${signature}`;
60
-
61
- // Encode to binary using zero-width characters
62
- const binary = this._textToBinary(data);
63
- const watermark = WM_START + binary + WM_END;
64
-
65
- // Insert watermark in the middle of the text to be less detectable
66
- const midpoint = Math.floor(text.length / 2);
67
- // Find a space near the midpoint
68
- let insertAt = text.indexOf(' ', midpoint);
69
- if (insertAt === -1) insertAt = midpoint;
70
-
71
- return text.slice(0, insertAt) + watermark + text.slice(insertAt);
72
- }
73
-
74
- /**
75
- * Extracts a watermark from text.
76
- *
77
- * @param {string} text - Text that may contain a watermark.
78
- * @returns {object} { found: boolean, metadata?: object, verified: boolean }
79
- */
80
- extract(text) {
81
- if (!text) return { found: false };
82
-
83
- const startIdx = text.indexOf(WM_START);
84
- const endIdx = text.indexOf(WM_END, startIdx);
85
-
86
- if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
87
- return { found: false };
88
- }
89
-
90
- const binary = text.slice(startIdx + 1, endIdx);
91
- const data = this._binaryToText(binary);
92
-
93
- if (!data) return { found: false };
94
-
95
- const pipeIdx = data.lastIndexOf('|');
96
- if (pipeIdx === -1) return { found: false };
97
-
98
- const payloadStr = data.substring(0, pipeIdx);
99
- const signature = data.substring(pipeIdx + 1);
100
-
101
- // Verify signature
102
- const expectedSig = crypto.createHmac('sha256', this.secret)
103
- .update(payloadStr)
104
- .digest('hex')
105
- .substring(0, 8);
106
-
107
- const verified = signature === expectedSig;
108
-
109
- let metadata = null;
110
- try {
111
- metadata = JSON.parse(payloadStr);
112
- } catch (e) {
113
- return { found: true, verified: false, raw: payloadStr };
114
- }
115
-
116
- return { found: true, metadata, verified };
117
- }
118
-
119
- /**
120
- * Removes watermark from text.
121
- *
122
- * @param {string} text
123
- * @returns {string} Clean text.
124
- */
125
- strip(text) {
126
- if (!text) return text;
127
- return text.replace(/[\u200B\u200C\u200D\uFEFF]/g, '');
128
- }
129
-
130
- /** @private */
131
- _textToBinary(text) {
132
- let binary = '';
133
- for (let i = 0; i < text.length; i++) {
134
- const charCode = text.charCodeAt(i);
135
- const bits = charCode.toString(2).padStart(8, '0');
136
- for (const bit of bits) {
137
- binary += bit === '0' ? WM_ZERO : WM_ONE;
138
- }
139
- }
140
- return binary;
141
- }
142
-
143
- /** @private */
144
- _binaryToText(binary) {
145
- try {
146
- let text = '';
147
- let bits = '';
148
- for (const char of binary) {
149
- if (char === WM_ZERO) bits += '0';
150
- else if (char === WM_ONE) bits += '1';
151
- else continue;
152
-
153
- if (bits.length === 8) {
154
- text += String.fromCharCode(parseInt(bits, 2));
155
- bits = '';
156
- }
157
- }
158
- return text;
159
- } catch (e) {
160
- return null;
161
- }
162
- }
163
- }
164
-
165
- // =========================================================================
166
- // DIFFERENTIAL PRIVACY FOR AGENT MEMORY
167
- // =========================================================================
168
-
169
- class DifferentialPrivacy {
170
- /**
171
- * Adds noise to stored conversation data so individual user data
172
- * can't be extracted even if the memory store is compromised.
173
- *
174
- * @param {object} [options]
175
- * @param {number} [options.epsilon=1.0] - Privacy budget. Lower = more private, noisier.
176
- * @param {number} [options.redactProbability=0.1] - Probability of redacting a token.
177
- */
178
- constructor(options = {}) {
179
- this.epsilon = options.epsilon || 1.0;
180
- this.redactProbability = options.redactProbability || 0.1;
181
- }
182
-
183
- /**
184
- * Sanitizes text for storage by adding noise.
185
- *
186
- * @param {string} text - Text to sanitize.
187
- * @returns {object} { sanitized: string, tokensRedacted: number }
188
- */
189
- sanitize(text) {
190
- if (!text) return { sanitized: text, tokensRedacted: 0 };
191
-
192
- const words = text.split(/\s+/);
193
- let tokensRedacted = 0;
194
-
195
- const sanitized = words.map(word => {
196
- // Higher epsilon = less noise (more utility)
197
- const threshold = this.redactProbability / this.epsilon;
198
-
199
- if (Math.random() < threshold) {
200
- tokensRedacted++;
201
- return '[REDACTED]';
202
- }
203
-
204
- // For numbers, add Laplacian noise
205
- if (/^\d+\.?\d*$/.test(word)) {
206
- const num = parseFloat(word);
207
- const noise = this._laplacianNoise(1 / this.epsilon);
208
- const noisy = Math.round((num + noise) * 100) / 100;
209
- return String(noisy);
210
- }
211
-
212
- return word;
213
- });
214
-
215
- return {
216
- sanitized: sanitized.join(' '),
217
- tokensRedacted
218
- };
219
- }
220
-
221
- /**
222
- * Generates Laplacian noise for numeric privacy.
223
- * @private
224
- * @param {number} scale - Scale parameter (b = sensitivity/epsilon).
225
- * @returns {number}
226
- */
227
- _laplacianNoise(scale) {
228
- // Use crypto for proper randomness instead of Math.random()
229
- const bytes = crypto.randomBytes(4);
230
- const u = (bytes.readUInt32BE(0) / 0xFFFFFFFF) - 0.5;
231
- return -scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u));
232
- }
233
- }
234
-
235
- module.exports = { OutputWatermark, DifferentialPrivacy };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Output Watermarking (#44) and Differential Privacy (#46)
5
+ *
6
+ * - Watermarking: Embed invisible watermarks in agent outputs for tracing.
7
+ * - Differential Privacy: Add noise to stored conversation data.
8
+ */
9
+
10
+ const crypto = require('crypto');
11
+
12
+ // =========================================================================
13
+ // OUTPUT WATERMARKING
14
+ // =========================================================================
15
+
16
+ /**
17
+ * Zero-width characters used for binary watermark encoding.
18
+ */
19
+ const WM_ZERO = '\u200B'; // zero-width space = 0
20
+ const WM_ONE = '\u200C'; // zero-width non-joiner = 1
21
+ const WM_START = '\u200D'; // zero-width joiner = start marker
22
+ const WM_END = '\uFEFF'; // byte order mark = end marker
23
+
24
+ class OutputWatermark {
25
+ /**
26
+ * @param {object} [options]
27
+ * @param {string} [options.secret] - Secret key for HMAC signing.
28
+ * @param {boolean} [options.includeTimestamp=true] - Include timestamp in watermark.
29
+ */
30
+ constructor(options = {}) {
31
+ this.secret = options.secret || crypto.randomBytes(16).toString('hex');
32
+ this.includeTimestamp = options.includeTimestamp !== undefined ? options.includeTimestamp : true;
33
+ }
34
+
35
+ /**
36
+ * Embeds an invisible watermark in text.
37
+ *
38
+ * @param {string} text - The text to watermark.
39
+ * @param {object} metadata - Data to encode in the watermark.
40
+ * @param {string} [metadata.agentId] - Agent identifier.
41
+ * @param {string} [metadata.sessionId] - Session identifier.
42
+ * @returns {string} Watermarked text.
43
+ */
44
+ embed(text, metadata = {}) {
45
+ if (!text) return text;
46
+
47
+ const payload = {
48
+ ...metadata,
49
+ ts: this.includeTimestamp ? Date.now() : undefined
50
+ };
51
+
52
+ // Create signed payload
53
+ const payloadStr = JSON.stringify(payload);
54
+ const signature = crypto.createHmac('sha256', this.secret)
55
+ .update(payloadStr)
56
+ .digest('hex')
57
+ .substring(0, 8);
58
+
59
+ const data = `${payloadStr}|${signature}`;
60
+
61
+ // Encode to binary using zero-width characters
62
+ const binary = this._textToBinary(data);
63
+ const watermark = WM_START + binary + WM_END;
64
+
65
+ // Insert watermark in the middle of the text to be less detectable
66
+ const midpoint = Math.floor(text.length / 2);
67
+ // Find a space near the midpoint
68
+ let insertAt = text.indexOf(' ', midpoint);
69
+ if (insertAt === -1) insertAt = midpoint;
70
+
71
+ return text.slice(0, insertAt) + watermark + text.slice(insertAt);
72
+ }
73
+
74
+ /**
75
+ * Extracts a watermark from text.
76
+ *
77
+ * @param {string} text - Text that may contain a watermark.
78
+ * @returns {object} { found: boolean, metadata?: object, verified: boolean }
79
+ */
80
+ extract(text) {
81
+ if (!text) return { found: false };
82
+
83
+ const startIdx = text.indexOf(WM_START);
84
+ const endIdx = text.indexOf(WM_END, startIdx);
85
+
86
+ if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
87
+ return { found: false };
88
+ }
89
+
90
+ const binary = text.slice(startIdx + 1, endIdx);
91
+ const data = this._binaryToText(binary);
92
+
93
+ if (!data) return { found: false };
94
+
95
+ const pipeIdx = data.lastIndexOf('|');
96
+ if (pipeIdx === -1) return { found: false };
97
+
98
+ const payloadStr = data.substring(0, pipeIdx);
99
+ const signature = data.substring(pipeIdx + 1);
100
+
101
+ // Verify signature
102
+ const expectedSig = crypto.createHmac('sha256', this.secret)
103
+ .update(payloadStr)
104
+ .digest('hex')
105
+ .substring(0, 8);
106
+
107
+ let verified = false;
108
+ try {
109
+ verified = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSig));
110
+ } catch (e) {
111
+ verified = false;
112
+ }
113
+
114
+ let metadata = null;
115
+ try {
116
+ metadata = JSON.parse(payloadStr);
117
+ } catch (e) {
118
+ return { found: true, verified: false, raw: payloadStr };
119
+ }
120
+
121
+ return { found: true, metadata, verified };
122
+ }
123
+
124
+ /**
125
+ * Removes watermark from text.
126
+ *
127
+ * @param {string} text
128
+ * @returns {string} Clean text.
129
+ */
130
+ strip(text) {
131
+ if (!text) return text;
132
+ return text.replace(/[\u200B\u200C\u200D\uFEFF]/g, '');
133
+ }
134
+
135
+ /** @private */
136
+ _textToBinary(text) {
137
+ let binary = '';
138
+ for (let i = 0; i < text.length; i++) {
139
+ const charCode = text.charCodeAt(i);
140
+ const bits = charCode.toString(2).padStart(16, '0');
141
+ for (const bit of bits) {
142
+ binary += bit === '0' ? WM_ZERO : WM_ONE;
143
+ }
144
+ }
145
+ return binary;
146
+ }
147
+
148
+ /** @private */
149
+ _binaryToText(binary) {
150
+ try {
151
+ let text = '';
152
+ let bits = '';
153
+ for (const char of binary) {
154
+ if (char === WM_ZERO) bits += '0';
155
+ else if (char === WM_ONE) bits += '1';
156
+ else continue;
157
+
158
+ if (bits.length === 16) {
159
+ text += String.fromCharCode(parseInt(bits, 2));
160
+ bits = '';
161
+ }
162
+ }
163
+ return text;
164
+ } catch (e) {
165
+ return null;
166
+ }
167
+ }
168
+ }
169
+
170
+ // =========================================================================
171
+ // DIFFERENTIAL PRIVACY FOR AGENT MEMORY
172
+ // =========================================================================
173
+
174
+ class DifferentialPrivacy {
175
+ /**
176
+ * Adds noise to stored conversation data so individual user data
177
+ * can't be extracted even if the memory store is compromised.
178
+ *
179
+ * @param {object} [options]
180
+ * @param {number} [options.epsilon=1.0] - Privacy budget. Lower = more private, noisier.
181
+ * @param {number} [options.redactProbability=0.1] - Probability of redacting a token.
182
+ */
183
+ constructor(options = {}) {
184
+ this.epsilon = options.epsilon || 1.0;
185
+ this.redactProbability = options.redactProbability || 0.1;
186
+ }
187
+
188
+ /**
189
+ * Sanitizes text for storage by adding noise.
190
+ *
191
+ * @param {string} text - Text to sanitize.
192
+ * @returns {object} { sanitized: string, tokensRedacted: number }
193
+ */
194
+ sanitize(text) {
195
+ if (!text) return { sanitized: text, tokensRedacted: 0 };
196
+
197
+ const words = text.split(/\s+/);
198
+ let tokensRedacted = 0;
199
+
200
+ const sanitized = words.map(word => {
201
+ // Higher epsilon = less noise (more utility)
202
+ const threshold = this.redactProbability / this.epsilon;
203
+
204
+ if (Math.random() < threshold) {
205
+ tokensRedacted++;
206
+ return '[REDACTED]';
207
+ }
208
+
209
+ // For numbers, add Laplacian noise
210
+ if (/^\d+\.?\d*$/.test(word)) {
211
+ const num = parseFloat(word);
212
+ const noise = this._laplacianNoise(1 / this.epsilon);
213
+ const noisy = Math.round((num + noise) * 100) / 100;
214
+ return String(noisy);
215
+ }
216
+
217
+ return word;
218
+ });
219
+
220
+ return {
221
+ sanitized: sanitized.join(' '),
222
+ tokensRedacted
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Generates Laplacian noise for numeric privacy.
228
+ * @private
229
+ * @param {number} scale - Scale parameter (b = sensitivity/epsilon).
230
+ * @returns {number}
231
+ */
232
+ _laplacianNoise(scale) {
233
+ // Use crypto for proper randomness instead of Math.random()
234
+ const bytes = crypto.randomBytes(4);
235
+ const u = (bytes.readUInt32BE(0) / 0xFFFFFFFF) - 0.5;
236
+ // Clamp to avoid Math.log(0) which produces -Infinity
237
+ const absU = Math.min(Math.abs(u), 0.4999999);
238
+ return -scale * Math.sign(u) * Math.log(1 - 2 * absU);
239
+ }
240
+ }
241
+
242
+ module.exports = { OutputWatermark, DifferentialPrivacy };