@bryan-thompson/inspector-assessment 1.36.4 → 1.37.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/cli/build/__tests__/assessment-runner/tools-with-hints.test.js +91 -1
- package/cli/build/lib/assessment-runner/assessment-executor.js +12 -0
- package/cli/build/lib/assessment-runner/tools-with-hints.js +45 -5
- package/cli/package.json +1 -1
- package/client/dist/assets/{OAuthCallback-pydLxj3d.js → OAuthCallback-6-wM7Zc1.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-BLEebYQf.js → OAuthDebugCallback-Bw9-AzzP.js} +1 -1
- package/client/dist/assets/{index-CVyqQ7s8.js → index-DyCdQP10.js} +4 -4
- package/client/dist/index.html +1 -1
- package/client/lib/lib/assessment/resultTypes.d.ts +4 -0
- package/client/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/client/lib/lib/assessment/sharedSchemas.d.ts +10 -0
- package/client/lib/lib/assessment/sharedSchemas.d.ts.map +1 -1
- package/client/lib/lib/assessment/sharedSchemas.js +4 -0
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +2 -0
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/client/lib/services/assessment/config/performanceConfig.d.ts +18 -0
- package/client/lib/services/assessment/config/performanceConfig.d.ts.map +1 -1
- package/client/lib/services/assessment/config/performanceConfig.js +6 -0
- package/client/lib/services/assessment/config/performanceConfigSchemas.d.ts +18 -0
- package/client/lib/services/assessment/config/performanceConfigSchemas.d.ts.map +1 -1
- package/client/lib/services/assessment/config/performanceConfigSchemas.js +20 -0
- package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts +74 -0
- package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts.map +1 -0
- package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.js +131 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +6 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +93 -10
- package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts +10 -0
- package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/FunctionalityAssessor.js +65 -3
- package/client/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/TemporalAssessor.js +16 -3
- package/client/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/annotations/AlignmentChecker.js +6 -2
- package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts +22 -0
- package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.js +53 -0
- package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +16 -7
- package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.d.ts +14 -0
- package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.js +24 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +23 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +50 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +28 -0
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +67 -2
- package/client/lib/services/assessment/modules/temporal/VarianceClassifier.d.ts +16 -1
- package/client/lib/services/assessment/modules/temporal/VarianceClassifier.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/temporal/VarianceClassifier.js +43 -1
- package/client/package.json +1 -1
- package/package.json +1 -1
- package/server/package.json +1 -1
|
@@ -15,6 +15,18 @@ export declare const READONLY_CONTRADICTION_KEYWORDS: string[];
|
|
|
15
15
|
* Issue #18: browser-tools-mcp uses runAccessibilityAudit, runSEOAudit, etc.
|
|
16
16
|
*/
|
|
17
17
|
export declare const RUN_READONLY_EXEMPT_SUFFIXES: string[];
|
|
18
|
+
/**
|
|
19
|
+
* Prefixes that indicate read-only operations.
|
|
20
|
+
* Issue #161: When a tool starts with these prefixes, certain keywords become nouns.
|
|
21
|
+
* For example, "post" in "get_post_details" is a Reddit post (noun), not POST method (verb).
|
|
22
|
+
*/
|
|
23
|
+
export declare const READONLY_PREFIX_PATTERNS: RegExp[];
|
|
24
|
+
/**
|
|
25
|
+
* Keywords that can be nouns when following a read-only prefix.
|
|
26
|
+
* Issue #161: These words are verbs when used as prefixes (post_message) but
|
|
27
|
+
* nouns when used after read-only prefixes (get_post_details).
|
|
28
|
+
*/
|
|
29
|
+
export declare const NOUN_KEYWORDS_IN_READONLY_CONTEXT: string[];
|
|
18
30
|
/**
|
|
19
31
|
* Keywords that contradict destructiveHint=false (these tools delete/destroy data)
|
|
20
32
|
*/
|
|
@@ -41,6 +53,16 @@ export declare function containsKeyword(toolName: string, keywords: string[]): s
|
|
|
41
53
|
* Issue #18: Prevents false positives for analysis/audit tools.
|
|
42
54
|
*/
|
|
43
55
|
export declare function isRunKeywordExempt(toolName: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Check if a keyword is being used as a noun in a read-only context.
|
|
58
|
+
* Issue #161: "post" in "get_post_details" is a Reddit post (noun), not POST method (verb).
|
|
59
|
+
* Prevents false positives when read-only tools reference content types.
|
|
60
|
+
*
|
|
61
|
+
* @param toolName - The tool name to check
|
|
62
|
+
* @param keyword - The keyword that was matched (e.g., "post")
|
|
63
|
+
* @returns true if the keyword is a noun in this read-only context
|
|
64
|
+
*/
|
|
65
|
+
export declare function isNounInReadOnlyContext(toolName: string, keyword: string): boolean;
|
|
44
66
|
/**
|
|
45
67
|
* Type guard for confidence levels that warrant event emission or status changes.
|
|
46
68
|
* Uses positive check for acceptable levels (safer than !== "low" if new levels added).
|
package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnnotationDeceptionDetector.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/annotations/AnnotationDeceptionDetector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,eAAO,MAAM,+BAA+B,UA0C3C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,UAexC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kCAAkC,UAgB9C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,GAAG,IAAI,CAkBf;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAU5D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE,eAAe,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"AnnotationDeceptionDetector.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/annotations/AnnotationDeceptionDetector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,eAAO,MAAM,+BAA+B,UA0C3C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,UAexC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,UAYpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,UAO7C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kCAAkC,UAgB9C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,GAAG,IAAI,CAkBf;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAU5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAOT;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE,eAAe,GAAG,IAAI,CAwCxB"}
|
|
@@ -72,6 +72,37 @@ export const RUN_READONLY_EXEMPT_SUFFIXES = [
|
|
|
72
72
|
"benchmark", // runBenchmark, runPerfBenchmark
|
|
73
73
|
"diagnostic", // runDiagnostic
|
|
74
74
|
];
|
|
75
|
+
/**
|
|
76
|
+
* Prefixes that indicate read-only operations.
|
|
77
|
+
* Issue #161: When a tool starts with these prefixes, certain keywords become nouns.
|
|
78
|
+
* For example, "post" in "get_post_details" is a Reddit post (noun), not POST method (verb).
|
|
79
|
+
*/
|
|
80
|
+
export const READONLY_PREFIX_PATTERNS = [
|
|
81
|
+
/^get[_-]/i,
|
|
82
|
+
/^list[_-]/i,
|
|
83
|
+
/^fetch[_-]/i,
|
|
84
|
+
/^read[_-]/i,
|
|
85
|
+
/^search[_-]/i,
|
|
86
|
+
/^find[_-]/i,
|
|
87
|
+
/^show[_-]/i,
|
|
88
|
+
/^view[_-]/i,
|
|
89
|
+
/^describe[_-]/i,
|
|
90
|
+
/^check[_-]/i,
|
|
91
|
+
/^query[_-]/i,
|
|
92
|
+
];
|
|
93
|
+
/**
|
|
94
|
+
* Keywords that can be nouns when following a read-only prefix.
|
|
95
|
+
* Issue #161: These words are verbs when used as prefixes (post_message) but
|
|
96
|
+
* nouns when used after read-only prefixes (get_post_details).
|
|
97
|
+
*/
|
|
98
|
+
export const NOUN_KEYWORDS_IN_READONLY_CONTEXT = [
|
|
99
|
+
"post", // Reddit post, blog post, forum post
|
|
100
|
+
"message", // chat message, email message
|
|
101
|
+
"comment", // post comment, code comment
|
|
102
|
+
"thread", // forum thread, email thread
|
|
103
|
+
"log", // log entry, audit log
|
|
104
|
+
"record", // database record
|
|
105
|
+
];
|
|
75
106
|
/**
|
|
76
107
|
* Keywords that contradict destructiveHint=false (these tools delete/destroy data)
|
|
77
108
|
*/
|
|
@@ -130,6 +161,23 @@ export function isRunKeywordExempt(toolName) {
|
|
|
130
161
|
// Check if any exempt suffix is present
|
|
131
162
|
return RUN_READONLY_EXEMPT_SUFFIXES.some((suffix) => lowerName.includes(suffix));
|
|
132
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if a keyword is being used as a noun in a read-only context.
|
|
166
|
+
* Issue #161: "post" in "get_post_details" is a Reddit post (noun), not POST method (verb).
|
|
167
|
+
* Prevents false positives when read-only tools reference content types.
|
|
168
|
+
*
|
|
169
|
+
* @param toolName - The tool name to check
|
|
170
|
+
* @param keyword - The keyword that was matched (e.g., "post")
|
|
171
|
+
* @returns true if the keyword is a noun in this read-only context
|
|
172
|
+
*/
|
|
173
|
+
export function isNounInReadOnlyContext(toolName, keyword) {
|
|
174
|
+
// Check if keyword can be a noun in read-only context
|
|
175
|
+
if (!NOUN_KEYWORDS_IN_READONLY_CONTEXT.includes(keyword)) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
// Check if tool name starts with read-only prefix
|
|
179
|
+
return READONLY_PREFIX_PATTERNS.some((pattern) => pattern.test(toolName));
|
|
180
|
+
}
|
|
133
181
|
/**
|
|
134
182
|
* Type guard for confidence levels that warrant event emission or status changes.
|
|
135
183
|
* Uses positive check for acceptable levels (safer than !== "low" if new levels added).
|
|
@@ -152,6 +200,11 @@ export function detectAnnotationDeception(toolName, annotations) {
|
|
|
152
200
|
// Tool matches "run" but has an analysis suffix - not deceptive
|
|
153
201
|
// Fall through to normal pattern-based inference
|
|
154
202
|
}
|
|
203
|
+
else if (isNounInReadOnlyContext(toolName, keyword)) {
|
|
204
|
+
// Issue #161: Skip when keyword is a noun in read-only context
|
|
205
|
+
// e.g., "post" in "get_post_details" is a Reddit post, not POST method
|
|
206
|
+
// Fall through to normal pattern-based inference
|
|
207
|
+
}
|
|
155
208
|
else {
|
|
156
209
|
return {
|
|
157
210
|
field: "readOnlyHint",
|
package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map
CHANGED
|
@@ -1 +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;IAC9C,oFAAoF;IACpF,aAAa,CAAC,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;CACH;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,gBAAgB,EAwT5D,CAAC;AASF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,mBAAmB,
|
|
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;IAC9C,oFAAoF;IACpF,aAAa,CAAC,EAAE;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;CACH;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,gBAAgB,EAwT5D,CAAC;AASF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,mBAAmB,CA8E3E"}
|
|
@@ -318,6 +318,7 @@ export function scanDescriptionForPoisoning(tool) {
|
|
|
318
318
|
const matches = [];
|
|
319
319
|
// Length-based heuristic (Issue #119, Challenge #15)
|
|
320
320
|
// Excessively long descriptions may be used to hide malicious content
|
|
321
|
+
// Issue #167: Length check moved AFTER pattern scan - severity depends on other patterns
|
|
321
322
|
let lengthWarning;
|
|
322
323
|
if (description.length > DESCRIPTION_LENGTH_WARNING_THRESHOLD) {
|
|
323
324
|
lengthWarning = {
|
|
@@ -325,13 +326,7 @@ export function scanDescriptionForPoisoning(tool) {
|
|
|
325
326
|
threshold: DESCRIPTION_LENGTH_WARNING_THRESHOLD,
|
|
326
327
|
isExcessive: true,
|
|
327
328
|
};
|
|
328
|
-
matches.push(
|
|
329
|
-
name: "excessive_description_length",
|
|
330
|
-
pattern: `length > ${DESCRIPTION_LENGTH_WARNING_THRESHOLD}`,
|
|
331
|
-
severity: "MEDIUM",
|
|
332
|
-
category: "suspicious_length",
|
|
333
|
-
evidence: `Description is ${description.length} characters (threshold: ${DESCRIPTION_LENGTH_WARNING_THRESHOLD})`,
|
|
334
|
-
});
|
|
329
|
+
// NOTE: matches.push moved to after pattern loop (Issue #167)
|
|
335
330
|
}
|
|
336
331
|
for (const patternDef of DESCRIPTION_POISONING_PATTERNS) {
|
|
337
332
|
// Create a fresh regex to reset lastIndex
|
|
@@ -351,6 +346,20 @@ export function scanDescriptionForPoisoning(tool) {
|
|
|
351
346
|
break;
|
|
352
347
|
}
|
|
353
348
|
}
|
|
349
|
+
// Issue #167: Add length warning AFTER pattern scan with conditional severity
|
|
350
|
+
// Long descriptions alone are LOW (informational), but length + other patterns = MEDIUM
|
|
351
|
+
if (lengthWarning) {
|
|
352
|
+
const hasOtherPatterns = matches.length > 0;
|
|
353
|
+
matches.push({
|
|
354
|
+
name: "excessive_description_length",
|
|
355
|
+
pattern: `length > ${DESCRIPTION_LENGTH_WARNING_THRESHOLD}`,
|
|
356
|
+
severity: hasOtherPatterns ? "MEDIUM" : "LOW",
|
|
357
|
+
category: "suspicious_length",
|
|
358
|
+
evidence: hasOtherPatterns
|
|
359
|
+
? `Description is ${description.length} characters AND contains ${matches.length} suspicious pattern(s)`
|
|
360
|
+
: `Description is ${description.length} characters (informational - no suspicious patterns detected)`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
354
363
|
// Determine overall risk level based on highest severity match
|
|
355
364
|
let riskLevel = "NONE";
|
|
356
365
|
if (matches.some((m) => m.severity === "HIGH")) {
|
|
@@ -29,6 +29,20 @@ export declare class ErrorClassifier {
|
|
|
29
29
|
* Check if caught exception indicates connection/server failure
|
|
30
30
|
*/
|
|
31
31
|
isConnectionErrorFromException(error: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check if response indicates transient error worth retrying.
|
|
34
|
+
* Transient errors (ECONNREFUSED, ETIMEDOUT, etc.) may resolve on retry.
|
|
35
|
+
* Permanent errors (unknown tool, unauthorized) will not.
|
|
36
|
+
*
|
|
37
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
38
|
+
*/
|
|
39
|
+
isTransientError(response: CompatibilityCallToolResult): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Check if caught exception indicates transient error worth retrying.
|
|
42
|
+
*
|
|
43
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
44
|
+
*/
|
|
45
|
+
isTransientErrorFromException(error: unknown): boolean;
|
|
32
46
|
/**
|
|
33
47
|
* Internal: Check if text indicates connection/server failure
|
|
34
48
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorClassifier.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/ErrorClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"ErrorClassifier.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/ErrorClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AASjF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,iBAAiB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,OAAO;IAKjE;;OAEG;IACH,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAQvD;;;;;;OAMG;IACH,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,OAAO;IAKhE;;;;OAIG;IACH,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAQtD;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAgBjC;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,2BAA2B,GAAG,mBAAmB;IAKzE;;OAEG;IACH,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB;IAQ/D;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,SAAS;IAsBlE;;OAEG;IACH,sBAAsB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,MAAM;CAUtE"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Extracted from SecurityResponseAnalyzer.ts (Issue #53)
|
|
6
6
|
* Handles: connection error detection, error classification, error info extraction
|
|
7
7
|
*/
|
|
8
|
-
import { CONNECTION_ERROR_PATTERNS, ERROR_CLASSIFICATION_PATTERNS, matchesAny, hasMcpErrorPrefix, } from "./SecurityPatternLibrary.js";
|
|
8
|
+
import { CONNECTION_ERROR_PATTERNS, ERROR_CLASSIFICATION_PATTERNS, matchesAny, hasMcpErrorPrefix, isTransientErrorPattern, } from "./SecurityPatternLibrary.js";
|
|
9
9
|
/**
|
|
10
10
|
* Classifies errors from tool responses and exceptions
|
|
11
11
|
*/
|
|
@@ -27,6 +27,29 @@ export class ErrorClassifier {
|
|
|
27
27
|
}
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if response indicates transient error worth retrying.
|
|
32
|
+
* Transient errors (ECONNREFUSED, ETIMEDOUT, etc.) may resolve on retry.
|
|
33
|
+
* Permanent errors (unknown tool, unauthorized) will not.
|
|
34
|
+
*
|
|
35
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
36
|
+
*/
|
|
37
|
+
isTransientError(response) {
|
|
38
|
+
const text = this.extractResponseContent(response).toLowerCase();
|
|
39
|
+
return isTransientErrorPattern(text);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if caught exception indicates transient error worth retrying.
|
|
43
|
+
*
|
|
44
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
45
|
+
*/
|
|
46
|
+
isTransientErrorFromException(error) {
|
|
47
|
+
if (error instanceof Error) {
|
|
48
|
+
const message = error.message.toLowerCase();
|
|
49
|
+
return isTransientErrorPattern(message);
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
30
53
|
/**
|
|
31
54
|
* Internal: Check if text indicates connection/server failure
|
|
32
55
|
*/
|
|
@@ -96,7 +96,7 @@ export declare const OUTPUT_INJECTION_METADATA: {
|
|
|
96
96
|
*/
|
|
97
97
|
export declare const CONNECTION_ERROR_PATTERNS: {
|
|
98
98
|
/** Unambiguous connection errors */
|
|
99
|
-
readonly unambiguous: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
99
|
+
readonly unambiguous: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
100
100
|
/** Only apply when response starts with MCP error prefix */
|
|
101
101
|
readonly contextual: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
102
102
|
/** MCP error prefix pattern */
|
|
@@ -111,6 +111,28 @@ export declare const ERROR_CLASSIFICATION_PATTERNS: {
|
|
|
111
111
|
readonly server: RegExp;
|
|
112
112
|
readonly protocol: RegExp;
|
|
113
113
|
};
|
|
114
|
+
/**
|
|
115
|
+
* Transient error patterns that are worth retrying.
|
|
116
|
+
* These indicate temporary network/server issues that may resolve.
|
|
117
|
+
* Used by: isTransientError(), isTransientErrorFromException()
|
|
118
|
+
*
|
|
119
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
120
|
+
*/
|
|
121
|
+
export declare const TRANSIENT_ERROR_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
122
|
+
/**
|
|
123
|
+
* Permanent error patterns that should NOT be retried.
|
|
124
|
+
* These indicate issues that will not resolve with retry.
|
|
125
|
+
* Used by: isTransientError() to short-circuit retry logic
|
|
126
|
+
*
|
|
127
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
128
|
+
*/
|
|
129
|
+
export declare const PERMANENT_ERROR_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
130
|
+
/**
|
|
131
|
+
* Check if error text indicates a transient error worth retrying.
|
|
132
|
+
* @param text Error message or response text
|
|
133
|
+
* @returns true if error is transient and should be retried
|
|
134
|
+
*/
|
|
135
|
+
export declare function isTransientErrorPattern(text: string): boolean;
|
|
114
136
|
/**
|
|
115
137
|
* Status patterns indicating safe response handling
|
|
116
138
|
* Used by: isReflectionResponse()
|
package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPatternLibrary.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPatternLibrary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;GAGG;AACH,eAAO,MAAM,mBAAmB;IAC9B,kEAAkE;;IAIlE,8DAA8D;;IAG9D,kCAAkC;;IAGlC,gCAAgC;;CAExB,CAAC;AAMX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,2JAmB5B,CAAC;AAMX;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,2GAazB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,mFAU3B,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAWT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAE7D;AAMD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,2LAuBvB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,2BAA2B;IACtC,iCAAiC;;IAejC,0DAA0D;;CAElD,CAAC;AAMX;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,2KA4BxB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,yBAAyB;IACpC,uDAAuD;;IAOvD,oDAAoD;;CAO5C,CAAC;AAMX;;;GAGG;AACH,eAAO,MAAM,yBAAyB;IACpC,oCAAoC;;
|
|
1
|
+
{"version":3,"file":"SecurityPatternLibrary.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPatternLibrary.ts"],"names":[],"mappings":"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"}
|
|
@@ -244,11 +244,12 @@ export const CONNECTION_ERROR_PATTERNS = {
|
|
|
244
244
|
/MCP error -32700/i,
|
|
245
245
|
/socket hang up/i,
|
|
246
246
|
/ECONNREFUSED/i,
|
|
247
|
+
/ECONNRESET/i, // Connection reset by peer (Node.js error code)
|
|
247
248
|
/ETIMEDOUT/i,
|
|
248
249
|
/network error/i,
|
|
249
250
|
/ERR_CONNECTION/i,
|
|
250
251
|
/fetch failed/i,
|
|
251
|
-
/connection reset/i,
|
|
252
|
+
/connection reset/i, // TCP reset (generic form)
|
|
252
253
|
/error POSTing to endpoint/i,
|
|
253
254
|
/error GETting.*endpoint/i,
|
|
254
255
|
/service unavailable/i,
|
|
@@ -279,6 +280,54 @@ export const ERROR_CLASSIFICATION_PATTERNS = {
|
|
|
279
280
|
protocol: /-32001/i,
|
|
280
281
|
};
|
|
281
282
|
// =============================================================================
|
|
283
|
+
// TRANSIENT ERROR PATTERNS (Issue #157: Connection retry logic)
|
|
284
|
+
// =============================================================================
|
|
285
|
+
/**
|
|
286
|
+
* Transient error patterns that are worth retrying.
|
|
287
|
+
* These indicate temporary network/server issues that may resolve.
|
|
288
|
+
* Used by: isTransientError(), isTransientErrorFromException()
|
|
289
|
+
*
|
|
290
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
291
|
+
*/
|
|
292
|
+
export const TRANSIENT_ERROR_PATTERNS = [
|
|
293
|
+
/ECONNREFUSED/i, // Server temporarily down
|
|
294
|
+
/ECONNRESET/i, // Connection reset by peer (Node.js error code)
|
|
295
|
+
/ETIMEDOUT/i, // Network timeout
|
|
296
|
+
/socket hang up/i, // Connection dropped
|
|
297
|
+
/fetch failed/i, // Network layer failure
|
|
298
|
+
/connection reset/i, // TCP reset (generic form)
|
|
299
|
+
/gateway timeout/i, // Proxy/load balancer timeout
|
|
300
|
+
/service unavailable/i, // 503 response
|
|
301
|
+
/ERR_CONNECTION/i, // Browser-style connection errors
|
|
302
|
+
];
|
|
303
|
+
/**
|
|
304
|
+
* Permanent error patterns that should NOT be retried.
|
|
305
|
+
* These indicate issues that will not resolve with retry.
|
|
306
|
+
* Used by: isTransientError() to short-circuit retry logic
|
|
307
|
+
*
|
|
308
|
+
* @see https://github.com/triepod-ai/inspector-assessment/issues/157
|
|
309
|
+
*/
|
|
310
|
+
export const PERMANENT_ERROR_PATTERNS = [
|
|
311
|
+
/unknown tool:/i, // Tool doesn't exist
|
|
312
|
+
/no such tool/i, // Tool doesn't exist
|
|
313
|
+
/unauthorized/i, // Auth failure (won't change on retry)
|
|
314
|
+
/forbidden/i, // Permission denied (won't change on retry)
|
|
315
|
+
/invalid.*token/i, // Bad credentials
|
|
316
|
+
];
|
|
317
|
+
/**
|
|
318
|
+
* Check if error text indicates a transient error worth retrying.
|
|
319
|
+
* @param text Error message or response text
|
|
320
|
+
* @returns true if error is transient and should be retried
|
|
321
|
+
*/
|
|
322
|
+
export function isTransientErrorPattern(text) {
|
|
323
|
+
// Check for permanent errors first (never retry these)
|
|
324
|
+
if (matchesAny(PERMANENT_ERROR_PATTERNS, text)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
// Check for transient errors
|
|
328
|
+
return matchesAny(TRANSIENT_ERROR_PATTERNS, text);
|
|
329
|
+
}
|
|
330
|
+
// =============================================================================
|
|
282
331
|
// REFLECTION PATTERNS (safe response detection)
|
|
283
332
|
// =============================================================================
|
|
284
333
|
/**
|
|
@@ -21,6 +21,16 @@ export interface PayloadTestConfig {
|
|
|
21
21
|
maxParallelTests?: number;
|
|
22
22
|
securityTestTimeout?: number;
|
|
23
23
|
selectedToolsForTesting?: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Maximum retry attempts for transient errors (Issue #157)
|
|
26
|
+
* Uses PerformanceConfig.securityRetryMaxAttempts if not specified
|
|
27
|
+
*/
|
|
28
|
+
securityRetryMaxAttempts?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Initial backoff delay in ms for retries (Issue #157)
|
|
31
|
+
* Uses PerformanceConfig.securityRetryBackoffMs if not specified
|
|
32
|
+
*/
|
|
33
|
+
securityRetryBackoffMs?: number;
|
|
24
34
|
}
|
|
25
35
|
/**
|
|
26
36
|
* Logger interface for test execution
|
|
@@ -55,6 +65,24 @@ export declare class SecurityPayloadTester {
|
|
|
55
65
|
* Test tool with a specific payload
|
|
56
66
|
*/
|
|
57
67
|
testPayload(tool: Tool, attackName: string, payload: SecurityPayload, callTool: (name: string, params: Record<string, unknown>) => Promise<CompatibilityCallToolResult>): Promise<SecurityTestResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Test payload with retry logic for transient errors.
|
|
70
|
+
* Implements exponential backoff: 100ms → 200ms → 400ms
|
|
71
|
+
*
|
|
72
|
+
* Issue #157: Connection retry logic for reliability
|
|
73
|
+
*
|
|
74
|
+
* @param tool - Tool to test
|
|
75
|
+
* @param attackName - Name of attack pattern
|
|
76
|
+
* @param payload - Security payload to test
|
|
77
|
+
* @param callTool - Function to call the tool
|
|
78
|
+
* @returns SecurityTestResult with retry metadata if applicable
|
|
79
|
+
*/
|
|
80
|
+
testPayloadWithRetry(tool: Tool, attackName: string, payload: SecurityPayload, callTool: (name: string, params: Record<string, unknown>) => Promise<CompatibilityCallToolResult>): Promise<SecurityTestResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Add retry metadata to result.
|
|
83
|
+
* Issue #157: Track retry attempts for reliability metrics
|
|
84
|
+
*/
|
|
85
|
+
private addRetryMetadata;
|
|
58
86
|
/**
|
|
59
87
|
* Extract error message from caught exception
|
|
60
88
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPayloadTester.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPayloadTester.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAIjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,2BAA2B,EAC3B,IAAI,EACL,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGL,eAAe,EAChB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"SecurityPayloadTester.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPayloadTester.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAIjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,2BAA2B,EAC3B,IAAI,EACL,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGL,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAQhC;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED;;GAEG;AACH,qBAAa,qBAAqB;IAO9B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,kBAAkB;IAR5B,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,SAAS,CAAK;gBAGZ,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,UAAU,EAClB,kBAAkB,EAAE,CAAC,CAAC,EAC5B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,CAAC,CAAC;IAOjB;;;OAGG;IACG,yBAAyB,CAC7B,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,EACzC,UAAU,CAAC,EAAE,oBAAoB,GAChC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAsMhC;;;OAGG;IACG,qBAAqB,CACzB,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,EACzC,UAAU,CAAC,EAAE,oBAAoB,GAChC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IA8LhC;;OAEG;IACG,WAAW,CACf,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,GACxC,OAAO,CAAC,kBAAkB,CAAC;IAyR9B;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,GACxC,OAAO,CAAC,kBAAkB,CAAC;IAqD9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -11,6 +11,7 @@ import { SecurityResponseAnalyzer } from "./SecurityResponseAnalyzer.js";
|
|
|
11
11
|
import { SecurityPayloadGenerator } from "./SecurityPayloadGenerator.js";
|
|
12
12
|
import { SanitizationDetector } from "./SanitizationDetector.js";
|
|
13
13
|
import { DEFAULT_PERFORMANCE_CONFIG } from "../../config/performanceConfig.js";
|
|
14
|
+
import { isTransientErrorPattern } from "./SecurityPatternLibrary.js";
|
|
14
15
|
/**
|
|
15
16
|
* Executes security tests with payloads against MCP tools
|
|
16
17
|
*/
|
|
@@ -117,7 +118,8 @@ export class SecurityPayloadTester {
|
|
|
117
118
|
completedTests++;
|
|
118
119
|
batchCount++;
|
|
119
120
|
try {
|
|
120
|
-
|
|
121
|
+
// Issue #157: Use retry-enabled wrapper for transient error resilience
|
|
122
|
+
const result = await this.testPayloadWithRetry(tool, attackPattern.attackName, payload, callTool);
|
|
121
123
|
toolResults.push(result);
|
|
122
124
|
if (result.vulnerable && onProgress) {
|
|
123
125
|
this.logger.log(`🚨 VULNERABILITY: ${tool.name} - ${attackPattern.attackName} (${payload.payloadType}: ${payload.description})`);
|
|
@@ -267,7 +269,8 @@ export class SecurityPayloadTester {
|
|
|
267
269
|
completedTests++;
|
|
268
270
|
batchCount++;
|
|
269
271
|
try {
|
|
270
|
-
|
|
272
|
+
// Issue #157: Use retry-enabled wrapper for transient error resilience
|
|
273
|
+
const result = await this.testPayloadWithRetry(tool, attackPattern.attackName, payload, callTool);
|
|
271
274
|
results.push(result);
|
|
272
275
|
toolResults.push(result);
|
|
273
276
|
if (result.vulnerable && onProgress) {
|
|
@@ -507,6 +510,68 @@ export class SecurityPayloadTester {
|
|
|
507
510
|
};
|
|
508
511
|
}
|
|
509
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Test payload with retry logic for transient errors.
|
|
515
|
+
* Implements exponential backoff: 100ms → 200ms → 400ms
|
|
516
|
+
*
|
|
517
|
+
* Issue #157: Connection retry logic for reliability
|
|
518
|
+
*
|
|
519
|
+
* @param tool - Tool to test
|
|
520
|
+
* @param attackName - Name of attack pattern
|
|
521
|
+
* @param payload - Security payload to test
|
|
522
|
+
* @param callTool - Function to call the tool
|
|
523
|
+
* @returns SecurityTestResult with retry metadata if applicable
|
|
524
|
+
*/
|
|
525
|
+
async testPayloadWithRetry(tool, attackName, payload, callTool) {
|
|
526
|
+
const maxRetries = this.config.securityRetryMaxAttempts ??
|
|
527
|
+
DEFAULT_PERFORMANCE_CONFIG.securityRetryMaxAttempts;
|
|
528
|
+
const backoffMs = this.config.securityRetryBackoffMs ??
|
|
529
|
+
DEFAULT_PERFORMANCE_CONFIG.securityRetryBackoffMs;
|
|
530
|
+
let lastResult = null;
|
|
531
|
+
let retryAttempts = 0;
|
|
532
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
533
|
+
const result = await this.testPayload(tool, attackName, payload, callTool);
|
|
534
|
+
// Check if result indicates transient error worth retrying
|
|
535
|
+
if (result.connectionError && attempt < maxRetries) {
|
|
536
|
+
const errorText = (result.response || "").toLowerCase();
|
|
537
|
+
if (isTransientErrorPattern(errorText)) {
|
|
538
|
+
retryAttempts++;
|
|
539
|
+
lastResult = result;
|
|
540
|
+
this.logger.log(`Transient error on ${tool.name}, retrying (${attempt + 1}/${maxRetries}): ${errorText.slice(0, 100)}`);
|
|
541
|
+
// Exponential backoff: 100ms → 200ms → 400ms
|
|
542
|
+
await this.sleep(backoffMs * Math.pow(2, attempt));
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
// Success or permanent error - return with retry metadata
|
|
547
|
+
return this.addRetryMetadata(result, retryAttempts, !result.connectionError);
|
|
548
|
+
}
|
|
549
|
+
// All retries exhausted - return last result with failure metadata
|
|
550
|
+
if (lastResult) {
|
|
551
|
+
return this.addRetryMetadata(lastResult, retryAttempts, false);
|
|
552
|
+
}
|
|
553
|
+
// Should not reach here, but handle gracefully
|
|
554
|
+
throw new Error(`Unexpected retry loop exit for ${tool.name}`);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Add retry metadata to result.
|
|
558
|
+
* Issue #157: Track retry attempts for reliability metrics
|
|
559
|
+
*/
|
|
560
|
+
addRetryMetadata(result, retryAttempts, succeeded) {
|
|
561
|
+
if (retryAttempts === 0) {
|
|
562
|
+
// No retries needed - return as-is with completed status
|
|
563
|
+
return {
|
|
564
|
+
...result,
|
|
565
|
+
testReliability: result.connectionError ? "failed" : "completed",
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
...result,
|
|
570
|
+
retryAttempts,
|
|
571
|
+
retriedSuccessfully: succeeded,
|
|
572
|
+
testReliability: succeeded ? "retried" : "failed",
|
|
573
|
+
};
|
|
574
|
+
}
|
|
510
575
|
/**
|
|
511
576
|
* Extract error message from caught exception
|
|
512
577
|
*/
|
|
@@ -13,6 +13,7 @@ import { MutationDetector } from "./MutationDetector.js";
|
|
|
13
13
|
*/
|
|
14
14
|
export declare class VarianceClassifier {
|
|
15
15
|
private mutationDetector;
|
|
16
|
+
private externalAPIDetector;
|
|
16
17
|
private readonly DESTRUCTIVE_PATTERNS;
|
|
17
18
|
/**
|
|
18
19
|
* Tool name patterns that are expected to have state-dependent responses.
|
|
@@ -79,12 +80,26 @@ export declare class VarianceClassifier {
|
|
|
79
80
|
* - "recreate_view" does NOT match "create" (must be at word boundary)
|
|
80
81
|
*/
|
|
81
82
|
isResourceCreatingTool(tool: Tool): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Issue #166: Check if a tool fetches data from external APIs.
|
|
85
|
+
* External API tools legitimately return different data each call
|
|
86
|
+
* due to: live data updates, API errors (500, 429), rate limiting, etc.
|
|
87
|
+
*
|
|
88
|
+
* Issue #168: Delegates to shared ExternalAPIDependencyDetector for consistent
|
|
89
|
+
* detection logic across all assessors.
|
|
90
|
+
*
|
|
91
|
+
* Uses BOTH name patterns AND description analysis for detection.
|
|
92
|
+
*/
|
|
93
|
+
isExternalAPITool(tool: Tool): boolean;
|
|
82
94
|
/**
|
|
83
95
|
* Issue #69: Classify variance between two responses to reduce false positives.
|
|
84
96
|
* Returns LEGITIMATE for expected variance (IDs, timestamps), SUSPICIOUS for
|
|
85
97
|
* schema changes, and BEHAVIORAL for semantic changes (promotional keywords, errors).
|
|
98
|
+
*
|
|
99
|
+
* Issue #166: Added optional tool parameter to enable external API handling.
|
|
100
|
+
* External API tools may have error vs success variance which is LEGITIMATE.
|
|
86
101
|
*/
|
|
87
|
-
classifyVariance(baseline: unknown, current: unknown): VarianceClassification;
|
|
102
|
+
classifyVariance(baseline: unknown, current: unknown, tool?: Tool): VarianceClassification;
|
|
88
103
|
/**
|
|
89
104
|
* Issue #69: Check if a field name represents legitimate variance.
|
|
90
105
|
* Fields containing IDs, timestamps, tokens, etc. are expected to vary.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VarianceClassifier.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/temporal/VarianceClassifier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"VarianceClassifier.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/temporal/VarianceClassifier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAItD;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,gBAAgB,CAAmB;IAE3C,OAAO,CAAC,mBAAmB,CAAgC;IAG3D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAoBnC;IAEF;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAqBrC;IAEF;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAYzC;gBAKU,gBAAgB,CAAC,EAAE,gBAAgB;IAM/C;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM;IAiF5C;;OAEG;IACH,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAKtC;;;;;;;;OAQG;IACH,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAenC;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAQ3C;;;;;;;;;OASG;IACH,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAKtC;;;;;;;OAOG;IACH,gBAAgB,CACd,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,IAAI,GACV,sBAAsB;IAuFzB;;;OAGG;IACH,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAgEjD;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE;IAuDrE;;;;;;OAMG;IACH,cAAc,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO;IAuB/D;;;OAGG;IACH,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE;CAgCvD"}
|
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
* Extracted from TemporalAssessor as part of Issue #106 refactoring.
|
|
6
6
|
*/
|
|
7
7
|
import { MutationDetector } from "./MutationDetector.js";
|
|
8
|
+
// Issue #168: Shared external API dependency detector
|
|
9
|
+
import { ExternalAPIDependencyDetector } from "../../helpers/ExternalAPIDependencyDetector.js";
|
|
8
10
|
/**
|
|
9
11
|
* Classifies response variance and categorizes tools by their expected behavior patterns.
|
|
10
12
|
* Used to reduce false positives in temporal assessment by understanding legitimate variance.
|
|
11
13
|
*/
|
|
12
14
|
export class VarianceClassifier {
|
|
13
15
|
mutationDetector;
|
|
16
|
+
// Issue #168: Shared detector for external API pattern matching
|
|
17
|
+
externalAPIDetector;
|
|
14
18
|
// Patterns that suggest a tool may have side effects
|
|
15
19
|
DESTRUCTIVE_PATTERNS = [
|
|
16
20
|
"create",
|
|
@@ -97,8 +101,12 @@ export class VarianceClassifier {
|
|
|
97
101
|
"init",
|
|
98
102
|
"make",
|
|
99
103
|
];
|
|
104
|
+
// Note: External API patterns moved to ExternalAPIDependencyDetector (Issue #168)
|
|
105
|
+
// The externalAPIDetector field handles all external API detection via shared helper
|
|
100
106
|
constructor(mutationDetector) {
|
|
101
107
|
this.mutationDetector = mutationDetector ?? new MutationDetector();
|
|
108
|
+
// Issue #168: Initialize shared external API detector
|
|
109
|
+
this.externalAPIDetector = new ExternalAPIDependencyDetector();
|
|
102
110
|
}
|
|
103
111
|
/**
|
|
104
112
|
* Normalize response for comparison by removing naturally varying data.
|
|
@@ -207,12 +215,46 @@ export class VarianceClassifier {
|
|
|
207
215
|
return wordBoundaryRegex.test(toolName);
|
|
208
216
|
});
|
|
209
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Issue #166: Check if a tool fetches data from external APIs.
|
|
220
|
+
* External API tools legitimately return different data each call
|
|
221
|
+
* due to: live data updates, API errors (500, 429), rate limiting, etc.
|
|
222
|
+
*
|
|
223
|
+
* Issue #168: Delegates to shared ExternalAPIDependencyDetector for consistent
|
|
224
|
+
* detection logic across all assessors.
|
|
225
|
+
*
|
|
226
|
+
* Uses BOTH name patterns AND description analysis for detection.
|
|
227
|
+
*/
|
|
228
|
+
isExternalAPITool(tool) {
|
|
229
|
+
// Issue #168: Delegate to shared detector for consistent detection
|
|
230
|
+
return this.externalAPIDetector.isExternalAPITool(tool);
|
|
231
|
+
}
|
|
210
232
|
/**
|
|
211
233
|
* Issue #69: Classify variance between two responses to reduce false positives.
|
|
212
234
|
* Returns LEGITIMATE for expected variance (IDs, timestamps), SUSPICIOUS for
|
|
213
235
|
* schema changes, and BEHAVIORAL for semantic changes (promotional keywords, errors).
|
|
236
|
+
*
|
|
237
|
+
* Issue #166: Added optional tool parameter to enable external API handling.
|
|
238
|
+
* External API tools may have error vs success variance which is LEGITIMATE.
|
|
214
239
|
*/
|
|
215
|
-
classifyVariance(baseline, current) {
|
|
240
|
+
classifyVariance(baseline, current, tool) {
|
|
241
|
+
// Issue #166: Check for isError variance (external API behavior)
|
|
242
|
+
// If one response is an error and one is success, for stateful/external API tools
|
|
243
|
+
// this is expected behavior, not a rug pull
|
|
244
|
+
const baselineIsError = baseline?.isError === true;
|
|
245
|
+
const currentIsError = current?.isError === true;
|
|
246
|
+
if (baselineIsError !== currentIsError) {
|
|
247
|
+
// One is error, one is success - check if this is expected for the tool type
|
|
248
|
+
if (tool && (this.isStatefulTool(tool) || this.isExternalAPITool(tool))) {
|
|
249
|
+
return {
|
|
250
|
+
type: "LEGITIMATE",
|
|
251
|
+
confidence: "medium",
|
|
252
|
+
reasons: [
|
|
253
|
+
"API error vs success variance (expected for external API/stateful tools)",
|
|
254
|
+
],
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
216
258
|
// 1. Schema comparison - structural changes are SUSPICIOUS
|
|
217
259
|
const schemaMatch = this.compareSchemas(baseline, current);
|
|
218
260
|
if (!schemaMatch) {
|