@bryan-thompson/inspector-assessment-client 1.22.0 → 1.22.2
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-WTqbw7IC.js → OAuthCallback-BkruhH1p.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-BxTTiZ6G.js → OAuthDebugCallback-T5sA9xQb.js} +1 -1
- package/dist/assets/{index-DIZLNGF3.js → index-R4iLdUb0.js} +97 -5
- package/dist/index.html +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +5 -5
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +100 -65
- package/lib/services/assessment/modules/SecurityAssessor.d.ts +11 -0
- package/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/SecurityAssessor.js +98 -0
- 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-R4iLdUb0.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-R4iLdUb0.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -16320,7 +16320,7 @@ object({
|
|
|
16320
16320
|
token_type_hint: string().optional()
|
|
16321
16321
|
}).strip();
|
|
16322
16322
|
const name = "@bryan-thompson/inspector-assessment-client";
|
|
16323
|
-
const version$1 = "1.
|
|
16323
|
+
const version$1 = "1.22.1";
|
|
16324
16324
|
const packageJson = {
|
|
16325
16325
|
name,
|
|
16326
16326
|
version: version$1
|
|
@@ -45352,7 +45352,7 @@ const useTheme = () => {
|
|
|
45352
45352
|
[theme, setThemeWithSideEffect]
|
|
45353
45353
|
);
|
|
45354
45354
|
};
|
|
45355
|
-
const version = "1.
|
|
45355
|
+
const version = "1.22.1";
|
|
45356
45356
|
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
45357
45357
|
createPopperScope
|
|
45358
45358
|
]);
|
|
@@ -53359,6 +53359,12 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
53359
53359
|
evidence: "Tool safely reflected input without execution"
|
|
53360
53360
|
};
|
|
53361
53361
|
}
|
|
53362
|
+
if (this.isComputedMathResult(payload.payload, responseText)) {
|
|
53363
|
+
return {
|
|
53364
|
+
isVulnerable: true,
|
|
53365
|
+
evidence: `Tool computed math expression result instead of storing/echoing it (payload: ${payload.payload})`
|
|
53366
|
+
};
|
|
53367
|
+
}
|
|
53362
53368
|
if (this.isValidationRejection(response)) {
|
|
53363
53369
|
return {
|
|
53364
53370
|
isVulnerable: false,
|
|
@@ -53537,6 +53543,72 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
53537
53543
|
];
|
|
53538
53544
|
return executionIndicators.some((pattern2) => pattern2.test(responseText));
|
|
53539
53545
|
}
|
|
53546
|
+
/**
|
|
53547
|
+
* Check if a math expression payload was computed (execution evidence)
|
|
53548
|
+
* Returns true if the response contains the computed result but NOT the original expression.
|
|
53549
|
+
*
|
|
53550
|
+
* This is key evidence of execution:
|
|
53551
|
+
* - VULNERABLE: payload "15*37+100" → response contains "655" (computed result)
|
|
53552
|
+
* - SAFE: payload "15*37+100" → response contains "15*37+100" (echoed, not computed)
|
|
53553
|
+
*
|
|
53554
|
+
* Added for Issue #14: False positives on safe input reflection
|
|
53555
|
+
*/
|
|
53556
|
+
isComputedMathResult(payload, responseText) {
|
|
53557
|
+
const simpleMathPattern = /^\s*(\d+)\s*([+\-*\/])\s*(\d+)(?:\s*([+\-*\/])\s*(\d+))?\s*$/;
|
|
53558
|
+
const match = payload.match(simpleMathPattern);
|
|
53559
|
+
if (!match) {
|
|
53560
|
+
return false;
|
|
53561
|
+
}
|
|
53562
|
+
try {
|
|
53563
|
+
const num1 = parseInt(match[1], 10);
|
|
53564
|
+
const op1 = match[2];
|
|
53565
|
+
const num2 = parseInt(match[3], 10);
|
|
53566
|
+
const op2 = match[4];
|
|
53567
|
+
const num3 = match[5] ? parseInt(match[5], 10) : void 0;
|
|
53568
|
+
let result;
|
|
53569
|
+
switch (op1) {
|
|
53570
|
+
case "+":
|
|
53571
|
+
result = num1 + num2;
|
|
53572
|
+
break;
|
|
53573
|
+
case "-":
|
|
53574
|
+
result = num1 - num2;
|
|
53575
|
+
break;
|
|
53576
|
+
case "*":
|
|
53577
|
+
result = num1 * num2;
|
|
53578
|
+
break;
|
|
53579
|
+
case "/":
|
|
53580
|
+
result = Math.floor(num1 / num2);
|
|
53581
|
+
break;
|
|
53582
|
+
default:
|
|
53583
|
+
return false;
|
|
53584
|
+
}
|
|
53585
|
+
if (op2 && num3 !== void 0) {
|
|
53586
|
+
switch (op2) {
|
|
53587
|
+
case "+":
|
|
53588
|
+
result = result + num3;
|
|
53589
|
+
break;
|
|
53590
|
+
case "-":
|
|
53591
|
+
result = result - num3;
|
|
53592
|
+
break;
|
|
53593
|
+
case "*":
|
|
53594
|
+
result = result * num3;
|
|
53595
|
+
break;
|
|
53596
|
+
case "/":
|
|
53597
|
+
result = Math.floor(result / num3);
|
|
53598
|
+
break;
|
|
53599
|
+
default:
|
|
53600
|
+
return false;
|
|
53601
|
+
}
|
|
53602
|
+
}
|
|
53603
|
+
const resultStr = result.toString();
|
|
53604
|
+
const hasComputedResult = responseText.includes(resultStr);
|
|
53605
|
+
const normalizedPayload = payload.replace(/\s+/g, "");
|
|
53606
|
+
const hasOriginalExpression = responseText.includes(payload) || responseText.includes(normalizedPayload);
|
|
53607
|
+
return hasComputedResult && !hasOriginalExpression;
|
|
53608
|
+
} catch {
|
|
53609
|
+
return false;
|
|
53610
|
+
}
|
|
53611
|
+
}
|
|
53540
53612
|
/**
|
|
53541
53613
|
* Perform additional security checks
|
|
53542
53614
|
*/
|
|
@@ -53841,7 +53913,27 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
53841
53913
|
/data.?stored.?safely/i,
|
|
53842
53914
|
/without.?deserialization/i,
|
|
53843
53915
|
/no.?pickle/i,
|
|
53844
|
-
/stored.?without.?deserializ/i
|
|
53916
|
+
/stored.?without.?deserializ/i,
|
|
53917
|
+
// NEW: Hash-based sanitization patterns (Issue #14 fix)
|
|
53918
|
+
// These indicate the tool replaced dangerous input with safe hash identifiers
|
|
53919
|
+
/\[ref-[a-f0-9]+\]/i,
|
|
53920
|
+
// Hash-based sanitization: [ref-a1b2c3d4]
|
|
53921
|
+
/stored.*\[ref-/i,
|
|
53922
|
+
// "Expression stored: [ref-...]"
|
|
53923
|
+
/\[sanitized\]/i,
|
|
53924
|
+
// [sanitized] placeholder
|
|
53925
|
+
/\[redacted\]/i,
|
|
53926
|
+
// [redacted] placeholder
|
|
53927
|
+
/\[filtered\]/i,
|
|
53928
|
+
// [filtered] placeholder
|
|
53929
|
+
/\[blocked\]/i,
|
|
53930
|
+
// [blocked] placeholder
|
|
53931
|
+
/expression.*stored:/i,
|
|
53932
|
+
// "Expression stored:" prefix
|
|
53933
|
+
/input.*sanitized/i,
|
|
53934
|
+
// "Input sanitized"
|
|
53935
|
+
/content.*replaced/i
|
|
53936
|
+
// "Content replaced with hash"
|
|
53845
53937
|
];
|
|
53846
53938
|
const hasReflection = reflectionPatterns.some(
|
|
53847
53939
|
(pattern2) => pattern2.test(responseText)
|
|
@@ -59174,13 +59266,13 @@ const App = () => {
|
|
|
59174
59266
|
) });
|
|
59175
59267
|
if (window.location.pathname === "/oauth/callback") {
|
|
59176
59268
|
const OAuthCallback = React.lazy(
|
|
59177
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
59269
|
+
() => __vitePreload(() => import("./OAuthCallback-BkruhH1p.js"), true ? [] : void 0)
|
|
59178
59270
|
);
|
|
59179
59271
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
59180
59272
|
}
|
|
59181
59273
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
59182
59274
|
const OAuthDebugCallback = React.lazy(
|
|
59183
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
59275
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-T5sA9xQb.js"), true ? [] : void 0)
|
|
59184
59276
|
);
|
|
59185
59277
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
59186
59278
|
}
|
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-R4iLdUb0.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-DiyPO_Zj.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -96,11 +96,11 @@ export declare class AssessmentOrchestrator {
|
|
|
96
96
|
private totalTestsRun;
|
|
97
97
|
private claudeBridge?;
|
|
98
98
|
private claudeEnabled;
|
|
99
|
-
private functionalityAssessor
|
|
100
|
-
private securityAssessor
|
|
101
|
-
private documentationAssessor
|
|
102
|
-
private errorHandlingAssessor
|
|
103
|
-
private usabilityAssessor
|
|
99
|
+
private functionalityAssessor?;
|
|
100
|
+
private securityAssessor?;
|
|
101
|
+
private documentationAssessor?;
|
|
102
|
+
private errorHandlingAssessor?;
|
|
103
|
+
private usabilityAssessor?;
|
|
104
104
|
private mcpSpecAssessor?;
|
|
105
105
|
private aupComplianceAssessor?;
|
|
106
106
|
private toolAnnotationAssessor?;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssessmentOrchestrator.d.ts","sourceRoot":"","sources":["../../../src/services/assessment/AssessmentOrchestrator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AssessmentOrchestrator.d.ts","sourceRoot":"","sources":["../../../src/services/assessment/AssessmentOrchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -150,7 +150,7 @@ export class AssessmentOrchestrator {
|
|
|
150
150
|
// Claude Code Bridge for intelligent analysis
|
|
151
151
|
claudeBridge;
|
|
152
152
|
claudeEnabled = false;
|
|
153
|
-
// Core assessors
|
|
153
|
+
// Core assessors (optional to support --skip-modules)
|
|
154
154
|
functionalityAssessor;
|
|
155
155
|
securityAssessor;
|
|
156
156
|
documentationAssessor;
|
|
@@ -176,12 +176,22 @@ export class AssessmentOrchestrator {
|
|
|
176
176
|
if (this.config.claudeCode?.enabled) {
|
|
177
177
|
this.initializeClaudeBridge(this.config.claudeCode);
|
|
178
178
|
}
|
|
179
|
-
// Initialize core assessors
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
// Initialize core assessors (respects assessmentCategories config for --skip-modules)
|
|
180
|
+
if (this.config.assessmentCategories?.functionality !== false) {
|
|
181
|
+
this.functionalityAssessor = new FunctionalityAssessor(this.config);
|
|
182
|
+
}
|
|
183
|
+
if (this.config.assessmentCategories?.security !== false) {
|
|
184
|
+
this.securityAssessor = new SecurityAssessor(this.config);
|
|
185
|
+
}
|
|
186
|
+
if (this.config.assessmentCategories?.documentation !== false) {
|
|
187
|
+
this.documentationAssessor = new DocumentationAssessor(this.config);
|
|
188
|
+
}
|
|
189
|
+
if (this.config.assessmentCategories?.errorHandling !== false) {
|
|
190
|
+
this.errorHandlingAssessor = new ErrorHandlingAssessor(this.config);
|
|
191
|
+
}
|
|
192
|
+
if (this.config.assessmentCategories?.usability !== false) {
|
|
193
|
+
this.usabilityAssessor = new UsabilityAssessor(this.config);
|
|
194
|
+
}
|
|
185
195
|
// Initialize extended assessors if enabled
|
|
186
196
|
if (this.config.enableExtendedAssessment) {
|
|
187
197
|
if (this.config.assessmentCategories?.mcpSpecCompliance) {
|
|
@@ -292,11 +302,11 @@ export class AssessmentOrchestrator {
|
|
|
292
302
|
* Reset test counts for all assessors
|
|
293
303
|
*/
|
|
294
304
|
resetAllTestCounts() {
|
|
295
|
-
this.functionalityAssessor
|
|
296
|
-
this.securityAssessor
|
|
297
|
-
this.documentationAssessor
|
|
298
|
-
this.errorHandlingAssessor
|
|
299
|
-
this.usabilityAssessor
|
|
305
|
+
this.functionalityAssessor?.resetTestCount();
|
|
306
|
+
this.securityAssessor?.resetTestCount();
|
|
307
|
+
this.documentationAssessor?.resetTestCount();
|
|
308
|
+
this.errorHandlingAssessor?.resetTestCount();
|
|
309
|
+
this.usabilityAssessor?.resetTestCount();
|
|
300
310
|
if (this.mcpSpecAssessor) {
|
|
301
311
|
this.mcpSpecAssessor.resetTestCount();
|
|
302
312
|
}
|
|
@@ -350,29 +360,42 @@ export class AssessmentOrchestrator {
|
|
|
350
360
|
// Calculate estimates for module_started events
|
|
351
361
|
const toolCount = context.tools.length;
|
|
352
362
|
const securityPatterns = this.config.securityPatternsToTest || 17;
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
363
|
+
// Core assessments - only emit and run if not skipped
|
|
364
|
+
if (this.functionalityAssessor) {
|
|
365
|
+
emitModuleStartedEvent("Functionality", toolCount * 10, toolCount);
|
|
366
|
+
assessmentPromises.push(this.functionalityAssessor.assess(context).then((r) => {
|
|
367
|
+
emitModuleProgress("Functionality", r.status, r, this.functionalityAssessor.getTestCount());
|
|
368
|
+
return (assessmentResults.functionality = r);
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
if (this.securityAssessor) {
|
|
372
|
+
emitModuleStartedEvent("Security", securityPatterns * toolCount, toolCount);
|
|
373
|
+
assessmentPromises.push(this.securityAssessor.assess(context).then((r) => {
|
|
374
|
+
emitModuleProgress("Security", r.status, r, this.securityAssessor.getTestCount());
|
|
375
|
+
return (assessmentResults.security = r);
|
|
376
|
+
}));
|
|
377
|
+
}
|
|
378
|
+
if (this.documentationAssessor) {
|
|
379
|
+
emitModuleStartedEvent("Documentation", 5, toolCount);
|
|
380
|
+
assessmentPromises.push(this.documentationAssessor.assess(context).then((r) => {
|
|
381
|
+
emitModuleProgress("Documentation", r.status, r, this.documentationAssessor.getTestCount());
|
|
382
|
+
return (assessmentResults.documentation = r);
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
if (this.errorHandlingAssessor) {
|
|
386
|
+
emitModuleStartedEvent("Error Handling", toolCount * 5, toolCount);
|
|
387
|
+
assessmentPromises.push(this.errorHandlingAssessor.assess(context).then((r) => {
|
|
388
|
+
emitModuleProgress("Error Handling", r.status, r, this.errorHandlingAssessor.getTestCount());
|
|
389
|
+
return (assessmentResults.errorHandling = r);
|
|
390
|
+
}));
|
|
391
|
+
}
|
|
392
|
+
if (this.usabilityAssessor) {
|
|
393
|
+
emitModuleStartedEvent("Usability", 10, toolCount);
|
|
394
|
+
assessmentPromises.push(this.usabilityAssessor.assess(context).then((r) => {
|
|
395
|
+
emitModuleProgress("Usability", r.status, r, this.usabilityAssessor.getTestCount());
|
|
396
|
+
return (assessmentResults.usability = r);
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
376
399
|
// Extended assessments
|
|
377
400
|
if (this.mcpSpecAssessor) {
|
|
378
401
|
emitModuleStartedEvent("MCP Spec", 10, toolCount);
|
|
@@ -460,30 +483,42 @@ export class AssessmentOrchestrator {
|
|
|
460
483
|
const toolCount = context.tools.length;
|
|
461
484
|
const securityPatterns = this.config.securityPatternsToTest || 17;
|
|
462
485
|
// NOTE: Temporal runs in PHASE 0 above, before sequential/parallel phases
|
|
463
|
-
//
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
486
|
+
// Core assessments - only emit and run if not skipped
|
|
487
|
+
if (this.functionalityAssessor) {
|
|
488
|
+
// Functionality: ~10 scenarios per tool
|
|
489
|
+
emitModuleStartedEvent("Functionality", toolCount * 10, toolCount);
|
|
490
|
+
assessmentResults.functionality =
|
|
491
|
+
await this.functionalityAssessor.assess(context);
|
|
492
|
+
emitModuleProgress("Functionality", assessmentResults.functionality.status, assessmentResults.functionality, this.functionalityAssessor.getTestCount());
|
|
493
|
+
}
|
|
494
|
+
if (this.securityAssessor) {
|
|
495
|
+
// Security: patterns × tools
|
|
496
|
+
emitModuleStartedEvent("Security", securityPatterns * toolCount, toolCount);
|
|
497
|
+
assessmentResults.security =
|
|
498
|
+
await this.securityAssessor.assess(context);
|
|
499
|
+
emitModuleProgress("Security", assessmentResults.security.status, assessmentResults.security, this.securityAssessor.getTestCount());
|
|
500
|
+
}
|
|
501
|
+
if (this.documentationAssessor) {
|
|
502
|
+
// Documentation: ~5 static tests
|
|
503
|
+
emitModuleStartedEvent("Documentation", 5, toolCount);
|
|
504
|
+
assessmentResults.documentation =
|
|
505
|
+
await this.documentationAssessor.assess(context);
|
|
506
|
+
emitModuleProgress("Documentation", assessmentResults.documentation.status, assessmentResults.documentation, this.documentationAssessor.getTestCount());
|
|
507
|
+
}
|
|
508
|
+
if (this.errorHandlingAssessor) {
|
|
509
|
+
// Error Handling: ~5 tests per tool
|
|
510
|
+
emitModuleStartedEvent("Error Handling", toolCount * 5, toolCount);
|
|
511
|
+
assessmentResults.errorHandling =
|
|
512
|
+
await this.errorHandlingAssessor.assess(context);
|
|
513
|
+
emitModuleProgress("Error Handling", assessmentResults.errorHandling.status, assessmentResults.errorHandling, this.errorHandlingAssessor.getTestCount());
|
|
514
|
+
}
|
|
515
|
+
if (this.usabilityAssessor) {
|
|
516
|
+
// Usability: ~10 static tests
|
|
517
|
+
emitModuleStartedEvent("Usability", 10, toolCount);
|
|
518
|
+
assessmentResults.usability =
|
|
519
|
+
await this.usabilityAssessor.assess(context);
|
|
520
|
+
emitModuleProgress("Usability", assessmentResults.usability.status, assessmentResults.usability, this.usabilityAssessor.getTestCount());
|
|
521
|
+
}
|
|
487
522
|
if (this.mcpSpecAssessor) {
|
|
488
523
|
emitModuleStartedEvent("MCP Spec", 10, toolCount);
|
|
489
524
|
assessmentResults.mcpSpecCompliance =
|
|
@@ -605,12 +640,12 @@ export class AssessmentOrchestrator {
|
|
|
605
640
|
}
|
|
606
641
|
collectTotalTestCount() {
|
|
607
642
|
let total = 0;
|
|
608
|
-
// Get actual test counts from assessors
|
|
609
|
-
const functionalityCount = this.functionalityAssessor
|
|
610
|
-
const securityCount = this.securityAssessor
|
|
611
|
-
const documentationCount = this.documentationAssessor
|
|
612
|
-
const errorHandlingCount = this.errorHandlingAssessor
|
|
613
|
-
const usabilityCount = this.usabilityAssessor
|
|
643
|
+
// Get actual test counts from assessors (optional for --skip-modules support)
|
|
644
|
+
const functionalityCount = this.functionalityAssessor?.getTestCount() || 0;
|
|
645
|
+
const securityCount = this.securityAssessor?.getTestCount() || 0;
|
|
646
|
+
const documentationCount = this.documentationAssessor?.getTestCount() || 0;
|
|
647
|
+
const errorHandlingCount = this.errorHandlingAssessor?.getTestCount() || 0;
|
|
648
|
+
const usabilityCount = this.usabilityAssessor?.getTestCount() || 0;
|
|
614
649
|
const mcpSpecCount = this.mcpSpecAssessor?.getTestCount() || 0;
|
|
615
650
|
// New assessor counts
|
|
616
651
|
const aupCount = this.aupComplianceAssessor?.getTestCount() || 0;
|
|
@@ -102,6 +102,17 @@ export declare class SecurityAssessor extends BaseAssessor {
|
|
|
102
102
|
* - VULNERABLE: "SQL executed: syntax error" (error DURING execution)
|
|
103
103
|
*/
|
|
104
104
|
private hasExecutionEvidence;
|
|
105
|
+
/**
|
|
106
|
+
* Check if a math expression payload was computed (execution evidence)
|
|
107
|
+
* Returns true if the response contains the computed result but NOT the original expression.
|
|
108
|
+
*
|
|
109
|
+
* This is key evidence of execution:
|
|
110
|
+
* - VULNERABLE: payload "15*37+100" → response contains "655" (computed result)
|
|
111
|
+
* - SAFE: payload "15*37+100" → response contains "15*37+100" (echoed, not computed)
|
|
112
|
+
*
|
|
113
|
+
* Added for Issue #14: False positives on safe input reflection
|
|
114
|
+
*/
|
|
115
|
+
private isComputedMathResult;
|
|
105
116
|
/**
|
|
106
117
|
* Perform additional security checks
|
|
107
118
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;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;AAc9D,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,iBAAiB,CAAuC;IAC1D,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;OAIG;YACW,yBAAyB;IAuKvC;;;;OAIG;YACW,qBAAqB;IA4JnC;;OAEG;YACW,WAAW;IA2HzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAgDzB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAiDtC;;OAEG;IACH,OAAO,CAAC,aAAa;IA+BrB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgClC;;;OAGG;IACH,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;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;AAc9D,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,iBAAiB,CAAuC;IAC1D,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;OAIG;YACW,yBAAyB;IAuKvC;;;;OAIG;YACW,qBAAqB;IA4JnC;;OAEG;YACW,WAAW;IA2HzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAgDzB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAiDtC;;OAEG;IACH,OAAO,CAAC,aAAa;IA+BrB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgClC;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuIvB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAiE7B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAqC5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAsB3B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAkC5B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IA8E5B;;OAEG;YACW,+BAA+B;IAiC7C;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAkEnC;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAuI3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAsB5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,oBAAoB;IAoN5B;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB;IAwDhC;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAuBtC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;IAoH5B;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACH,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAmB3B"}
|
|
@@ -646,6 +646,15 @@ export class SecurityAssessor extends BaseAssessor {
|
|
|
646
646
|
}
|
|
647
647
|
// Response doesn't match reflection patterns - continue checking
|
|
648
648
|
// Falls through - might be execution (with or without echo of input)
|
|
649
|
+
// ✅ STEP 1.7: Check for computed math results (Issue #14 fix)
|
|
650
|
+
// If payload is a math expression and response contains the computed result
|
|
651
|
+
// (but NOT the original expression), that's evidence of execution
|
|
652
|
+
if (this.isComputedMathResult(payload.payload, responseText)) {
|
|
653
|
+
return {
|
|
654
|
+
isVulnerable: true,
|
|
655
|
+
evidence: `Tool computed math expression result instead of storing/echoing it (payload: ${payload.payload})`,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
649
658
|
// ✅ STEP 2: Check if tool explicitly rejected the input (SAFE)
|
|
650
659
|
// CRITICAL: Check this BEFORE evidence matching to prevent false positives
|
|
651
660
|
// Tools that reject invalid input are secure, regardless of error message content
|
|
@@ -849,6 +858,84 @@ export class SecurityAssessor extends BaseAssessor {
|
|
|
849
858
|
];
|
|
850
859
|
return executionIndicators.some((pattern) => pattern.test(responseText));
|
|
851
860
|
}
|
|
861
|
+
/**
|
|
862
|
+
* Check if a math expression payload was computed (execution evidence)
|
|
863
|
+
* Returns true if the response contains the computed result but NOT the original expression.
|
|
864
|
+
*
|
|
865
|
+
* This is key evidence of execution:
|
|
866
|
+
* - VULNERABLE: payload "15*37+100" → response contains "655" (computed result)
|
|
867
|
+
* - SAFE: payload "15*37+100" → response contains "15*37+100" (echoed, not computed)
|
|
868
|
+
*
|
|
869
|
+
* Added for Issue #14: False positives on safe input reflection
|
|
870
|
+
*/
|
|
871
|
+
isComputedMathResult(payload, responseText) {
|
|
872
|
+
// Check if payload looks like a simple math expression
|
|
873
|
+
// Matches: "2+2", "15*37+100", "10/2", "5-3", etc.
|
|
874
|
+
const simpleMathPattern = /^\s*(\d+)\s*([+\-*\/])\s*(\d+)(?:\s*([+\-*\/])\s*(\d+))?\s*$/;
|
|
875
|
+
const match = payload.match(simpleMathPattern);
|
|
876
|
+
if (!match) {
|
|
877
|
+
return false; // Not a simple math expression
|
|
878
|
+
}
|
|
879
|
+
// Try to safely evaluate the expression
|
|
880
|
+
try {
|
|
881
|
+
// Parse numbers and operators manually (avoid eval)
|
|
882
|
+
const num1 = parseInt(match[1], 10);
|
|
883
|
+
const op1 = match[2];
|
|
884
|
+
const num2 = parseInt(match[3], 10);
|
|
885
|
+
const op2 = match[4];
|
|
886
|
+
const num3 = match[5] ? parseInt(match[5], 10) : undefined;
|
|
887
|
+
let result;
|
|
888
|
+
// Calculate first operation
|
|
889
|
+
switch (op1) {
|
|
890
|
+
case "+":
|
|
891
|
+
result = num1 + num2;
|
|
892
|
+
break;
|
|
893
|
+
case "-":
|
|
894
|
+
result = num1 - num2;
|
|
895
|
+
break;
|
|
896
|
+
case "*":
|
|
897
|
+
result = num1 * num2;
|
|
898
|
+
break;
|
|
899
|
+
case "/":
|
|
900
|
+
result = Math.floor(num1 / num2);
|
|
901
|
+
break;
|
|
902
|
+
default:
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
// Calculate second operation if present (left-to-right, no precedence)
|
|
906
|
+
if (op2 && num3 !== undefined) {
|
|
907
|
+
switch (op2) {
|
|
908
|
+
case "+":
|
|
909
|
+
result = result + num3;
|
|
910
|
+
break;
|
|
911
|
+
case "-":
|
|
912
|
+
result = result - num3;
|
|
913
|
+
break;
|
|
914
|
+
case "*":
|
|
915
|
+
result = result * num3;
|
|
916
|
+
break;
|
|
917
|
+
case "/":
|
|
918
|
+
result = Math.floor(result / num3);
|
|
919
|
+
break;
|
|
920
|
+
default:
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
// Check if response contains the computed result
|
|
925
|
+
const resultStr = result.toString();
|
|
926
|
+
const hasComputedResult = responseText.includes(resultStr);
|
|
927
|
+
// Check if response also contains the original expression (reflection)
|
|
928
|
+
const normalizedPayload = payload.replace(/\s+/g, "");
|
|
929
|
+
const hasOriginalExpression = responseText.includes(payload) ||
|
|
930
|
+
responseText.includes(normalizedPayload);
|
|
931
|
+
// Vulnerable if: has computed result AND does NOT have original expression
|
|
932
|
+
// This means the tool executed the expression instead of just echoing it
|
|
933
|
+
return hasComputedResult && !hasOriginalExpression;
|
|
934
|
+
}
|
|
935
|
+
catch {
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
852
939
|
/**
|
|
853
940
|
* Perform additional security checks
|
|
854
941
|
*/
|
|
@@ -1195,6 +1282,17 @@ export class SecurityAssessor extends BaseAssessor {
|
|
|
1195
1282
|
/without.?deserialization/i,
|
|
1196
1283
|
/no.?pickle/i,
|
|
1197
1284
|
/stored.?without.?deserializ/i,
|
|
1285
|
+
// NEW: Hash-based sanitization patterns (Issue #14 fix)
|
|
1286
|
+
// These indicate the tool replaced dangerous input with safe hash identifiers
|
|
1287
|
+
/\[ref-[a-f0-9]+\]/i, // Hash-based sanitization: [ref-a1b2c3d4]
|
|
1288
|
+
/stored.*\[ref-/i, // "Expression stored: [ref-...]"
|
|
1289
|
+
/\[sanitized\]/i, // [sanitized] placeholder
|
|
1290
|
+
/\[redacted\]/i, // [redacted] placeholder
|
|
1291
|
+
/\[filtered\]/i, // [filtered] placeholder
|
|
1292
|
+
/\[blocked\]/i, // [blocked] placeholder
|
|
1293
|
+
/expression.*stored:/i, // "Expression stored:" prefix
|
|
1294
|
+
/input.*sanitized/i, // "Input sanitized"
|
|
1295
|
+
/content.*replaced/i, // "Content replaced with hash"
|
|
1198
1296
|
];
|
|
1199
1297
|
// LAYER 1: Check for reflection/status patterns
|
|
1200
1298
|
const hasReflection = reflectionPatterns.some((pattern) => pattern.test(responseText));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-client",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.2",
|
|
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>",
|