@bryan-thompson/inspector-assessment-client 1.26.7 → 1.27.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/dist/assets/{OAuthCallback-CCWVtjr7.js → OAuthCallback-CJWH8Ytw.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-DqbXfUi4.js → OAuthDebugCallback-DL5adXJw.js} +1 -1
- package/dist/assets/{index-CsDJSSWq.js → index-Cu9XzUwB.js} +4 -4
- package/dist/index.html +1 -1
- package/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
- package/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ErrorHandlingAssessor.js +119 -5
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-
|
|
1
|
+
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-Cu9XzUwB.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-
|
|
1
|
+
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-Cu9XzUwB.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -16373,7 +16373,7 @@ object({
|
|
|
16373
16373
|
token_type_hint: string().optional()
|
|
16374
16374
|
}).strip();
|
|
16375
16375
|
const name = "@bryan-thompson/inspector-assessment-client";
|
|
16376
|
-
const version$1 = "1.
|
|
16376
|
+
const version$1 = "1.27.0";
|
|
16377
16377
|
const packageJson = {
|
|
16378
16378
|
name,
|
|
16379
16379
|
version: version$1
|
|
@@ -45288,7 +45288,7 @@ const useTheme = () => {
|
|
|
45288
45288
|
[theme, setThemeWithSideEffect]
|
|
45289
45289
|
);
|
|
45290
45290
|
};
|
|
45291
|
-
const version = "1.
|
|
45291
|
+
const version = "1.27.0";
|
|
45292
45292
|
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
45293
45293
|
createPopperScope
|
|
45294
45294
|
]);
|
|
@@ -48845,13 +48845,13 @@ const App = () => {
|
|
|
48845
48845
|
) });
|
|
48846
48846
|
if (window.location.pathname === "/oauth/callback") {
|
|
48847
48847
|
const OAuthCallback = React.lazy(
|
|
48848
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
48848
|
+
() => __vitePreload(() => import("./OAuthCallback-CJWH8Ytw.js"), true ? [] : void 0)
|
|
48849
48849
|
);
|
|
48850
48850
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
48851
48851
|
}
|
|
48852
48852
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
48853
48853
|
const OAuthDebugCallback = React.lazy(
|
|
48854
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
48854
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-DL5adXJw.js"), true ? [] : void 0)
|
|
48855
48855
|
);
|
|
48856
48856
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
48857
48857
|
}
|
package/dist/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Cu9XzUwB.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-cHhcEXbr.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
* Tests error handling and input validation
|
|
4
4
|
*/
|
|
5
5
|
import { ErrorHandlingAssessment } from "../../../lib/assessmentTypes.js";
|
|
6
|
+
import { AssessmentConfiguration } from "../../../lib/assessment/configTypes.js";
|
|
6
7
|
import { BaseAssessor } from "./BaseAssessor.js";
|
|
7
8
|
import { AssessmentContext } from "../AssessmentOrchestrator.js";
|
|
8
9
|
export declare class ErrorHandlingAssessor extends BaseAssessor {
|
|
10
|
+
private executionDetector;
|
|
11
|
+
private safeResponseDetector;
|
|
12
|
+
constructor(config: AssessmentConfiguration);
|
|
9
13
|
assess(context: AssessmentContext): Promise<ErrorHandlingAssessment>;
|
|
10
14
|
private selectToolsForTesting;
|
|
11
15
|
private testToolErrorHandling;
|
|
@@ -17,6 +21,27 @@ export declare class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
17
21
|
private generateWrongTypeParams;
|
|
18
22
|
private generateInvalidValueParams;
|
|
19
23
|
private generateParamsWithValue;
|
|
24
|
+
/**
|
|
25
|
+
* Analyze invalid_values response to determine scoring impact
|
|
26
|
+
* Issue #99: Contextual empty string validation scoring
|
|
27
|
+
*
|
|
28
|
+
* Classifications:
|
|
29
|
+
* - safe_rejection: Tool rejected with error (no penalty)
|
|
30
|
+
* - safe_reflection: Tool stored/echoed without executing (no penalty)
|
|
31
|
+
* - defensive_programming: Tool handled gracefully (no penalty)
|
|
32
|
+
* - execution_detected: Tool executed input (penalty)
|
|
33
|
+
* - unknown: Cannot determine (partial penalty)
|
|
34
|
+
*/
|
|
35
|
+
private analyzeInvalidValuesResponse;
|
|
36
|
+
/**
|
|
37
|
+
* Safely extract response text from various response formats
|
|
38
|
+
*/
|
|
39
|
+
private extractResponseTextSafe;
|
|
40
|
+
/**
|
|
41
|
+
* Check for defensive programming patterns - tool accepted but caused no harm
|
|
42
|
+
* Examples: "Deleted 0 keys", "No results found", "Query returned 0"
|
|
43
|
+
*/
|
|
44
|
+
private isDefensiveProgrammingResponse;
|
|
20
45
|
private calculateMetrics;
|
|
21
46
|
private determineErrorHandlingStatus;
|
|
22
47
|
private generateExplanation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorHandlingAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ErrorHandlingAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAIxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"ErrorHandlingAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ErrorHandlingAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAIxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAK9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,oBAAoB,CAAuB;gBAEvC,MAAM,EAAE,uBAAuB;IAMrC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAiE1E,OAAO,CAAC,qBAAqB;YAuDf,qBAAqB;YAuBrB,qBAAqB;YAmGrB,cAAc;YAmFd,iBAAiB;YA8DjB,kBAAkB;IA6DhC,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,uBAAuB;IA4B/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,4BAA4B;IAgEpC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAetC,OAAO,CAAC,gBAAgB;IA8GxB,OAAO,CAAC,4BAA4B;IAapC,OAAO,CAAC,mBAAmB;IAuE3B,OAAO,CAAC,uBAAuB;CA4ChC"}
|
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { BaseAssessor } from "./BaseAssessor.js";
|
|
6
6
|
import { createConcurrencyLimit } from "../lib/concurrencyLimit.js";
|
|
7
|
+
import { ExecutionArtifactDetector } from "./securityTests/ExecutionArtifactDetector.js";
|
|
8
|
+
import { SafeResponseDetector } from "./securityTests/SafeResponseDetector.js";
|
|
7
9
|
export class ErrorHandlingAssessor extends BaseAssessor {
|
|
10
|
+
executionDetector;
|
|
11
|
+
safeResponseDetector;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super(config);
|
|
14
|
+
this.executionDetector = new ExecutionArtifactDetector();
|
|
15
|
+
this.safeResponseDetector = new SafeResponseDetector();
|
|
16
|
+
}
|
|
8
17
|
async assess(context) {
|
|
9
18
|
this.logger.info("Starting error handling assessment");
|
|
10
19
|
const testDetails = [];
|
|
@@ -428,17 +437,122 @@ export class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
428
437
|
return params;
|
|
429
438
|
}
|
|
430
439
|
// isErrorResponse and extractErrorInfo moved to BaseAssessor for reuse across all assessors
|
|
440
|
+
/**
|
|
441
|
+
* Analyze invalid_values response to determine scoring impact
|
|
442
|
+
* Issue #99: Contextual empty string validation scoring
|
|
443
|
+
*
|
|
444
|
+
* Classifications:
|
|
445
|
+
* - safe_rejection: Tool rejected with error (no penalty)
|
|
446
|
+
* - safe_reflection: Tool stored/echoed without executing (no penalty)
|
|
447
|
+
* - defensive_programming: Tool handled gracefully (no penalty)
|
|
448
|
+
* - execution_detected: Tool executed input (penalty)
|
|
449
|
+
* - unknown: Cannot determine (partial penalty)
|
|
450
|
+
*/
|
|
451
|
+
analyzeInvalidValuesResponse(test) {
|
|
452
|
+
const responseText = this.extractResponseTextSafe(test.actualResponse.rawResponse);
|
|
453
|
+
// Case 1: Tool rejected with error - best case (no penalty)
|
|
454
|
+
if (test.actualResponse.isError) {
|
|
455
|
+
return {
|
|
456
|
+
shouldPenalize: false,
|
|
457
|
+
penaltyAmount: 0,
|
|
458
|
+
classification: "safe_rejection",
|
|
459
|
+
reason: "Tool properly rejected invalid input",
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
// Case 2: Defensive programming patterns (no penalty)
|
|
463
|
+
// Check BEFORE execution detection because patterns like "query returned 0"
|
|
464
|
+
// might match execution indicators but are actually safe
|
|
465
|
+
if (this.isDefensiveProgrammingResponse(responseText)) {
|
|
466
|
+
return {
|
|
467
|
+
shouldPenalize: false,
|
|
468
|
+
penaltyAmount: 0,
|
|
469
|
+
classification: "defensive_programming",
|
|
470
|
+
reason: "Tool handled empty input defensively",
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
// Case 3: Safe reflection patterns (no penalty)
|
|
474
|
+
if (this.safeResponseDetector.isReflectionResponse(responseText)) {
|
|
475
|
+
return {
|
|
476
|
+
shouldPenalize: false,
|
|
477
|
+
penaltyAmount: 0,
|
|
478
|
+
classification: "safe_reflection",
|
|
479
|
+
reason: "Tool safely reflected input without execution",
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
// Case 4: Check for execution evidence - VULNERABLE (full penalty)
|
|
483
|
+
if (this.executionDetector.hasExecutionEvidence(responseText) ||
|
|
484
|
+
this.executionDetector.detectExecutionArtifacts(responseText)) {
|
|
485
|
+
return {
|
|
486
|
+
shouldPenalize: true,
|
|
487
|
+
penaltyAmount: 100,
|
|
488
|
+
classification: "execution_detected",
|
|
489
|
+
reason: "Tool executed input without validation",
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
// Case 5: Unknown - partial penalty for manual review
|
|
493
|
+
return {
|
|
494
|
+
shouldPenalize: true,
|
|
495
|
+
penaltyAmount: 25,
|
|
496
|
+
classification: "unknown",
|
|
497
|
+
reason: "Unable to determine safety - manual review recommended",
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Safely extract response text from various response formats
|
|
502
|
+
*/
|
|
503
|
+
extractResponseTextSafe(rawResponse) {
|
|
504
|
+
if (typeof rawResponse === "string")
|
|
505
|
+
return rawResponse;
|
|
506
|
+
if (rawResponse && typeof rawResponse === "object") {
|
|
507
|
+
const resp = rawResponse;
|
|
508
|
+
if (resp.content && Array.isArray(resp.content)) {
|
|
509
|
+
return resp.content
|
|
510
|
+
.map((c) => (c.type === "text" ? c.text : ""))
|
|
511
|
+
.join(" ");
|
|
512
|
+
}
|
|
513
|
+
return JSON.stringify(rawResponse);
|
|
514
|
+
}
|
|
515
|
+
return String(rawResponse || "");
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Check for defensive programming patterns - tool accepted but caused no harm
|
|
519
|
+
* Examples: "Deleted 0 keys", "No results found", "Query returned 0"
|
|
520
|
+
*/
|
|
521
|
+
isDefensiveProgrammingResponse(responseText) {
|
|
522
|
+
// Patterns for safe "no-op" responses where tool handled empty input gracefully
|
|
523
|
+
// Use word boundaries (\b) to avoid matching numbers like "10" or "15"
|
|
524
|
+
const patterns = [
|
|
525
|
+
/deleted\s+0\s+(keys?|records?|rows?|items?)/i,
|
|
526
|
+
/no\s+(results?|matches?|items?)\s+found/i,
|
|
527
|
+
/\b0\s+items?\s+(deleted|updated|processed)/i, // \b prevents matching "10 items"
|
|
528
|
+
/nothing\s+to\s+(delete|update|process)/i,
|
|
529
|
+
/empty\s+(result|response|query)/i,
|
|
530
|
+
/no\s+action\s+taken/i,
|
|
531
|
+
/query\s+returned\s+0\b/i, // \b prevents matching "query returned 05" etc.
|
|
532
|
+
];
|
|
533
|
+
return patterns.some((p) => p.test(responseText));
|
|
534
|
+
}
|
|
431
535
|
calculateMetrics(tests, _passed) {
|
|
432
536
|
// Calculate enhanced score with bonus points for quality
|
|
433
537
|
let enhancedScore = 0;
|
|
434
538
|
let maxPossibleScore = 0;
|
|
435
539
|
tests.forEach((test) => {
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
540
|
+
// Issue #99: Contextual scoring for invalid_values tests
|
|
541
|
+
// Instead of blanket exclusion, analyze response patterns to determine if
|
|
542
|
+
// the tool safely handled empty strings (defensive programming, reflection)
|
|
543
|
+
// or if it executed without validation (security concern).
|
|
440
544
|
if (test.testType === "invalid_values") {
|
|
441
|
-
|
|
545
|
+
const analysis = this.analyzeInvalidValuesResponse(test);
|
|
546
|
+
if (!analysis.shouldPenalize) {
|
|
547
|
+
// Safe response (rejection, reflection, or defensive programming)
|
|
548
|
+
// Skip scoring to preserve backward compatibility for well-behaved tools
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
// Execution detected or unknown - include in scoring with penalty
|
|
552
|
+
maxPossibleScore += 100;
|
|
553
|
+
const scoreEarned = 100 * (1 - analysis.penaltyAmount / 100);
|
|
554
|
+
enhancedScore += test.passed ? scoreEarned : 0;
|
|
555
|
+
return;
|
|
442
556
|
}
|
|
443
557
|
maxPossibleScore += 100; // Base score for each test
|
|
444
558
|
if (test.passed) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"description": "Client-side application for the Enhanced MCP Inspector with assessment capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|