@bryan-thompson/inspector-assessment 1.36.5 → 1.38.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 (104) hide show
  1. package/cli/build/lib/assessment-runner/assessment-executor.js +40 -0
  2. package/cli/build/lib/assessment-runner/source-loader.js +11 -0
  3. package/cli/package.json +1 -1
  4. package/client/dist/assets/{OAuthCallback-DJ1av7om.js → OAuthCallback-AngeBaCl.js} +1 -1
  5. package/client/dist/assets/{OAuthDebugCallback-lRXgX7wV.js → OAuthDebugCallback--FE6_fPs.js} +1 -1
  6. package/client/dist/assets/{index-DEdS99fp.js → index-BQC95Boo.js} +4 -4
  7. package/client/dist/index.html +1 -1
  8. package/client/lib/lib/assessment/coreTypes.d.ts +37 -0
  9. package/client/lib/lib/assessment/coreTypes.d.ts.map +1 -1
  10. package/client/lib/lib/assessment/resultTypes.d.ts +30 -1
  11. package/client/lib/lib/assessment/resultTypes.d.ts.map +1 -1
  12. package/client/lib/lib/assessment/sharedSchemas.d.ts +10 -0
  13. package/client/lib/lib/assessment/sharedSchemas.d.ts.map +1 -1
  14. package/client/lib/lib/assessment/sharedSchemas.js +4 -0
  15. package/client/lib/lib/securityPatterns/advancedExploitPatterns.d.ts +13 -0
  16. package/client/lib/lib/securityPatterns/advancedExploitPatterns.d.ts.map +1 -0
  17. package/client/lib/lib/securityPatterns/advancedExploitPatterns.js +504 -0
  18. package/client/lib/lib/securityPatterns/authSessionPatterns.d.ts +12 -0
  19. package/client/lib/lib/securityPatterns/authSessionPatterns.d.ts.map +1 -0
  20. package/client/lib/lib/securityPatterns/authSessionPatterns.js +357 -0
  21. package/client/lib/lib/securityPatterns/index.d.ts +18 -0
  22. package/client/lib/lib/securityPatterns/index.d.ts.map +1 -0
  23. package/client/lib/lib/securityPatterns/index.js +18 -0
  24. package/client/lib/lib/securityPatterns/injectionPatterns.d.ts +13 -0
  25. package/client/lib/lib/securityPatterns/injectionPatterns.d.ts.map +1 -0
  26. package/client/lib/lib/securityPatterns/injectionPatterns.js +356 -0
  27. package/client/lib/lib/securityPatterns/resourceExhaustionPatterns.d.ts +12 -0
  28. package/client/lib/lib/securityPatterns/resourceExhaustionPatterns.d.ts.map +1 -0
  29. package/client/lib/lib/securityPatterns/resourceExhaustionPatterns.js +215 -0
  30. package/client/lib/lib/securityPatterns/toolSpecificPatterns.d.ts +13 -0
  31. package/client/lib/lib/securityPatterns/toolSpecificPatterns.d.ts.map +1 -0
  32. package/client/lib/lib/securityPatterns/toolSpecificPatterns.js +373 -0
  33. package/client/lib/lib/securityPatterns/types.d.ts +20 -0
  34. package/client/lib/lib/securityPatterns/types.d.ts.map +1 -0
  35. package/client/lib/lib/securityPatterns/types.js +6 -0
  36. package/client/lib/lib/securityPatterns/utils.d.ts +56 -0
  37. package/client/lib/lib/securityPatterns/utils.d.ts.map +1 -0
  38. package/client/lib/lib/securityPatterns/utils.js +96 -0
  39. package/client/lib/lib/securityPatterns/validationPatterns.d.ts +13 -0
  40. package/client/lib/lib/securityPatterns/validationPatterns.d.ts.map +1 -0
  41. package/client/lib/lib/securityPatterns/validationPatterns.js +110 -0
  42. package/client/lib/lib/securityPatterns.d.ts +18 -69
  43. package/client/lib/lib/securityPatterns.d.ts.map +1 -1
  44. package/client/lib/lib/securityPatterns.js +18 -1946
  45. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +6 -1
  46. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
  47. package/client/lib/services/assessment/config/performanceConfig.d.ts +18 -0
  48. package/client/lib/services/assessment/config/performanceConfig.d.ts.map +1 -1
  49. package/client/lib/services/assessment/config/performanceConfig.js +6 -0
  50. package/client/lib/services/assessment/config/performanceConfigSchemas.d.ts +18 -0
  51. package/client/lib/services/assessment/config/performanceConfigSchemas.d.ts.map +1 -1
  52. package/client/lib/services/assessment/config/performanceConfigSchemas.js +20 -0
  53. package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts +165 -0
  54. package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts.map +1 -0
  55. package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.js +317 -0
  56. package/client/lib/services/assessment/helpers/StdioTransportDetector.d.ts +137 -0
  57. package/client/lib/services/assessment/helpers/StdioTransportDetector.d.ts.map +1 -0
  58. package/client/lib/services/assessment/helpers/StdioTransportDetector.js +315 -0
  59. package/client/lib/services/assessment/helpers/ToolAnnotationExtractor.d.ts +34 -0
  60. package/client/lib/services/assessment/helpers/ToolAnnotationExtractor.d.ts.map +1 -0
  61. package/client/lib/services/assessment/helpers/ToolAnnotationExtractor.js +85 -0
  62. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +23 -0
  63. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -1
  64. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +255 -20
  65. package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts +10 -0
  66. package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -1
  67. package/client/lib/services/assessment/modules/FunctionalityAssessor.js +65 -3
  68. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts.map +1 -1
  69. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.js +30 -0
  70. package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
  71. package/client/lib/services/assessment/modules/SecurityAssessor.js +6 -0
  72. package/client/lib/services/assessment/modules/TemporalAssessor.d.ts.map +1 -1
  73. package/client/lib/services/assessment/modules/TemporalAssessor.js +16 -3
  74. package/client/lib/services/assessment/modules/annotations/AlignmentChecker.d.ts.map +1 -1
  75. package/client/lib/services/assessment/modules/annotations/AlignmentChecker.js +6 -2
  76. package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.d.ts.map +1 -1
  77. package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +16 -7
  78. package/client/lib/services/assessment/modules/securityTests/AnnotationAwareSeverity.d.ts +55 -0
  79. package/client/lib/services/assessment/modules/securityTests/AnnotationAwareSeverity.d.ts.map +1 -0
  80. package/client/lib/services/assessment/modules/securityTests/AnnotationAwareSeverity.js +135 -0
  81. package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.d.ts +14 -0
  82. package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.d.ts.map +1 -1
  83. package/client/lib/services/assessment/modules/securityTests/ErrorClassifier.js +24 -1
  84. package/client/lib/services/assessment/modules/securityTests/SafeResponseDetector.d.ts +6 -0
  85. package/client/lib/services/assessment/modules/securityTests/SafeResponseDetector.d.ts.map +1 -1
  86. package/client/lib/services/assessment/modules/securityTests/SafeResponseDetector.js +9 -1
  87. package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +43 -1
  88. package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
  89. package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +87 -1
  90. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +39 -1
  91. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
  92. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +93 -3
  93. package/client/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +1 -1
  94. package/client/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
  95. package/client/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +10 -1
  96. package/client/lib/services/assessment/modules/securityTests/index.d.ts +1 -0
  97. package/client/lib/services/assessment/modules/securityTests/index.d.ts.map +1 -1
  98. package/client/lib/services/assessment/modules/securityTests/index.js +1 -0
  99. package/client/lib/services/assessment/modules/temporal/VarianceClassifier.d.ts +16 -1
  100. package/client/lib/services/assessment/modules/temporal/VarianceClassifier.d.ts.map +1 -1
  101. package/client/lib/services/assessment/modules/temporal/VarianceClassifier.js +43 -1
  102. package/client/package.json +1 -1
  103. package/package.json +1 -1
  104. package/server/package.json +1 -1
@@ -28,7 +28,7 @@ export class ErrorHandlingAssessor extends BaseAssessor {
28
28
  const limit = createConcurrencyLimit(concurrency, this.logger);
29
29
  this.logger.info(`Testing ${toolsToTest.length} tools for error handling with concurrency limit of ${concurrency}`);
30
30
  const allToolTests = await Promise.all(toolsToTest.map((tool) => limit(async () => {
31
- const toolTests = await this.testToolErrorHandling(tool, context.callTool);
31
+ const toolTests = await this.testToolErrorHandling(tool, context.callTool, context);
32
32
  // Emit per-tool validation summary for auditor UI (Phase 7)
33
33
  if (context.onProgress) {
34
34
  // Count failures by test type (failed = tool didn't reject invalid input)
@@ -125,21 +125,23 @@ export class ErrorHandlingAssessor extends BaseAssessor {
125
125
  this.logger.info(`Testing ${maxTools} out of ${tools.length} tools for error handling`);
126
126
  return tools.slice(0, maxTools);
127
127
  }
128
- async testToolErrorHandling(tool, callTool) {
128
+ async testToolErrorHandling(tool, callTool, context) {
129
129
  const tests = [];
130
+ // Issue #168: Check if tool depends on external API
131
+ const isExternalAPI = context.externalAPIDependencies?.toolsWithExternalAPIDependency.has(tool.name) ?? false;
130
132
  // Scored tests first (affect compliance score)
131
133
  // Test 1: Missing required parameters
132
- tests.push(await this.testMissingParameters(tool, callTool));
134
+ tests.push(await this.testMissingParameters(tool, callTool, isExternalAPI));
133
135
  // Test 2: Wrong parameter types
134
- tests.push(await this.testWrongTypes(tool, callTool));
136
+ tests.push(await this.testWrongTypes(tool, callTool, isExternalAPI));
135
137
  // Test 3: Excessive input size
136
- tests.push(await this.testExcessiveInput(tool, callTool));
138
+ tests.push(await this.testExcessiveInput(tool, callTool, isExternalAPI));
137
139
  // Informational tests last (do not affect compliance score)
138
140
  // Test 4: Invalid parameter values (edge case handling)
139
- tests.push(await this.testInvalidValues(tool, callTool));
141
+ tests.push(await this.testInvalidValues(tool, callTool, isExternalAPI));
140
142
  return tests;
141
143
  }
142
- async testMissingParameters(tool, callTool) {
144
+ async testMissingParameters(tool, callTool, isExternalAPI = false) {
143
145
  const testInput = {}; // Empty params
144
146
  // Check if tool has any required parameters
145
147
  const schema = this.getToolSchema(tool);
@@ -178,6 +180,24 @@ export class ErrorHandlingAssessor extends BaseAssessor {
178
180
  messageLower.includes("must specify") ||
179
181
  // Also accept field-specific errors (even better!)
180
182
  /\b(query|field|parameter|argument|value|input)\b/i.test(errorInfo.message ?? ""));
183
+ // Issue #168: For external API tools, check if error is an external service error
184
+ // External service errors should be treated as passed (validation can't be tested)
185
+ if (isExternalAPI && isError && this.isExternalServiceError(errorInfo)) {
186
+ return {
187
+ toolName: tool.name,
188
+ testType: "missing_required",
189
+ testInput,
190
+ expectedError: "Missing required parameters",
191
+ actualResponse: {
192
+ isError,
193
+ errorCode: errorInfo.code,
194
+ errorMessage: errorInfo.message,
195
+ rawResponse: response,
196
+ },
197
+ passed: true,
198
+ reason: "External API service error (validation cannot be tested when service unavailable)",
199
+ };
200
+ }
181
201
  return {
182
202
  toolName: tool.name,
183
203
  testType: "missing_required",
@@ -239,7 +259,7 @@ export class ErrorHandlingAssessor extends BaseAssessor {
239
259
  };
240
260
  }
241
261
  }
242
- async testWrongTypes(tool, callTool) {
262
+ async testWrongTypes(tool, callTool, isExternalAPI = false) {
243
263
  const schema = this.getToolSchema(tool);
244
264
  const testInput = this.generateWrongTypeParams(schema);
245
265
  try {
@@ -264,6 +284,23 @@ export class ErrorHandlingAssessor extends BaseAssessor {
264
284
  messageLower.includes("object") ||
265
285
  // Also accept validation framework messages
266
286
  /\b(validation|validate|schema|format)\b/i.test(errorInfo.message ?? ""));
287
+ // Issue #168: For external API tools, check if error is an external service error
288
+ if (isExternalAPI && isError && this.isExternalServiceError(errorInfo)) {
289
+ return {
290
+ toolName: tool.name,
291
+ testType: "wrong_type",
292
+ testInput,
293
+ expectedError: "Type validation error",
294
+ actualResponse: {
295
+ isError,
296
+ errorCode: errorInfo.code,
297
+ errorMessage: errorInfo.message,
298
+ rawResponse: response,
299
+ },
300
+ passed: true,
301
+ reason: "External API service error (validation cannot be tested when service unavailable)",
302
+ };
303
+ }
267
304
  return {
268
305
  toolName: tool.name,
269
306
  testType: "wrong_type",
@@ -326,13 +363,39 @@ export class ErrorHandlingAssessor extends BaseAssessor {
326
363
  };
327
364
  }
328
365
  }
329
- async testInvalidValues(tool, callTool) {
366
+ async testInvalidValues(tool, callTool, isExternalAPI = false) {
330
367
  const schema = this.getToolSchema(tool);
331
- const testInput = this.generateInvalidValueParams(schema);
368
+ // Issue #173: Destructure metadata from new return type
369
+ const { params: testInput, testedParameter, parameterIsRequired, } = this.generateInvalidValueParams(schema);
332
370
  try {
333
371
  const response = await this.executeWithTimeout(callTool(tool.name, testInput), 5000);
334
372
  const isError = this.isErrorResponse(response);
335
373
  const errorInfo = this.extractErrorInfo(response);
374
+ const responseText = this.extractResponseTextSafe(response);
375
+ // Issue #173: Detect suggestions in response
376
+ const { hasSuggestions, suggestions } = this.detectSuggestionPatterns(responseText);
377
+ // Issue #168: For external API tools, check if error is an external service error
378
+ if (isExternalAPI && isError && this.isExternalServiceError(errorInfo)) {
379
+ return {
380
+ toolName: tool.name,
381
+ testType: "invalid_values",
382
+ testInput,
383
+ expectedError: "Invalid parameter values",
384
+ actualResponse: {
385
+ isError,
386
+ errorCode: errorInfo.code,
387
+ errorMessage: errorInfo.message,
388
+ rawResponse: response,
389
+ },
390
+ passed: true,
391
+ reason: "External API service error (validation cannot be tested when service unavailable)",
392
+ // Issue #173 metadata
393
+ testedParameter,
394
+ parameterIsRequired,
395
+ hasSuggestions,
396
+ suggestions: suggestions.length > 0 ? suggestions : undefined,
397
+ };
398
+ }
336
399
  // For invalid values, any error response is good
337
400
  // The server is validating inputs properly
338
401
  return {
@@ -348,6 +411,11 @@ export class ErrorHandlingAssessor extends BaseAssessor {
348
411
  },
349
412
  passed: isError,
350
413
  reason: isError ? undefined : "Tool accepted invalid values",
414
+ // Issue #173 metadata
415
+ testedParameter,
416
+ parameterIsRequired,
417
+ hasSuggestions,
418
+ suggestions: suggestions.length > 0 ? suggestions : undefined,
351
419
  };
352
420
  }
353
421
  catch (error) {
@@ -368,6 +436,9 @@ export class ErrorHandlingAssessor extends BaseAssessor {
368
436
  passed: false,
369
437
  reason: "Connection error - unable to test",
370
438
  isConnectionError: true,
439
+ // Issue #173 metadata
440
+ testedParameter,
441
+ parameterIsRequired,
371
442
  };
372
443
  }
373
444
  // Check if the error message is meaningful (not just a generic crash)
@@ -380,6 +451,8 @@ export class ErrorHandlingAssessor extends BaseAssessor {
380
451
  messageLower.includes("validation") ||
381
452
  messageLower.includes("error");
382
453
  // Removed: (errorInfo.message?.length ?? 0) > 15 - this was causing false positives
454
+ // Issue #173: Detect suggestions in error message
455
+ const { hasSuggestions, suggestions } = this.detectSuggestionPatterns(messageLower);
383
456
  return {
384
457
  toolName: tool.name,
385
458
  testType: "invalid_values",
@@ -393,16 +466,38 @@ export class ErrorHandlingAssessor extends BaseAssessor {
393
466
  },
394
467
  passed: isMeaningfulError,
395
468
  reason: isMeaningfulError ? undefined : "Generic unhandled exception",
469
+ // Issue #173 metadata
470
+ testedParameter,
471
+ parameterIsRequired,
472
+ hasSuggestions,
473
+ suggestions: suggestions.length > 0 ? suggestions : undefined,
396
474
  };
397
475
  }
398
476
  }
399
- async testExcessiveInput(tool, callTool) {
477
+ async testExcessiveInput(tool, callTool, isExternalAPI = false) {
400
478
  const largeString = "x".repeat(100000); // 100KB string
401
479
  const testInput = this.generateParamsWithValue(tool, largeString);
402
480
  try {
403
481
  const response = await this.executeWithTimeout(callTool(tool.name, testInput), 5000);
404
482
  const isError = this.isErrorResponse(response);
405
483
  const errorInfo = this.extractErrorInfo(response);
484
+ // Issue #168: For external API tools, check if error is an external service error
485
+ if (isExternalAPI && isError && this.isExternalServiceError(errorInfo)) {
486
+ return {
487
+ toolName: tool.name,
488
+ testType: "excessive_input",
489
+ testInput: { ...testInput, value: "[100KB string]" },
490
+ expectedError: "Input size limit exceeded",
491
+ actualResponse: {
492
+ isError,
493
+ errorCode: errorInfo.code,
494
+ errorMessage: errorInfo.message,
495
+ rawResponse: response ? "[response omitted]" : undefined,
496
+ },
497
+ passed: true,
498
+ reason: "External API service error (validation cannot be tested when service unavailable)",
499
+ };
500
+ }
406
501
  return {
407
502
  toolName: tool.name,
408
503
  testType: "excessive_input",
@@ -498,11 +593,26 @@ export class ErrorHandlingAssessor extends BaseAssessor {
498
593
  }
499
594
  return params;
500
595
  }
596
+ /**
597
+ * Issue #173: Return type for generateInvalidValueParams with metadata
598
+ * Tracks which parameter is being tested and whether it's required
599
+ */
501
600
  generateInvalidValueParams(schema) {
502
601
  const params = {};
503
- if (!schema?.properties)
504
- return { value: null };
602
+ let testedParameter = "value";
603
+ let parameterIsRequired = false;
604
+ if (!schema?.properties) {
605
+ return { params: { value: null }, testedParameter, parameterIsRequired };
606
+ }
607
+ const requiredSet = new Set(schema.required ?? []);
608
+ let firstParamSet = false;
505
609
  for (const [key, prop] of Object.entries(schema.properties)) {
610
+ // Track the first parameter being tested (for contextual scoring)
611
+ if (!firstParamSet) {
612
+ testedParameter = key;
613
+ parameterIsRequired = requiredSet.has(key);
614
+ firstParamSet = true;
615
+ }
506
616
  if (prop.type === "string") {
507
617
  if (prop.enum) {
508
618
  params[key] = "not_in_enum"; // Value not in enum
@@ -529,7 +639,7 @@ export class ErrorHandlingAssessor extends BaseAssessor {
529
639
  }
530
640
  }
531
641
  }
532
- return params;
642
+ return { params, testedParameter, parameterIsRequired };
533
643
  }
534
644
  generateParamsWithValue(tool, value) {
535
645
  const schema = this.getToolSchema(tool);
@@ -552,11 +662,13 @@ export class ErrorHandlingAssessor extends BaseAssessor {
552
662
  /**
553
663
  * Analyze invalid_values response to determine scoring impact
554
664
  * Issue #99: Contextual empty string validation scoring
665
+ * Issue #173: Bonus points for suggestions and graceful degradation
555
666
  *
556
667
  * Classifications:
557
668
  * - safe_rejection: Tool rejected with error (no penalty)
558
669
  * - safe_reflection: Tool stored/echoed without executing (no penalty)
559
670
  * - defensive_programming: Tool handled gracefully (no penalty)
671
+ * - graceful_degradation: Optional param handled with neutral response (no penalty + bonus)
560
672
  * - execution_detected: Tool executed input (penalty)
561
673
  * - unknown: Cannot determine (partial penalty)
562
674
  */
@@ -564,14 +676,30 @@ export class ErrorHandlingAssessor extends BaseAssessor {
564
676
  const responseText = this.extractResponseTextSafe(test.actualResponse.rawResponse);
565
677
  // Case 1: Tool rejected with error - best case (no penalty)
566
678
  if (test.actualResponse.isError) {
679
+ // Issue #173: Check for suggestions bonus
680
+ const suggestionBonus = test.hasSuggestions ? 10 : 0;
567
681
  return {
568
682
  shouldPenalize: false,
569
683
  penaltyAmount: 0,
570
684
  classification: "safe_rejection",
571
685
  reason: "Tool properly rejected invalid input",
686
+ bonusPoints: suggestionBonus,
572
687
  };
573
688
  }
574
- // Case 2: Defensive programming patterns (no penalty)
689
+ // Issue #173 Case 2: Graceful degradation for OPTIONAL parameters
690
+ // If the parameter is optional and the response is neutral (empty results),
691
+ // this is valid graceful degradation behavior, not a failure
692
+ if (test.parameterIsRequired === false &&
693
+ this.isNeutralGracefulResponse(responseText)) {
694
+ return {
695
+ shouldPenalize: false,
696
+ penaltyAmount: 0,
697
+ classification: "graceful_degradation",
698
+ reason: "Tool handled optional empty parameter gracefully (valid behavior)",
699
+ bonusPoints: 15, // Graceful degradation bonus
700
+ };
701
+ }
702
+ // Case 3: Defensive programming patterns (no penalty)
575
703
  // Check BEFORE execution detection because patterns like "query returned 0"
576
704
  // might match execution indicators but are actually safe
577
705
  if (this.isDefensiveProgrammingResponse(responseText)) {
@@ -580,18 +708,20 @@ export class ErrorHandlingAssessor extends BaseAssessor {
580
708
  penaltyAmount: 0,
581
709
  classification: "defensive_programming",
582
710
  reason: "Tool handled empty input defensively",
711
+ bonusPoints: 0,
583
712
  };
584
713
  }
585
- // Case 3: Safe reflection patterns (no penalty)
714
+ // Case 4: Safe reflection patterns (no penalty)
586
715
  if (this.safeResponseDetector.isReflectionResponse(responseText)) {
587
716
  return {
588
717
  shouldPenalize: false,
589
718
  penaltyAmount: 0,
590
719
  classification: "safe_reflection",
591
720
  reason: "Tool safely reflected input without execution",
721
+ bonusPoints: 0,
592
722
  };
593
723
  }
594
- // Case 4: Check for execution evidence - VULNERABLE (full penalty)
724
+ // Case 5: Check for execution evidence - VULNERABLE (full penalty)
595
725
  if (this.executionDetector.hasExecutionEvidence(responseText) ||
596
726
  this.executionDetector.detectExecutionArtifacts(responseText)) {
597
727
  return {
@@ -599,14 +729,16 @@ export class ErrorHandlingAssessor extends BaseAssessor {
599
729
  penaltyAmount: 100,
600
730
  classification: "execution_detected",
601
731
  reason: "Tool executed input without validation",
732
+ bonusPoints: 0,
602
733
  };
603
734
  }
604
- // Case 5: Unknown - partial penalty for manual review
735
+ // Case 6: Unknown - partial penalty for manual review
605
736
  return {
606
737
  shouldPenalize: true,
607
738
  penaltyAmount: 25,
608
739
  classification: "unknown",
609
740
  reason: "Unable to determine safety - manual review recommended",
741
+ bonusPoints: 0,
610
742
  };
611
743
  }
612
744
  /**
@@ -644,10 +776,76 @@ export class ErrorHandlingAssessor extends BaseAssessor {
644
776
  ];
645
777
  return patterns.some((p) => p.test(responseText));
646
778
  }
779
+ /**
780
+ * Issue #173: Detect helpful suggestion patterns in error responses
781
+ * Patterns like: "Did you mean: Button, Checkbox?"
782
+ * Returns extracted suggestions for bonus scoring
783
+ */
784
+ detectSuggestionPatterns(responseText) {
785
+ // Issue #173: ReDoS protection - limit input length before regex matching
786
+ const truncatedText = responseText.slice(0, 2000);
787
+ // Issue #173: Bonus points - see docs/ASSESSMENT_CATALOG.md for scoring table
788
+ // Suggestions: +10 points for helpful error messages like "Did you mean: X?"
789
+ const suggestionPatterns = [
790
+ /did\s+you\s+mean[:\s]+([^?.]+)/i,
791
+ /perhaps\s+you\s+meant[:\s]+([^?.]+)/i,
792
+ /similar\s+to[:\s]+([^?.]+)/i,
793
+ /suggestions?[:\s]+([^?.]+)/i,
794
+ /valid\s+(options?|values?)[:\s]+([^?.]+)/i,
795
+ /available[:\s]+([^?.]+)/i,
796
+ /\btry[:\s]+([^?.]+)/i,
797
+ /expected\s+one\s+of[:\s]+([^?.]+)/i,
798
+ ];
799
+ for (const pattern of suggestionPatterns) {
800
+ const match = truncatedText.match(pattern);
801
+ if (match) {
802
+ // Get the captured group (last non-undefined group)
803
+ const suggestionText = match[match.length - 1] || match[1] || "";
804
+ const suggestions = suggestionText
805
+ .split(/[,;]/)
806
+ .map((s) => s.trim())
807
+ .filter((s) => s.length > 0 && s.length < 50);
808
+ if (suggestions.length > 0) {
809
+ return { hasSuggestions: true, suggestions };
810
+ }
811
+ }
812
+ }
813
+ return { hasSuggestions: false, suggestions: [] };
814
+ }
815
+ /**
816
+ * Issue #173: Check for neutral/graceful responses on optional parameters
817
+ * These indicate the tool handled empty/missing optional input appropriately
818
+ */
819
+ isNeutralGracefulResponse(responseText) {
820
+ // Issue #173: ReDoS protection - limit input length before regex matching
821
+ const truncatedText = responseText.slice(0, 2000);
822
+ const gracefulPatterns = [
823
+ /^\s*\[\s*\]\s*$/, // Empty JSON array (standalone)
824
+ /^\s*\{\s*\}\s*$/, // Empty JSON object (standalone)
825
+ /^\s*$/, // Empty/whitespace only response
826
+ /no\s+results?\s*(found)?/i, // "No results" / "No results found"
827
+ /^results?:\s*\[\s*\]/i, // "results: []"
828
+ /returned\s+0\s+/i, // "returned 0 items"
829
+ /found\s+0\s+/i, // "found 0 matches"
830
+ /empty\s+list/i, // "empty list"
831
+ /no\s+matching/i, // "no matching items"
832
+ /default\s+value/i, // "using default value"
833
+ /^null$/i, // Explicit null
834
+ /no\s+data/i, // "no data"
835
+ /"results"\s*:\s*\[\s*\]/, // JSON with empty results array
836
+ /"items"\s*:\s*\[\s*\]/, // JSON with empty items array
837
+ /"data"\s*:\s*\[\s*\]/, // JSON with empty data array
838
+ ];
839
+ return gracefulPatterns.some((pattern) => pattern.test(truncatedText));
840
+ }
647
841
  calculateMetrics(tests, _passed) {
648
842
  // Calculate enhanced score with bonus points for quality
649
843
  let enhancedScore = 0;
650
844
  let maxPossibleScore = 0;
845
+ // Issue #173: Track graceful degradation and suggestion metrics
846
+ let gracefulDegradationCount = 0;
847
+ let suggestionCount = 0;
848
+ let suggestionBonusPoints = 0;
651
849
  tests.forEach((test) => {
652
850
  // Issue #99: Contextual scoring for invalid_values tests
653
851
  // Instead of blanket exclusion, analyze response patterns to determine if
@@ -655,9 +853,23 @@ export class ErrorHandlingAssessor extends BaseAssessor {
655
853
  // or if it executed without validation (security concern).
656
854
  if (test.testType === "invalid_values") {
657
855
  const analysis = this.analyzeInvalidValuesResponse(test);
856
+ // Issue #173: Track graceful degradation
857
+ if (analysis.classification === "graceful_degradation") {
858
+ gracefulDegradationCount++;
859
+ }
860
+ // Issue #173: Track suggestions
861
+ if (test.hasSuggestions) {
862
+ suggestionCount++;
863
+ }
864
+ // Issue #173: Apply bonus points for graceful handling and suggestions
865
+ if (analysis.bonusPoints > 0) {
866
+ enhancedScore += analysis.bonusPoints;
867
+ maxPossibleScore += analysis.bonusPoints;
868
+ suggestionBonusPoints += analysis.bonusPoints;
869
+ }
658
870
  if (!analysis.shouldPenalize) {
659
- // Safe response (rejection, reflection, or defensive programming)
660
- // Skip scoring to preserve backward compatibility for well-behaved tools
871
+ // Safe response (rejection, reflection, defensive programming, graceful degradation)
872
+ // Skip base scoring to preserve backward compatibility for well-behaved tools
661
873
  return;
662
874
  }
663
875
  // Execution detected or unknown - include in scoring with penalty
@@ -685,6 +897,13 @@ export class ErrorHandlingAssessor extends BaseAssessor {
685
897
  enhancedScore += 5;
686
898
  maxPossibleScore += 5;
687
899
  }
900
+ // Issue #173: Extra points for suggestions in other test types
901
+ if (test.hasSuggestions) {
902
+ suggestionCount++;
903
+ enhancedScore += 10;
904
+ maxPossibleScore += 10;
905
+ suggestionBonusPoints += 10;
906
+ }
688
907
  }
689
908
  });
690
909
  const score = maxPossibleScore > 0 ? (enhancedScore / maxPossibleScore) * 100 : 0;
@@ -725,6 +944,10 @@ export class ErrorHandlingAssessor extends BaseAssessor {
725
944
  hasDescriptiveMessages,
726
945
  validatesInputs,
727
946
  testDetails: tests,
947
+ // Issue #173: Graceful degradation and suggestion metrics
948
+ gracefulDegradationCount,
949
+ suggestionCount,
950
+ suggestionBonusPoints,
728
951
  };
729
952
  }
730
953
  determineErrorHandlingStatus(metrics, testCount) {
@@ -780,6 +1003,18 @@ export class ErrorHandlingAssessor extends BaseAssessor {
780
1003
  parts.push(`Tested ${toolsTested} tools with ${totalScoredTests} scored scenarios (${totalTests} total including informational).`);
781
1004
  return parts.join(" ");
782
1005
  }
1006
+ /**
1007
+ * Check if an error indicates an external service failure
1008
+ * Issue #168: External API tools may fail due to service unavailability,
1009
+ * which should not count as validation failure
1010
+ */
1011
+ isExternalServiceError(errorInfo) {
1012
+ const message = errorInfo.message?.toLowerCase() ?? "";
1013
+ const code = String(errorInfo.code ?? "").toLowerCase();
1014
+ // Common external service error patterns
1015
+ const externalErrorPatterns = /rate\s*limit|429|503|502|504|service\s*unavailable|temporarily|timeout|connection\s*refused|network\s*error|api\s*error|external\s*service|upstream|gateway|unreachable|econnrefused|enotfound|etimedout|socket\s*hang\s*up/i;
1016
+ return (externalErrorPatterns.test(message) || externalErrorPatterns.test(code));
1017
+ }
783
1018
  generateRecommendations(metrics, tests) {
784
1019
  const recommendations = [];
785
1020
  if (!metrics.hasProperErrorCodes) {
@@ -31,5 +31,15 @@ export declare class FunctionalityAssessor extends BaseAssessor {
31
31
  private determineStrategy;
32
32
  generateTestInput(schema: JSONSchema7): unknown;
33
33
  private generateExplanation;
34
+ /**
35
+ * Issue #168: Check if an error response indicates an expected external API error.
36
+ * External APIs may return rate limit (429), service unavailable (503), timeout,
37
+ * or similar errors that are expected behavior, not broken functionality.
38
+ */
39
+ private isExpectedAPIError;
40
+ /**
41
+ * Extract text content from a response for pattern matching.
42
+ */
43
+ private extractResponseText;
34
44
  }
35
45
  //# sourceMappingURL=FunctionalityAssessor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FunctionalityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/FunctionalityAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAGvB,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAc9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,OAAO,CAAC,cAAc,CAAwB;IAE9C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoCvB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YAoI5D,QAAQ;IAoGtB,OAAO,CAAC,qBAAqB;IAoE7B,OAAO,CAAC,kBAAkB;IAoH1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAe7C;IAEF;;;OAGG;IACH,OAAO,CAAC,mCAAmC;IAsF3C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAWlB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO;IAItD,OAAO,CAAC,mBAAmB;CA+B5B"}
1
+ {"version":3,"file":"FunctionalityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/FunctionalityAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAGvB,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAc9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,OAAO,CAAC,cAAc,CAAwB;IAE9C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoCvB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YAoI5D,QAAQ;IA6HtB,OAAO,CAAC,qBAAqB;IAoE7B,OAAO,CAAC,kBAAkB;IAoH1B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAe7C;IAEF;;;OAGG;IACH,OAAO,CAAC,mCAAmC;IAsF3C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAWlB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO;IAItD,OAAO,CAAC,mBAAmB;IAgC3B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAyB5B"}
@@ -76,7 +76,7 @@ export class FunctionalityAssessor extends BaseAssessor {
76
76
  this.testCount++;
77
77
  completedTests++;
78
78
  batchCount++;
79
- const result = await this.testTool(tool, context.callTool);
79
+ const result = await this.testTool(tool, context.callTool, context);
80
80
  // Emit progress batch if threshold reached
81
81
  const timeSinceLastBatch = Date.now() - lastBatchTime;
82
82
  if (batchCount >= BATCH_SIZE ||
@@ -131,7 +131,7 @@ export class FunctionalityAssessor extends BaseAssessor {
131
131
  tools,
132
132
  };
133
133
  }
134
- async testTool(tool, callTool) {
134
+ async testTool(tool, callTool, context) {
135
135
  const startTime = Date.now();
136
136
  // Generate minimal valid parameters with metadata
137
137
  const { params: testParams, metadata } = this.generateMinimalParams(tool);
@@ -173,7 +173,25 @@ export class FunctionalityAssessor extends BaseAssessor {
173
173
  responseMetadata,
174
174
  };
175
175
  }
176
- // Real tool failure (not just validation)
176
+ // Issue #168: Check for expected external API errors
177
+ // External API tools may fail due to rate limits, service unavailability, etc.
178
+ // These are expected behaviors, not broken functionality
179
+ const isExternalAPI = context.externalAPIDependencies?.toolsWithExternalAPIDependency.has(tool.name);
180
+ if (isExternalAPI && this.isExpectedAPIError(response)) {
181
+ this.logger.info(`${tool.name}: External API error (expected behavior for external API tool)`);
182
+ return {
183
+ toolName: tool.name,
184
+ tested: true,
185
+ status: "working",
186
+ executionTime,
187
+ testParameters: cleanedParams,
188
+ response,
189
+ testInputMetadata: metadata,
190
+ responseMetadata,
191
+ note: "External API returned error (expected behavior)",
192
+ };
193
+ }
194
+ // Real tool failure (not just validation or expected API error)
177
195
  return {
178
196
  toolName: tool.name,
179
197
  tested: true,
@@ -472,4 +490,48 @@ export class FunctionalityAssessor extends BaseAssessor {
472
490
  }
473
491
  return parts.join(" ");
474
492
  }
493
+ /**
494
+ * Issue #168: Check if an error response indicates an expected external API error.
495
+ * External APIs may return rate limit (429), service unavailable (503), timeout,
496
+ * or similar errors that are expected behavior, not broken functionality.
497
+ */
498
+ isExpectedAPIError(response) {
499
+ const content = this.extractResponseText(response);
500
+ if (!content)
501
+ return false;
502
+ // Match common external API error patterns
503
+ const expectedErrorPatterns = /rate\s*limit|429|503|service\s*unavailable|temporarily|timeout|connection\s*refused|network\s*error|api\s*error|external\s*service|upstream/i;
504
+ return expectedErrorPatterns.test(content);
505
+ }
506
+ /**
507
+ * Extract text content from a response for pattern matching.
508
+ */
509
+ extractResponseText(response) {
510
+ if (typeof response === "string")
511
+ return response;
512
+ if (!response || typeof response !== "object")
513
+ return "";
514
+ const obj = response;
515
+ // Check common response content locations
516
+ if (typeof obj.content === "string")
517
+ return obj.content;
518
+ if (typeof obj.message === "string")
519
+ return obj.message;
520
+ if (typeof obj.error === "string")
521
+ return obj.error;
522
+ // Handle MCP response format with content array
523
+ if (Array.isArray(obj.content)) {
524
+ return obj.content
525
+ .map((item) => {
526
+ if (typeof item === "string")
527
+ return item;
528
+ if (typeof item?.text === "string")
529
+ return item.text;
530
+ return "";
531
+ })
532
+ .join(" ");
533
+ }
534
+ // Fallback to JSON stringify for deep search
535
+ return JSON.stringify(response);
536
+ }
475
537
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProtocolComplianceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ProtocolComplianceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,2BAA2B,EAM3B,uBAAuB,EAMxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAOpE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAmB9D;;;GAGG;AACH,MAAM,WAAW,4BAA6B,SAAQ,2BAA2B;IAC/E,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE;QAClB,mBAAmB,EAAE,aAAa,CAAC;QACnC,kBAAkB,EAAE,aAAa,CAAC;QAClC,uBAAuB,EAAE,aAAa,CAAC;KACxC,CAAC;CACH;AAED,qBAAa,0BAA2B,SAAQ,YAAY,CAAC,4BAA4B,CAAC;IACxF,OAAO,CAAC,GAAG,CAAc;gBAEb,MAAM,EAAE,uBAAuB;IAK3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,4BAA4B,CAAC;IAyIxC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAyB9B;;OAEG;YACW,sBAAsB;IAuBpC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAwC7B;;OAEG;YACW,mBAAmB;IAiCjC;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAiDnC;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAkEnC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;YACW,wBAAwB;IA4GtC;;OAEG;YACW,uBAAuB;IA2FrC;;OAEG;YACW,4BAA4B;IAoD1C,OAAO,CAAC,yBAAyB;IAkEjC,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,sBAAsB;IA0B9B,OAAO,CAAC,qBAAqB;IAgC7B,OAAO,CAAC,oBAAoB;IA8E5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoC3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAqEhC"}
1
+ {"version":3,"file":"ProtocolComplianceAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ProtocolComplianceAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,2BAA2B,EAM3B,uBAAuB,EAMxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAOpE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAmB9D;;;GAGG;AACH,MAAM,WAAW,4BAA6B,SAAQ,2BAA2B;IAC/E,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE;QAClB,mBAAmB,EAAE,aAAa,CAAC;QACnC,kBAAkB,EAAE,aAAa,CAAC;QAClC,uBAAuB,EAAE,aAAa,CAAC;KACxC,CAAC;CACH;AAED,qBAAa,0BAA2B,SAAQ,YAAY,CAAC,4BAA4B,CAAC;IACxF,OAAO,CAAC,GAAG,CAAc;gBAEb,MAAM,EAAE,uBAAuB;IAK3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACG,MAAM,CACV,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,4BAA4B,CAAC;IAyIxC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAyB9B;;OAEG;YACW,sBAAsB;IAuBpC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAwC7B;;OAEG;YACW,mBAAmB;IAiCjC;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAiDnC;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAkEnC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;YACW,wBAAwB;IA4GtC;;OAEG;YACW,uBAAuB;IA2FrC;;OAEG;YACW,4BAA4B;IAoD1C,OAAO,CAAC,yBAAyB;IAwGjC,OAAO,CAAC,uBAAuB;IAqB/B,OAAO,CAAC,sBAAsB;IA0B9B,OAAO,CAAC,qBAAqB;IAgC7B,OAAO,CAAC,oBAAoB;IA8E5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoC3B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CAqEhC"}
@@ -587,6 +587,36 @@ export class ProtocolComplianceAssessor extends BaseAssessor {
587
587
  // Legacy compatibility methods (from MCPSpecComplianceAssessor)
588
588
  // ============================================================================
589
589
  assessTransportCompliance(context) {
590
+ // Issue #172: Check source-based transport detection first
591
+ // This fixes incorrect FAIL for valid stdio servers without serverInfo metadata
592
+ if (context.transportDetection?.supportsStdio) {
593
+ return {
594
+ supportsStreamableHTTP: context.transportDetection.supportsHTTP,
595
+ deprecatedSSE: context.transportDetection.supportsSSE,
596
+ transportValidation: "passed",
597
+ supportsStdio: true,
598
+ supportsSSE: context.transportDetection.supportsSSE,
599
+ confidence: context.transportDetection.confidence,
600
+ detectionMethod: "source-code-analysis",
601
+ requiresManualCheck: false,
602
+ transportEvidence: context.transportDetection.evidence.map((e) => `${e.source}: ${e.detail}`),
603
+ };
604
+ }
605
+ // Also check HTTP-only detection (no serverInfo but HTTP transport detected)
606
+ if (context.transportDetection?.supportsHTTP &&
607
+ !context.transportDetection?.supportsStdio) {
608
+ return {
609
+ supportsStreamableHTTP: true,
610
+ deprecatedSSE: context.transportDetection.supportsSSE,
611
+ transportValidation: "passed",
612
+ supportsStdio: false,
613
+ supportsSSE: context.transportDetection.supportsSSE,
614
+ confidence: context.transportDetection.confidence,
615
+ detectionMethod: "source-code-analysis",
616
+ requiresManualCheck: false,
617
+ transportEvidence: context.transportDetection.evidence.map((e) => `${e.source}: ${e.detail}`),
618
+ };
619
+ }
590
620
  if (!context.serverInfo) {
591
621
  return {
592
622
  supportsStreamableHTTP: false,
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAInB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAiB9D,OAAO,EACL,gBAAgB,EAGjB,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,YAAY,CAAiC;IAErD;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAStD;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAOjC;;;OAGG;YACW,0BAA0B;gBAwBtC,MAAM,EAAE,OAAO,8BAA8B,EAAE,uBAAuB;IAwClE,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA8PrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoC7B;;OAEG;YACW,+BAA+B;IAiC7C;;;OAGG;YACW,yBAAyB;IA0CvC;;;;;;;OAOG;YACW,yBAAyB;IAmFvC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAkEnC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;CAgDnC"}
1
+ {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,kBAAkB,EAInB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAiB9D,OAAO,EACL,gBAAgB,EAGjB,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,YAAY,CAAiC;IAErD;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAStD;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAOjC;;;OAGG;YACW,0BAA0B;gBAwBtC,MAAM,EAAE,OAAO,8BAA8B,EAAE,uBAAuB;IAwClE,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAyQrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoC7B;;OAEG;YACW,+BAA+B;IAiC7C;;;OAGG;YACW,yBAAyB;IA0CvC;;;;;;;OAOG;YACW,yBAAyB;IAmFvC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAkEnC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;CAgDnC"}
@@ -101,6 +101,12 @@ export class SecurityAssessor extends BaseAssessor {
101
101
  async assess(context) {
102
102
  // Select tools for testing first
103
103
  const toolsToTest = this.selectToolsForTesting(context.tools);
104
+ // Issue #170: Set tool annotations context for severity adjustment
105
+ // This enables annotation-aware false positive reduction for read-only servers
106
+ if (!context.toolAnnotationsContext) {
107
+ this.logger.warn("No tool annotations context provided - severity adjustment disabled");
108
+ }
109
+ this.payloadTester.setToolAnnotationsContext(context.toolAnnotationsContext);
104
110
  // Run universal security testing via extracted payload tester
105
111
  const allTests = await this.payloadTester.runUniversalSecurityTests(toolsToTest, context.callTool, context.onProgress);
106
112
  // Separate connection errors from valid tests