@bryan-thompson/inspector-assessment 1.43.2 → 1.43.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +1062 -224
  2. package/cli/build/assess-full.js +532 -106
  3. package/cli/build/assess-security.js +54 -90
  4. package/cli/build/lib/cli-parser.js +14 -1
  5. package/cli/build/lib/cli-parserSchemas.js +1 -0
  6. package/cli/build/lib/result-output.js +21 -0
  7. package/cli/build/profiles.js +20 -0
  8. package/cli/build/validate-testbed.js +0 -0
  9. package/cli/package.json +1 -1
  10. package/client/dist/assets/{OAuthCallback-BS8-A1sU.js → OAuthCallback-Chi58kRc.js} +1 -1
  11. package/client/dist/assets/{OAuthDebugCallback-025_TM2i.js → OAuthDebugCallback-BluD_Wxg.js} +1 -1
  12. package/client/dist/assets/{index-DEhlIjy-.js → index-KW2LwGdp.js} +4 -4
  13. package/client/dist/index.html +1 -1
  14. package/client/lib/lib/assessment/configSchemas.d.ts +64 -64
  15. package/client/lib/lib/assessment/jsonlEventSchemas.d.ts +286 -286
  16. package/client/lib/lib/assessment/resultTypes.d.ts +10 -0
  17. package/client/lib/lib/assessment/resultTypes.d.ts.map +1 -1
  18. package/client/lib/lib/assessmentTypes.d.ts +1 -20
  19. package/client/lib/lib/assessmentTypes.d.ts.map +1 -1
  20. package/client/lib/lib/assessmentTypes.js +1 -20
  21. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +57 -104
  22. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
  23. package/client/lib/services/assessment/AssessmentOrchestrator.js +298 -133
  24. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
  25. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -0
  26. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.deprecated.js +1 -1
  27. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +564 -0
  28. package/client/lib/services/assessment/modules/SecurityAssessor.d.ts +5 -0
  29. package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
  30. package/client/lib/services/assessment/modules/SecurityAssessor.js +62 -0
  31. package/client/lib/services/assessment/modules/index.d.ts +1 -1
  32. package/client/lib/services/assessment/modules/index.js +1 -1
  33. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +15 -0
  34. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
  35. package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +72 -0
  36. package/client/lib/services/assessment/modules/securityTests/factory.d.ts +2 -0
  37. package/client/lib/services/assessment/modules/securityTests/factory.d.ts.map +1 -1
  38. package/client/lib/services/assessment/modules/securityTests/factory.js +1 -0
  39. package/client/lib/services/assessment/registry/AssessorDefinitions.js +1 -1
  40. package/client/lib/services/assessment/responseValidatorSchemas.d.ts +12 -12
  41. package/client/package.json +3 -3
  42. package/package.json +4 -2
  43. package/server/package.json +1 -1
  44. package/cli/build/lib/__tests__/zodErrorFormatter.test.js +0 -282
  45. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts +0 -109
  46. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts.map +0 -1
  47. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.deprecated.d.ts +0 -109
  48. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.deprecated.d.ts.map +0 -1
  49. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.deprecated.js +0 -852
  50. package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.js +0 -852
@@ -11,73 +11,58 @@
11
11
  */
12
12
  import * as fs from "fs";
13
13
  import * as path from "path";
14
- import { execSync } from "child_process";
14
+ import * as os from "os";
15
15
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
16
16
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
17
17
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
18
18
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
19
- // Import shared server config loading (Issue #84 - Zod validation)
20
- import { loadServerConfig } from "./lib/assessment-runner/server-config.js";
21
- /**
22
- * Validate that a command is safe to execute
23
- * - Must be an absolute path or resolvable via PATH
24
- * - Must not contain shell metacharacters
25
- */
26
- function validateCommand(command) {
27
- // Check for shell metacharacters that could indicate injection
28
- const dangerousChars = /[;&|`$(){}[\]<>!\\]/;
29
- if (dangerousChars.test(command)) {
30
- throw new Error(`Invalid command: contains shell metacharacters: ${command}`);
31
- }
32
- // Verify the command exists and is executable
33
- try {
34
- // Use 'which' on Unix-like systems, 'where' on Windows
35
- const whichCmd = process.platform === "win32" ? "where" : "which";
36
- execSync(`${whichCmd} "${command}"`, { stdio: "pipe" });
37
- }
38
- catch {
39
- // Check if it's an absolute path that exists
40
- if (path.isAbsolute(command) && fs.existsSync(command)) {
41
- try {
42
- fs.accessSync(command, fs.constants.X_OK);
43
- return; // Command exists and is executable
44
- }
45
- catch {
46
- throw new Error(`Command not executable: ${command}`);
47
- }
48
- }
49
- throw new Error(`Command not found: ${command}`);
50
- }
51
- }
19
+ // Import from local client lib (will use package exports when published)
20
+ import { SecurityAssessor } from "../../client/lib/services/assessment/modules/SecurityAssessor.js";
21
+ import { DEFAULT_ASSESSMENT_CONFIG, } from "../../client/lib/lib/assessmentTypes.js";
52
22
  /**
53
- * Validate environment variables from config
54
- * - Keys must be valid env var names (alphanumeric + underscore)
55
- * - Values should not contain null bytes
23
+ * Load server configuration from Claude Code's MCP settings
56
24
  */
57
- function validateEnvVars(env) {
58
- if (!env)
59
- return {};
60
- const validatedEnv = {};
61
- const validKeyPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
62
- for (const [key, value] of Object.entries(env)) {
63
- // Validate key format
64
- if (!validKeyPattern.test(key)) {
65
- console.warn(`Skipping invalid environment variable name: ${key} (must match [a-zA-Z_][a-zA-Z0-9_]*)`);
25
+ function loadServerConfig(serverName, configPath) {
26
+ const possiblePaths = [
27
+ configPath,
28
+ path.join(os.homedir(), ".config", "mcp", "servers", `${serverName}.json`),
29
+ path.join(os.homedir(), ".config", "claude", "claude_desktop_config.json"),
30
+ ].filter(Boolean);
31
+ for (const tryPath of possiblePaths) {
32
+ if (!fs.existsSync(tryPath))
66
33
  continue;
34
+ const config = JSON.parse(fs.readFileSync(tryPath, "utf-8"));
35
+ if (config.mcpServers && config.mcpServers[serverName]) {
36
+ const serverConfig = config.mcpServers[serverName];
37
+ return {
38
+ transport: "stdio",
39
+ command: serverConfig.command,
40
+ args: serverConfig.args || [],
41
+ env: serverConfig.env || {},
42
+ };
67
43
  }
68
- // Check for null bytes in value (could truncate strings)
69
- if (typeof value === "string" && value.includes("\0")) {
70
- console.warn(`Skipping environment variable with null byte: ${key}`);
71
- continue;
44
+ if (config.url ||
45
+ config.transport === "http" ||
46
+ config.transport === "sse") {
47
+ if (!config.url) {
48
+ throw new Error(`Invalid server config: transport is '${config.transport}' but 'url' is missing`);
49
+ }
50
+ return {
51
+ transport: config.transport || "http",
52
+ url: config.url,
53
+ };
54
+ }
55
+ if (config.command) {
56
+ return {
57
+ transport: "stdio",
58
+ command: config.command,
59
+ args: config.args || [],
60
+ env: config.env || {},
61
+ };
72
62
  }
73
- validatedEnv[key] = String(value);
74
63
  }
75
- return validatedEnv;
64
+ throw new Error(`Server config not found for: ${serverName}\nTried: ${possiblePaths.join(", ")}`);
76
65
  }
77
- // Import from local client lib (will use package exports when published)
78
- import { SecurityAssessor } from "../../client/lib/services/assessment/modules/SecurityAssessor.js";
79
- import { DEFAULT_ASSESSMENT_CONFIG, } from "../../client/lib/lib/assessmentTypes.js";
80
- import { loadPerformanceConfig } from "../../client/lib/services/assessment/config/performanceConfig.js";
81
66
  /**
82
67
  * Connect to MCP server via configured transport
83
68
  */
@@ -98,16 +83,12 @@ async function connectToServer(config) {
98
83
  default:
99
84
  if (!config.command)
100
85
  throw new Error("Command required for stdio transport");
101
- // Validate command before execution to prevent injection attacks
102
- validateCommand(config.command);
103
- // Validate and sanitize environment variables from config
104
- const validatedEnv = validateEnvVars(config.env);
105
86
  transport = new StdioClientTransport({
106
87
  command: config.command,
107
88
  args: config.args,
108
89
  env: {
109
90
  ...Object.fromEntries(Object.entries(process.env).filter(([, v]) => v !== undefined)),
110
- ...validatedEnv,
91
+ ...config.env,
111
92
  },
112
93
  stderr: "pipe",
113
94
  });
@@ -172,22 +153,6 @@ function createCallToolWrapper(client) {
172
153
  async function runSecurityAssessment(options) {
173
154
  console.log(`\n🔍 Connecting to MCP server: ${options.serverName}`);
174
155
  const serverConfig = loadServerConfig(options.serverName, options.serverConfigPath);
175
- // Load custom performance config if provided (Issue #37)
176
- // Note: Currently, modules use DEFAULT_PERFORMANCE_CONFIG directly.
177
- // This validates the config file but doesn't override runtime values yet.
178
- if (options.performanceConfigPath) {
179
- try {
180
- const performanceConfig = loadPerformanceConfig(options.performanceConfigPath);
181
- console.log(`📊 Performance config loaded from: ${options.performanceConfigPath}`);
182
- console.log(` Batch interval: ${performanceConfig.batchFlushIntervalMs}ms, ` +
183
- `Security batch: ${performanceConfig.securityBatchSize}`);
184
- // TODO: Wire performanceConfig through to SecurityAssessor
185
- }
186
- catch (error) {
187
- console.error(`❌ Failed to load performance config: ${error instanceof Error ? error.message : String(error)}`);
188
- throw error;
189
- }
190
- }
191
156
  const client = await connectToServer(serverConfig);
192
157
  console.log("✅ Connected successfully");
193
158
  const tools = await getTools(client, options.toolName);
@@ -197,7 +162,7 @@ async function runSecurityAssessment(options) {
197
162
  }
198
163
  const config = {
199
164
  ...DEFAULT_ASSESSMENT_CONFIG,
200
- securityPatternsToTest: 30,
165
+ securityPatternsToTest: 13,
201
166
  reviewerMode: false,
202
167
  testTimeout: 30000,
203
168
  };
@@ -206,8 +171,9 @@ async function runSecurityAssessment(options) {
206
171
  tools,
207
172
  callTool: createCallToolWrapper(client),
208
173
  config,
174
+ transportType: serverConfig.transport || "stdio",
209
175
  };
210
- console.log(`🛡️ Running security assessment with 30 attack patterns...`);
176
+ console.log(`🛡️ Running security assessment with 13 attack patterns...`);
211
177
  const assessor = new SecurityAssessor(config);
212
178
  const results = await assessor.assess(context);
213
179
  await client.close();
@@ -287,9 +253,6 @@ function parseArgs() {
287
253
  case "-v":
288
254
  options.verbose = true;
289
255
  break;
290
- case "--performance-config":
291
- options.performanceConfigPath = args[++i];
292
- break;
293
256
  case "--help":
294
257
  case "-h":
295
258
  printHelp();
@@ -326,23 +289,24 @@ function printHelp() {
326
289
  console.log(`
327
290
  Usage: mcp-assess-security [options] [server-name]
328
291
 
329
- Run security assessment against an MCP server with 30 attack patterns.
292
+ Run security assessment against an MCP server with 13 attack patterns.
330
293
 
331
294
  Options:
332
295
  --server, -s <name> Server name (required, or pass as first positional arg)
333
296
  --config, -c <path> Path to server config JSON
334
297
  --output, -o <path> Output JSON path (default: /tmp/inspector-security-assessment-<server>.json)
335
298
  --tool, -t <name> Test only specific tool (default: test all tools)
336
- --performance-config <path> Path to performance tuning JSON (batch sizes, timeouts, etc.)
337
299
  --verbose, -v Enable verbose logging
338
300
  --help, -h Show this help message
339
301
 
340
- Attack Patterns Tested (30 total):
341
- • Command Injection, SQL Injection, Path Traversal
342
- • Calculator Injection, Code Execution, XXE
343
- Data Exfiltration, Token Theft, NoSQL Injection
344
- Unicode Bypass, Nested Injection, Package Squatting
345
- Session Management, Auth Bypass, and more...
302
+ Attack Patterns Tested (13 total):
303
+ • Command Injection SQL Injection
304
+ • Calculator Injection Path Traversal
305
+ Type Safety • Boundary Testing
306
+ Required Fields • MCP Error Format
307
+ Timeout Handling • Indirect Prompt Injection
308
+ • Unicode Bypass • Nested Injection
309
+ • Package Squatting
346
310
 
347
311
  Examples:
348
312
  mcp-assess-security my-server
@@ -316,6 +316,11 @@ export function parseArgs(argv) {
316
316
  // Issue #137: Stage B enrichment for Claude semantic analysis
317
317
  options.stageBVerbose = true;
318
318
  break;
319
+ case "--audit-mode":
320
+ // Reduced false positives for automated MCP auditing
321
+ options.auditMode = true;
322
+ options.profile = "audit";
323
+ break;
319
324
  case "--static-only":
320
325
  // Issue #213: Static-only assessment without server connection
321
326
  options.staticOnly = true;
@@ -417,6 +422,13 @@ export function parseArgs(argv) {
417
422
  options.helpRequested = true;
418
423
  return options;
419
424
  }
425
+ // Validate mutual exclusivity of --audit-mode with --profile (audit-mode sets profile internally)
426
+ if (options.auditMode && options.profile && options.profile !== "audit") {
427
+ console.error("Error: --audit-mode cannot be used with --profile (audit mode sets its own profile)");
428
+ setTimeout(() => process.exit(1), 10);
429
+ options.helpRequested = true;
430
+ return options;
431
+ }
420
432
  // Validate mutual exclusivity of --module with orchestrator options (Issue #184)
421
433
  if (options.singleModule &&
422
434
  (options.skipModules?.length ||
@@ -547,7 +559,8 @@ Options:
547
559
  --claude-http Enable Claude Code via HTTP transport (connects to mcp-auditor proxy)
548
560
  --mcp-auditor-url <url> mcp-auditor URL for HTTP transport (default: http://localhost:8085)
549
561
  --full Enable all assessment modules (default)
550
- --profile <name> Use predefined module profile (quick, security, compliance, full, dev)
562
+ --audit-mode Reduced false positives for automated MCP auditing (sets --profile audit)
563
+ --profile <name> Use predefined module profile (quick, security, compliance, full, dev, audit)
551
564
  --temporal-invocations <n> Number of invocations per tool for rug pull detection (default: 3)
552
565
  --skip-temporal Skip temporal/rug pull testing (faster assessment)
553
566
  --conformance Enable official MCP conformance tests (experimental, requires HTTP/SSE transport)
@@ -24,6 +24,7 @@ export const AssessmentProfileNameSchema = z.enum([
24
24
  "full",
25
25
  "dev",
26
26
  "all",
27
+ "audit",
27
28
  ]);
28
29
  /**
29
30
  * Valid assessment module names.
@@ -303,6 +303,27 @@ export function displaySummary(results) {
303
303
  console.log(` • ${rec}`);
304
304
  }
305
305
  }
306
+ // Audit mode summary
307
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
308
+ const auditAnalysis = security?.auditAnalysis;
309
+ if (auditAnalysis) {
310
+ console.log("\n🔍 AUDIT ANALYSIS (reduced false positives):");
311
+ if (auditAnalysis.highConfidenceVulnerabilities?.length > 0) {
312
+ console.log(` 🚨 High-confidence vulnerabilities: ${auditAnalysis.highConfidenceVulnerabilities.length}`);
313
+ for (const vuln of auditAnalysis.highConfidenceVulnerabilities.slice(0, 5)) {
314
+ console.log(` • ${vuln}`);
315
+ }
316
+ }
317
+ else {
318
+ console.log(" ✅ No high-confidence vulnerabilities detected");
319
+ }
320
+ if (auditAnalysis.needsReview?.length > 0) {
321
+ console.log(` ⚠️ Needs manual review: ${auditAnalysis.needsReview.length}`);
322
+ for (const item of auditAnalysis.needsReview.slice(0, 3)) {
323
+ console.log(` • ${item}`);
324
+ }
325
+ }
326
+ }
306
327
  console.log("\n" + "=".repeat(70));
307
328
  }
308
329
  // ============================================================================
@@ -160,6 +160,20 @@ export const ASSESSMENT_PROFILES = {
160
160
  * Includes: Tier 1-4 + opt-in (prohibitedLibraries, manifestValidation, etc.)
161
161
  */
162
162
  all: [...ALL_MODULES],
163
+ /**
164
+ * Audit profile: Optimized for automated MCP auditing with reduced false positives
165
+ * Use when: --audit-mode flag, CI/CD pipeline audits
166
+ * Time: ~8-12 minutes
167
+ * Includes: Core security + compliance + capability + tool annotations
168
+ */
169
+ audit: [
170
+ "functionality",
171
+ "security",
172
+ "errorHandling",
173
+ "protocolCompliance",
174
+ "aupCompliance",
175
+ "toolAnnotations",
176
+ ],
163
177
  };
164
178
  export const PROFILE_METADATA = {
165
179
  quick: {
@@ -214,6 +228,12 @@ export const PROFILE_METADATA = {
214
228
  "Opt-In",
215
229
  ],
216
230
  },
231
+ audit: {
232
+ description: "Automated MCP auditing with reduced false positives (--audit-mode)",
233
+ estimatedTime: "~8-12 minutes",
234
+ moduleCount: ASSESSMENT_PROFILES.audit.length,
235
+ tiers: ["Tier 1 (Core Security)", "Tier 2 (Compliance, partial)"],
236
+ },
217
237
  };
218
238
  /**
219
239
  * Resolve module names, applying aliases for deprecated names.
File without changes
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bryan-thompson/inspector-assessment-cli",
3
- "version": "1.43.2",
3
+ "version": "1.43.4",
4
4
  "description": "CLI for the Enhanced MCP Inspector with assessment capabilities",
5
5
  "license": "MIT",
6
6
  "author": "Bryan Thompson <bryan@triepod.ai>",
@@ -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-DEhlIjy-.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-KW2LwGdp.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-DEhlIjy-.js";
1
+ import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-KW2LwGdp.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.43.2";
16376
+ const version$1 = "1.43.4";
16377
16377
  const packageJson = {
16378
16378
  name,
16379
16379
  version: version$1
@@ -49456,7 +49456,7 @@ const useTheme = () => {
49456
49456
  [theme, setThemeWithSideEffect]
49457
49457
  );
49458
49458
  };
49459
- const version = "1.43.2";
49459
+ const version = "1.43.4";
49460
49460
  var [createTooltipContext] = createContextScope("Tooltip", [
49461
49461
  createPopperScope
49462
49462
  ]);
@@ -52799,13 +52799,13 @@ const App = () => {
52799
52799
  };
52800
52800
  if (window.location.pathname === "/oauth/callback") {
52801
52801
  const OAuthCallback = React.lazy(
52802
- () => __vitePreload(() => import("./OAuthCallback-BS8-A1sU.js"), true ? [] : void 0)
52802
+ () => __vitePreload(() => import("./OAuthCallback-Chi58kRc.js"), true ? [] : void 0)
52803
52803
  );
52804
52804
  return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
52805
52805
  }
52806
52806
  if (window.location.pathname === "/oauth/callback/debug") {
52807
52807
  const OAuthDebugCallback = React.lazy(
52808
- () => __vitePreload(() => import("./OAuthDebugCallback-025_TM2i.js"), true ? [] : void 0)
52808
+ () => __vitePreload(() => import("./OAuthDebugCallback-BluD_Wxg.js"), true ? [] : void 0)
52809
52809
  );
52810
52810
  return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
52811
52811
  }
@@ -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-DEhlIjy-.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-KW2LwGdp.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-BoUA5OL1.css">
10
10
  </head>
11
11
  <body>