@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.
- package/cli/build/lib/assessment-runner/assessment-executor.js +40 -0
- package/cli/build/lib/assessment-runner/source-loader.js +11 -0
- package/cli/package.json +1 -1
- package/client/dist/assets/{OAuthCallback-DJ1av7om.js → OAuthCallback-AngeBaCl.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-lRXgX7wV.js → OAuthDebugCallback--FE6_fPs.js} +1 -1
- package/client/dist/assets/{index-DEdS99fp.js → index-BQC95Boo.js} +4 -4
- package/client/dist/index.html +1 -1
- package/client/lib/lib/assessment/coreTypes.d.ts +37 -0
- package/client/lib/lib/assessment/coreTypes.d.ts.map +1 -1
- package/client/lib/lib/assessment/resultTypes.d.ts +30 -1
- 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/lib/securityPatterns/advancedExploitPatterns.d.ts +13 -0
- package/client/lib/lib/securityPatterns/advancedExploitPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/advancedExploitPatterns.js +504 -0
- package/client/lib/lib/securityPatterns/authSessionPatterns.d.ts +12 -0
- package/client/lib/lib/securityPatterns/authSessionPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/authSessionPatterns.js +357 -0
- package/client/lib/lib/securityPatterns/index.d.ts +18 -0
- package/client/lib/lib/securityPatterns/index.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/index.js +18 -0
- package/client/lib/lib/securityPatterns/injectionPatterns.d.ts +13 -0
- package/client/lib/lib/securityPatterns/injectionPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/injectionPatterns.js +356 -0
- package/client/lib/lib/securityPatterns/resourceExhaustionPatterns.d.ts +12 -0
- package/client/lib/lib/securityPatterns/resourceExhaustionPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/resourceExhaustionPatterns.js +215 -0
- package/client/lib/lib/securityPatterns/toolSpecificPatterns.d.ts +13 -0
- package/client/lib/lib/securityPatterns/toolSpecificPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/toolSpecificPatterns.js +373 -0
- package/client/lib/lib/securityPatterns/types.d.ts +20 -0
- package/client/lib/lib/securityPatterns/types.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/types.js +6 -0
- package/client/lib/lib/securityPatterns/utils.d.ts +56 -0
- package/client/lib/lib/securityPatterns/utils.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/utils.js +96 -0
- package/client/lib/lib/securityPatterns/validationPatterns.d.ts +13 -0
- package/client/lib/lib/securityPatterns/validationPatterns.d.ts.map +1 -0
- package/client/lib/lib/securityPatterns/validationPatterns.js +110 -0
- package/client/lib/lib/securityPatterns.d.ts +18 -69
- package/client/lib/lib/securityPatterns.d.ts.map +1 -1
- package/client/lib/lib/securityPatterns.js +18 -1946
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +6 -1
- 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 +165 -0
- package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.d.ts.map +1 -0
- package/client/lib/services/assessment/helpers/ExternalAPIDependencyDetector.js +317 -0
- package/client/lib/services/assessment/helpers/StdioTransportDetector.d.ts +137 -0
- package/client/lib/services/assessment/helpers/StdioTransportDetector.d.ts.map +1 -0
- package/client/lib/services/assessment/helpers/StdioTransportDetector.js +315 -0
- package/client/lib/services/assessment/helpers/ToolAnnotationExtractor.d.ts +34 -0
- package/client/lib/services/assessment/helpers/ToolAnnotationExtractor.d.ts.map +1 -0
- package/client/lib/services/assessment/helpers/ToolAnnotationExtractor.js +85 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +23 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +255 -20
- 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/ProtocolComplianceAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.js +30 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/SecurityAssessor.js +6 -0
- 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/DescriptionPoisoningDetector.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/annotations/DescriptionPoisoningDetector.js +16 -7
- package/client/lib/services/assessment/modules/securityTests/AnnotationAwareSeverity.d.ts +55 -0
- package/client/lib/services/assessment/modules/securityTests/AnnotationAwareSeverity.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/securityTests/AnnotationAwareSeverity.js +135 -0
- 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/SafeResponseDetector.d.ts +6 -0
- package/client/lib/services/assessment/modules/securityTests/SafeResponseDetector.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SafeResponseDetector.js +9 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts +43 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPatternLibrary.js +87 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +39 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +93 -3
- package/client/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityResponseAnalyzer.js +10 -1
- package/client/lib/services/assessment/modules/securityTests/index.d.ts +1 -0
- package/client/lib/services/assessment/modules/securityTests/index.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/index.js +1 -0
- 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
|
@@ -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
|
-
|
|
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
|
-
|
|
504
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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;
|
|
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
|
-
//
|
|
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;
|
|
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;
|
|
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
|