@bryan-thompson/inspector-assessment-client 1.7.0 → 1.7.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/index.html CHANGED
@@ -5,8 +5,8 @@
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-nCPw6E-c.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-Bj7kEsw0.css">
8
+ <script type="module" crossorigin src="/assets/index-BZfyfKpR.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-B9Z2VpMu.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root" class="w-full"></div>
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Simple concurrency limiter for parallel async operations
3
+ * Provides the same interface as p-limit but is CJS-compatible
4
+ */
5
+ export type LimitFunction = <T>(fn: () => Promise<T>) => Promise<T>;
6
+ /**
7
+ * Creates a concurrency limiter that allows only a specified number
8
+ * of async operations to run simultaneously
9
+ *
10
+ * @param concurrency - Maximum number of concurrent operations
11
+ * @returns A function that wraps async operations with the concurrency limit
12
+ *
13
+ * @example
14
+ * const limit = createConcurrencyLimit(5);
15
+ * const results = await Promise.all(
16
+ * items.map(item => limit(() => processItem(item)))
17
+ * );
18
+ */
19
+ export declare function createConcurrencyLimit(concurrency: number): LimitFunction;
20
+ //# sourceMappingURL=concurrencyLimit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concurrencyLimit.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/lib/concurrencyLimit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAyCzE"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Simple concurrency limiter for parallel async operations
3
+ * Provides the same interface as p-limit but is CJS-compatible
4
+ */
5
+ /**
6
+ * Creates a concurrency limiter that allows only a specified number
7
+ * of async operations to run simultaneously
8
+ *
9
+ * @param concurrency - Maximum number of concurrent operations
10
+ * @returns A function that wraps async operations with the concurrency limit
11
+ *
12
+ * @example
13
+ * const limit = createConcurrencyLimit(5);
14
+ * const results = await Promise.all(
15
+ * items.map(item => limit(() => processItem(item)))
16
+ * );
17
+ */
18
+ export function createConcurrencyLimit(concurrency) {
19
+ if (concurrency < 1) {
20
+ throw new Error("Concurrency must be at least 1");
21
+ }
22
+ let activeCount = 0;
23
+ const queue = [];
24
+ const next = () => {
25
+ if (activeCount < concurrency && queue.length > 0) {
26
+ const { fn, resolve, reject } = queue.shift();
27
+ activeCount++;
28
+ fn()
29
+ .then((result) => {
30
+ activeCount--;
31
+ resolve(result);
32
+ next();
33
+ })
34
+ .catch((error) => {
35
+ activeCount--;
36
+ reject(error);
37
+ next();
38
+ });
39
+ }
40
+ };
41
+ return (fn) => {
42
+ return new Promise((resolve, reject) => {
43
+ queue.push({
44
+ fn: fn,
45
+ resolve: resolve,
46
+ reject,
47
+ });
48
+ next();
49
+ });
50
+ };
51
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorHandlingAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ErrorHandlingAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAIxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,qBAAa,qBAAsB,SAAQ,YAAY;IAC/C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAyC1E,OAAO,CAAC,qBAAqB;YA8Cf,qBAAqB;YAuBrB,qBAAqB;YAmGrB,cAAc;YAmFd,iBAAiB;YA8DjB,kBAAkB;IA6DhC,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,uBAAuB;IA4B/B,OAAO,CAAC,gBAAgB;IAoGxB,OAAO,CAAC,4BAA4B;IAapC,OAAO,CAAC,mBAAmB;IAuE3B,OAAO,CAAC,uBAAuB;CA4ChC"}
1
+ {"version":3,"file":"ErrorHandlingAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ErrorHandlingAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAIxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,qBAAa,qBAAsB,SAAQ,YAAY;IAC/C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA8D1E,OAAO,CAAC,qBAAqB;YA8Cf,qBAAqB;YAuBrB,qBAAqB;YAmGrB,cAAc;YAmFd,iBAAiB;YA8DjB,kBAAkB;IA6DhC,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,uBAAuB;IA4B/B,OAAO,CAAC,gBAAgB;IAoGxB,OAAO,CAAC,4BAA4B;IAapC,OAAO,CAAC,mBAAmB;IAuE3B,OAAO,CAAC,uBAAuB;CA4ChC"}
@@ -3,6 +3,7 @@
3
3
  * Tests error handling and input validation
4
4
  */
5
5
  import { BaseAssessor } from "./BaseAssessor.js";
6
+ import { createConcurrencyLimit } from "../lib/concurrencyLimit.js";
6
7
  export class ErrorHandlingAssessor extends BaseAssessor {
7
8
  async assess(context) {
8
9
  this.log("Starting error handling assessment");
@@ -10,14 +11,23 @@ export class ErrorHandlingAssessor extends BaseAssessor {
10
11
  let passedTests = 0;
11
12
  // Test a sample of tools for error handling
12
13
  const toolsToTest = this.selectToolsForTesting(context.tools);
13
- for (const tool of toolsToTest) {
14
+ // Parallel tool testing with concurrency limit
15
+ const concurrency = this.config.maxParallelTests ?? 5;
16
+ const limit = createConcurrencyLimit(concurrency);
17
+ this.log(`Testing ${toolsToTest.length} tools for error handling with concurrency limit of ${concurrency}`);
18
+ const allToolTests = await Promise.all(toolsToTest.map((tool) => limit(async () => {
14
19
  const toolTests = await this.testToolErrorHandling(tool, context.callTool);
15
- testDetails.push(...toolTests);
16
- passedTests += toolTests.filter((t) => t.passed).length;
17
20
  // Add delay between tests to avoid rate limiting
18
- if (this.config.delayBetweenTests && this.config.delayBetweenTests > 0) {
21
+ if (this.config.delayBetweenTests &&
22
+ this.config.delayBetweenTests > 0) {
19
23
  await this.sleep(this.config.delayBetweenTests);
20
24
  }
25
+ return toolTests;
26
+ })));
27
+ // Post-process results after parallel execution
28
+ for (const toolTests of allToolTests) {
29
+ testDetails.push(...toolTests);
30
+ passedTests += toolTests.filter((t) => t.passed).length;
21
31
  }
22
32
  this.testCount = testDetails.length;
23
33
  const metrics = this.calculateMetrics(testDetails, passedTests);
@@ -1 +1 @@
1
- {"version":3,"file":"FunctionalityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/FunctionalityAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAkB,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoCvB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YA2D5D,QAAQ;IA6EtB,OAAO,CAAC,qBAAqB;IA2B7B,OAAO,CAAC,kBAAkB;IA0FnB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO;IAI9C,OAAO,CAAC,mBAAmB;CA+B5B"}
1
+ {"version":3,"file":"FunctionalityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/FunctionalityAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,uBAAuB,EAAkB,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAI9D,qBAAa,qBAAsB,SAAQ,YAAY;IACrD;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAoCvB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;YAiF5D,QAAQ;IA6EtB,OAAO,CAAC,qBAAqB;IA2B7B,OAAO,CAAC,kBAAkB;IA0FnB,iBAAiB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO;IAI9C,OAAO,CAAC,mBAAmB;CA+B5B"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { BaseAssessor } from "./BaseAssessor.js";
6
6
  import { ResponseValidator } from "../ResponseValidator.js";
7
+ import { createConcurrencyLimit } from "../lib/concurrencyLimit.js";
7
8
  export class FunctionalityAssessor extends BaseAssessor {
8
9
  /**
9
10
  * Select tools for testing based on configuration
@@ -38,21 +39,30 @@ export class FunctionalityAssessor extends BaseAssessor {
38
39
  let workingTools = 0;
39
40
  // Select tools for testing
40
41
  const toolsToTest = this.selectToolsForTesting(context.tools);
41
- for (const tool of toolsToTest) {
42
+ // Parallel tool testing with concurrency limit
43
+ const concurrency = this.config.maxParallelTests ?? 5;
44
+ const limit = createConcurrencyLimit(concurrency);
45
+ this.log(`Testing ${toolsToTest.length} tools with concurrency limit of ${concurrency}`);
46
+ const results = await Promise.all(toolsToTest.map((tool) => limit(async () => {
42
47
  this.testCount++;
43
48
  const result = await this.testTool(tool, context.callTool);
44
- toolResults.push(result);
45
49
  // Add delay between tests to avoid rate limiting
46
- if (this.config.delayBetweenTests && this.config.delayBetweenTests > 0) {
50
+ if (this.config.delayBetweenTests &&
51
+ this.config.delayBetweenTests > 0) {
47
52
  await this.sleep(this.config.delayBetweenTests);
48
53
  }
54
+ return result;
55
+ })));
56
+ // Post-process results after parallel execution
57
+ for (const result of results) {
58
+ toolResults.push(result);
49
59
  if (result.status === "working") {
50
60
  workingTools++;
51
61
  }
52
62
  else if (result.status === "broken") {
53
- brokenTools.push(tool.name);
63
+ brokenTools.push(result.toolName);
54
64
  if (this.config.skipBrokenTools) {
55
- this.log(`Skipping further tests for broken tool: ${tool.name}`);
65
+ this.log(`Skipping further tests for broken tool: ${result.toolName}`);
56
66
  }
57
67
  }
58
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/SecurityAssessor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;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;AAS9D,qBAAa,gBAAiB,SAAQ,YAAY;IAC1C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;OAIG;YACW,yBAAyB;IA+FvC;;;;OAIG;YACW,qBAAqB;IAwGnC;;OAEG;YACW,WAAW;IA2HzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAmDtC;;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;IAiM5B;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IAiDhC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;IAoE5B;;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;;;;;;GAMG;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;AAU9D,qBAAa,gBAAiB,SAAQ,YAAY;IAC1C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFrE;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;;;OAIG;YACW,yBAAyB;IAgHvC;;;;OAIG;YACW,qBAAqB;IAwGnC;;OAEG;YACW,WAAW;IA2HzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAmDtC;;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;IAiM5B;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IAiDhC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;IAoE5B;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACH,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAmB3B"}
@@ -8,6 +8,7 @@
8
8
  import { BaseAssessor } from "./BaseAssessor.js";
9
9
  import { getAllAttackPatterns, getPayloadsForAttack, } from "../../../lib/securityPatterns.js";
10
10
  import { ToolClassifier, ToolCategory } from "../ToolClassifier.js";
11
+ import { createConcurrencyLimit } from "../lib/concurrencyLimit.js";
11
12
  export class SecurityAssessor extends BaseAssessor {
12
13
  async assess(context) {
13
14
  // Select tools for testing first
@@ -108,8 +109,12 @@ export class SecurityAssessor extends BaseAssessor {
108
109
  const attackPatterns = getAllAttackPatterns();
109
110
  // Select tools for testing
110
111
  const toolsToTest = this.selectToolsForTesting(context.tools);
111
- this.log(`Starting ADVANCED security assessment - testing ${toolsToTest.length} tools with ${attackPatterns.length} security patterns (~${toolsToTest.length * attackPatterns.length * 3} tests)`);
112
- for (const tool of toolsToTest) {
112
+ // Parallel tool testing with concurrency limit
113
+ const concurrency = this.config.maxParallelTests ?? 5;
114
+ const limit = createConcurrencyLimit(concurrency);
115
+ this.log(`Starting ADVANCED security assessment - testing ${toolsToTest.length} tools with ${attackPatterns.length} security patterns (~${toolsToTest.length * attackPatterns.length * 3} tests) [concurrency: ${concurrency}]`);
116
+ const allToolResults = await Promise.all(toolsToTest.map((tool) => limit(async () => {
117
+ const toolResults = [];
113
118
  // Tools with no input parameters can't be exploited via payload injection
114
119
  // Add passing results so they appear in the UI
115
120
  if (!this.hasInputParameters(tool)) {
@@ -119,7 +124,7 @@ export class SecurityAssessor extends BaseAssessor {
119
124
  const payloads = getPayloadsForAttack(attackPattern.attackName);
120
125
  // Add one passing result per payload type
121
126
  for (const payload of payloads) {
122
- results.push({
127
+ toolResults.push({
123
128
  testName: attackPattern.attackName,
124
129
  description: payload.description,
125
130
  payload: payload.payload,
@@ -130,7 +135,7 @@ export class SecurityAssessor extends BaseAssessor {
130
135
  });
131
136
  }
132
137
  }
133
- continue;
138
+ return toolResults;
134
139
  }
135
140
  this.log(`Testing ${tool.name} with all attack patterns`);
136
141
  // Test with each attack type (all patterns in advanced mode)
@@ -142,7 +147,7 @@ export class SecurityAssessor extends BaseAssessor {
142
147
  this.testCount++;
143
148
  try {
144
149
  const result = await this.testPayload(tool, attackPattern.attackName, payload, context.callTool);
145
- results.push(result);
150
+ toolResults.push(result);
146
151
  if (result.vulnerable) {
147
152
  this.log(`🚨 VULNERABILITY: ${tool.name} - ${attackPattern.attackName} (${payload.payloadType}: ${payload.description})`);
148
153
  }
@@ -156,6 +161,11 @@ export class SecurityAssessor extends BaseAssessor {
156
161
  }
157
162
  }
158
163
  }
164
+ return toolResults;
165
+ })));
166
+ // Flatten all tool results into the main results array
167
+ for (const toolResults of allToolResults) {
168
+ results.push(...toolResults);
159
169
  }
160
170
  this.log(`ADVANCED security assessment complete: ${results.length} tests executed, ${results.filter((r) => r.vulnerable).length} vulnerabilities found`);
161
171
  return results;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bryan-thompson/inspector-assessment-client",
3
- "version": "1.7.0",
3
+ "version": "1.7.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>",