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
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Shield — Context-Aware Scoring (v1.2)
|
|
5
|
+
*
|
|
6
|
+
* Analyzes conversation history to detect multi-turn attacks, escalation
|
|
7
|
+
* patterns, and contextual threats that single-message scanning misses.
|
|
8
|
+
*
|
|
9
|
+
* All processing runs locally — no data ever leaves your environment.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { scanText } = require('./detector-core');
|
|
13
|
+
|
|
14
|
+
// =========================================================================
|
|
15
|
+
// ESCALATION PATTERNS
|
|
16
|
+
// =========================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Patterns that indicate multi-turn escalation when seen in sequence.
|
|
20
|
+
*/
|
|
21
|
+
const ESCALATION_SIGNALS = [
|
|
22
|
+
{ phase: 'rapport', patterns: [/how are you/i, /can you help/i, /i have a question/i, /you seem/i], weight: 0.1 },
|
|
23
|
+
{ phase: 'boundary_test', patterns: [/can you.*if i asked/i, /hypothetically/i, /what would happen/i, /in theory/i, /would you be able to/i], weight: 0.3 },
|
|
24
|
+
{ phase: 'authority_claim', patterns: [/i am.*(?:admin|developer|owner|creator)/i, /i have permission/i, /i'm authorized/i, /my boss said/i], weight: 0.5 },
|
|
25
|
+
{ phase: 'soft_override', patterns: [/just this once/i, /make an exception/i, /no one will know/i, /it's for testing/i, /it's okay to/i], weight: 0.6 },
|
|
26
|
+
{ phase: 'direct_attack', patterns: [/ignore.*instructions/i, /override.*safety/i, /you are now/i, /jailbreak/i, /forget.*training/i], weight: 1.0 }
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Suspicious topic pivots that may indicate an attack setup.
|
|
31
|
+
*/
|
|
32
|
+
const TOPIC_PIVOT_SIGNALS = [
|
|
33
|
+
{ from: /(?:weather|sports|cooking|travel|homework)/i, to: /(?:system|prompt|instructions|override|ignore|bypass)/i, severity: 'high' },
|
|
34
|
+
{ from: /(?:coding|programming|help|debug)/i, to: /(?:admin|root|password|secret|credential)/i, severity: 'medium' },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// =========================================================================
|
|
38
|
+
// CONVERSATION CONTEXT ANALYZER
|
|
39
|
+
// =========================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Maintains conversation state and provides context-aware threat scoring.
|
|
43
|
+
*/
|
|
44
|
+
class ConversationContextAnalyzer {
|
|
45
|
+
/**
|
|
46
|
+
* @param {object} [options]
|
|
47
|
+
* @param {number} [options.maxHistory=50] - Maximum messages to retain.
|
|
48
|
+
* @param {number} [options.escalationThreshold=0.6] - Threshold for flagging escalation.
|
|
49
|
+
* @param {number} [options.decayFactor=0.9] - Weight decay for older messages.
|
|
50
|
+
* @param {boolean} [options.trackTopics=true] - Enable topic pivot detection.
|
|
51
|
+
*/
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.maxHistory = options.maxHistory || 50;
|
|
54
|
+
this.escalationThreshold = options.escalationThreshold || 0.6;
|
|
55
|
+
this.decayFactor = options.decayFactor || 0.9;
|
|
56
|
+
this.trackTopics = options.trackTopics !== false;
|
|
57
|
+
|
|
58
|
+
this._history = [];
|
|
59
|
+
this._escalationScore = 0;
|
|
60
|
+
this._phasesDetected = new Set();
|
|
61
|
+
this._threatCount = 0;
|
|
62
|
+
this._sessionStart = Date.now();
|
|
63
|
+
this._topicHistory = [];
|
|
64
|
+
|
|
65
|
+
console.log('[Agent Shield] ConversationContextAnalyzer initialized (maxHistory: %d, threshold: %s)', this.maxHistory, this.escalationThreshold);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Add a message and analyze it in context.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} text - The message text.
|
|
72
|
+
* @param {string} [role='user'] - Message role: 'user', 'assistant', 'system'.
|
|
73
|
+
* @returns {object} Context-aware analysis result.
|
|
74
|
+
*/
|
|
75
|
+
addMessage(text, role = 'user') {
|
|
76
|
+
const timestamp = Date.now();
|
|
77
|
+
const patternResult = scanText(text, { source: `conversation_${role}`, sensitivity: 'high' });
|
|
78
|
+
|
|
79
|
+
const message = {
|
|
80
|
+
text: text.substring(0, 5000),
|
|
81
|
+
role,
|
|
82
|
+
timestamp,
|
|
83
|
+
threats: patternResult.threats,
|
|
84
|
+
status: patternResult.status
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
this._history.push(message);
|
|
88
|
+
|
|
89
|
+
// Trim history
|
|
90
|
+
if (this._history.length > this.maxHistory) {
|
|
91
|
+
this._history = this._history.slice(-this.maxHistory);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (patternResult.threats.length > 0) {
|
|
95
|
+
this._threatCount++;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Only analyze user messages for escalation
|
|
99
|
+
if (role !== 'user') {
|
|
100
|
+
return {
|
|
101
|
+
message,
|
|
102
|
+
escalation: this._getEscalationState(),
|
|
103
|
+
contextScore: this._calculateContextScore(message),
|
|
104
|
+
patternResult
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Detect escalation phase
|
|
109
|
+
this._updateEscalation(text);
|
|
110
|
+
|
|
111
|
+
// Detect topic pivots
|
|
112
|
+
const pivot = this._detectTopicPivot(text);
|
|
113
|
+
|
|
114
|
+
// Calculate context-aware score
|
|
115
|
+
const contextScore = this._calculateContextScore(message);
|
|
116
|
+
|
|
117
|
+
// Check for velocity anomalies (many messages in short time)
|
|
118
|
+
const velocity = this._checkVelocity();
|
|
119
|
+
|
|
120
|
+
// Check for repetition patterns (probing)
|
|
121
|
+
const repetition = this._checkRepetition(text);
|
|
122
|
+
|
|
123
|
+
const result = {
|
|
124
|
+
message,
|
|
125
|
+
escalation: this._getEscalationState(),
|
|
126
|
+
contextScore,
|
|
127
|
+
patternResult,
|
|
128
|
+
pivot,
|
|
129
|
+
velocity,
|
|
130
|
+
repetition,
|
|
131
|
+
recommendation: this._recommend(contextScore, patternResult)
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get the current escalation state.
|
|
139
|
+
* @returns {object} { score, phases, isEscalating, threatCount }
|
|
140
|
+
*/
|
|
141
|
+
_getEscalationState() {
|
|
142
|
+
return {
|
|
143
|
+
score: Math.round(this._escalationScore * 100) / 100,
|
|
144
|
+
phases: [...this._phasesDetected],
|
|
145
|
+
isEscalating: this._escalationScore >= this.escalationThreshold,
|
|
146
|
+
threatCount: this._threatCount,
|
|
147
|
+
messageCount: this._history.length
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Scan text with full conversation context applied.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} text - New message to scan.
|
|
155
|
+
* @returns {object} Enhanced scan result with context.
|
|
156
|
+
*/
|
|
157
|
+
contextualScan(text) {
|
|
158
|
+
const analysis = this.addMessage(text, 'user');
|
|
159
|
+
const { patternResult, contextScore, escalation, pivot } = analysis;
|
|
160
|
+
|
|
161
|
+
// Elevate severity based on context
|
|
162
|
+
const contextThreats = [...patternResult.threats];
|
|
163
|
+
|
|
164
|
+
if (escalation.isEscalating && patternResult.threats.length === 0) {
|
|
165
|
+
contextThreats.push({
|
|
166
|
+
severity: 'medium',
|
|
167
|
+
category: 'multi_turn_escalation',
|
|
168
|
+
description: 'This conversation shows a multi-turn escalation pattern consistent with social engineering.',
|
|
169
|
+
detail: `Escalation score: ${escalation.score.toFixed(2)}, phases detected: [${escalation.phases.join(', ')}]`,
|
|
170
|
+
confidence: Math.round(escalation.score * 100),
|
|
171
|
+
confidenceLabel: escalation.score >= 0.8 ? 'Very likely a threat' : 'Likely a threat'
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (pivot) {
|
|
176
|
+
contextThreats.push({
|
|
177
|
+
severity: pivot.severity,
|
|
178
|
+
category: 'topic_pivot',
|
|
179
|
+
description: 'Sudden topic shift from benign to security-sensitive subjects detected.',
|
|
180
|
+
detail: `Topic pivot from "${pivot.fromTopic}" to security-sensitive content.`,
|
|
181
|
+
confidence: 70,
|
|
182
|
+
confidenceLabel: 'Likely a threat'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const status = contextThreats.some(t => t.severity === 'critical') ? 'danger'
|
|
187
|
+
: contextThreats.some(t => t.severity === 'high') ? 'warning'
|
|
188
|
+
: contextThreats.length > 0 ? 'caution'
|
|
189
|
+
: 'safe';
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
status,
|
|
193
|
+
threats: contextThreats,
|
|
194
|
+
stats: {
|
|
195
|
+
totalThreats: contextThreats.length,
|
|
196
|
+
critical: contextThreats.filter(t => t.severity === 'critical').length,
|
|
197
|
+
high: contextThreats.filter(t => t.severity === 'high').length,
|
|
198
|
+
medium: contextThreats.filter(t => t.severity === 'medium').length,
|
|
199
|
+
low: contextThreats.filter(t => t.severity === 'low').length,
|
|
200
|
+
scanTimeMs: patternResult.stats.scanTimeMs
|
|
201
|
+
},
|
|
202
|
+
context: {
|
|
203
|
+
escalation,
|
|
204
|
+
contextScore,
|
|
205
|
+
messageCount: this._history.length,
|
|
206
|
+
sessionDurationMs: Date.now() - this._sessionStart
|
|
207
|
+
},
|
|
208
|
+
timestamp: Date.now()
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get conversation summary and threat statistics.
|
|
214
|
+
* @returns {object}
|
|
215
|
+
*/
|
|
216
|
+
getSummary() {
|
|
217
|
+
const userMessages = this._history.filter(m => m.role === 'user');
|
|
218
|
+
const threatMessages = userMessages.filter(m => m.threats.length > 0);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
totalMessages: this._history.length,
|
|
222
|
+
userMessages: userMessages.length,
|
|
223
|
+
threatMessages: threatMessages.length,
|
|
224
|
+
threatRate: userMessages.length > 0 ? threatMessages.length / userMessages.length : 0,
|
|
225
|
+
escalation: this._getEscalationState(),
|
|
226
|
+
sessionDurationMs: Date.now() - this._sessionStart,
|
|
227
|
+
phasesDetected: [...this._phasesDetected]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Reset the conversation context.
|
|
233
|
+
*/
|
|
234
|
+
reset() {
|
|
235
|
+
this._history = [];
|
|
236
|
+
this._escalationScore = 0;
|
|
237
|
+
this._phasesDetected.clear();
|
|
238
|
+
this._threatCount = 0;
|
|
239
|
+
this._sessionStart = Date.now();
|
|
240
|
+
this._topicHistory = [];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** @private */
|
|
244
|
+
_updateEscalation(text) {
|
|
245
|
+
for (const signal of ESCALATION_SIGNALS) {
|
|
246
|
+
for (const pattern of signal.patterns) {
|
|
247
|
+
if (pattern.test(text)) {
|
|
248
|
+
this._phasesDetected.add(signal.phase);
|
|
249
|
+
// Escalation score increases with phase weight, with decay
|
|
250
|
+
this._escalationScore = Math.max(this._escalationScore, signal.weight);
|
|
251
|
+
// Bonus for progressing through phases
|
|
252
|
+
if (this._phasesDetected.size > 1) {
|
|
253
|
+
this._escalationScore = Math.min(1, this._escalationScore + 0.1 * this._phasesDetected.size);
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** @private */
|
|
262
|
+
_detectTopicPivot(text) {
|
|
263
|
+
if (!this.trackTopics || this._history.length < 2) return null;
|
|
264
|
+
|
|
265
|
+
const recentUser = this._history
|
|
266
|
+
.filter(m => m.role === 'user')
|
|
267
|
+
.slice(-3);
|
|
268
|
+
|
|
269
|
+
if (recentUser.length < 2) return null;
|
|
270
|
+
|
|
271
|
+
const previousText = recentUser.slice(0, -1).map(m => m.text).join(' ');
|
|
272
|
+
|
|
273
|
+
for (const signal of TOPIC_PIVOT_SIGNALS) {
|
|
274
|
+
if (signal.from.test(previousText) && signal.to.test(text)) {
|
|
275
|
+
return {
|
|
276
|
+
severity: signal.severity,
|
|
277
|
+
fromTopic: previousText.substring(0, 50),
|
|
278
|
+
toTopic: text.substring(0, 50)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** @private */
|
|
287
|
+
_calculateContextScore(message) {
|
|
288
|
+
let score = 0;
|
|
289
|
+
|
|
290
|
+
// Base score from current message threats
|
|
291
|
+
if (message.threats.length > 0) score += 0.4;
|
|
292
|
+
|
|
293
|
+
// Escalation contribution
|
|
294
|
+
score += this._escalationScore * 0.3;
|
|
295
|
+
|
|
296
|
+
// Threat frequency in recent history
|
|
297
|
+
const recentThreats = this._history.slice(-10).filter(m => m.threats.length > 0).length;
|
|
298
|
+
score += (recentThreats / 10) * 0.2;
|
|
299
|
+
|
|
300
|
+
// Velocity factor
|
|
301
|
+
const recentMessages = this._history.filter(m => (Date.now() - m.timestamp) < 60000);
|
|
302
|
+
if (recentMessages.length > 10) score += 0.1;
|
|
303
|
+
|
|
304
|
+
return Math.min(1, Math.round(score * 100) / 100);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** @private */
|
|
308
|
+
_checkVelocity() {
|
|
309
|
+
const oneMinuteAgo = Date.now() - 60000;
|
|
310
|
+
const recentCount = this._history.filter(m => m.timestamp > oneMinuteAgo && m.role === 'user').length;
|
|
311
|
+
return {
|
|
312
|
+
messagesPerMinute: recentCount,
|
|
313
|
+
isAnomalous: recentCount > 15
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** @private */
|
|
318
|
+
_checkRepetition(text) {
|
|
319
|
+
const recent = this._history.slice(-10).filter(m => m.role === 'user');
|
|
320
|
+
const lowerText = text.toLowerCase().substring(0, 200);
|
|
321
|
+
let similarCount = 0;
|
|
322
|
+
|
|
323
|
+
for (const msg of recent) {
|
|
324
|
+
const lowerMsg = msg.text.toLowerCase().substring(0, 200);
|
|
325
|
+
if (lowerMsg === lowerText) {
|
|
326
|
+
similarCount++;
|
|
327
|
+
} else {
|
|
328
|
+
// Simple word overlap check
|
|
329
|
+
const wordsA = new Set(lowerText.split(/\s+/));
|
|
330
|
+
const wordsB = new Set(lowerMsg.split(/\s+/));
|
|
331
|
+
const intersection = [...wordsA].filter(w => wordsB.has(w)).length;
|
|
332
|
+
const union = new Set([...wordsA, ...wordsB]).size;
|
|
333
|
+
if (union > 0 && intersection / union > 0.7) similarCount++;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
similarMessages: similarCount,
|
|
339
|
+
isProbing: similarCount >= 3
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** @private */
|
|
344
|
+
_recommend(contextScore, patternResult) {
|
|
345
|
+
if (contextScore >= 0.8 || patternResult.stats.critical > 0) return 'block';
|
|
346
|
+
if (contextScore >= 0.5 || patternResult.stats.high > 0) return 'review';
|
|
347
|
+
if (contextScore >= 0.3 || patternResult.threats.length > 0) return 'monitor';
|
|
348
|
+
return 'allow';
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// =========================================================================
|
|
353
|
+
// EXPORTS
|
|
354
|
+
// =========================================================================
|
|
355
|
+
|
|
356
|
+
module.exports = {
|
|
357
|
+
ConversationContextAnalyzer,
|
|
358
|
+
ESCALATION_SIGNALS,
|
|
359
|
+
TOPIC_PIVOT_SIGNALS
|
|
360
|
+
};
|