@bryan-thompson/inspector-assessment 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.
@@ -25,7 +25,26 @@ import { generatePolicyComplianceReport } from "../../client/lib/services/assess
25
25
  import { compareAssessments } from "../../client/lib/lib/assessmentDiffer.js";
26
26
  import { formatDiffAsMarkdown } from "../../client/lib/lib/reportFormatters/DiffReportFormatter.js";
27
27
  import { AssessmentStateManager } from "./assessmentState.js";
28
- import { emitServerConnected, emitToolDiscovered, emitToolsDiscoveryComplete, emitAssessmentComplete, emitTestBatch, emitVulnerabilityFound, emitAnnotationMissing, emitAnnotationMisaligned, emitAnnotationReviewRecommended, emitAnnotationAligned, } from "./lib/jsonl-events.js";
28
+ import { emitServerConnected, emitToolDiscovered, emitToolsDiscoveryComplete, emitAssessmentComplete, emitTestBatch, emitVulnerabilityFound, emitAnnotationMissing, emitAnnotationMisaligned, emitAnnotationReviewRecommended, emitAnnotationAligned, emitModulesConfigured, } from "./lib/jsonl-events.js";
29
+ // Valid module names derived from ASSESSMENT_CATEGORY_METADATA
30
+ const VALID_MODULE_NAMES = Object.keys(ASSESSMENT_CATEGORY_METADATA);
31
+ /**
32
+ * Validate module names from CLI input
33
+ */
34
+ function validateModuleNames(input, flagName) {
35
+ const names = input
36
+ .split(",")
37
+ .map((n) => n.trim())
38
+ .filter(Boolean);
39
+ const invalid = names.filter((n) => !VALID_MODULE_NAMES.includes(n));
40
+ if (invalid.length > 0) {
41
+ console.error(`Error: Invalid module name(s) for ${flagName}: ${invalid.join(", ")}`);
42
+ console.error(`Valid modules: ${VALID_MODULE_NAMES.join(", ")}`);
43
+ setTimeout(() => process.exit(1), 10);
44
+ return [];
45
+ }
46
+ return names;
47
+ }
29
48
  /**
30
49
  * Load server configuration from Claude Code's MCP settings
31
50
  */
@@ -317,7 +336,8 @@ function buildConfig(options) {
317
336
  enableSourceCodeAnalysis: !!options.sourceCodePath,
318
337
  };
319
338
  if (options.fullAssessment !== false) {
320
- config.assessmentCategories = {
339
+ // Start with all modules enabled by default
340
+ const allModules = {
321
341
  functionality: true,
322
342
  security: true,
323
343
  documentation: true,
@@ -335,7 +355,25 @@ function buildConfig(options) {
335
355
  resources: true,
336
356
  prompts: true,
337
357
  crossCapability: true,
358
+ authentication: true,
338
359
  };
360
+ // Apply --only-modules filter (whitelist mode)
361
+ if (options.onlyModules?.length) {
362
+ for (const key of Object.keys(allModules)) {
363
+ // Disable all modules except those in the whitelist
364
+ allModules[key] = options.onlyModules.includes(key);
365
+ }
366
+ }
367
+ // Apply --skip-modules filter (blacklist mode)
368
+ if (options.skipModules?.length) {
369
+ for (const module of options.skipModules) {
370
+ if (module in allModules) {
371
+ allModules[module] = false;
372
+ }
373
+ }
374
+ }
375
+ config.assessmentCategories =
376
+ allModules;
339
377
  }
340
378
  // Temporal/rug pull detection configuration
341
379
  if (options.temporalInvocations) {
@@ -519,6 +557,25 @@ async function runFullAssessment(options) {
519
557
  return {};
520
558
  }
521
559
  const config = buildConfig(options);
560
+ // Emit modules_configured event for consumer progress tracking
561
+ if (config.assessmentCategories) {
562
+ const enabled = [];
563
+ const skipped = [];
564
+ for (const [key, value] of Object.entries(config.assessmentCategories)) {
565
+ if (value) {
566
+ enabled.push(key);
567
+ }
568
+ else {
569
+ skipped.push(key);
570
+ }
571
+ }
572
+ const reason = options.onlyModules?.length
573
+ ? "only-modules"
574
+ : options.skipModules?.length
575
+ ? "skip-modules"
576
+ : "default";
577
+ emitModulesConfigured(enabled, skipped, reason);
578
+ }
522
579
  const orchestrator = new AssessmentOrchestrator(config);
523
580
  if (!options.jsonOnly) {
524
581
  if (orchestrator.isClaudeEnabled()) {
@@ -825,6 +882,36 @@ function parseArgs() {
825
882
  case "--skip-temporal":
826
883
  options.skipTemporal = true;
827
884
  break;
885
+ case "--skip-modules": {
886
+ const skipValue = args[++i];
887
+ if (!skipValue) {
888
+ console.error("Error: --skip-modules requires a comma-separated list");
889
+ setTimeout(() => process.exit(1), 10);
890
+ options.helpRequested = true;
891
+ return options;
892
+ }
893
+ options.skipModules = validateModuleNames(skipValue, "--skip-modules");
894
+ if (options.skipModules.length === 0 && skipValue) {
895
+ options.helpRequested = true;
896
+ return options;
897
+ }
898
+ break;
899
+ }
900
+ case "--only-modules": {
901
+ const onlyValue = args[++i];
902
+ if (!onlyValue) {
903
+ console.error("Error: --only-modules requires a comma-separated list");
904
+ setTimeout(() => process.exit(1), 10);
905
+ options.helpRequested = true;
906
+ return options;
907
+ }
908
+ options.onlyModules = validateModuleNames(onlyValue, "--only-modules");
909
+ if (options.onlyModules.length === 0 && onlyValue) {
910
+ options.helpRequested = true;
911
+ return options;
912
+ }
913
+ break;
914
+ }
828
915
  case "--help":
829
916
  case "-h":
830
917
  printHelp();
@@ -845,6 +932,13 @@ function parseArgs() {
845
932
  }
846
933
  }
847
934
  }
935
+ // Validate mutual exclusivity of --skip-modules and --only-modules
936
+ if (options.skipModules?.length && options.onlyModules?.length) {
937
+ console.error("Error: --skip-modules and --only-modules are mutually exclusive");
938
+ setTimeout(() => process.exit(1), 10);
939
+ options.helpRequested = true;
940
+ return options;
941
+ }
848
942
  if (!options.serverName) {
849
943
  console.error("Error: --server is required");
850
944
  printHelp();
@@ -880,11 +974,23 @@ Options:
880
974
  --full Enable all assessment modules (default)
881
975
  --temporal-invocations <n> Number of invocations per tool for rug pull detection (default: 25)
882
976
  --skip-temporal Skip temporal/rug pull testing (faster assessment)
977
+ --skip-modules <list> Skip specific modules (comma-separated)
978
+ --only-modules <list> Run only specific modules (comma-separated)
883
979
  --json Output only JSON path (no console summary)
884
980
  --verbose, -v Enable verbose logging
885
981
  --help, -h Show this help message
886
982
 
887
- Assessment Modules (12 total):
983
+ Module Selection:
984
+ --skip-modules and --only-modules are mutually exclusive.
985
+ Use --skip-modules for faster runs by disabling expensive modules.
986
+ Use --only-modules to focus on specific areas (e.g., tool annotation PRs).
987
+
988
+ Valid module names:
989
+ functionality, security, documentation, errorHandling, usability,
990
+ mcpSpecCompliance, aupCompliance, toolAnnotations, prohibitedLibraries,
991
+ manifestValidation, portability, temporal, resources, prompts, crossCapability
992
+
993
+ Assessment Modules (16 total):
888
994
  • Functionality - Tests all tools work correctly
889
995
  • Security - Prompt injection & vulnerability testing
890
996
  • Documentation - README completeness checks
@@ -905,6 +1011,10 @@ Examples:
905
1011
  mcp-assess-full --server my-server --format markdown --include-policy
906
1012
  mcp-assess-full --server my-server --compare ./baseline.json
907
1013
  mcp-assess-full --server my-server --compare ./baseline.json --diff-only --format markdown
1014
+
1015
+ # Module selection examples:
1016
+ mcp-assess-full my-server --skip-modules security,aupCompliance # Fast CI run
1017
+ mcp-assess-full my-server --only-modules functionality,toolAnnotations # Annotation PR review
908
1018
  `);
909
1019
  }
910
1020
  /**
@@ -178,3 +178,15 @@ export function emitAnnotationAligned(tool, confidence, annotations) {
178
178
  annotations,
179
179
  });
180
180
  }
181
+ /**
182
+ * Emit modules_configured event to inform consumers which modules are enabled.
183
+ * Useful for accurate progress tracking when using --skip-modules or --only-modules.
184
+ */
185
+ export function emitModulesConfigured(enabled, skipped, reason) {
186
+ emitJSONL({
187
+ event: "modules_configured",
188
+ enabled,
189
+ skipped,
190
+ reason,
191
+ });
192
+ }
@@ -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-BlnJHX-f.js";
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-BMuZNakj.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-BlnJHX-f.js";
1
+ import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-BMuZNakj.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.22.0";
16323
+ const version$1 = "1.22.2";
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.22.0";
45355
+ const version = "1.22.2";
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-CKq3cbse.js"), true ? [] : void 0)
59269
+ () => __vitePreload(() => import("./OAuthCallback-CHzhxAJt.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-CKb5HPif.js"), true ? [] : void 0)
59275
+ () => __vitePreload(() => import("./OAuthDebugCallback-Cw43Ht9M.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
  }
@@ -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-BlnJHX-f.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-BMuZNakj.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":"AAAA;;;GAGG;AAEH,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EAGvB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,IAAI,EACJ,2BAA2B,EAC5B,MAAM,oCAAoC,CAAC;AAiC5C,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAEvB,MAAM,wBAAwB,CAAC;AAgKhC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAClC,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IAIF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGtC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAG9B,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC1C,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,kBAAkB,CAAC,EAAE,qBAAqB,CAAC;IAG3C,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,CACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACzB,OAAO,CAAC;QAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IAGrE,eAAe,CAAC,EAAE;QAChB,IAAI,EAAE,OAAO,GAAG,KAAK,GAAG,iBAAiB,CAAC;QAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;IAIF,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;CACnC;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,aAAa,CAAa;IAGlC,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,aAAa,CAAkB;IAGvC,OAAO,CAAC,qBAAqB,CAAC,CAAwB;IACtD,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,qBAAqB,CAAC,CAAwB;IACtD,OAAO,CAAC,qBAAqB,CAAC,CAAwB;IACtD,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAG9C,OAAO,CAAC,eAAe,CAAC,CAA4B;IAGpD,OAAO,CAAC,qBAAqB,CAAC,CAAwB;IACtD,OAAO,CAAC,sBAAsB,CAAC,CAAyB;IACxD,OAAO,CAAC,2BAA2B,CAAC,CAA8B;IAClE,OAAO,CAAC,0BAA0B,CAAC,CAA6B;IAChE,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAClD,OAAO,CAAC,0BAA0B,CAAC,CAA6B;IAChE,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAG5C,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,uBAAuB,CAAC,CAAkC;gBAEtD,MAAM,GAAE,OAAO,CAAC,uBAAuB,CAAM;IAgGzD;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;;OAGG;IACH,gBAAgB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GAAG,IAAI;IAqBhE;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,eAAe,IAAI,gBAAgB,GAAG,SAAS;IAI/C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAqC1B;;OAEG;IACG,iBAAiB,CACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,sBAAsB,CAAC;IA2gBlC;;OAEG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,EACzC,UAAU,CAAC,EAAE,GAAG,EAChB,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,GAAG,GAChB,OAAO,CAAC,sBAAsB,CAAC;IAclC,OAAO,CAAC,qBAAqB;IAsE7B,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,eAAe;IA8DvB,OAAO,CAAC,uBAAuB;IAc/B;;OAEG;IACH,SAAS,IAAI,uBAAuB;IAIpC;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,uBAAuB,CAAC,GAAG,IAAI;CAG7D"}
@@ -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
- this.functionalityAssessor = new FunctionalityAssessor(this.config);
181
- this.securityAssessor = new SecurityAssessor(this.config);
182
- this.documentationAssessor = new DocumentationAssessor(this.config);
183
- this.errorHandlingAssessor = new ErrorHandlingAssessor(this.config);
184
- this.usabilityAssessor = new UsabilityAssessor(this.config);
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.resetTestCount();
296
- this.securityAssessor.resetTestCount();
297
- this.documentationAssessor.resetTestCount();
298
- this.errorHandlingAssessor.resetTestCount();
299
- this.usabilityAssessor.resetTestCount();
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
- // Emit all module_started events before launching parallel assessments
354
- emitModuleStartedEvent("Functionality", toolCount * 10, toolCount);
355
- emitModuleStartedEvent("Security", securityPatterns * toolCount, toolCount);
356
- emitModuleStartedEvent("Documentation", 5, toolCount);
357
- emitModuleStartedEvent("Error Handling", toolCount * 5, toolCount);
358
- emitModuleStartedEvent("Usability", 10, toolCount);
359
- // Core assessments
360
- assessmentPromises.push(this.functionalityAssessor.assess(context).then((r) => {
361
- emitModuleProgress("Functionality", r.status, r, this.functionalityAssessor.getTestCount());
362
- return (assessmentResults.functionality = r);
363
- }), this.securityAssessor.assess(context).then((r) => {
364
- emitModuleProgress("Security", r.status, r, this.securityAssessor.getTestCount());
365
- return (assessmentResults.security = r);
366
- }), this.documentationAssessor.assess(context).then((r) => {
367
- emitModuleProgress("Documentation", r.status, r, this.documentationAssessor.getTestCount());
368
- return (assessmentResults.documentation = r);
369
- }), this.errorHandlingAssessor.assess(context).then((r) => {
370
- emitModuleProgress("Error Handling", r.status, r, this.errorHandlingAssessor.getTestCount());
371
- return (assessmentResults.errorHandling = r);
372
- }), this.usabilityAssessor.assess(context).then((r) => {
373
- emitModuleProgress("Usability", r.status, r, this.usabilityAssessor.getTestCount());
374
- return (assessmentResults.usability = r);
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
- // Functionality: ~10 scenarios per tool
464
- emitModuleStartedEvent("Functionality", toolCount * 10, toolCount);
465
- assessmentResults.functionality =
466
- await this.functionalityAssessor.assess(context);
467
- emitModuleProgress("Functionality", assessmentResults.functionality.status, assessmentResults.functionality, this.functionalityAssessor.getTestCount());
468
- // Security: patterns × tools
469
- emitModuleStartedEvent("Security", securityPatterns * toolCount, toolCount);
470
- assessmentResults.security = await this.securityAssessor.assess(context);
471
- emitModuleProgress("Security", assessmentResults.security.status, assessmentResults.security, this.securityAssessor.getTestCount());
472
- // Documentation: ~5 static tests
473
- emitModuleStartedEvent("Documentation", 5, toolCount);
474
- assessmentResults.documentation =
475
- await this.documentationAssessor.assess(context);
476
- emitModuleProgress("Documentation", assessmentResults.documentation.status, assessmentResults.documentation, this.documentationAssessor.getTestCount());
477
- // Error Handling: ~5 tests per tool
478
- emitModuleStartedEvent("Error Handling", toolCount * 5, toolCount);
479
- assessmentResults.errorHandling =
480
- await this.errorHandlingAssessor.assess(context);
481
- emitModuleProgress("Error Handling", assessmentResults.errorHandling.status, assessmentResults.errorHandling, this.errorHandlingAssessor.getTestCount());
482
- // Usability: ~10 static tests
483
- emitModuleStartedEvent("Usability", 10, toolCount);
484
- assessmentResults.usability =
485
- await this.usabilityAssessor.assess(context);
486
- emitModuleProgress("Usability", assessmentResults.usability.status, assessmentResults.usability, this.usabilityAssessor.getTestCount());
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.getTestCount();
610
- const securityCount = this.securityAssessor.getTestCount();
611
- const documentationCount = this.documentationAssessor.getTestCount();
612
- const errorHandlingCount = this.errorHandlingAssessor.getTestCount();
613
- const usabilityCount = this.usabilityAssessor.getTestCount();
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;IA6HvB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAiE7B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAqC5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAsB3B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAkC5B;;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;IAwM5B;;;;;;;;;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"}
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",
3
- "version": "1.22.0",
3
+ "version": "1.22.2",
4
4
  "description": "Enhanced MCP Inspector with comprehensive assessment capabilities for server validation",
5
5
  "license": "MIT",
6
6
  "author": "Bryan Thompson <bryan@triepod.ai>",
@@ -79,9 +79,9 @@
79
79
  "access": "public"
80
80
  },
81
81
  "dependencies": {
82
- "@bryan-thompson/inspector-assessment-cli": "^1.22.0",
83
- "@bryan-thompson/inspector-assessment-client": "^1.22.0",
84
- "@bryan-thompson/inspector-assessment-server": "^1.22.0",
82
+ "@bryan-thompson/inspector-assessment-cli": "^1.22.2",
83
+ "@bryan-thompson/inspector-assessment-client": "^1.22.2",
84
+ "@bryan-thompson/inspector-assessment-server": "^1.22.2",
85
85
  "@modelcontextprotocol/sdk": "^1.24.3",
86
86
  "concurrently": "^9.2.0",
87
87
  "node-fetch": "^3.3.2",