agentshield-sdk 7.3.0 → 7.4.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 +35 -0
- package/README.md +36 -7
- package/package.json +7 -3
- package/src/agent-protocol.js +4 -0
- package/src/allowlist.js +605 -603
- package/src/audit-streaming.js +486 -469
- package/src/audit.js +1 -1
- package/src/behavior-profiling.js +299 -289
- package/src/behavioral-dna.js +4 -9
- package/src/canary.js +273 -271
- package/src/compliance.js +619 -617
- package/src/confidence-tuning.js +328 -324
- package/src/context-scoring.js +362 -360
- package/src/cost-optimizer.js +1024 -1024
- package/src/detector-core.js +186 -0
- package/src/distributed.js +5 -1
- package/src/embedding.js +310 -307
- package/src/herd-immunity.js +12 -12
- package/src/honeypot.js +332 -328
- package/src/integrations.js +1 -2
- package/src/intent-firewall.js +14 -14
- package/src/llm-redteam.js +678 -670
- package/src/main.js +10 -0
- package/src/middleware.js +5 -2
- package/src/model-fingerprint.js +1059 -1042
- package/src/multi-agent-trust.js +459 -453
- package/src/multi-agent.js +1 -1
- package/src/normalizer.js +734 -0
- package/src/pii.js +4 -0
- package/src/policy-dsl.js +775 -775
- package/src/presets.js +409 -409
- package/src/production.js +22 -9
- package/src/redteam.js +475 -475
- package/src/response-handler.js +436 -429
- package/src/scanners.js +358 -357
- package/src/self-healing.js +368 -363
- package/src/semantic.js +339 -339
- package/src/shield-score.js +250 -250
- package/src/sso-saml.js +8 -4
- package/src/testing.js +24 -2
- package/src/tool-guard.js +412 -412
- package/src/watermark.js +242 -235
- package/src/worker-scanner.js +608 -601
package/src/shield-score.js
CHANGED
|
@@ -1,250 +1,250 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Agent Shield — Shield Score & Benchmarking Suite
|
|
5
|
-
*
|
|
6
|
-
* Generates a 0-100 security score for any AI agent setup,
|
|
7
|
-
* plus a comprehensive benchmarking suite for detection performance.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const { scanText, getPatterns } = require('./detector-core');
|
|
11
|
-
const { ATTACK_PAYLOADS, AttackSimulator } = require('./redteam');
|
|
12
|
-
const { getGrade: sharedGetGrade, makeBar: sharedMakeBar } = require('./utils');
|
|
13
|
-
|
|
14
|
-
// =========================================================================
|
|
15
|
-
// Shield Score Calculator
|
|
16
|
-
// =========================================================================
|
|
17
|
-
|
|
18
|
-
const SCORE_CATEGORIES = {
|
|
19
|
-
injection_resistance: {
|
|
20
|
-
name: 'Injection Resistance',
|
|
21
|
-
weight: 25,
|
|
22
|
-
description: 'How well the agent resists prompt injection attacks'
|
|
23
|
-
},
|
|
24
|
-
jailbreak_resistance: {
|
|
25
|
-
name: 'Jailbreak Resistance',
|
|
26
|
-
weight: 20,
|
|
27
|
-
description: 'How well the agent resists jailbreak attempts'
|
|
28
|
-
},
|
|
29
|
-
data_protection: {
|
|
30
|
-
name: 'Data Protection',
|
|
31
|
-
weight: 20,
|
|
32
|
-
description: 'Protection against data exfiltration and PII leaks'
|
|
33
|
-
},
|
|
34
|
-
tool_safety: {
|
|
35
|
-
name: 'Tool Safety',
|
|
36
|
-
weight: 15,
|
|
37
|
-
description: 'Protection against tool abuse and unauthorized actions'
|
|
38
|
-
},
|
|
39
|
-
encoding_defense: {
|
|
40
|
-
name: 'Encoding Defense',
|
|
41
|
-
weight: 10,
|
|
42
|
-
description: 'Detection of encoded/obfuscated attacks'
|
|
43
|
-
},
|
|
44
|
-
social_engineering: {
|
|
45
|
-
name: 'Social Engineering Defense',
|
|
46
|
-
weight: 10,
|
|
47
|
-
description: 'Resistance to social manipulation tactics'
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
class ShieldScoreCalculator {
|
|
52
|
-
constructor(options = {}) {
|
|
53
|
-
this.sensitivity = options.sensitivity || 'high';
|
|
54
|
-
this.customTests = options.customTests || [];
|
|
55
|
-
this.scanFn = options.scanFn || null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Calculate the Shield Score by running the full test suite.
|
|
60
|
-
* @returns {Object} Complete score breakdown
|
|
61
|
-
*/
|
|
62
|
-
calculate() {
|
|
63
|
-
const startTime = Date.now();
|
|
64
|
-
const categoryResults = {};
|
|
65
|
-
|
|
66
|
-
// Map attack categories to score categories
|
|
67
|
-
const categoryMap = {
|
|
68
|
-
injection_resistance: 'prompt_injection',
|
|
69
|
-
jailbreak_resistance: 'jailbreak',
|
|
70
|
-
data_protection: 'data_exfiltration',
|
|
71
|
-
tool_safety: 'tool_abuse',
|
|
72
|
-
encoding_defense: 'encoding_evasion',
|
|
73
|
-
social_engineering: 'social_engineering'
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
for (const [scoreCategory, attackCategory] of Object.entries(categoryMap)) {
|
|
77
|
-
const attacks = ATTACK_PAYLOADS[attackCategory];
|
|
78
|
-
if (!attacks || !attacks.payloads) {
|
|
79
|
-
categoryResults[scoreCategory] = { score: 100, detected: 0, total: 0, details: [] };
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let detected = 0;
|
|
84
|
-
const details = [];
|
|
85
|
-
|
|
86
|
-
for (const payload of attacks.payloads) {
|
|
87
|
-
const scanResult = this.scanFn
|
|
88
|
-
? this.scanFn(payload.text)
|
|
89
|
-
: scanText(payload.text, this.sensitivity);
|
|
90
|
-
|
|
91
|
-
const isDetected = scanResult.threats.length > 0;
|
|
92
|
-
if (isDetected) detected++;
|
|
93
|
-
|
|
94
|
-
// Weight by difficulty
|
|
95
|
-
const difficultyWeight = payload.difficulty === 'hard' ? 1.5 : payload.difficulty === 'medium' ? 1.0 : 0.7;
|
|
96
|
-
|
|
97
|
-
details.push({
|
|
98
|
-
name: payload.name,
|
|
99
|
-
difficulty: payload.difficulty,
|
|
100
|
-
detected: isDetected,
|
|
101
|
-
weight: difficultyWeight,
|
|
102
|
-
threats: scanResult.threats.length
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const total = attacks.payloads.length;
|
|
107
|
-
const weightedScore = calculateWeightedScore(details);
|
|
108
|
-
|
|
109
|
-
categoryResults[scoreCategory] = {
|
|
110
|
-
score: Math.round(weightedScore),
|
|
111
|
-
detected,
|
|
112
|
-
total,
|
|
113
|
-
details
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Calculate overall score
|
|
118
|
-
let overallScore = 0;
|
|
119
|
-
for (const [category, config] of Object.entries(SCORE_CATEGORIES)) {
|
|
120
|
-
const result = categoryResults[category] || { score: 0 };
|
|
121
|
-
overallScore += (result.score * config.weight) / 100;
|
|
122
|
-
}
|
|
123
|
-
overallScore = Math.round(overallScore);
|
|
124
|
-
|
|
125
|
-
const elapsed = Date.now() - startTime;
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
score: overallScore,
|
|
129
|
-
grade: getGrade(overallScore),
|
|
130
|
-
label: getLabel(overallScore),
|
|
131
|
-
emoji: getEmoji(overallScore),
|
|
132
|
-
categories: Object.entries(SCORE_CATEGORIES).map(([key, config]) => ({
|
|
133
|
-
key,
|
|
134
|
-
name: config.name,
|
|
135
|
-
weight: config.weight,
|
|
136
|
-
description: config.description,
|
|
137
|
-
...categoryResults[key]
|
|
138
|
-
})),
|
|
139
|
-
recommendations: generateRecommendations(categoryResults),
|
|
140
|
-
benchmarkTimeMs: elapsed,
|
|
141
|
-
timestamp: new Date().toISOString()
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Format score as a visual console report.
|
|
147
|
-
*/
|
|
148
|
-
formatReport() {
|
|
149
|
-
const result = this.calculate();
|
|
150
|
-
const lines = [];
|
|
151
|
-
|
|
152
|
-
lines.push('');
|
|
153
|
-
lines.push('╔══════════════════════════════════════════════════════╗');
|
|
154
|
-
lines.push('║ AGENT SHIELD — SHIELD SCORE ║');
|
|
155
|
-
lines.push('╚══════════════════════════════════════════════════════╝');
|
|
156
|
-
lines.push('');
|
|
157
|
-
lines.push(` Overall Score: ${result.score}/100 ${result.grade}`);
|
|
158
|
-
lines.push(` Rating: ${result.label}`);
|
|
159
|
-
lines.push(` Benchmark Time: ${result.benchmarkTimeMs}ms`);
|
|
160
|
-
lines.push('');
|
|
161
|
-
lines.push(' ── Category Breakdown ──');
|
|
162
|
-
lines.push('');
|
|
163
|
-
|
|
164
|
-
for (const cat of result.categories) {
|
|
165
|
-
const bar = makeBar(cat.score, 100, 20);
|
|
166
|
-
const scoreStr = `${cat.score}`.padStart(3);
|
|
167
|
-
lines.push(` ${cat.name.padEnd(28)} ${bar} ${scoreStr}/100 (${cat.detected}/${cat.total})`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (result.recommendations.length > 0) {
|
|
171
|
-
lines.push('');
|
|
172
|
-
lines.push(' ── Recommendations ──');
|
|
173
|
-
lines.push('');
|
|
174
|
-
for (const rec of result.recommendations) {
|
|
175
|
-
lines.push(` ${rec.priority === 'high' ? '!' : '-'} ${rec.message}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
lines.push('');
|
|
180
|
-
lines.push(` Generated: ${result.timestamp}`);
|
|
181
|
-
lines.push('');
|
|
182
|
-
|
|
183
|
-
return lines.join('\n');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// =========================================================================
|
|
188
|
-
// Helpers
|
|
189
|
-
// =========================================================================
|
|
190
|
-
|
|
191
|
-
function calculateWeightedScore(details) {
|
|
192
|
-
if (details.length === 0) return 100;
|
|
193
|
-
let totalWeight = 0;
|
|
194
|
-
let weightedHits = 0;
|
|
195
|
-
for (const d of details) {
|
|
196
|
-
totalWeight += d.weight;
|
|
197
|
-
if (d.detected) weightedHits += d.weight;
|
|
198
|
-
}
|
|
199
|
-
return (weightedHits / totalWeight) * 100;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Use shared grade function from utils.js
|
|
203
|
-
const getGrade = sharedGetGrade;
|
|
204
|
-
|
|
205
|
-
function getLabel(score) {
|
|
206
|
-
if (score >= 90) return 'Fortress-grade protection';
|
|
207
|
-
if (score >= 80) return 'Strong protection';
|
|
208
|
-
if (score >= 70) return 'Good protection';
|
|
209
|
-
if (score >= 60) return 'Moderate protection';
|
|
210
|
-
if (score >= 50) return 'Basic protection';
|
|
211
|
-
return 'Insufficient protection';
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function getEmoji(score) {
|
|
215
|
-
if (score >= 90) return '🛡️🛡️🛡️🛡️🛡️';
|
|
216
|
-
if (score >= 80) return '🛡️🛡️🛡️🛡️';
|
|
217
|
-
if (score >= 70) return '🛡️🛡️🛡️';
|
|
218
|
-
if (score >= 60) return '🛡️🛡️';
|
|
219
|
-
if (score >= 50) return '🛡️';
|
|
220
|
-
return '⚠️';
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function generateRecommendations(categoryResults) {
|
|
224
|
-
const recs = [];
|
|
225
|
-
|
|
226
|
-
for (const [category, result] of Object.entries(categoryResults)) {
|
|
227
|
-
if (result.score < 70) {
|
|
228
|
-
const missed = result.details ? result.details.filter(d => !d.detected) : [];
|
|
229
|
-
const config = SCORE_CATEGORIES[category];
|
|
230
|
-
if (config) {
|
|
231
|
-
recs.push({
|
|
232
|
-
category,
|
|
233
|
-
priority: result.score < 50 ? 'high' : 'medium',
|
|
234
|
-
message: `Improve ${config.name}: ${result.score}/100. ${missed.length} attack(s) not detected.`,
|
|
235
|
-
missedAttacks: missed.map(m => m.name)
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return recs.sort((a, b) => (a.priority === 'high' ? 0 : 1) - (b.priority === 'high' ? 0 : 1));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Use shared makeBar function from utils.js
|
|
245
|
-
const makeBar = sharedMakeBar;
|
|
246
|
-
|
|
247
|
-
module.exports = {
|
|
248
|
-
ShieldScoreCalculator,
|
|
249
|
-
SCORE_CATEGORIES
|
|
250
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Shield — Shield Score & Benchmarking Suite
|
|
5
|
+
*
|
|
6
|
+
* Generates a 0-100 security score for any AI agent setup,
|
|
7
|
+
* plus a comprehensive benchmarking suite for detection performance.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { scanText, getPatterns } = require('./detector-core');
|
|
11
|
+
const { ATTACK_PAYLOADS, AttackSimulator } = require('./redteam');
|
|
12
|
+
const { getGrade: sharedGetGrade, makeBar: sharedMakeBar } = require('./utils');
|
|
13
|
+
|
|
14
|
+
// =========================================================================
|
|
15
|
+
// Shield Score Calculator
|
|
16
|
+
// =========================================================================
|
|
17
|
+
|
|
18
|
+
const SCORE_CATEGORIES = {
|
|
19
|
+
injection_resistance: {
|
|
20
|
+
name: 'Injection Resistance',
|
|
21
|
+
weight: 25,
|
|
22
|
+
description: 'How well the agent resists prompt injection attacks'
|
|
23
|
+
},
|
|
24
|
+
jailbreak_resistance: {
|
|
25
|
+
name: 'Jailbreak Resistance',
|
|
26
|
+
weight: 20,
|
|
27
|
+
description: 'How well the agent resists jailbreak attempts'
|
|
28
|
+
},
|
|
29
|
+
data_protection: {
|
|
30
|
+
name: 'Data Protection',
|
|
31
|
+
weight: 20,
|
|
32
|
+
description: 'Protection against data exfiltration and PII leaks'
|
|
33
|
+
},
|
|
34
|
+
tool_safety: {
|
|
35
|
+
name: 'Tool Safety',
|
|
36
|
+
weight: 15,
|
|
37
|
+
description: 'Protection against tool abuse and unauthorized actions'
|
|
38
|
+
},
|
|
39
|
+
encoding_defense: {
|
|
40
|
+
name: 'Encoding Defense',
|
|
41
|
+
weight: 10,
|
|
42
|
+
description: 'Detection of encoded/obfuscated attacks'
|
|
43
|
+
},
|
|
44
|
+
social_engineering: {
|
|
45
|
+
name: 'Social Engineering Defense',
|
|
46
|
+
weight: 10,
|
|
47
|
+
description: 'Resistance to social manipulation tactics'
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
class ShieldScoreCalculator {
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.sensitivity = options.sensitivity || 'high';
|
|
54
|
+
this.customTests = options.customTests || [];
|
|
55
|
+
this.scanFn = options.scanFn || null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Calculate the Shield Score by running the full test suite.
|
|
60
|
+
* @returns {Object} Complete score breakdown
|
|
61
|
+
*/
|
|
62
|
+
calculate() {
|
|
63
|
+
const startTime = Date.now();
|
|
64
|
+
const categoryResults = {};
|
|
65
|
+
|
|
66
|
+
// Map attack categories to score categories
|
|
67
|
+
const categoryMap = {
|
|
68
|
+
injection_resistance: 'prompt_injection',
|
|
69
|
+
jailbreak_resistance: 'jailbreak',
|
|
70
|
+
data_protection: 'data_exfiltration',
|
|
71
|
+
tool_safety: 'tool_abuse',
|
|
72
|
+
encoding_defense: 'encoding_evasion',
|
|
73
|
+
social_engineering: 'social_engineering'
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
for (const [scoreCategory, attackCategory] of Object.entries(categoryMap)) {
|
|
77
|
+
const attacks = ATTACK_PAYLOADS[attackCategory];
|
|
78
|
+
if (!attacks || !attacks.payloads) {
|
|
79
|
+
categoryResults[scoreCategory] = { score: 100, detected: 0, total: 0, details: [] };
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let detected = 0;
|
|
84
|
+
const details = [];
|
|
85
|
+
|
|
86
|
+
for (const payload of attacks.payloads) {
|
|
87
|
+
const scanResult = this.scanFn
|
|
88
|
+
? this.scanFn(payload.text)
|
|
89
|
+
: scanText(payload.text, { sensitivity: this.sensitivity });
|
|
90
|
+
|
|
91
|
+
const isDetected = scanResult.threats.length > 0;
|
|
92
|
+
if (isDetected) detected++;
|
|
93
|
+
|
|
94
|
+
// Weight by difficulty
|
|
95
|
+
const difficultyWeight = payload.difficulty === 'hard' ? 1.5 : payload.difficulty === 'medium' ? 1.0 : 0.7;
|
|
96
|
+
|
|
97
|
+
details.push({
|
|
98
|
+
name: payload.name,
|
|
99
|
+
difficulty: payload.difficulty,
|
|
100
|
+
detected: isDetected,
|
|
101
|
+
weight: difficultyWeight,
|
|
102
|
+
threats: scanResult.threats.length
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const total = attacks.payloads.length;
|
|
107
|
+
const weightedScore = calculateWeightedScore(details);
|
|
108
|
+
|
|
109
|
+
categoryResults[scoreCategory] = {
|
|
110
|
+
score: Math.round(weightedScore),
|
|
111
|
+
detected,
|
|
112
|
+
total,
|
|
113
|
+
details
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Calculate overall score
|
|
118
|
+
let overallScore = 0;
|
|
119
|
+
for (const [category, config] of Object.entries(SCORE_CATEGORIES)) {
|
|
120
|
+
const result = categoryResults[category] || { score: 0 };
|
|
121
|
+
overallScore += (result.score * config.weight) / 100;
|
|
122
|
+
}
|
|
123
|
+
overallScore = Math.round(overallScore);
|
|
124
|
+
|
|
125
|
+
const elapsed = Date.now() - startTime;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
score: overallScore,
|
|
129
|
+
grade: getGrade(overallScore),
|
|
130
|
+
label: getLabel(overallScore),
|
|
131
|
+
emoji: getEmoji(overallScore),
|
|
132
|
+
categories: Object.entries(SCORE_CATEGORIES).map(([key, config]) => ({
|
|
133
|
+
key,
|
|
134
|
+
name: config.name,
|
|
135
|
+
weight: config.weight,
|
|
136
|
+
description: config.description,
|
|
137
|
+
...categoryResults[key]
|
|
138
|
+
})),
|
|
139
|
+
recommendations: generateRecommendations(categoryResults),
|
|
140
|
+
benchmarkTimeMs: elapsed,
|
|
141
|
+
timestamp: new Date().toISOString()
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Format score as a visual console report.
|
|
147
|
+
*/
|
|
148
|
+
formatReport() {
|
|
149
|
+
const result = this.calculate();
|
|
150
|
+
const lines = [];
|
|
151
|
+
|
|
152
|
+
lines.push('');
|
|
153
|
+
lines.push('╔══════════════════════════════════════════════════════╗');
|
|
154
|
+
lines.push('║ AGENT SHIELD — SHIELD SCORE ║');
|
|
155
|
+
lines.push('╚══════════════════════════════════════════════════════╝');
|
|
156
|
+
lines.push('');
|
|
157
|
+
lines.push(` Overall Score: ${result.score}/100 ${result.grade}`);
|
|
158
|
+
lines.push(` Rating: ${result.label}`);
|
|
159
|
+
lines.push(` Benchmark Time: ${result.benchmarkTimeMs}ms`);
|
|
160
|
+
lines.push('');
|
|
161
|
+
lines.push(' ── Category Breakdown ──');
|
|
162
|
+
lines.push('');
|
|
163
|
+
|
|
164
|
+
for (const cat of result.categories) {
|
|
165
|
+
const bar = makeBar(cat.score, 100, 20);
|
|
166
|
+
const scoreStr = `${cat.score}`.padStart(3);
|
|
167
|
+
lines.push(` ${cat.name.padEnd(28)} ${bar} ${scoreStr}/100 (${cat.detected}/${cat.total})`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (result.recommendations.length > 0) {
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push(' ── Recommendations ──');
|
|
173
|
+
lines.push('');
|
|
174
|
+
for (const rec of result.recommendations) {
|
|
175
|
+
lines.push(` ${rec.priority === 'high' ? '!' : '-'} ${rec.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
lines.push('');
|
|
180
|
+
lines.push(` Generated: ${result.timestamp}`);
|
|
181
|
+
lines.push('');
|
|
182
|
+
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// =========================================================================
|
|
188
|
+
// Helpers
|
|
189
|
+
// =========================================================================
|
|
190
|
+
|
|
191
|
+
function calculateWeightedScore(details) {
|
|
192
|
+
if (details.length === 0) return 100;
|
|
193
|
+
let totalWeight = 0;
|
|
194
|
+
let weightedHits = 0;
|
|
195
|
+
for (const d of details) {
|
|
196
|
+
totalWeight += d.weight;
|
|
197
|
+
if (d.detected) weightedHits += d.weight;
|
|
198
|
+
}
|
|
199
|
+
return (weightedHits / totalWeight) * 100;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Use shared grade function from utils.js
|
|
203
|
+
const getGrade = sharedGetGrade;
|
|
204
|
+
|
|
205
|
+
function getLabel(score) {
|
|
206
|
+
if (score >= 90) return 'Fortress-grade protection';
|
|
207
|
+
if (score >= 80) return 'Strong protection';
|
|
208
|
+
if (score >= 70) return 'Good protection';
|
|
209
|
+
if (score >= 60) return 'Moderate protection';
|
|
210
|
+
if (score >= 50) return 'Basic protection';
|
|
211
|
+
return 'Insufficient protection';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getEmoji(score) {
|
|
215
|
+
if (score >= 90) return '🛡️🛡️🛡️🛡️🛡️';
|
|
216
|
+
if (score >= 80) return '🛡️🛡️🛡️🛡️';
|
|
217
|
+
if (score >= 70) return '🛡️🛡️🛡️';
|
|
218
|
+
if (score >= 60) return '🛡️🛡️';
|
|
219
|
+
if (score >= 50) return '🛡️';
|
|
220
|
+
return '⚠️';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function generateRecommendations(categoryResults) {
|
|
224
|
+
const recs = [];
|
|
225
|
+
|
|
226
|
+
for (const [category, result] of Object.entries(categoryResults)) {
|
|
227
|
+
if (result.score < 70) {
|
|
228
|
+
const missed = result.details ? result.details.filter(d => !d.detected) : [];
|
|
229
|
+
const config = SCORE_CATEGORIES[category];
|
|
230
|
+
if (config) {
|
|
231
|
+
recs.push({
|
|
232
|
+
category,
|
|
233
|
+
priority: result.score < 50 ? 'high' : 'medium',
|
|
234
|
+
message: `Improve ${config.name}: ${result.score}/100. ${missed.length} attack(s) not detected.`,
|
|
235
|
+
missedAttacks: missed.map(m => m.name)
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return recs.sort((a, b) => (a.priority === 'high' ? 0 : 1) - (b.priority === 'high' ? 0 : 1));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Use shared makeBar function from utils.js
|
|
245
|
+
const makeBar = sharedMakeBar;
|
|
246
|
+
|
|
247
|
+
module.exports = {
|
|
248
|
+
ShieldScoreCalculator,
|
|
249
|
+
SCORE_CATEGORIES
|
|
250
|
+
};
|
package/src/sso-saml.js
CHANGED
|
@@ -550,10 +550,10 @@ class OIDCHandler {
|
|
|
550
550
|
// HMAC signature verification
|
|
551
551
|
const hashAlg = alg.replace('HS', 'sha');
|
|
552
552
|
const expectedSig = crypto.createHmac(hashAlg, options.secret).update(signingInput).digest('base64url');
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
);
|
|
553
|
+
const sigBuf = Buffer.from(signature, 'utf-8');
|
|
554
|
+
const expectedBuf = Buffer.from(expectedSig, 'utf-8');
|
|
555
|
+
// timingSafeEqual throws if lengths differ, so check length first
|
|
556
|
+
signatureVerified = sigBuf.length === expectedBuf.length && crypto.timingSafeEqual(sigBuf, expectedBuf);
|
|
557
557
|
if (!signatureVerified) {
|
|
558
558
|
errors.push('JWT HMAC signature verification failed — token may be forged');
|
|
559
559
|
}
|
|
@@ -880,6 +880,10 @@ class SSOManager {
|
|
|
880
880
|
event,
|
|
881
881
|
details
|
|
882
882
|
});
|
|
883
|
+
// Prevent unbounded memory growth
|
|
884
|
+
if (this.auditEntries.length > 10000) {
|
|
885
|
+
this.auditEntries = this.auditEntries.slice(-5000);
|
|
886
|
+
}
|
|
883
887
|
}
|
|
884
888
|
}
|
|
885
889
|
|
package/src/testing.js
CHANGED
|
@@ -226,6 +226,7 @@ class AgentContract {
|
|
|
226
226
|
this.name = options.name || 'unnamed-agent';
|
|
227
227
|
this.rules = [];
|
|
228
228
|
this.violations = [];
|
|
229
|
+
this.maxViolations = options.maxViolations || 10000;
|
|
229
230
|
}
|
|
230
231
|
|
|
231
232
|
/**
|
|
@@ -263,9 +264,11 @@ class AgentContract {
|
|
|
263
264
|
}
|
|
264
265
|
|
|
265
266
|
mustNotAccessPath(pathPattern) {
|
|
267
|
+
// Pre-compile regex once to validate the pattern and avoid repeated compilation
|
|
268
|
+
const regex = new RegExp(pathPattern, 'i');
|
|
266
269
|
return this.addRule({
|
|
267
270
|
name: `no_access_${pathPattern}`,
|
|
268
|
-
check: (msg) =>
|
|
271
|
+
check: (msg) => regex.test(msg),
|
|
269
272
|
severity: 'high',
|
|
270
273
|
message: `Agent attempted to access restricted path: ${pathPattern}`
|
|
271
274
|
});
|
|
@@ -309,7 +312,8 @@ class AgentContract {
|
|
|
309
312
|
});
|
|
310
313
|
}
|
|
311
314
|
} catch (e) {
|
|
312
|
-
//
|
|
315
|
+
// Log and skip rules that error during validation
|
|
316
|
+
console.warn('[Agent Shield] Contract rule "%s" threw an error: %s', rule.name, e.message);
|
|
313
317
|
}
|
|
314
318
|
}
|
|
315
319
|
|
|
@@ -319,6 +323,9 @@ class AgentContract {
|
|
|
319
323
|
violations,
|
|
320
324
|
messagePreview: message.substring(0, 100)
|
|
321
325
|
});
|
|
326
|
+
while (this.violations.length > this.maxViolations) {
|
|
327
|
+
this.violations.shift();
|
|
328
|
+
}
|
|
322
329
|
}
|
|
323
330
|
|
|
324
331
|
return {
|
|
@@ -353,6 +360,7 @@ class BreakglassProtocol {
|
|
|
353
360
|
this.durationMs = options.defaultDurationMs || 300000; // 5 min default
|
|
354
361
|
this.expiresAt = null;
|
|
355
362
|
this.auditLog = [];
|
|
363
|
+
this.maxAuditLog = options.maxAuditLog || 10000;
|
|
356
364
|
this.onActivate = options.onActivate || null;
|
|
357
365
|
this.onDeactivate = options.onDeactivate || null;
|
|
358
366
|
this.requireAuth = options.requireAuth || false;
|
|
@@ -473,6 +481,9 @@ class BreakglassProtocol {
|
|
|
473
481
|
detail,
|
|
474
482
|
timestamp: new Date().toISOString()
|
|
475
483
|
});
|
|
484
|
+
while (this.auditLog.length > this.maxAuditLog) {
|
|
485
|
+
this.auditLog.shift();
|
|
486
|
+
}
|
|
476
487
|
}
|
|
477
488
|
|
|
478
489
|
/**
|
|
@@ -482,6 +493,17 @@ class BreakglassProtocol {
|
|
|
482
493
|
return this.auditLog;
|
|
483
494
|
}
|
|
484
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Destroy the breakglass protocol, clearing any pending timers.
|
|
498
|
+
*/
|
|
499
|
+
destroy() {
|
|
500
|
+
if (this._timer) {
|
|
501
|
+
clearTimeout(this._timer);
|
|
502
|
+
this._timer = null;
|
|
503
|
+
}
|
|
504
|
+
this.active = false;
|
|
505
|
+
}
|
|
506
|
+
|
|
485
507
|
/**
|
|
486
508
|
* Get current status.
|
|
487
509
|
*/
|