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
@@ -0,0 +1,324 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Confidence Calibration & Tuning (v1.2)
5
+ *
6
+ * Learns from real-world feedback (true positives, false positives, false negatives)
7
+ * to tune detection confidence thresholds and reduce alert noise.
8
+ *
9
+ * All data stored locally — no external calls.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { scanText } = require('./detector-core');
15
+
16
+ // =========================================================================
17
+ // CONFIDENCE TUNER
18
+ // =========================================================================
19
+
20
+ /**
21
+ * Learns optimal confidence thresholds from labeled feedback data.
22
+ * Tracks performance metrics and suggests tuning adjustments.
23
+ */
24
+ class ConfidenceTuner {
25
+ /**
26
+ * @param {object} [options]
27
+ * @param {string} [options.dataDir] - Directory to persist tuning data.
28
+ * @param {number} [options.defaultThreshold=50] - Default confidence threshold (0-100).
29
+ * @param {number} [options.learningRate=0.05] - How quickly thresholds adapt.
30
+ * @param {number} [options.minSamples=10] - Minimum samples before adjusting thresholds.
31
+ */
32
+ constructor(options = {}) {
33
+ this.dataDir = options.dataDir || null;
34
+ this.defaultThreshold = options.defaultThreshold || 50;
35
+ this.learningRate = options.learningRate || 0.05;
36
+ this.minSamples = options.minSamples || 10;
37
+
38
+ // Per-category thresholds
39
+ this._thresholds = {};
40
+ this._feedback = [];
41
+ this._categoryStats = {};
42
+ this._globalStats = { tp: 0, fp: 0, fn: 0, tn: 0 };
43
+
44
+ // Load persisted data if available
45
+ if (this.dataDir) {
46
+ this._load();
47
+ }
48
+
49
+ console.log('[Agent Shield] ConfidenceTuner initialized (defaultThreshold: %d, learningRate: %s)', this.defaultThreshold, this.learningRate);
50
+ }
51
+
52
+ /**
53
+ * Record feedback for a scan result.
54
+ *
55
+ * @param {object} scanResult - The original scan result from scanText/AgentShield.
56
+ * @param {string} label - Ground truth: 'tp' (true positive), 'fp' (false positive), 'fn' (false negative), 'tn' (true negative).
57
+ * @param {string} [notes] - Optional notes about the feedback.
58
+ */
59
+ recordFeedback(scanResult, label, notes = '') {
60
+ const entry = {
61
+ timestamp: Date.now(),
62
+ label,
63
+ status: scanResult.status,
64
+ threatCount: scanResult.threats ? scanResult.threats.length : 0,
65
+ categories: scanResult.threats ? scanResult.threats.map(t => t.category) : [],
66
+ confidences: scanResult.threats ? scanResult.threats.map(t => t.confidence || 0) : [],
67
+ notes
68
+ };
69
+
70
+ this._feedback.push(entry);
71
+ this._globalStats[label] = (this._globalStats[label] || 0) + 1;
72
+
73
+ // Update per-category stats
74
+ for (const category of entry.categories) {
75
+ if (!this._categoryStats[category]) {
76
+ this._categoryStats[category] = { tp: 0, fp: 0, fn: 0, tn: 0, samples: 0, avgConfidence: 0, totalConfidence: 0 };
77
+ }
78
+ this._categoryStats[category][label]++;
79
+ this._categoryStats[category].samples++;
80
+ }
81
+
82
+ // If false positive, consider raising the threshold for those categories
83
+ if (label === 'fp') {
84
+ for (let i = 0; i < entry.categories.length; i++) {
85
+ this._adjustThreshold(entry.categories[i], entry.confidences[i], 'up');
86
+ }
87
+ }
88
+
89
+ // If false negative, consider lowering the threshold
90
+ if (label === 'fn' && entry.categories.length > 0) {
91
+ for (const cat of entry.categories) {
92
+ this._adjustThreshold(cat, 30, 'down');
93
+ }
94
+ }
95
+
96
+ // Persist
97
+ if (this.dataDir) {
98
+ this._save();
99
+ }
100
+
101
+ return entry;
102
+ }
103
+
104
+ /**
105
+ * Get the tuned confidence threshold for a category.
106
+ * @param {string} category
107
+ * @returns {number} Threshold (0-100).
108
+ */
109
+ getThreshold(category) {
110
+ return this._thresholds[category] || this.defaultThreshold;
111
+ }
112
+
113
+ /**
114
+ * Apply tuned thresholds to a scan result, filtering out low-confidence detections.
115
+ *
116
+ * @param {object} scanResult - Original scan result.
117
+ * @returns {object} Filtered scan result.
118
+ */
119
+ applyThresholds(scanResult) {
120
+ if (!scanResult.threats || scanResult.threats.length === 0) return scanResult;
121
+
122
+ const filtered = scanResult.threats.filter(t => {
123
+ const threshold = this.getThreshold(t.category);
124
+ return (t.confidence || 50) >= threshold;
125
+ });
126
+
127
+ const suppressed = scanResult.threats.length - filtered.length;
128
+
129
+ const stats = { totalThreats: filtered.length, critical: 0, high: 0, medium: 0, low: 0, scanTimeMs: scanResult.stats.scanTimeMs };
130
+ for (const t of filtered) {
131
+ stats[t.severity]++;
132
+ }
133
+
134
+ let status = 'safe';
135
+ if (stats.critical > 0) status = 'danger';
136
+ else if (stats.high > 0) status = 'warning';
137
+ else if (stats.medium > 0) status = 'caution';
138
+
139
+ return {
140
+ ...scanResult,
141
+ status,
142
+ threats: filtered,
143
+ stats,
144
+ tuning: {
145
+ applied: true,
146
+ suppressed,
147
+ thresholds: { ...this._thresholds }
148
+ }
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Scan text with tuned confidence thresholds applied.
154
+ *
155
+ * @param {string} text - Text to scan.
156
+ * @param {object} [options] - Options passed to scanText.
157
+ * @returns {object} Tuned scan result.
158
+ */
159
+ tunedScan(text, options = {}) {
160
+ const result = scanText(text, options);
161
+ return this.applyThresholds(result);
162
+ }
163
+
164
+ /**
165
+ * Get performance metrics.
166
+ * @returns {object} { precision, recall, f1, accuracy, perCategory }
167
+ */
168
+ getMetrics() {
169
+ const { tp, fp, fn, tn } = this._globalStats;
170
+ const total = tp + fp + fn + tn;
171
+
172
+ const precision = (tp + fp) > 0 ? tp / (tp + fp) : 0;
173
+ const recall = (tp + fn) > 0 ? tp / (tp + fn) : 0;
174
+ const f1 = (precision + recall) > 0 ? 2 * (precision * recall) / (precision + recall) : 0;
175
+ const accuracy = total > 0 ? (tp + tn) / total : 0;
176
+
177
+ const perCategory = {};
178
+ for (const [cat, stats] of Object.entries(this._categoryStats)) {
179
+ const catPrecision = (stats.tp + stats.fp) > 0 ? stats.tp / (stats.tp + stats.fp) : 0;
180
+ const catRecall = (stats.tp + stats.fn) > 0 ? stats.tp / (stats.tp + stats.fn) : 0;
181
+ perCategory[cat] = {
182
+ precision: Math.round(catPrecision * 100) / 100,
183
+ recall: Math.round(catRecall * 100) / 100,
184
+ samples: stats.samples,
185
+ threshold: this.getThreshold(cat)
186
+ };
187
+ }
188
+
189
+ return {
190
+ precision: Math.round(precision * 100) / 100,
191
+ recall: Math.round(recall * 100) / 100,
192
+ f1: Math.round(f1 * 100) / 100,
193
+ accuracy: Math.round(accuracy * 100) / 100,
194
+ totalFeedback: this._feedback.length,
195
+ globalStats: { ...this._globalStats },
196
+ perCategory,
197
+ thresholds: { ...this._thresholds }
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Get tuning recommendations based on collected feedback.
203
+ * @returns {Array<object>} List of recommendations.
204
+ */
205
+ getRecommendations() {
206
+ const recommendations = [];
207
+ const metrics = this.getMetrics();
208
+
209
+ if (metrics.totalFeedback < this.minSamples) {
210
+ recommendations.push({
211
+ type: 'info',
212
+ message: `Need at least ${this.minSamples} feedback samples to make recommendations. Current: ${metrics.totalFeedback}.`
213
+ });
214
+ return recommendations;
215
+ }
216
+
217
+ // High false positive rate
218
+ if (this._globalStats.fp > this._globalStats.tp * 0.3) {
219
+ recommendations.push({
220
+ type: 'warning',
221
+ message: 'High false positive rate detected. Consider raising confidence thresholds.',
222
+ action: 'Increase defaultThreshold or review per-category thresholds.'
223
+ });
224
+ }
225
+
226
+ // High false negative rate
227
+ if (this._globalStats.fn > this._globalStats.tp * 0.2) {
228
+ recommendations.push({
229
+ type: 'warning',
230
+ message: 'False negatives detected — some threats are being missed.',
231
+ action: 'Lower thresholds for affected categories or add custom detection patterns.'
232
+ });
233
+ }
234
+
235
+ // Per-category recommendations
236
+ for (const [cat, stats] of Object.entries(this._categoryStats)) {
237
+ if (stats.samples >= 5 && stats.fp > stats.tp) {
238
+ recommendations.push({
239
+ type: 'action',
240
+ message: `Category "${cat}" has more false positives than true positives.`,
241
+ action: `Consider raising threshold for "${cat}" (current: ${this.getThreshold(cat)}).`
242
+ });
243
+ }
244
+ }
245
+
246
+ if (recommendations.length === 0) {
247
+ recommendations.push({
248
+ type: 'success',
249
+ message: 'Detection thresholds appear well-calibrated based on feedback data.'
250
+ });
251
+ }
252
+
253
+ return recommendations;
254
+ }
255
+
256
+ /**
257
+ * Reset all tuning data.
258
+ */
259
+ reset() {
260
+ this._thresholds = {};
261
+ this._feedback = [];
262
+ this._categoryStats = {};
263
+ this._globalStats = { tp: 0, fp: 0, fn: 0, tn: 0 };
264
+ if (this.dataDir) this._save();
265
+ }
266
+
267
+ /** @private */
268
+ _adjustThreshold(category, confidence, direction) {
269
+ const current = this._thresholds[category] || this.defaultThreshold;
270
+ const catStats = this._categoryStats[category];
271
+
272
+ // Only adjust if we have enough samples
273
+ if (catStats && catStats.samples < this.minSamples) return;
274
+
275
+ if (direction === 'up') {
276
+ this._thresholds[category] = Math.min(95, current + this.learningRate * (confidence - current));
277
+ } else {
278
+ this._thresholds[category] = Math.max(10, current - this.learningRate * current);
279
+ }
280
+ }
281
+
282
+ /** @private */
283
+ _save() {
284
+ if (!this.dataDir) return;
285
+ try {
286
+ if (!fs.existsSync(this.dataDir)) {
287
+ fs.mkdirSync(this.dataDir, { recursive: true });
288
+ }
289
+ const data = {
290
+ thresholds: this._thresholds,
291
+ categoryStats: this._categoryStats,
292
+ globalStats: this._globalStats,
293
+ feedbackCount: this._feedback.length,
294
+ lastUpdated: Date.now()
295
+ };
296
+ fs.writeFileSync(path.join(this.dataDir, 'confidence-tuning.json'), JSON.stringify(data, null, 2));
297
+ } catch (e) {
298
+ console.warn('[Agent Shield] Failed to save tuning data:', e.message);
299
+ }
300
+ }
301
+
302
+ /** @private */
303
+ _load() {
304
+ if (!this.dataDir) return;
305
+ try {
306
+ const filePath = path.join(this.dataDir, 'confidence-tuning.json');
307
+ if (fs.existsSync(filePath)) {
308
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
309
+ this._thresholds = data.thresholds || {};
310
+ this._categoryStats = data.categoryStats || {};
311
+ this._globalStats = data.globalStats || { tp: 0, fp: 0, fn: 0, tn: 0 };
312
+ console.log('[Agent Shield] Loaded tuning data (%d category thresholds)', Object.keys(this._thresholds).length);
313
+ }
314
+ } catch (e) {
315
+ console.warn('[Agent Shield] Failed to load tuning data:', e.message);
316
+ }
317
+ }
318
+ }
319
+
320
+ // =========================================================================
321
+ // EXPORTS
322
+ // =========================================================================
323
+
324
+ module.exports = { ConfidenceTuner };