@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.
Files changed (54) hide show
  1. package/cli/build/__tests__/assessment-runner/tools-with-hints.test.js +91 -1
  2. package/cli/build/lib/assessment-runner/assessment-executor.js +12 -0
  3. package/cli/build/lib/assessment-runner/tools-with-hints.js +45 -5
  4. package/cli/package.json +1 -1
  5. package/client/dist/assets/{OAuthCallback-pydLxj3d.js → OAuthCallback-6-wM7Zc1.js} +1 -1
  6. package/client/dist/assets/{OAuthDebugCallback-BLEebYQf.js → OAuthDebugCallback-Bw9-AzzP.js} +1 -1
  7. package/client/dist/assets/{index-CVyqQ7s8.js → index-DyCdQP10.js} +4 -4
  8. package/client/dist/index.html +1 -1
  9. package/client/lib/lib/assessment/resultTypes.d.ts +4 -0
  10. package/client/lib/lib/assessment/resultTypes.d.ts.map +1 -1
  11. package/client/lib/lib/assessment/sharedSchemas.d.ts +10 -0
  12. package/client/lib/lib/assessment/sharedSchemas.d.ts.map +1 -1
  13. package/client/lib/lib/assessment/sharedSchemas.js +4 -0
  14. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +2 -0
  15. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
  16. package/client/lib/services/assessment/config/performanceConfig.d.ts +18 -0
  17. package/client/lib/services/assessment/config/performanceConfig.d.ts.map +1 -1
  18. package/client/lib/services/assessment/config/performanceConfig.js +6 -0
  19. package/client/lib/services/assessment/config/performanceConfigSchemas.d.ts +18 -0
  20. package/client/lib/services/assessment/config/performanceConfigSchemas.d.ts.map +1 -1
  21. package/client/lib/services/assessment/config/performanceConfigSchemas.js +20 -0
  22. package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts +74 -0
  23. package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts.map +1 -0
  24. package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.js +131 -0
  25. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +6 -0
  26. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -1
  27. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +93 -10
  28. package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts +10 -0
  29. package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
  30. package/client/lib/services/assessment/modules/FunctionalityAssessor.js +65 -3
  31. package/client/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
  32. package/client/lib/services/assessment/modules/TemporalAssessor.js +16 -3
  33. package/client/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts.map +1 -1
  34. package/client/lib/services/assessment/modules/annotations/AlignmentChecker.js +6 -2
  35. package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts +22 -0
  36. package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.d.ts.map +1 -1
  37. package/client/lib/services/assessment/modules/annotations/AnnotationDeceptionDetector.js +53 -0
  38. package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map +1 -1
  39. package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +16 -7
  40. package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.d.ts +14 -0
  41. package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.d.ts.map +1 -1
  42. package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.js +24 -1
  43. package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +23 -1
  44. package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
  45. package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +50 -1
  46. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +28 -0
  47. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
  48. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +67 -2
  49. package/client/lib/services/assessment/modules/temporal/VarianceClassifier.d.ts +16 -1
  50. package/client/lib/services/assessment/modules/temporal/VarianceClassifier.d.ts.map +1 -1
  51. package/client/lib/services/assessment/modules/temporal/VarianceClassifier.js +43 -1
  52. package/client/package.json +1 -1
  53. package/package.json +1 -1
  54. 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).
@@ -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,CAoCxB"}
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",
@@ -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,CAoE3E"}
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;AAQjF;;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;;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"}
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()
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityPatternLibrary.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPatternLibrary.ts"],"names":[],"mappings":"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"}
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;AAOhC;;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;CACpC;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;IAqMhC;;;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;IA6LhC;;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;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"SecurityPayloadTester.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPayloadTester.ts"],"names":[],"mappings":"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"}
@@ -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
- const result = await this.testPayload(tool, attackPattern.attackName, payload, callTool);
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
- const result = await this.testPayload(tool, attackPattern.attackName, payload, callTool);
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;AAEtD;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,gBAAgB,CAAmB;IAG3C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAoBnC;IAEF;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAqBrC;IAEF;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAYzC;gBAEU,gBAAgB,CAAC,EAAE,gBAAgB;IAI/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;;;;OAIG;IACH,gBAAgB,CACd,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,GACf,sBAAsB;IAkEzB;;;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"}
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) {