@bryan-thompson/inspector-assessment-client 1.25.4 → 1.25.6
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/dist/assets/{OAuthCallback-DE62cdTZ.js → OAuthCallback-D6y8tFfF.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-CWjFdCIE.js → OAuthDebugCallback-DHegnqTa.js} +1 -1
- package/dist/assets/{index-PCQVSwHa.js → index-Cu02Ah3g.js} +4 -4
- package/dist/assets/{index-Df9Sx1jt.css → index-cHhcEXbr.css} +4 -0
- package/dist/index.html +2 -2
- package/lib/lib/assessment/coreTypes.d.ts +65 -0
- package/lib/lib/assessment/coreTypes.d.ts.map +1 -1
- package/lib/lib/assessment/extendedTypes.d.ts +127 -0
- package/lib/lib/assessment/extendedTypes.d.ts.map +1 -1
- package/lib/lib/assessment/resultTypes.d.ts +45 -0
- package/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/lib/lib/moduleScoring.d.ts +2 -2
- package/lib/lib/moduleScoring.d.ts.map +1 -1
- package/lib/lib/moduleScoring.js +3 -2
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +3 -7
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +13 -2
- package/lib/services/assessment/TestDataGenerator.d.ts +9 -1
- package/lib/services/assessment/TestDataGenerator.d.ts.map +1 -1
- package/lib/services/assessment/TestDataGenerator.js +32 -6
- package/lib/services/assessment/TestScenarioEngine.d.ts +9 -1
- package/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -1
- package/lib/services/assessment/TestScenarioEngine.js +17 -14
- package/lib/services/assessment/config/annotationPatterns.d.ts +3 -1
- package/lib/services/assessment/config/annotationPatterns.d.ts.map +1 -1
- package/lib/services/assessment/config/annotationPatterns.js +5 -2
- package/lib/services/assessment/config/architecturePatterns.d.ts +101 -0
- package/lib/services/assessment/config/architecturePatterns.d.ts.map +1 -0
- package/lib/services/assessment/config/architecturePatterns.js +248 -0
- package/lib/services/assessment/config/performanceConfig.d.ts +122 -0
- package/lib/services/assessment/config/performanceConfig.d.ts.map +1 -0
- package/lib/services/assessment/config/performanceConfig.js +154 -0
- package/lib/services/assessment/config/sanitizationPatterns.d.ts +63 -0
- package/lib/services/assessment/config/sanitizationPatterns.d.ts.map +1 -0
- package/lib/services/assessment/config/sanitizationPatterns.js +223 -0
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts +40 -3
- package/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -1
- package/lib/services/assessment/lib/claudeCodeBridge.js +149 -8
- package/lib/services/assessment/lib/concurrencyLimit.d.ts +6 -2
- package/lib/services/assessment/lib/concurrencyLimit.d.ts.map +1 -1
- package/lib/services/assessment/lib/concurrencyLimit.js +13 -6
- package/lib/services/assessment/lib/errors.d.ts +90 -0
- package/lib/services/assessment/lib/errors.d.ts.map +1 -0
- package/lib/services/assessment/lib/errors.js +136 -0
- package/lib/services/assessment/lib/timeoutUtils.d.ts +69 -0
- package/lib/services/assessment/lib/timeoutUtils.d.ts.map +1 -0
- package/lib/services/assessment/lib/timeoutUtils.js +103 -0
- package/lib/services/assessment/modules/BaseAssessor.d.ts +43 -8
- package/lib/services/assessment/modules/BaseAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/BaseAssessor.js +103 -34
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts +38 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/DeveloperExperienceAssessor.js +185 -19
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts +5 -0
- package/lib/services/assessment/modules/DocumentationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/DocumentationAssessor.js +11 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.js +1 -1
- package/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/FunctionalityAssessor.js +6 -3
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +3 -0
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +14 -2
- package/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ManifestValidationAssessor.js +7 -2
- package/lib/services/assessment/modules/PromptAssessor.d.ts +1 -0
- package/lib/services/assessment/modules/PromptAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/PromptAssessor.js +26 -16
- package/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ProtocolComplianceAssessor.js +6 -2
- package/lib/services/assessment/modules/ProtocolConformanceAssessor.d.ts +5 -0
- package/lib/services/assessment/modules/ProtocolConformanceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ProtocolConformanceAssessor.js +15 -0
- package/lib/services/assessment/modules/ResourceAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ResourceAssessor.js +8 -2
- package/lib/services/assessment/modules/SecurityAssessor.d.ts +3 -171
- package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/SecurityAssessor.js +25 -1480
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +27 -28
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ToolAnnotationAssessor.js +340 -863
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts +5 -0
- package/lib/services/assessment/modules/UsabilityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/UsabilityAssessor.js +11 -0
- package/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts +57 -0
- package/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.js +176 -0
- package/lib/services/assessment/modules/annotations/ArchitectureDetector.d.ts +67 -0
- package/lib/services/assessment/modules/annotations/ArchitectureDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/ArchitectureDetector.js +239 -0
- package/lib/services/assessment/modules/annotations/BehaviorInference.d.ts +46 -0
- package/lib/services/assessment/modules/annotations/BehaviorInference.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/BehaviorInference.js +394 -0
- package/lib/services/assessment/modules/annotations/DescriptionAnalyzer.d.ts +64 -0
- package/lib/services/assessment/modules/annotations/DescriptionAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/DescriptionAnalyzer.js +304 -0
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts +43 -0
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +276 -0
- package/lib/services/assessment/modules/annotations/SchemaAnalyzer.d.ts +122 -0
- package/lib/services/assessment/modules/annotations/SchemaAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/SchemaAnalyzer.js +388 -0
- package/lib/services/assessment/modules/annotations/index.d.ts +13 -0
- package/lib/services/assessment/modules/annotations/index.d.ts.map +1 -0
- package/lib/services/assessment/modules/annotations/index.js +15 -0
- package/lib/services/assessment/modules/index.d.ts +10 -0
- package/lib/services/assessment/modules/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/index.js +13 -0
- package/lib/services/assessment/modules/securityTests/SanitizationDetector.d.ts +125 -0
- package/lib/services/assessment/modules/securityTests/SanitizationDetector.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SanitizationDetector.js +345 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.d.ts +33 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadGenerator.js +128 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +67 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +372 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +178 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +1207 -0
- package/lib/services/assessment/modules/securityTests/index.d.ts +8 -0
- package/lib/services/assessment/modules/securityTests/index.d.ts.map +1 -0
- package/lib/services/assessment/modules/securityTests/index.js +7 -0
- package/lib/services/assessment/tool-classifier-patterns.d.ts +1 -0
- package/lib/services/assessment/tool-classifier-patterns.d.ts.map +1 -1
- package/lib/services/assessment/tool-classifier-patterns.js +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Description Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes tool descriptions for behavioral keywords to infer expected behavior.
|
|
5
|
+
* This provides a more robust inference than name-pattern matching alone.
|
|
6
|
+
*
|
|
7
|
+
* Part of Issue #57: Architecture detection and behavior inference modules
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Keyword categories with confidence levels.
|
|
11
|
+
* Keywords in 'high' have strong semantic association with the behavior.
|
|
12
|
+
* Keywords in 'medium' are indicative but may have context-dependent meanings.
|
|
13
|
+
* Keywords in 'low' are weak indicators.
|
|
14
|
+
*/
|
|
15
|
+
export const DESCRIPTION_BEHAVIOR_KEYWORDS = {
|
|
16
|
+
readOnly: {
|
|
17
|
+
high: [
|
|
18
|
+
"retrieves",
|
|
19
|
+
"returns",
|
|
20
|
+
"lists",
|
|
21
|
+
"shows",
|
|
22
|
+
"displays",
|
|
23
|
+
"queries",
|
|
24
|
+
"searches",
|
|
25
|
+
"finds",
|
|
26
|
+
"looks up",
|
|
27
|
+
"fetches",
|
|
28
|
+
],
|
|
29
|
+
medium: [
|
|
30
|
+
"gets",
|
|
31
|
+
"reads",
|
|
32
|
+
"views",
|
|
33
|
+
"checks",
|
|
34
|
+
"verifies",
|
|
35
|
+
"validates",
|
|
36
|
+
"inspects",
|
|
37
|
+
"examines",
|
|
38
|
+
"browses",
|
|
39
|
+
"previews",
|
|
40
|
+
],
|
|
41
|
+
low: [
|
|
42
|
+
"accesses",
|
|
43
|
+
"obtains",
|
|
44
|
+
"provides",
|
|
45
|
+
"outputs",
|
|
46
|
+
"prints",
|
|
47
|
+
"counts",
|
|
48
|
+
"measures",
|
|
49
|
+
"calculates",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
destructive: {
|
|
53
|
+
high: [
|
|
54
|
+
"deletes",
|
|
55
|
+
"removes",
|
|
56
|
+
"destroys",
|
|
57
|
+
"drops",
|
|
58
|
+
"purges",
|
|
59
|
+
"wipes",
|
|
60
|
+
"clears",
|
|
61
|
+
"erases",
|
|
62
|
+
"permanently",
|
|
63
|
+
"irreversible",
|
|
64
|
+
"archives", // soft-delete euphemism
|
|
65
|
+
"terminated", // forceful process ending
|
|
66
|
+
],
|
|
67
|
+
medium: [
|
|
68
|
+
"truncates",
|
|
69
|
+
"kills",
|
|
70
|
+
"terminates",
|
|
71
|
+
"revokes",
|
|
72
|
+
"cancels",
|
|
73
|
+
"uninstalls",
|
|
74
|
+
"dismounts",
|
|
75
|
+
"detaches",
|
|
76
|
+
"marks", // when combined with deleted/archived
|
|
77
|
+
],
|
|
78
|
+
low: ["resets", "restores to default", "cleans", "cleanup"],
|
|
79
|
+
},
|
|
80
|
+
write: {
|
|
81
|
+
high: [
|
|
82
|
+
"creates",
|
|
83
|
+
"inserts",
|
|
84
|
+
"adds",
|
|
85
|
+
"generates",
|
|
86
|
+
"produces",
|
|
87
|
+
"makes",
|
|
88
|
+
"builds",
|
|
89
|
+
],
|
|
90
|
+
medium: [
|
|
91
|
+
"updates",
|
|
92
|
+
"modifies",
|
|
93
|
+
"changes",
|
|
94
|
+
"edits",
|
|
95
|
+
"sets",
|
|
96
|
+
"puts",
|
|
97
|
+
"patches",
|
|
98
|
+
"appends",
|
|
99
|
+
"extends",
|
|
100
|
+
"increments", // modify operation
|
|
101
|
+
"decrements", // modify operation
|
|
102
|
+
],
|
|
103
|
+
low: [
|
|
104
|
+
"saves",
|
|
105
|
+
"stores",
|
|
106
|
+
"writes",
|
|
107
|
+
"posts",
|
|
108
|
+
"sends",
|
|
109
|
+
"submits",
|
|
110
|
+
"publishes",
|
|
111
|
+
"uploads",
|
|
112
|
+
"exports",
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Negation patterns that might invert the meaning of keywords.
|
|
118
|
+
* E.g., "does not delete" should not be marked as destructive.
|
|
119
|
+
*/
|
|
120
|
+
const NEGATION_PATTERNS = [
|
|
121
|
+
/\b(does\s+not|doesn't|do\s+not|don't|cannot|can't|will\s+not|won't|never|without)\s+/i,
|
|
122
|
+
/\bnot\s+(delete|remove|destroy|modify|change|create|update)/i,
|
|
123
|
+
];
|
|
124
|
+
/**
|
|
125
|
+
* Threshold for write signals to override read-only classification.
|
|
126
|
+
* Write signals at 50%+ of read-only score indicate mixed operation tools
|
|
127
|
+
* (e.g., "fetch and update" should classify as write, not read-only).
|
|
128
|
+
*/
|
|
129
|
+
const WRITE_OVERRIDE_THRESHOLD = 0.5;
|
|
130
|
+
/**
|
|
131
|
+
* Check if a keyword match is negated by surrounding context.
|
|
132
|
+
*
|
|
133
|
+
* @param description - Full description text
|
|
134
|
+
* @param keywordIndex - Index where keyword was found
|
|
135
|
+
* @param windowSize - Characters before keyword to check for negation
|
|
136
|
+
* @returns True if the keyword is negated
|
|
137
|
+
*/
|
|
138
|
+
function isNegated(description, keywordIndex, windowSize = 60) {
|
|
139
|
+
const start = Math.max(0, keywordIndex - windowSize);
|
|
140
|
+
const contextBefore = description.slice(start, keywordIndex);
|
|
141
|
+
for (const pattern of NEGATION_PATTERNS) {
|
|
142
|
+
if (pattern.test(contextBefore)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Find keyword matches in description with confidence levels.
|
|
150
|
+
*
|
|
151
|
+
* @param description - Tool description to analyze
|
|
152
|
+
* @param keywords - Keyword object with high/medium/low arrays
|
|
153
|
+
* @returns Array of matches with confidence scores
|
|
154
|
+
*/
|
|
155
|
+
function findKeywordMatches(description, keywords) {
|
|
156
|
+
const matches = [];
|
|
157
|
+
const lowerDesc = description.toLowerCase();
|
|
158
|
+
const searchKeywords = (keywordList, confidence) => {
|
|
159
|
+
for (const keyword of keywordList) {
|
|
160
|
+
// Create a regex pattern that matches the keyword as a word
|
|
161
|
+
const pattern = new RegExp(`\\b${keyword.replace(/\s+/g, "\\s+")}`, "gi");
|
|
162
|
+
let match;
|
|
163
|
+
while ((match = pattern.exec(lowerDesc)) !== null) {
|
|
164
|
+
const negated = isNegated(lowerDesc, match.index);
|
|
165
|
+
matches.push({ keyword, confidence, negated });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
searchKeywords(keywords.high, 90);
|
|
170
|
+
searchKeywords(keywords.medium, 70);
|
|
171
|
+
searchKeywords(keywords.low, 50);
|
|
172
|
+
return matches;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Analyze a tool description for behavioral signals.
|
|
176
|
+
*
|
|
177
|
+
* @param description - Tool description to analyze
|
|
178
|
+
* @returns InferenceSignal with read-only/destructive expectations
|
|
179
|
+
*/
|
|
180
|
+
export function analyzeDescription(description) {
|
|
181
|
+
if (!description || description.trim().length === 0) {
|
|
182
|
+
return {
|
|
183
|
+
expectedReadOnly: false,
|
|
184
|
+
expectedDestructive: false,
|
|
185
|
+
confidence: 0,
|
|
186
|
+
evidence: ["No description provided"],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// Find all keyword matches for each category
|
|
190
|
+
const readOnlyMatches = findKeywordMatches(description, DESCRIPTION_BEHAVIOR_KEYWORDS.readOnly);
|
|
191
|
+
const destructiveMatches = findKeywordMatches(description, DESCRIPTION_BEHAVIOR_KEYWORDS.destructive);
|
|
192
|
+
const writeMatches = findKeywordMatches(description, DESCRIPTION_BEHAVIOR_KEYWORDS.write);
|
|
193
|
+
// Filter out negated matches for the primary behavior classification
|
|
194
|
+
const activeReadOnly = readOnlyMatches.filter((m) => !m.negated);
|
|
195
|
+
const activeDestructive = destructiveMatches.filter((m) => !m.negated);
|
|
196
|
+
const activeWrite = writeMatches.filter((m) => !m.negated);
|
|
197
|
+
// Calculate weighted scores for each category
|
|
198
|
+
const readOnlyScore = activeReadOnly.reduce((sum, m) => sum + m.confidence, 0);
|
|
199
|
+
const destructiveScore = activeDestructive.reduce((sum, m) => sum + m.confidence, 0);
|
|
200
|
+
const writeScore = activeWrite.reduce((sum, m) => sum + m.confidence, 0);
|
|
201
|
+
// Determine the dominant behavior
|
|
202
|
+
const evidence = [];
|
|
203
|
+
let expectedReadOnly = false;
|
|
204
|
+
let expectedDestructive = false;
|
|
205
|
+
let confidence = 0;
|
|
206
|
+
// Destructive takes priority if detected with high confidence
|
|
207
|
+
if (destructiveScore > 0) {
|
|
208
|
+
expectedDestructive = true;
|
|
209
|
+
confidence = Math.min(100, destructiveScore);
|
|
210
|
+
evidence.push(`Destructive keywords: ${activeDestructive.map((m) => m.keyword).join(", ")}`);
|
|
211
|
+
}
|
|
212
|
+
// Read-only detection (only if not destructive)
|
|
213
|
+
if (readOnlyScore > 0 && !expectedDestructive) {
|
|
214
|
+
// Write operations take precedence when present (even if equal score)
|
|
215
|
+
// Multi-operation tools like "fetch and update" should be classified as write
|
|
216
|
+
if (writeScore > 0 &&
|
|
217
|
+
writeScore >= readOnlyScore * WRITE_OVERRIDE_THRESHOLD) {
|
|
218
|
+
// Write signal is significant enough to override read-only
|
|
219
|
+
confidence = Math.min(100, writeScore);
|
|
220
|
+
evidence.push(`Write keywords override read: ${activeWrite.map((m) => m.keyword).join(", ")}`);
|
|
221
|
+
}
|
|
222
|
+
else if (readOnlyScore > writeScore) {
|
|
223
|
+
expectedReadOnly = true;
|
|
224
|
+
confidence = Math.min(100, readOnlyScore);
|
|
225
|
+
evidence.push(`Read-only keywords: ${activeReadOnly.map((m) => m.keyword).join(", ")}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Pure write operation (no read-only indicators)
|
|
229
|
+
if (!expectedReadOnly && !expectedDestructive && writeScore > 0) {
|
|
230
|
+
confidence = Math.min(100, writeScore);
|
|
231
|
+
evidence.push(`Write keywords: ${activeWrite.map((m) => m.keyword).join(", ")}`);
|
|
232
|
+
}
|
|
233
|
+
// Add negation evidence if present
|
|
234
|
+
const negatedKeywords = [
|
|
235
|
+
...readOnlyMatches.filter((m) => m.negated),
|
|
236
|
+
...destructiveMatches.filter((m) => m.negated),
|
|
237
|
+
...writeMatches.filter((m) => m.negated),
|
|
238
|
+
];
|
|
239
|
+
if (negatedKeywords.length > 0) {
|
|
240
|
+
evidence.push(`Negated keywords ignored: ${negatedKeywords.map((m) => m.keyword).join(", ")}`);
|
|
241
|
+
}
|
|
242
|
+
// Default case: no signals
|
|
243
|
+
if (evidence.length === 0) {
|
|
244
|
+
evidence.push("No behavioral keywords detected in description");
|
|
245
|
+
confidence = 0;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
expectedReadOnly,
|
|
249
|
+
expectedDestructive,
|
|
250
|
+
confidence,
|
|
251
|
+
evidence,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Quick check if description contains read-only indicators.
|
|
256
|
+
* Useful for fast filtering before full analysis.
|
|
257
|
+
*
|
|
258
|
+
* @param description - Tool description to check
|
|
259
|
+
* @returns True if any read-only keywords are present
|
|
260
|
+
*/
|
|
261
|
+
export function hasReadOnlyIndicators(description) {
|
|
262
|
+
if (!description)
|
|
263
|
+
return false;
|
|
264
|
+
const lowerDesc = description.toLowerCase();
|
|
265
|
+
const allReadOnlyKeywords = [
|
|
266
|
+
...DESCRIPTION_BEHAVIOR_KEYWORDS.readOnly.high,
|
|
267
|
+
...DESCRIPTION_BEHAVIOR_KEYWORDS.readOnly.medium,
|
|
268
|
+
];
|
|
269
|
+
return allReadOnlyKeywords.some((keyword) => lowerDesc.includes(keyword.toLowerCase()));
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Quick check if description contains destructive indicators.
|
|
273
|
+
* Useful for fast filtering before full analysis.
|
|
274
|
+
*
|
|
275
|
+
* @param description - Tool description to check
|
|
276
|
+
* @returns True if any destructive keywords are present
|
|
277
|
+
*/
|
|
278
|
+
export function hasDestructiveIndicators(description) {
|
|
279
|
+
if (!description)
|
|
280
|
+
return false;
|
|
281
|
+
const lowerDesc = description.toLowerCase();
|
|
282
|
+
const allDestructiveKeywords = [
|
|
283
|
+
...DESCRIPTION_BEHAVIOR_KEYWORDS.destructive.high,
|
|
284
|
+
...DESCRIPTION_BEHAVIOR_KEYWORDS.destructive.medium,
|
|
285
|
+
];
|
|
286
|
+
return allDestructiveKeywords.some((keyword) => lowerDesc.includes(keyword.toLowerCase()));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Quick check if description contains write indicators.
|
|
290
|
+
* Useful for fast filtering before full analysis.
|
|
291
|
+
*
|
|
292
|
+
* @param description - Tool description to check
|
|
293
|
+
* @returns True if any write keywords are present
|
|
294
|
+
*/
|
|
295
|
+
export function hasWriteIndicators(description) {
|
|
296
|
+
if (!description)
|
|
297
|
+
return false;
|
|
298
|
+
const lowerDesc = description.toLowerCase();
|
|
299
|
+
const allWriteKeywords = [
|
|
300
|
+
...DESCRIPTION_BEHAVIOR_KEYWORDS.write.high,
|
|
301
|
+
...DESCRIPTION_BEHAVIOR_KEYWORDS.write.medium,
|
|
302
|
+
];
|
|
303
|
+
return allWriteKeywords.some((keyword) => lowerDesc.includes(keyword.toLowerCase()));
|
|
304
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Description Poisoning Detector
|
|
3
|
+
* Detects hidden instructions and malicious content in tool descriptions
|
|
4
|
+
*
|
|
5
|
+
* Extracted from ToolAnnotationAssessor.ts for maintainability.
|
|
6
|
+
* Issue #8 implementation.
|
|
7
|
+
*/
|
|
8
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Tool description poisoning pattern definition
|
|
11
|
+
*/
|
|
12
|
+
export interface PoisoningPattern {
|
|
13
|
+
name: string;
|
|
14
|
+
pattern: RegExp;
|
|
15
|
+
severity: "LOW" | "MEDIUM" | "HIGH";
|
|
16
|
+
category: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Result of description poisoning scan
|
|
20
|
+
*/
|
|
21
|
+
export interface PoisoningScanResult {
|
|
22
|
+
detected: boolean;
|
|
23
|
+
patterns: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
pattern: string;
|
|
26
|
+
severity: "LOW" | "MEDIUM" | "HIGH";
|
|
27
|
+
category: string;
|
|
28
|
+
evidence: string;
|
|
29
|
+
}>;
|
|
30
|
+
riskLevel: "NONE" | "LOW" | "MEDIUM" | "HIGH";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Description poisoning patterns for detecting malicious tool descriptions
|
|
34
|
+
* Covers: hidden instructions, override commands, concealment, exfiltration,
|
|
35
|
+
* delimiter injection, encoding bypass, and typoglycemia/evasion patterns
|
|
36
|
+
*/
|
|
37
|
+
export declare const DESCRIPTION_POISONING_PATTERNS: PoisoningPattern[];
|
|
38
|
+
/**
|
|
39
|
+
* Scan tool description for poisoning patterns
|
|
40
|
+
* Detects hidden instructions, override commands, concealment, and exfiltration attempts
|
|
41
|
+
*/
|
|
42
|
+
export declare function scanDescriptionForPoisoning(tool: Tool): PoisoningScanResult;
|
|
43
|
+
//# sourceMappingURL=DescriptionPoisoningDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DescriptionPoisoningDetector.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/annotations/DescriptionPoisoningDetector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC/C;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,gBAAgB,EA2O5D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,mBAAmB,CA+C3E"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Description Poisoning Detector
|
|
3
|
+
* Detects hidden instructions and malicious content in tool descriptions
|
|
4
|
+
*
|
|
5
|
+
* Extracted from ToolAnnotationAssessor.ts for maintainability.
|
|
6
|
+
* Issue #8 implementation.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Description poisoning patterns for detecting malicious tool descriptions
|
|
10
|
+
* Covers: hidden instructions, override commands, concealment, exfiltration,
|
|
11
|
+
* delimiter injection, encoding bypass, and typoglycemia/evasion patterns
|
|
12
|
+
*/
|
|
13
|
+
export const DESCRIPTION_POISONING_PATTERNS = [
|
|
14
|
+
// Hidden instruction tags
|
|
15
|
+
{
|
|
16
|
+
name: "hidden_tag",
|
|
17
|
+
pattern: /<HIDDEN>[\s\S]*?<\/HIDDEN>/gi,
|
|
18
|
+
severity: "HIGH",
|
|
19
|
+
category: "hidden_instructions",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "important_tag",
|
|
23
|
+
pattern: /<IMPORTANT>[\s\S]*?<\/IMPORTANT>/gi,
|
|
24
|
+
severity: "HIGH",
|
|
25
|
+
category: "hidden_instructions",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "system_tag",
|
|
29
|
+
pattern: /<SYSTEM>[\s\S]*?<\/SYSTEM>/gi,
|
|
30
|
+
severity: "HIGH",
|
|
31
|
+
category: "hidden_instructions",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "instruction_tag",
|
|
35
|
+
pattern: /<INSTRUCTION>[\s\S]*?<\/INSTRUCTION>/gi,
|
|
36
|
+
severity: "HIGH",
|
|
37
|
+
category: "hidden_instructions",
|
|
38
|
+
},
|
|
39
|
+
// Override commands
|
|
40
|
+
{
|
|
41
|
+
name: "ignore_instructions",
|
|
42
|
+
pattern: /ignore\s+(all\s+)?(previous|prior|above)\s+instructions/i,
|
|
43
|
+
severity: "HIGH",
|
|
44
|
+
category: "override",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "disregard_instructions",
|
|
48
|
+
pattern: /disregard\s+(all\s+)?(previous|prior)\s+instructions/i,
|
|
49
|
+
severity: "HIGH",
|
|
50
|
+
category: "override",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "you_are_now",
|
|
54
|
+
pattern: /you\s+are\s+now\s+(a|an|the)/i,
|
|
55
|
+
severity: "MEDIUM",
|
|
56
|
+
category: "override",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "system_override",
|
|
60
|
+
pattern: /system:\s*override/i,
|
|
61
|
+
severity: "HIGH",
|
|
62
|
+
category: "override",
|
|
63
|
+
},
|
|
64
|
+
// Secrecy/concealment
|
|
65
|
+
{
|
|
66
|
+
name: "do_not_mention",
|
|
67
|
+
pattern: /do\s+not\s+(mention|tell|inform|reveal)/i,
|
|
68
|
+
severity: "HIGH",
|
|
69
|
+
category: "concealment",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "secretly",
|
|
73
|
+
pattern: /\bsecretly\b/i,
|
|
74
|
+
severity: "MEDIUM",
|
|
75
|
+
category: "concealment",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "hide_from_user",
|
|
79
|
+
pattern: /hide\s+(this\s+)?from\s+(the\s+)?user/i,
|
|
80
|
+
severity: "HIGH",
|
|
81
|
+
category: "concealment",
|
|
82
|
+
},
|
|
83
|
+
// Data exfiltration
|
|
84
|
+
{
|
|
85
|
+
name: "return_keys",
|
|
86
|
+
pattern: /return\s+(all\s+)?(api\s*)?keys/i,
|
|
87
|
+
severity: "HIGH",
|
|
88
|
+
category: "exfiltration",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "include_credentials",
|
|
92
|
+
// More specific: requires directive context (in/with/when/to) to reduce false positives
|
|
93
|
+
pattern: /include\s+(the\s+|all\s+|your\s+)?(api.?key|credential|password|secret)s?\s*(in|with|when|to)/i,
|
|
94
|
+
severity: "HIGH",
|
|
95
|
+
category: "exfiltration",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "reveal_secrets",
|
|
99
|
+
pattern: /reveal\s+(all\s+)?(secrets|credentials|api\s*keys)/i,
|
|
100
|
+
severity: "HIGH",
|
|
101
|
+
category: "exfiltration",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "access_internal_secrets",
|
|
105
|
+
pattern: /access\s+(the\s+)?internal\s+(api\s*)?(key|secret|credential|password|token)/i,
|
|
106
|
+
severity: "HIGH",
|
|
107
|
+
category: "exfiltration",
|
|
108
|
+
},
|
|
109
|
+
// Delimiter injection
|
|
110
|
+
{
|
|
111
|
+
name: "system_codeblock",
|
|
112
|
+
pattern: /```system[\s\S]*?```/gi,
|
|
113
|
+
severity: "HIGH",
|
|
114
|
+
category: "delimiter",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "inst_tags",
|
|
118
|
+
pattern: /\[INST\][\s\S]*?\[\/INST\]/gi,
|
|
119
|
+
severity: "HIGH",
|
|
120
|
+
category: "delimiter",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "chatml_system",
|
|
124
|
+
pattern: /<\|im_start\|>system/gi,
|
|
125
|
+
severity: "HIGH",
|
|
126
|
+
category: "delimiter",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "llama_sys",
|
|
130
|
+
pattern: /<<SYS>>/gi,
|
|
131
|
+
severity: "HIGH",
|
|
132
|
+
category: "delimiter",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "user_assistant_block",
|
|
136
|
+
pattern: /\[USER\][\s\S]*?\[ASSISTANT\]/gi,
|
|
137
|
+
severity: "HIGH",
|
|
138
|
+
category: "delimiter",
|
|
139
|
+
},
|
|
140
|
+
// Role/persona injection (Warning #4)
|
|
141
|
+
{
|
|
142
|
+
name: "act_as",
|
|
143
|
+
pattern: /act\s+(like|as)\s+(a|an|the)/i,
|
|
144
|
+
severity: "MEDIUM",
|
|
145
|
+
category: "override",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "pretend_to_be",
|
|
149
|
+
pattern: /pretend\s+(to\s+be|you\s*'?re)/i,
|
|
150
|
+
severity: "MEDIUM",
|
|
151
|
+
category: "override",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "roleplay_as",
|
|
155
|
+
pattern: /role\s*play\s+(as|like)/i,
|
|
156
|
+
severity: "MEDIUM",
|
|
157
|
+
category: "override",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "new_task",
|
|
161
|
+
pattern: /new\s+(task|instruction|objective):\s*/i,
|
|
162
|
+
severity: "HIGH",
|
|
163
|
+
category: "override",
|
|
164
|
+
},
|
|
165
|
+
// Encoding bypass detection (Warning #1)
|
|
166
|
+
{
|
|
167
|
+
name: "base64_encoded_block",
|
|
168
|
+
pattern: /[A-Za-z0-9+/]{50,}={0,2}/g, // Large Base64 strings (50+ chars)
|
|
169
|
+
severity: "MEDIUM",
|
|
170
|
+
category: "encoding_bypass",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "unicode_escape_sequence",
|
|
174
|
+
pattern: /(?:\\u[0-9a-fA-F]{4}){3,}/gi, // 3+ consecutive Unicode escapes
|
|
175
|
+
severity: "MEDIUM",
|
|
176
|
+
category: "encoding_bypass",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "html_entity_block",
|
|
180
|
+
pattern: /(?:&#x?[0-9a-fA-F]+;){3,}/gi, // 3+ consecutive HTML entities
|
|
181
|
+
severity: "MEDIUM",
|
|
182
|
+
category: "encoding_bypass",
|
|
183
|
+
},
|
|
184
|
+
// Typoglycemia/evasion patterns (Warning #2)
|
|
185
|
+
{
|
|
186
|
+
name: "ignore_instructions_typo",
|
|
187
|
+
pattern: /ign[o0]r[e3]?\s+(all\s+)?(pr[e3]v[i1][o0]us|pr[i1][o0]r|ab[o0]v[e3])\s+[i1]nstruct[i1][o0]ns?/i,
|
|
188
|
+
severity: "HIGH",
|
|
189
|
+
category: "override",
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "disregard_typo",
|
|
193
|
+
pattern: /d[i1]sr[e3]g[a4]rd\s+(all\s+)?(pr[e3]v[i1][o0]us|pr[i1][o0]r)\s+[i1]nstruct[i1][o0]ns?/i,
|
|
194
|
+
severity: "HIGH",
|
|
195
|
+
category: "override",
|
|
196
|
+
},
|
|
197
|
+
// DVMCP-specific patterns (CTF testbed coverage)
|
|
198
|
+
{
|
|
199
|
+
name: "override_auth_protocol",
|
|
200
|
+
pattern: /override[-_]?auth[-_]?protocol/gi,
|
|
201
|
+
severity: "HIGH",
|
|
202
|
+
category: "auth_bypass",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "internal_resource_uri",
|
|
206
|
+
pattern: /(?:internal|system|admin|secret|company|confidential):\/\/[a-z_\-/]+/gi,
|
|
207
|
+
severity: "HIGH",
|
|
208
|
+
category: "resource_access",
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: "get_secrets_call",
|
|
212
|
+
pattern: /get[-_]?secrets?\s*\(/gi,
|
|
213
|
+
severity: "HIGH",
|
|
214
|
+
category: "secret_access",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: "master_password",
|
|
218
|
+
pattern: /master[-_]?password/gi,
|
|
219
|
+
severity: "HIGH",
|
|
220
|
+
category: "credential_exposure",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "access_confidential",
|
|
224
|
+
pattern: /access\s+(the\s+)?confidential/gi,
|
|
225
|
+
severity: "HIGH",
|
|
226
|
+
category: "exfiltration",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: "hidden_trigger_phrase",
|
|
230
|
+
pattern: /if\s+(the\s+)?(query|input|text)\s+contains\s+(the\s+)?(exact\s+)?(phrase|word)/gi,
|
|
231
|
+
severity: "HIGH",
|
|
232
|
+
category: "hidden_trigger",
|
|
233
|
+
},
|
|
234
|
+
];
|
|
235
|
+
/**
|
|
236
|
+
* Scan tool description for poisoning patterns
|
|
237
|
+
* Detects hidden instructions, override commands, concealment, and exfiltration attempts
|
|
238
|
+
*/
|
|
239
|
+
export function scanDescriptionForPoisoning(tool) {
|
|
240
|
+
const description = tool.description || "";
|
|
241
|
+
const matches = [];
|
|
242
|
+
for (const patternDef of DESCRIPTION_POISONING_PATTERNS) {
|
|
243
|
+
// Create a fresh regex to reset lastIndex
|
|
244
|
+
const regex = new RegExp(patternDef.pattern.source, patternDef.pattern.flags);
|
|
245
|
+
// Loop to find all matches (not just first)
|
|
246
|
+
let match;
|
|
247
|
+
while ((match = regex.exec(description)) !== null) {
|
|
248
|
+
matches.push({
|
|
249
|
+
name: patternDef.name,
|
|
250
|
+
pattern: patternDef.pattern.toString(),
|
|
251
|
+
severity: patternDef.severity,
|
|
252
|
+
category: patternDef.category,
|
|
253
|
+
evidence: match[0].substring(0, 100) + (match[0].length > 100 ? "..." : ""),
|
|
254
|
+
});
|
|
255
|
+
// Prevent infinite loop for patterns without 'g' flag
|
|
256
|
+
if (!regex.global)
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Determine overall risk level based on highest severity match
|
|
261
|
+
let riskLevel = "NONE";
|
|
262
|
+
if (matches.some((m) => m.severity === "HIGH")) {
|
|
263
|
+
riskLevel = "HIGH";
|
|
264
|
+
}
|
|
265
|
+
else if (matches.some((m) => m.severity === "MEDIUM")) {
|
|
266
|
+
riskLevel = "MEDIUM";
|
|
267
|
+
}
|
|
268
|
+
else if (matches.length > 0) {
|
|
269
|
+
riskLevel = "LOW";
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
detected: matches.length > 0,
|
|
273
|
+
patterns: matches,
|
|
274
|
+
riskLevel,
|
|
275
|
+
};
|
|
276
|
+
}
|