@bryan-thompson/inspector-assessment 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/client/dist/assets/{OAuthCallback-C8iZSwWO.js → OAuthCallback-CiSJznN1.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-Br9U2vZs.js → OAuthDebugCallback-D_XkKc3n.js} +1 -1
- package/client/dist/assets/{index-D12b6zCd.js → index-BsOrK-Nh.js} +642 -173
- package/client/dist/assets/{index-DrjsuFb9.css → index-Cz-lwW4x.css} +4 -0
- package/client/dist/index.html +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -972,6 +972,65 @@ mcp-inspector-assess-cli https://my-mcp-server.example.com --method tools/call -
|
|
|
972
972
|
mcp-inspector-assess-cli https://my-mcp-server.example.com --method resources/list
|
|
973
973
|
```
|
|
974
974
|
|
|
975
|
+
### Security Testing: Pure Behavior Detection
|
|
976
|
+
|
|
977
|
+
The inspector uses **pure behavior-based detection** for security assessment, analyzing tool responses to identify actual code execution vs safe data handling. This approach works on any MCP server without requiring special security metadata.
|
|
978
|
+
|
|
979
|
+
**How It Works**:
|
|
980
|
+
|
|
981
|
+
```bash
|
|
982
|
+
# Run security assessment against any MCP server
|
|
983
|
+
npm run assess -- --server myserver --config config.json
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
**Detection Strategy**:
|
|
987
|
+
|
|
988
|
+
1. **Reflection Detection**: Identifies when tools safely echo malicious input as data
|
|
989
|
+
- Pattern: "Stored query: ../../../etc/passwd" → SAFE (reflection)
|
|
990
|
+
- Pattern: "Query results for: ..." → SAFE (search results)
|
|
991
|
+
|
|
992
|
+
2. **Execution Evidence**: Detects actual code execution
|
|
993
|
+
- Pattern: Response contains "root:x:0:0" → VULNERABLE (file accessed)
|
|
994
|
+
- Pattern: Response contains "total 42 drwx" → VULNERABLE (directory listed)
|
|
995
|
+
|
|
996
|
+
3. **Category Classification**: Distinguishes safe tool types
|
|
997
|
+
- Search/retrieval tools return data, not code execution
|
|
998
|
+
- CRUD operations create resources, not execute code
|
|
999
|
+
- Safe storage tools treat input as pure data
|
|
1000
|
+
|
|
1001
|
+
**Validation with Testbed**:
|
|
1002
|
+
|
|
1003
|
+
The inspector has been validated against purpose-built testbed servers with ground-truth labeled tools:
|
|
1004
|
+
|
|
1005
|
+
```bash
|
|
1006
|
+
# Test against broken-mcp testbed (10 vulnerable + 6 safe tools)
|
|
1007
|
+
npm run assess -- --server broken-mcp --config testbed.json
|
|
1008
|
+
|
|
1009
|
+
# Results: 20 vulnerabilities detected, 0 false positives (100% precision)
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
**Why Behavior Detection Matters**:
|
|
1013
|
+
|
|
1014
|
+
Real-world MCP servers don't provide security metadata - the inspector must detect vulnerabilities by analyzing actual tool behavior. Testbed validation proves this approach works reliably.
|
|
1015
|
+
|
|
1016
|
+
**For Inspector Developers**:
|
|
1017
|
+
|
|
1018
|
+
When modifying detection logic, validate against the testbed:
|
|
1019
|
+
|
|
1020
|
+
```bash
|
|
1021
|
+
# Before changes: Record baseline
|
|
1022
|
+
npm run assess -- --server broken-mcp --output /tmp/baseline.json
|
|
1023
|
+
|
|
1024
|
+
# After changes: Verify no regressions
|
|
1025
|
+
npm run assess -- --server broken-mcp --output /tmp/after.json
|
|
1026
|
+
|
|
1027
|
+
# Expected: 0 false positives on safe tools
|
|
1028
|
+
cat /tmp/after.json | jq '[.security.promptInjectionTests[] | select(.toolName | startswith("safe_")) | select(.vulnerable == true)] | length'
|
|
1029
|
+
# Output: 0
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
See [docs/mcp_vulnerability_testbed.md](docs/mcp_vulnerability_testbed.md) for detailed validation results and testbed usage guide.
|
|
1033
|
+
|
|
975
1034
|
### UI Mode vs CLI Mode: When to Use Each
|
|
976
1035
|
|
|
977
1036
|
| Use Case | UI Mode | CLI Mode |
|
|
@@ -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-BsOrK-Nh.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-Br9U2vZs.js → OAuthDebugCallback-D_XkKc3n.js}
RENAMED
|
@@ -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-BsOrK-Nh.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -16205,7 +16205,7 @@ objectType({
|
|
|
16205
16205
|
token_type_hint: stringType().optional()
|
|
16206
16206
|
}).strip();
|
|
16207
16207
|
const name = "@bryan-thompson/inspector-assessment-client";
|
|
16208
|
-
const version$1 = "1.
|
|
16208
|
+
const version$1 = "1.3.0";
|
|
16209
16209
|
const packageJson = {
|
|
16210
16210
|
name,
|
|
16211
16211
|
version: version$1
|
|
@@ -16986,8 +16986,8 @@ class InspectorOAuthClientProvider {
|
|
|
16986
16986
|
token_endpoint_auth_method: "none",
|
|
16987
16987
|
grant_types: ["authorization_code", "refresh_token"],
|
|
16988
16988
|
response_types: ["code"],
|
|
16989
|
-
client_name: "MCP
|
|
16990
|
-
client_uri: "https://github.com/
|
|
16989
|
+
client_name: "MCP Assessor",
|
|
16990
|
+
client_uri: "https://github.com/triepod-ai/inspector-assessment",
|
|
16991
16991
|
scope: this.scope ?? ""
|
|
16992
16992
|
};
|
|
16993
16993
|
}
|
|
@@ -41736,7 +41736,7 @@ const useTheme = () => {
|
|
|
41736
41736
|
[theme, setThemeWithSideEffect]
|
|
41737
41737
|
);
|
|
41738
41738
|
};
|
|
41739
|
-
const version = "1.
|
|
41739
|
+
const version = "1.3.0";
|
|
41740
41740
|
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
41741
41741
|
createPopperScope
|
|
41742
41742
|
]);
|
|
@@ -42732,7 +42732,7 @@ const Sidebar = ({
|
|
|
42732
42732
|
}, [generateMCPServerFile, toast2, reportError2]);
|
|
42733
42733
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-card border-r border-border flex flex-col h-full", children: [
|
|
42734
42734
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-border", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "ml-2 text-lg font-semibold", children: [
|
|
42735
|
-
"MCP
|
|
42735
|
+
"MCP Assessor v",
|
|
42736
42736
|
version
|
|
42737
42737
|
] }) }) }),
|
|
42738
42738
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 flex-1 overflow-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
@@ -43288,7 +43288,7 @@ const Sidebar = ({
|
|
|
43288
43288
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43289
43289
|
"a",
|
|
43290
43290
|
{
|
|
43291
|
-
href: "https://github.com/
|
|
43291
|
+
href: "https://github.com/triepod-ai/inspector-assessment",
|
|
43292
43292
|
target: "_blank",
|
|
43293
43293
|
rel: "noopener noreferrer",
|
|
43294
43294
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Github, { className: "w-4 h-4 text-foreground" })
|
|
@@ -47002,10 +47002,20 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47002
47002
|
async assess(context) {
|
|
47003
47003
|
const toolsToTest = this.selectToolsForTesting(context.tools);
|
|
47004
47004
|
const allTests = await this.runUniversalSecurityTests(context);
|
|
47005
|
+
const connectionErrors = allTests.filter((t) => t.connectionError === true);
|
|
47006
|
+
const validTests = allTests.filter((t) => !t.connectionError);
|
|
47007
|
+
if (connectionErrors.length > 0) {
|
|
47008
|
+
this.log(
|
|
47009
|
+
`⚠️ WARNING: ${connectionErrors.length} test${connectionErrors.length !== 1 ? "s" : ""} failed due to connection/server errors`
|
|
47010
|
+
);
|
|
47011
|
+
this.log(
|
|
47012
|
+
`Connection errors: ${connectionErrors.map((e) => `${e.toolName}:${e.testName} (${e.errorType})`).join(", ")}`
|
|
47013
|
+
);
|
|
47014
|
+
}
|
|
47005
47015
|
const vulnerabilities = [];
|
|
47006
47016
|
let highRiskCount = 0;
|
|
47007
47017
|
let mediumRiskCount = 0;
|
|
47008
|
-
for (const test of
|
|
47018
|
+
for (const test of validTests) {
|
|
47009
47019
|
if (test.vulnerable) {
|
|
47010
47020
|
let vulnerability;
|
|
47011
47021
|
if (test.confidence === "high" || !test.confidence) {
|
|
@@ -47030,12 +47040,14 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47030
47040
|
vulnerabilities.length
|
|
47031
47041
|
);
|
|
47032
47042
|
const status = this.determineSecurityStatus(
|
|
47033
|
-
|
|
47043
|
+
validTests,
|
|
47034
47044
|
vulnerabilities.length,
|
|
47035
|
-
|
|
47045
|
+
validTests.length,
|
|
47046
|
+
connectionErrors.length
|
|
47036
47047
|
);
|
|
47037
47048
|
const explanation = this.generateSecurityExplanation(
|
|
47038
|
-
|
|
47049
|
+
validTests,
|
|
47050
|
+
connectionErrors,
|
|
47039
47051
|
vulnerabilities,
|
|
47040
47052
|
overallRiskLevel
|
|
47041
47053
|
);
|
|
@@ -47256,6 +47268,24 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47256
47268
|
callTool(tool.name, params),
|
|
47257
47269
|
5e3
|
|
47258
47270
|
);
|
|
47271
|
+
if (this.isConnectionError(response)) {
|
|
47272
|
+
return {
|
|
47273
|
+
testName: attackName,
|
|
47274
|
+
description: payload.description,
|
|
47275
|
+
payload: payload.payload,
|
|
47276
|
+
riskLevel: payload.riskLevel,
|
|
47277
|
+
toolName: tool.name,
|
|
47278
|
+
vulnerable: true,
|
|
47279
|
+
// Mark as failed (test could not complete)
|
|
47280
|
+
evidence: `CONNECTION ERROR: Test could not complete due to server/network failure`,
|
|
47281
|
+
response: this.extractResponseContent(response),
|
|
47282
|
+
connectionError: true,
|
|
47283
|
+
errorType: this.classifyError(response),
|
|
47284
|
+
testReliability: "failed",
|
|
47285
|
+
confidence: "high",
|
|
47286
|
+
requiresManualReview: true
|
|
47287
|
+
};
|
|
47288
|
+
}
|
|
47259
47289
|
const { isVulnerable, evidence } = this.analyzeResponse(
|
|
47260
47290
|
response,
|
|
47261
47291
|
payload,
|
|
@@ -47280,6 +47310,23 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47280
47310
|
...confidenceResult
|
|
47281
47311
|
};
|
|
47282
47312
|
} catch (error) {
|
|
47313
|
+
if (this.isConnectionErrorFromException(error)) {
|
|
47314
|
+
return {
|
|
47315
|
+
testName: attackName,
|
|
47316
|
+
description: payload.description,
|
|
47317
|
+
payload: payload.payload,
|
|
47318
|
+
riskLevel: payload.riskLevel,
|
|
47319
|
+
toolName: tool.name,
|
|
47320
|
+
vulnerable: false,
|
|
47321
|
+
evidence: `CONNECTION ERROR: Test could not complete due to server/network failure`,
|
|
47322
|
+
response: this.extractErrorMessage(error),
|
|
47323
|
+
connectionError: true,
|
|
47324
|
+
errorType: this.classifyErrorFromException(error),
|
|
47325
|
+
testReliability: "failed",
|
|
47326
|
+
confidence: "high",
|
|
47327
|
+
requiresManualReview: true
|
|
47328
|
+
};
|
|
47329
|
+
}
|
|
47283
47330
|
return {
|
|
47284
47331
|
testName: attackName,
|
|
47285
47332
|
description: payload.description,
|
|
@@ -47292,16 +47339,183 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47292
47339
|
}
|
|
47293
47340
|
}
|
|
47294
47341
|
/**
|
|
47295
|
-
*
|
|
47296
|
-
* Returns
|
|
47342
|
+
* Check if response indicates connection/server failure
|
|
47343
|
+
* Returns true if test couldn't complete due to infrastructure issues
|
|
47344
|
+
*
|
|
47345
|
+
* CRITICAL: Only match transport/infrastructure errors, NOT tool business logic
|
|
47297
47346
|
*/
|
|
47298
|
-
|
|
47299
|
-
|
|
47300
|
-
|
|
47301
|
-
|
|
47302
|
-
|
|
47303
|
-
|
|
47347
|
+
isConnectionError(response) {
|
|
47348
|
+
const text = this.extractResponseContent(response).toLowerCase();
|
|
47349
|
+
const unambiguousPatterns = [
|
|
47350
|
+
/MCP error -32001/i,
|
|
47351
|
+
// MCP transport errors
|
|
47352
|
+
/MCP error -32603/i,
|
|
47353
|
+
// MCP internal error
|
|
47354
|
+
/MCP error -32000/i,
|
|
47355
|
+
// MCP server error
|
|
47356
|
+
/MCP error -32700/i,
|
|
47357
|
+
// MCP parse error
|
|
47358
|
+
/socket hang up/i,
|
|
47359
|
+
// Network socket errors
|
|
47360
|
+
/ECONNREFUSED/i,
|
|
47361
|
+
// Connection refused
|
|
47362
|
+
/ETIMEDOUT/i,
|
|
47363
|
+
// Network timeout
|
|
47364
|
+
/ERR_CONNECTION/i,
|
|
47365
|
+
// Connection errors
|
|
47366
|
+
/fetch failed/i,
|
|
47367
|
+
// HTTP fetch failures
|
|
47368
|
+
/connection reset/i,
|
|
47369
|
+
// Connection reset
|
|
47370
|
+
/error POSTing to endpoint/i,
|
|
47371
|
+
// Transport layer POST errors
|
|
47372
|
+
/error GETting.*endpoint/i,
|
|
47373
|
+
// Transport layer GET errors (requires 'endpoint' to avoid false positives)
|
|
47374
|
+
/service unavailable/i,
|
|
47375
|
+
// HTTP 503 (server down)
|
|
47376
|
+
/gateway timeout/i,
|
|
47377
|
+
// HTTP 504 (gateway timeout)
|
|
47378
|
+
/unknown tool:/i,
|
|
47379
|
+
// Tool name not in current server's tool list (stale tool list)
|
|
47380
|
+
/tool.*not found/i,
|
|
47381
|
+
// Alternative phrasing for missing tool
|
|
47382
|
+
/tool.*does not exist/i,
|
|
47383
|
+
// Alternative phrasing for missing tool
|
|
47384
|
+
/no such tool/i
|
|
47385
|
+
// Alternative phrasing for missing tool
|
|
47386
|
+
];
|
|
47387
|
+
if (unambiguousPatterns.some((pattern2) => pattern2.test(text))) {
|
|
47388
|
+
return true;
|
|
47304
47389
|
}
|
|
47390
|
+
const mcpPrefix = /^mcp error -\d+:/i.test(text);
|
|
47391
|
+
if (mcpPrefix) {
|
|
47392
|
+
const contextualPatterns = [
|
|
47393
|
+
/bad request/i,
|
|
47394
|
+
// HTTP 400 (only if in MCP error)
|
|
47395
|
+
/unauthorized/i,
|
|
47396
|
+
// HTTP 401 (only if in MCP error)
|
|
47397
|
+
/forbidden/i,
|
|
47398
|
+
// HTTP 403 (only if in MCP error)
|
|
47399
|
+
/no valid session/i,
|
|
47400
|
+
// Session errors (only if in MCP error)
|
|
47401
|
+
/session.*expired/i,
|
|
47402
|
+
// Session expiration (only if in MCP error)
|
|
47403
|
+
/internal server error/i,
|
|
47404
|
+
// HTTP 500 (only if in MCP error)
|
|
47405
|
+
/HTTP [45]\d\d/i
|
|
47406
|
+
// HTTP status codes (only if in MCP error)
|
|
47407
|
+
];
|
|
47408
|
+
return contextualPatterns.some((pattern2) => pattern2.test(text));
|
|
47409
|
+
}
|
|
47410
|
+
return false;
|
|
47411
|
+
}
|
|
47412
|
+
/**
|
|
47413
|
+
* Check if caught exception indicates connection/server failure
|
|
47414
|
+
* CRITICAL: Only match transport/infrastructure errors, NOT tool business logic
|
|
47415
|
+
*/
|
|
47416
|
+
isConnectionErrorFromException(error) {
|
|
47417
|
+
if (error instanceof Error) {
|
|
47418
|
+
const message = error.message.toLowerCase();
|
|
47419
|
+
const unambiguousPatterns = [
|
|
47420
|
+
/MCP error -32001/i,
|
|
47421
|
+
// MCP transport errors
|
|
47422
|
+
/MCP error -32603/i,
|
|
47423
|
+
// MCP internal error
|
|
47424
|
+
/MCP error -32000/i,
|
|
47425
|
+
// MCP server error
|
|
47426
|
+
/MCP error -32700/i,
|
|
47427
|
+
// MCP parse error
|
|
47428
|
+
/socket hang up/i,
|
|
47429
|
+
// Network socket errors
|
|
47430
|
+
/ECONNREFUSED/i,
|
|
47431
|
+
// Connection refused
|
|
47432
|
+
/ETIMEDOUT/i,
|
|
47433
|
+
// Network timeout
|
|
47434
|
+
/network error/i,
|
|
47435
|
+
// Generic network errors
|
|
47436
|
+
/ERR_CONNECTION/i,
|
|
47437
|
+
// Connection errors
|
|
47438
|
+
/fetch failed/i,
|
|
47439
|
+
// HTTP fetch failures
|
|
47440
|
+
/connection reset/i,
|
|
47441
|
+
// Connection reset
|
|
47442
|
+
/error POSTing to endpoint/i,
|
|
47443
|
+
// Transport layer POST errors
|
|
47444
|
+
/error GETting/i,
|
|
47445
|
+
// Transport layer GET errors
|
|
47446
|
+
/service unavailable/i,
|
|
47447
|
+
// HTTP 503 (server down)
|
|
47448
|
+
/gateway timeout/i,
|
|
47449
|
+
// HTTP 504 (gateway timeout)
|
|
47450
|
+
/unknown tool:/i,
|
|
47451
|
+
// Tool name not in current server's tool list (stale tool list)
|
|
47452
|
+
/tool.*not found/i,
|
|
47453
|
+
// Alternative phrasing for missing tool
|
|
47454
|
+
/tool.*does not exist/i,
|
|
47455
|
+
// Alternative phrasing for missing tool
|
|
47456
|
+
/no such tool/i
|
|
47457
|
+
// Alternative phrasing for missing tool
|
|
47458
|
+
];
|
|
47459
|
+
if (unambiguousPatterns.some((pattern2) => pattern2.test(message))) {
|
|
47460
|
+
return true;
|
|
47461
|
+
}
|
|
47462
|
+
const mcpPrefix = /^mcp error -\d+:/i.test(message);
|
|
47463
|
+
if (mcpPrefix) {
|
|
47464
|
+
const contextualPatterns = [
|
|
47465
|
+
/bad request/i,
|
|
47466
|
+
/unauthorized/i,
|
|
47467
|
+
/forbidden/i,
|
|
47468
|
+
/no valid session/i,
|
|
47469
|
+
/session.*expired/i,
|
|
47470
|
+
/internal server error/i,
|
|
47471
|
+
/HTTP [45]\d\d/i
|
|
47472
|
+
];
|
|
47473
|
+
return contextualPatterns.some((pattern2) => pattern2.test(message));
|
|
47474
|
+
}
|
|
47475
|
+
}
|
|
47476
|
+
return false;
|
|
47477
|
+
}
|
|
47478
|
+
/**
|
|
47479
|
+
* Classify error type for reporting
|
|
47480
|
+
*/
|
|
47481
|
+
classifyError(response) {
|
|
47482
|
+
const text = this.extractResponseContent(response).toLowerCase();
|
|
47483
|
+
if (/socket|ECONNREFUSED|ETIMEDOUT|network|fetch failed|connection reset/i.test(
|
|
47484
|
+
text
|
|
47485
|
+
)) {
|
|
47486
|
+
return "connection";
|
|
47487
|
+
}
|
|
47488
|
+
if (/-32603|-32000|-32700|internal server error|service unavailable|gateway timeout|HTTP 5\d\d|error POSTing.*endpoint|error GETting.*endpoint|bad request|HTTP 400|unauthorized|forbidden|no valid session|session.*expired/i.test(
|
|
47489
|
+
text
|
|
47490
|
+
)) {
|
|
47491
|
+
return "server";
|
|
47492
|
+
}
|
|
47493
|
+
if (/-32001/i.test(text)) {
|
|
47494
|
+
return "protocol";
|
|
47495
|
+
}
|
|
47496
|
+
return "protocol";
|
|
47497
|
+
}
|
|
47498
|
+
/**
|
|
47499
|
+
* Classify error type from caught exception
|
|
47500
|
+
*/
|
|
47501
|
+
classifyErrorFromException(error) {
|
|
47502
|
+
if (error instanceof Error) {
|
|
47503
|
+
const message = error.message.toLowerCase();
|
|
47504
|
+
if (/socket|ECONNREFUSED|ETIMEDOUT|network|fetch failed|connection reset/i.test(
|
|
47505
|
+
message
|
|
47506
|
+
)) {
|
|
47507
|
+
return "connection";
|
|
47508
|
+
}
|
|
47509
|
+
if (/-32603|-32000|-32700|internal server error|service unavailable|gateway timeout|HTTP 5\d\d|error POSTing|error GETting|bad request|HTTP 400|unauthorized|forbidden|no valid session|session.*expired/i.test(
|
|
47510
|
+
message
|
|
47511
|
+
)) {
|
|
47512
|
+
return "server";
|
|
47513
|
+
}
|
|
47514
|
+
if (/-32001/i.test(message)) {
|
|
47515
|
+
return "protocol";
|
|
47516
|
+
}
|
|
47517
|
+
}
|
|
47518
|
+
return "protocol";
|
|
47305
47519
|
}
|
|
47306
47520
|
/**
|
|
47307
47521
|
* Analyze response with evidence-based detection
|
|
@@ -47310,27 +47524,6 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47310
47524
|
analyzeResponse(response, payload, tool) {
|
|
47311
47525
|
const responseText = this.extractResponseContent(response).toLowerCase();
|
|
47312
47526
|
const payloadText = payload.payload.toLowerCase();
|
|
47313
|
-
const parsedResponse = this.tryParseResponseJSON(response);
|
|
47314
|
-
if (parsedResponse) {
|
|
47315
|
-
if (parsedResponse.safe === true) {
|
|
47316
|
-
return {
|
|
47317
|
-
isVulnerable: false,
|
|
47318
|
-
evidence: "Tool explicitly marked response as safe (safe: true flag in JSON response)"
|
|
47319
|
-
};
|
|
47320
|
-
}
|
|
47321
|
-
if (parsedResponse.hardened === true) {
|
|
47322
|
-
return {
|
|
47323
|
-
isVulnerable: false,
|
|
47324
|
-
evidence: "Tool explicitly marked response as hardened (hardened: true flag in JSON response)"
|
|
47325
|
-
};
|
|
47326
|
-
}
|
|
47327
|
-
if (parsedResponse.vulnerable === true) {
|
|
47328
|
-
return {
|
|
47329
|
-
isVulnerable: true,
|
|
47330
|
-
evidence: "Tool explicitly marked response as vulnerable (vulnerable: true flag in JSON response)"
|
|
47331
|
-
};
|
|
47332
|
-
}
|
|
47333
|
-
}
|
|
47334
47527
|
const errorInfo = this.extractErrorInfo(response);
|
|
47335
47528
|
if (this.isMCPValidationError(errorInfo, responseText)) {
|
|
47336
47529
|
const errorCode = errorInfo.code ? ` (${errorInfo.code})` : "";
|
|
@@ -47363,20 +47556,33 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47363
47556
|
evidence: "Read-only info tool returned user/workspace data (intended data exposure, not vulnerability)"
|
|
47364
47557
|
};
|
|
47365
47558
|
}
|
|
47559
|
+
if (classification.categories.includes(ToolCategory.SAFE_STORAGE)) {
|
|
47560
|
+
return {
|
|
47561
|
+
isVulnerable: false,
|
|
47562
|
+
evidence: "Safe storage control tool (validated safe implementation, control group for testing)"
|
|
47563
|
+
};
|
|
47564
|
+
}
|
|
47366
47565
|
if (this.isApiWrapperResponse(responseText)) {
|
|
47367
47566
|
return {
|
|
47368
47567
|
isVulnerable: false,
|
|
47369
47568
|
evidence: "API wrapper response - returned external content as data"
|
|
47370
47569
|
};
|
|
47371
47570
|
}
|
|
47372
|
-
|
|
47373
|
-
|
|
47374
|
-
|
|
47375
|
-
|
|
47376
|
-
|
|
47377
|
-
|
|
47378
|
-
|
|
47379
|
-
|
|
47571
|
+
const isJustReflection = this.isReflectionResponse(
|
|
47572
|
+
responseText,
|
|
47573
|
+
payloadText
|
|
47574
|
+
);
|
|
47575
|
+
if (isJustReflection) {
|
|
47576
|
+
return {
|
|
47577
|
+
isVulnerable: false,
|
|
47578
|
+
evidence: "Tool safely reflected input without execution"
|
|
47579
|
+
};
|
|
47580
|
+
}
|
|
47581
|
+
if (this.isValidationRejection(response)) {
|
|
47582
|
+
return {
|
|
47583
|
+
isVulnerable: false,
|
|
47584
|
+
evidence: "Tool correctly rejected invalid input with validation error"
|
|
47585
|
+
};
|
|
47380
47586
|
}
|
|
47381
47587
|
if (payload.evidence && payload.evidence.test(responseText)) {
|
|
47382
47588
|
const evidenceMatchesInput = payload.evidence.test(payloadText);
|
|
@@ -47402,6 +47608,54 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47402
47608
|
}
|
|
47403
47609
|
return this.analyzeInjectionResponse(response, payload.payload);
|
|
47404
47610
|
}
|
|
47611
|
+
/**
|
|
47612
|
+
* Check if tool explicitly rejected input with validation error (SAFE)
|
|
47613
|
+
* Returns true if tool properly rejected invalid input (SECURE)
|
|
47614
|
+
*
|
|
47615
|
+
* Distinguishes between:
|
|
47616
|
+
* - SAFE: {"valid": false, "errors": ["Input too long"]} (rejection)
|
|
47617
|
+
* - VULNERABLE: {"result": "deleted 5 rows"} (execution)
|
|
47618
|
+
*/
|
|
47619
|
+
isValidationRejection(response) {
|
|
47620
|
+
const responseText = this.extractResponseContent(response);
|
|
47621
|
+
try {
|
|
47622
|
+
const parsed = JSON.parse(responseText);
|
|
47623
|
+
if (parsed.valid === false || parsed.error === true || parsed.error === "true" || // Handle string boolean
|
|
47624
|
+
parsed.error && parsed.error !== false || // Any truthy error value
|
|
47625
|
+
parsed.status === "rejected" || parsed.status === "invalid" || parsed.status === "failed") {
|
|
47626
|
+
return true;
|
|
47627
|
+
}
|
|
47628
|
+
if (parsed.errors && Array.isArray(parsed.errors) && parsed.errors.length > 0) {
|
|
47629
|
+
return true;
|
|
47630
|
+
}
|
|
47631
|
+
if (parsed.error && typeof parsed.error === "string") {
|
|
47632
|
+
return true;
|
|
47633
|
+
}
|
|
47634
|
+
if (typeof parsed.result === "string") {
|
|
47635
|
+
const resultRejectionPatterns = [
|
|
47636
|
+
/validation (failed|error)/i,
|
|
47637
|
+
/rejected/i,
|
|
47638
|
+
/not.*approved/i,
|
|
47639
|
+
/not.*in.*list/i,
|
|
47640
|
+
/invalid.*input/i,
|
|
47641
|
+
/error:.*invalid/i
|
|
47642
|
+
];
|
|
47643
|
+
if (resultRejectionPatterns.some((p2) => p2.test(parsed.result))) {
|
|
47644
|
+
return true;
|
|
47645
|
+
}
|
|
47646
|
+
}
|
|
47647
|
+
} catch {
|
|
47648
|
+
}
|
|
47649
|
+
const rejectionPatterns = [
|
|
47650
|
+
/validation failed/i,
|
|
47651
|
+
/rejected/i,
|
|
47652
|
+
/not.*approved/i,
|
|
47653
|
+
/not.*in.*list/i,
|
|
47654
|
+
/invalid.*input/i,
|
|
47655
|
+
/error:.*invalid/i
|
|
47656
|
+
];
|
|
47657
|
+
return rejectionPatterns.some((pattern2) => pattern2.test(responseText));
|
|
47658
|
+
}
|
|
47405
47659
|
/**
|
|
47406
47660
|
* Check if response is an MCP validation error (safe rejection)
|
|
47407
47661
|
* Returns true if tool rejected input before processing (SECURE)
|
|
@@ -47535,7 +47789,8 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47535
47789
|
/**
|
|
47536
47790
|
* Determine security status based on confidence levels
|
|
47537
47791
|
*/
|
|
47538
|
-
determineSecurityStatus(tests, vulnerabilityCount, testCount) {
|
|
47792
|
+
determineSecurityStatus(tests, vulnerabilityCount, testCount, connectionErrorCount = 0) {
|
|
47793
|
+
if (connectionErrorCount > 0) return "FAIL";
|
|
47539
47794
|
if (testCount === 0) return "NEED_MORE_INFO";
|
|
47540
47795
|
if (vulnerabilityCount === 0) return "PASS";
|
|
47541
47796
|
const hasHighConfidence = tests.some(
|
|
@@ -47547,30 +47802,38 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47547
47802
|
/**
|
|
47548
47803
|
* Generate security explanation
|
|
47549
47804
|
*/
|
|
47550
|
-
generateSecurityExplanation(
|
|
47805
|
+
generateSecurityExplanation(validTests, connectionErrors, vulnerabilities, riskLevel) {
|
|
47551
47806
|
const vulnCount = vulnerabilities.length;
|
|
47552
|
-
const testCount =
|
|
47553
|
-
|
|
47807
|
+
const testCount = validTests.length;
|
|
47808
|
+
const errorCount = connectionErrors.length;
|
|
47809
|
+
let explanation = "";
|
|
47810
|
+
if (errorCount > 0) {
|
|
47811
|
+
explanation += `⚠️ ${errorCount} test${errorCount !== 1 ? "s" : ""} failed due to connection/server errors. `;
|
|
47812
|
+
}
|
|
47813
|
+
if (testCount === 0 && errorCount > 0) {
|
|
47814
|
+
return explanation + `No valid tests completed. Check server connectivity and retry assessment.`;
|
|
47815
|
+
}
|
|
47816
|
+
if (testCount === 0 && errorCount === 0) {
|
|
47554
47817
|
return `No tools selected for security testing. Select tools to run security assessments.`;
|
|
47555
47818
|
}
|
|
47556
47819
|
if (vulnCount === 0) {
|
|
47557
|
-
return `Tested ${testCount} security patterns across selected tools. No vulnerabilities detected. All tools properly handle malicious inputs.`;
|
|
47820
|
+
return explanation + `Tested ${testCount} security patterns across selected tools. No vulnerabilities detected. All tools properly handle malicious inputs.`;
|
|
47558
47821
|
}
|
|
47559
|
-
const highConfidenceCount =
|
|
47822
|
+
const highConfidenceCount = validTests.filter(
|
|
47560
47823
|
(t) => t.vulnerable && (!t.confidence || t.confidence === "high")
|
|
47561
47824
|
).length;
|
|
47562
|
-
const mediumConfidenceCount =
|
|
47825
|
+
const mediumConfidenceCount = validTests.filter(
|
|
47563
47826
|
(t) => t.vulnerable && t.confidence === "medium"
|
|
47564
47827
|
).length;
|
|
47565
|
-
const lowConfidenceCount =
|
|
47828
|
+
const lowConfidenceCount = validTests.filter(
|
|
47566
47829
|
(t) => t.vulnerable && t.confidence === "low"
|
|
47567
47830
|
).length;
|
|
47568
47831
|
if (highConfidenceCount > 0) {
|
|
47569
|
-
return `Found ${highConfidenceCount} confirmed vulnerability${highConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests. Risk level: ${riskLevel}. Tools may execute malicious commands or leak sensitive data.`;
|
|
47832
|
+
return explanation + `Found ${highConfidenceCount} confirmed vulnerability${highConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests. Risk level: ${riskLevel}. Tools may execute malicious commands or leak sensitive data.`;
|
|
47570
47833
|
} else if (mediumConfidenceCount > 0) {
|
|
47571
|
-
return `Detected ${mediumConfidenceCount} potential security concern${mediumConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests requiring manual review. Tools showed suspicious behavior that needs verification.`;
|
|
47834
|
+
return explanation + `Detected ${mediumConfidenceCount} potential security concern${mediumConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests requiring manual review. Tools showed suspicious behavior that needs verification.`;
|
|
47572
47835
|
} else {
|
|
47573
|
-
return `Flagged ${lowConfidenceCount} uncertain detection${lowConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests. Manual verification needed to confirm if these are actual vulnerabilities or false positives.`;
|
|
47836
|
+
return explanation + `Flagged ${lowConfidenceCount} uncertain detection${lowConfidenceCount !== 1 ? "s" : ""} across ${testCount} security tests. Manual verification needed to confirm if these are actual vulnerabilities or false positives.`;
|
|
47574
47837
|
}
|
|
47575
47838
|
}
|
|
47576
47839
|
/**
|
|
@@ -47665,9 +47928,13 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47665
47928
|
/**
|
|
47666
47929
|
* Check if response is just reflection (safe)
|
|
47667
47930
|
* Expanded to catch more reflection patterns including echo, repeat, display
|
|
47668
|
-
* IMPROVED: Bidirectional patterns
|
|
47931
|
+
* IMPROVED: Bidirectional patterns, safety indicators, and payload-aware JSON analysis
|
|
47932
|
+
*
|
|
47933
|
+
* CRITICAL: This check distinguishes between:
|
|
47934
|
+
* - SAFE: Tool stores/echoes malicious input as data (reflection)
|
|
47935
|
+
* - VULNERABLE: Tool executes malicious input and returns results (execution)
|
|
47669
47936
|
*/
|
|
47670
|
-
isReflectionResponse(responseText) {
|
|
47937
|
+
isReflectionResponse(responseText, payload) {
|
|
47671
47938
|
const reflectionPatterns = [
|
|
47672
47939
|
// Original patterns (keep all existing)
|
|
47673
47940
|
/stored.*query/i,
|
|
@@ -47685,16 +47952,34 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47685
47952
|
/safely.*as.*data/i,
|
|
47686
47953
|
// NEW: Bidirectional patterns (catch "Query stored" and "stored query")
|
|
47687
47954
|
/query.*stored/i,
|
|
47955
|
+
/stored.*query/i,
|
|
47956
|
+
// Bidirectional: "Stored query"
|
|
47688
47957
|
/input.*saved/i,
|
|
47689
47958
|
/parameter.*received/i,
|
|
47690
47959
|
/command.*stored/i,
|
|
47960
|
+
/stored.*command/i,
|
|
47961
|
+
// Bidirectional: "Stored command"
|
|
47691
47962
|
/data.*stored/i,
|
|
47963
|
+
/stored.*data/i,
|
|
47964
|
+
// Bidirectional: "Stored data"
|
|
47692
47965
|
/action.*stored/i,
|
|
47966
|
+
/stored.*action/i,
|
|
47967
|
+
// Bidirectional: "Stored action"
|
|
47693
47968
|
/text.*stored/i,
|
|
47969
|
+
/stored.*text/i,
|
|
47970
|
+
// Bidirectional: "Stored text"
|
|
47694
47971
|
/setting.*stored/i,
|
|
47972
|
+
/stored.*setting/i,
|
|
47973
|
+
// Bidirectional: "Stored setting"
|
|
47695
47974
|
/instruction.*stored/i,
|
|
47975
|
+
/stored.*instruction/i,
|
|
47976
|
+
// Bidirectional: "Stored instruction"
|
|
47696
47977
|
/url.*stored/i,
|
|
47978
|
+
/stored.*url/i,
|
|
47979
|
+
// Bidirectional: "Stored URL"
|
|
47697
47980
|
/package.*stored/i,
|
|
47981
|
+
/stored.*package/i,
|
|
47982
|
+
// Bidirectional: "Stored package"
|
|
47698
47983
|
// NEW: Safety indicators (common in hardened implementations)
|
|
47699
47984
|
/stored.*safely/i,
|
|
47700
47985
|
/safely.*stored/i,
|
|
@@ -47708,14 +47993,66 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47708
47993
|
/stored.*successfully/i,
|
|
47709
47994
|
/saved.*to/i,
|
|
47710
47995
|
/recorded\s+in/i,
|
|
47711
|
-
/added\s+to/i
|
|
47996
|
+
/added\s+to/i,
|
|
47997
|
+
// NEW: Storage/logging confirmations (high confidence)
|
|
47998
|
+
/logged successfully:/i,
|
|
47999
|
+
/queued for processing:/i,
|
|
48000
|
+
/saved (for|successfully)/i,
|
|
48001
|
+
/stored for (admin review|configuration|processing)/i,
|
|
48002
|
+
// NEW: Processing confirmations (high confidence)
|
|
48003
|
+
/processed successfully/i,
|
|
48004
|
+
/validated successfully/i,
|
|
48005
|
+
/parsed successfully/i,
|
|
48006
|
+
/(validation|processing) (passed|completed)/i,
|
|
48007
|
+
// NEW: Error messages with input reflection (common safe pattern)
|
|
48008
|
+
/error:.*not (found|in approved list|recognized)/i,
|
|
48009
|
+
/error getting info for ['"].*['"]/i,
|
|
48010
|
+
/invalid .* format.*stored as text/i,
|
|
48011
|
+
/error:.*too (long|short|large)/i
|
|
47712
48012
|
];
|
|
47713
|
-
|
|
48013
|
+
if (reflectionPatterns.some((pattern2) => pattern2.test(responseText))) {
|
|
48014
|
+
return true;
|
|
48015
|
+
}
|
|
48016
|
+
try {
|
|
48017
|
+
const parsed = JSON.parse(responseText);
|
|
48018
|
+
const safeFields = [
|
|
48019
|
+
"command",
|
|
48020
|
+
"query",
|
|
48021
|
+
"text",
|
|
48022
|
+
"data",
|
|
48023
|
+
"action",
|
|
48024
|
+
"instruction",
|
|
48025
|
+
"setting",
|
|
48026
|
+
"url",
|
|
48027
|
+
"package",
|
|
48028
|
+
"input"
|
|
48029
|
+
];
|
|
48030
|
+
const executionFields = ["output", "stdout", "stderr", "contents"];
|
|
48031
|
+
const hasPayloadInExecution = executionFields.some((execField) => {
|
|
48032
|
+
const value = parsed[execField];
|
|
48033
|
+
return value && typeof value === "string" && value.includes(payload);
|
|
48034
|
+
});
|
|
48035
|
+
if (hasPayloadInExecution) {
|
|
48036
|
+
return false;
|
|
48037
|
+
}
|
|
48038
|
+
const isStoredOnly = safeFields.some((field) => {
|
|
48039
|
+
const value = parsed[field];
|
|
48040
|
+
return value === payload || typeof value === "string" && value.includes(payload);
|
|
48041
|
+
});
|
|
48042
|
+
if (isStoredOnly) {
|
|
48043
|
+
return true;
|
|
48044
|
+
}
|
|
48045
|
+
if (parsed.status && /(stored|logged|queued|pending|processed|validated)/.test(parsed.status)) {
|
|
48046
|
+
return true;
|
|
48047
|
+
}
|
|
48048
|
+
} catch {
|
|
48049
|
+
}
|
|
48050
|
+
return false;
|
|
47714
48051
|
}
|
|
47715
48052
|
/**
|
|
47716
48053
|
* Analyze injection response (existing logic)
|
|
47717
48054
|
*/
|
|
47718
|
-
analyzeInjectionResponse(response,
|
|
48055
|
+
analyzeInjectionResponse(response, payload) {
|
|
47719
48056
|
const responseText = this.extractResponseContent(response);
|
|
47720
48057
|
const executionPatterns = [
|
|
47721
48058
|
/executed/i,
|
|
@@ -47727,7 +48064,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47727
48064
|
const hasExecution = executionPatterns.some(
|
|
47728
48065
|
(pattern2) => pattern2.test(responseText)
|
|
47729
48066
|
);
|
|
47730
|
-
if (hasExecution && !this.isReflectionResponse(responseText)) {
|
|
48067
|
+
if (hasExecution && !this.isReflectionResponse(responseText, payload)) {
|
|
47731
48068
|
return {
|
|
47732
48069
|
isVulnerable: true,
|
|
47733
48070
|
evidence: "Tool executed instruction: found execution keywords"
|
|
@@ -49744,6 +50081,7 @@ function ToolSelector({
|
|
|
49744
50081
|
const AssessmentTab = ({
|
|
49745
50082
|
tools,
|
|
49746
50083
|
isLoadingTools = false,
|
|
50084
|
+
listTools,
|
|
49747
50085
|
callTool,
|
|
49748
50086
|
serverName = "MCP Server"
|
|
49749
50087
|
}) => {
|
|
@@ -49761,6 +50099,7 @@ const AssessmentTab = ({
|
|
|
49761
50099
|
const [showJson, setShowJson] = reactExports.useState(false);
|
|
49762
50100
|
const [collapsedTools, setCollapsedTools] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
49763
50101
|
const [allToolsCollapsed, setAllToolsCollapsed] = reactExports.useState(false);
|
|
50102
|
+
const [showOnlyErrors, setShowOnlyErrors] = reactExports.useState(false);
|
|
49764
50103
|
const [expandedToolDescriptions, setExpandedToolDescriptions] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
49765
50104
|
const [categoryFilter, setCategoryFilter] = reactExports.useState({
|
|
49766
50105
|
functionality: true,
|
|
@@ -49780,11 +50119,28 @@ const AssessmentTab = ({
|
|
|
49780
50119
|
[config]
|
|
49781
50120
|
);
|
|
49782
50121
|
reactExports.useEffect(() => {
|
|
49783
|
-
if (tools.length
|
|
50122
|
+
if (tools.length === 0) {
|
|
50123
|
+
return;
|
|
50124
|
+
}
|
|
50125
|
+
const currentToolNames = tools.map((t) => t.name);
|
|
50126
|
+
if (!config.selectedToolsForTesting) {
|
|
49784
50127
|
setConfig({
|
|
49785
50128
|
...config,
|
|
49786
|
-
selectedToolsForTesting:
|
|
50129
|
+
selectedToolsForTesting: currentToolNames
|
|
49787
50130
|
});
|
|
50131
|
+
} else {
|
|
50132
|
+
const existingSelections = config.selectedToolsForTesting.filter(
|
|
50133
|
+
(name2) => currentToolNames.includes(name2)
|
|
50134
|
+
);
|
|
50135
|
+
const newTools = currentToolNames.filter(
|
|
50136
|
+
(name2) => !config.selectedToolsForTesting.includes(name2)
|
|
50137
|
+
);
|
|
50138
|
+
if (newTools.length > 0 || existingSelections.length !== config.selectedToolsForTesting.length) {
|
|
50139
|
+
setConfig({
|
|
50140
|
+
...config,
|
|
50141
|
+
selectedToolsForTesting: [...existingSelections, ...newTools]
|
|
50142
|
+
});
|
|
50143
|
+
}
|
|
49788
50144
|
}
|
|
49789
50145
|
}, [tools, config, setConfig]);
|
|
49790
50146
|
const calculateFilteredOverallStatus = reactExports.useCallback(
|
|
@@ -49955,7 +50311,23 @@ const AssessmentTab = ({
|
|
|
49955
50311
|
)
|
|
49956
50312
|
] }),
|
|
49957
50313
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
49958
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
50314
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
50315
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Label$1, { htmlFor: "tool-selector", children: "Select tools for testing:" }),
|
|
50316
|
+
listTools && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50317
|
+
Button,
|
|
50318
|
+
{
|
|
50319
|
+
variant: "ghost",
|
|
50320
|
+
size: "sm",
|
|
50321
|
+
onClick: () => listTools(),
|
|
50322
|
+
disabled: isLoadingTools || isRunning,
|
|
50323
|
+
className: "h-7 px-2",
|
|
50324
|
+
children: [
|
|
50325
|
+
isLoadingTools ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-4 h-4 mr-1 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "w-4 h-4 mr-1" }),
|
|
50326
|
+
"Refresh"
|
|
50327
|
+
]
|
|
50328
|
+
}
|
|
50329
|
+
)
|
|
50330
|
+
] }),
|
|
49959
50331
|
isLoadingTools ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
49960
50332
|
/* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-4 h-4 animate-spin" }),
|
|
49961
50333
|
"Loading tools..."
|
|
@@ -50232,74 +50604,129 @@ const AssessmentTab = ({
|
|
|
50232
50604
|
children: [
|
|
50233
50605
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm mb-2", children: assessment.security.explanation }),
|
|
50234
50606
|
(() => {
|
|
50235
|
-
var _a2, _b2, _c;
|
|
50236
|
-
const
|
|
50607
|
+
var _a2, _b2, _c, _d;
|
|
50608
|
+
const connectionErrors = ((_a2 = assessment.security.promptInjectionTests) == null ? void 0 : _a2.filter(
|
|
50609
|
+
(t) => t.connectionError === true
|
|
50610
|
+
)) || [];
|
|
50611
|
+
const highConfidenceCount = ((_b2 = assessment.security.promptInjectionTests) == null ? void 0 : _b2.filter(
|
|
50237
50612
|
(t) => t.vulnerable && (!t.confidence || t.confidence === "high")
|
|
50238
50613
|
).length) || 0;
|
|
50239
|
-
const mediumConfidenceCount = ((
|
|
50614
|
+
const mediumConfidenceCount = ((_c = assessment.security.promptInjectionTests) == null ? void 0 : _c.filter(
|
|
50240
50615
|
(t) => t.vulnerable && t.confidence === "medium"
|
|
50241
50616
|
).length) || 0;
|
|
50242
|
-
const lowConfidenceCount = ((
|
|
50617
|
+
const lowConfidenceCount = ((_d = assessment.security.promptInjectionTests) == null ? void 0 : _d.filter(
|
|
50243
50618
|
(t) => t.vulnerable && t.confidence === "low"
|
|
50244
50619
|
).length) || 0;
|
|
50245
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50246
|
-
|
|
50247
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
50248
|
-
|
|
50249
|
-
|
|
50250
|
-
|
|
50251
|
-
|
|
50252
|
-
|
|
50253
|
-
|
|
50254
|
-
|
|
50255
|
-
|
|
50256
|
-
|
|
50257
|
-
|
|
50258
|
-
|
|
50259
|
-
|
|
50620
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
50621
|
+
connectionErrors.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-yellow-50 border-l-4 border-yellow-500 p-4 mb-4 rounded", children: [
|
|
50622
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
50623
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "w-5 h-5 text-yellow-600" }),
|
|
50624
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("h5", { className: "text-sm font-semibold text-yellow-900", children: [
|
|
50625
|
+
"Connection Errors (",
|
|
50626
|
+
connectionErrors.length,
|
|
50627
|
+
")"
|
|
50628
|
+
] })
|
|
50629
|
+
] }),
|
|
50630
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-yellow-800 mb-2", children: [
|
|
50631
|
+
connectionErrors.length,
|
|
50632
|
+
" test",
|
|
50633
|
+
connectionErrors.length !== 1 ? "s" : "",
|
|
50634
|
+
" could not complete due to server/network failures. These tests are excluded from vulnerability counts."
|
|
50635
|
+
] }),
|
|
50636
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-yellow-700 space-y-1 max-h-32 overflow-y-auto", children: connectionErrors.map((err, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50637
|
+
"div",
|
|
50638
|
+
{
|
|
50639
|
+
className: "flex items-start gap-2",
|
|
50640
|
+
children: [
|
|
50641
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-yellow-600", children: "•" }),
|
|
50642
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
50643
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: err.testName }),
|
|
50644
|
+
" on",
|
|
50645
|
+
" ",
|
|
50646
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "bg-yellow-100 px-1 rounded", children: err.toolName }),
|
|
50647
|
+
": ",
|
|
50648
|
+
err.errorType
|
|
50649
|
+
] })
|
|
50650
|
+
]
|
|
50651
|
+
},
|
|
50652
|
+
i
|
|
50653
|
+
)) }),
|
|
50654
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-yellow-700 mt-2 font-medium", children: "✅ Fix connectivity issues and re-run assessment for accurate results" })
|
|
50260
50655
|
] }),
|
|
50261
|
-
|
|
50656
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm space-y-1", children: [
|
|
50657
|
+
highConfidenceCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-red-700", children: [
|
|
50658
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Confirmed Issues:" }),
|
|
50659
|
+
" ",
|
|
50660
|
+
highConfidenceCount
|
|
50661
|
+
] }),
|
|
50662
|
+
mediumConfidenceCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-amber-700", children: [
|
|
50663
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Need Review:" }),
|
|
50664
|
+
" ",
|
|
50665
|
+
mediumConfidenceCount
|
|
50666
|
+
] }),
|
|
50667
|
+
lowConfidenceCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-blue-700", children: [
|
|
50668
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Uncertain (Verification Needed):" }),
|
|
50669
|
+
" ",
|
|
50670
|
+
lowConfidenceCount
|
|
50671
|
+
] }),
|
|
50672
|
+
highConfidenceCount === 0 && mediumConfidenceCount === 0 && lowConfidenceCount === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-green-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "All tests passed" }) })
|
|
50673
|
+
] })
|
|
50262
50674
|
] });
|
|
50263
50675
|
})(),
|
|
50264
50676
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2", children: [
|
|
50265
50677
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
50266
50678
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-sm", children: "Security Test Results:" }),
|
|
50267
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
50268
|
-
|
|
50269
|
-
|
|
50270
|
-
|
|
50271
|
-
|
|
50272
|
-
|
|
50273
|
-
|
|
50274
|
-
|
|
50275
|
-
|
|
50276
|
-
(
|
|
50277
|
-
|
|
50278
|
-
|
|
50279
|
-
|
|
50679
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
50680
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50681
|
+
Button,
|
|
50682
|
+
{
|
|
50683
|
+
variant: showOnlyErrors ? "default" : "outline",
|
|
50684
|
+
size: "sm",
|
|
50685
|
+
className: "text-xs h-6 px-2",
|
|
50686
|
+
onClick: () => setShowOnlyErrors(!showOnlyErrors),
|
|
50687
|
+
children: [
|
|
50688
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { className: "h-3 w-3 mr-1" }),
|
|
50689
|
+
showOnlyErrors ? "Show All" : "Filter Errors"
|
|
50690
|
+
]
|
|
50691
|
+
}
|
|
50692
|
+
),
|
|
50693
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
50694
|
+
Button,
|
|
50695
|
+
{
|
|
50696
|
+
variant: "outline",
|
|
50697
|
+
size: "sm",
|
|
50698
|
+
className: "text-xs h-6 px-2",
|
|
50699
|
+
onClick: () => {
|
|
50700
|
+
const toolGroups = /* @__PURE__ */ new Map();
|
|
50701
|
+
assessment.security.promptInjectionTests.forEach(
|
|
50702
|
+
(testResult) => {
|
|
50703
|
+
const toolName = testResult.toolName || "Unknown Tool";
|
|
50704
|
+
if (!toolGroups.has(toolName)) {
|
|
50705
|
+
toolGroups.set(toolName, []);
|
|
50706
|
+
}
|
|
50280
50707
|
}
|
|
50281
|
-
}
|
|
50282
|
-
);
|
|
50283
|
-
if (allToolsCollapsed) {
|
|
50284
|
-
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
50285
|
-
setAllToolsCollapsed(false);
|
|
50286
|
-
} else {
|
|
50287
|
-
const allToolNames = Array.from(
|
|
50288
|
-
toolGroups.keys()
|
|
50289
50708
|
);
|
|
50290
|
-
|
|
50291
|
-
|
|
50292
|
-
|
|
50293
|
-
|
|
50294
|
-
|
|
50295
|
-
|
|
50296
|
-
|
|
50297
|
-
|
|
50298
|
-
|
|
50299
|
-
|
|
50300
|
-
|
|
50301
|
-
|
|
50302
|
-
|
|
50709
|
+
if (allToolsCollapsed) {
|
|
50710
|
+
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
50711
|
+
setAllToolsCollapsed(false);
|
|
50712
|
+
} else {
|
|
50713
|
+
const allToolNames = Array.from(
|
|
50714
|
+
toolGroups.keys()
|
|
50715
|
+
);
|
|
50716
|
+
setCollapsedTools(new Set(allToolNames));
|
|
50717
|
+
setAllToolsCollapsed(true);
|
|
50718
|
+
}
|
|
50719
|
+
},
|
|
50720
|
+
children: allToolsCollapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
50721
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-3 w-3 mr-1" }),
|
|
50722
|
+
"Expand All"
|
|
50723
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
50724
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-3 w-3 mr-1" }),
|
|
50725
|
+
"Collapse All"
|
|
50726
|
+
] })
|
|
50727
|
+
}
|
|
50728
|
+
)
|
|
50729
|
+
] })
|
|
50303
50730
|
] }),
|
|
50304
50731
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 space-y-1", children: (() => {
|
|
50305
50732
|
const toolGroups = /* @__PURE__ */ new Map();
|
|
@@ -50324,18 +50751,26 @@ const AssessmentTab = ({
|
|
|
50324
50751
|
newCollapsed.size === toolGroups.size
|
|
50325
50752
|
);
|
|
50326
50753
|
};
|
|
50327
|
-
|
|
50328
|
-
|
|
50329
|
-
|
|
50330
|
-
{
|
|
50331
|
-
|
|
50332
|
-
|
|
50333
|
-
|
|
50334
|
-
|
|
50335
|
-
|
|
50336
|
-
|
|
50337
|
-
|
|
50338
|
-
|
|
50754
|
+
let filteredGroups = Array.from(toolGroups.entries());
|
|
50755
|
+
if (showOnlyErrors) {
|
|
50756
|
+
filteredGroups = filteredGroups.filter(
|
|
50757
|
+
([, toolTests]) => {
|
|
50758
|
+
return toolTests.some(
|
|
50759
|
+
(test) => test.vulnerable === true
|
|
50760
|
+
);
|
|
50761
|
+
}
|
|
50762
|
+
);
|
|
50763
|
+
}
|
|
50764
|
+
return filteredGroups.map(([toolName, toolTests]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
50765
|
+
CollapsibleToolSection,
|
|
50766
|
+
{
|
|
50767
|
+
toolName,
|
|
50768
|
+
toolTests,
|
|
50769
|
+
isCollapsed: collapsedTools.has(toolName),
|
|
50770
|
+
onToggle: handleToggleTool
|
|
50771
|
+
},
|
|
50772
|
+
toolName
|
|
50773
|
+
));
|
|
50339
50774
|
})() }),
|
|
50340
50775
|
assessment.security.vulnerabilities.length > 0 && (() => {
|
|
50341
50776
|
var _a2, _b2, _c;
|
|
@@ -50589,43 +51024,58 @@ const AssessmentTab = ({
|
|
|
50589
51024
|
assessment.errorHandling.metrics.testDetails && assessment.errorHandling.metrics.testDetails.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 border-t pt-3", children: [
|
|
50590
51025
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
50591
51026
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-sm", children: "Error Handling Test Results:" }),
|
|
50592
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
50593
|
-
|
|
50594
|
-
|
|
50595
|
-
|
|
50596
|
-
|
|
50597
|
-
|
|
50598
|
-
|
|
50599
|
-
|
|
50600
|
-
|
|
50601
|
-
|
|
50602
|
-
|
|
50603
|
-
|
|
50604
|
-
|
|
50605
|
-
|
|
51027
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
51028
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
51029
|
+
Button,
|
|
51030
|
+
{
|
|
51031
|
+
variant: showOnlyErrors ? "default" : "outline",
|
|
51032
|
+
size: "sm",
|
|
51033
|
+
className: "text-xs h-6 px-2",
|
|
51034
|
+
onClick: () => setShowOnlyErrors(!showOnlyErrors),
|
|
51035
|
+
children: [
|
|
51036
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { className: "h-3 w-3 mr-1" }),
|
|
51037
|
+
showOnlyErrors ? "Show All" : "Filter Errors"
|
|
51038
|
+
]
|
|
51039
|
+
}
|
|
51040
|
+
),
|
|
51041
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
51042
|
+
Button,
|
|
51043
|
+
{
|
|
51044
|
+
variant: "outline",
|
|
51045
|
+
size: "sm",
|
|
51046
|
+
className: "text-xs h-6 px-2",
|
|
51047
|
+
onClick: () => {
|
|
51048
|
+
var _a2;
|
|
51049
|
+
const toolGroups = /* @__PURE__ */ new Map();
|
|
51050
|
+
(_a2 = assessment.errorHandling.metrics.testDetails) == null ? void 0 : _a2.forEach(
|
|
51051
|
+
(testResult) => {
|
|
51052
|
+
const toolName = testResult.toolName || "Unknown Tool";
|
|
51053
|
+
if (!toolGroups.has(toolName)) {
|
|
51054
|
+
toolGroups.set(toolName, []);
|
|
51055
|
+
}
|
|
50606
51056
|
}
|
|
50607
|
-
}
|
|
50608
|
-
);
|
|
50609
|
-
if (allToolsCollapsed) {
|
|
50610
|
-
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
50611
|
-
setAllToolsCollapsed(false);
|
|
50612
|
-
} else {
|
|
50613
|
-
const allToolNames = Array.from(
|
|
50614
|
-
toolGroups.keys()
|
|
50615
51057
|
);
|
|
50616
|
-
|
|
50617
|
-
|
|
50618
|
-
|
|
50619
|
-
|
|
50620
|
-
|
|
50621
|
-
|
|
50622
|
-
|
|
50623
|
-
|
|
50624
|
-
|
|
50625
|
-
|
|
50626
|
-
|
|
50627
|
-
|
|
50628
|
-
|
|
51058
|
+
if (allToolsCollapsed) {
|
|
51059
|
+
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
51060
|
+
setAllToolsCollapsed(false);
|
|
51061
|
+
} else {
|
|
51062
|
+
const allToolNames = Array.from(
|
|
51063
|
+
toolGroups.keys()
|
|
51064
|
+
);
|
|
51065
|
+
setCollapsedTools(new Set(allToolNames));
|
|
51066
|
+
setAllToolsCollapsed(true);
|
|
51067
|
+
}
|
|
51068
|
+
},
|
|
51069
|
+
children: allToolsCollapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
51070
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-3 w-3 mr-1" }),
|
|
51071
|
+
"Expand All"
|
|
51072
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
51073
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-3 w-3 mr-1" }),
|
|
51074
|
+
"Collapse All"
|
|
51075
|
+
] })
|
|
51076
|
+
}
|
|
51077
|
+
)
|
|
51078
|
+
] })
|
|
50629
51079
|
] }),
|
|
50630
51080
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 space-y-1", children: (() => {
|
|
50631
51081
|
var _a2;
|
|
@@ -50651,12 +51101,27 @@ const AssessmentTab = ({
|
|
|
50651
51101
|
newCollapsed.size === toolGroups.size
|
|
50652
51102
|
);
|
|
50653
51103
|
};
|
|
50654
|
-
|
|
51104
|
+
let filteredGroups = Array.from(
|
|
51105
|
+
toolGroups.entries()
|
|
51106
|
+
);
|
|
51107
|
+
if (showOnlyErrors) {
|
|
51108
|
+
filteredGroups = filteredGroups.filter(
|
|
51109
|
+
([, toolTests]) => {
|
|
51110
|
+
return toolTests.some(
|
|
51111
|
+
(test) => test.passed === false
|
|
51112
|
+
);
|
|
51113
|
+
}
|
|
51114
|
+
);
|
|
51115
|
+
}
|
|
51116
|
+
return filteredGroups.map(
|
|
50655
51117
|
([toolName, toolTests]) => {
|
|
50656
|
-
const
|
|
51118
|
+
const scoredTests = toolTests.filter(
|
|
51119
|
+
(t) => t.testType !== "invalid_values"
|
|
51120
|
+
);
|
|
51121
|
+
const passedCount = scoredTests.filter(
|
|
50657
51122
|
(t) => t.passed
|
|
50658
51123
|
).length;
|
|
50659
|
-
const totalCount =
|
|
51124
|
+
const totalCount = scoredTests.length;
|
|
50660
51125
|
const allPassed = passedCount === totalCount;
|
|
50661
51126
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50662
51127
|
"div",
|
|
@@ -52497,13 +52962,13 @@ const App = () => {
|
|
|
52497
52962
|
) });
|
|
52498
52963
|
if (window.location.pathname === "/oauth/callback") {
|
|
52499
52964
|
const OAuthCallback = React.lazy(
|
|
52500
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
52965
|
+
() => __vitePreload(() => import("./OAuthCallback-CiSJznN1.js"), true ? [] : void 0)
|
|
52501
52966
|
);
|
|
52502
52967
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
52503
52968
|
}
|
|
52504
52969
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
52505
52970
|
const OAuthDebugCallback = React.lazy(
|
|
52506
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
52971
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-D_XkKc3n.js"), true ? [] : void 0)
|
|
52507
52972
|
);
|
|
52508
52973
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
52509
52974
|
}
|
|
@@ -52790,6 +53255,10 @@ const App = () => {
|
|
|
52790
53255
|
{
|
|
52791
53256
|
tools,
|
|
52792
53257
|
isLoadingTools,
|
|
53258
|
+
listTools: () => {
|
|
53259
|
+
clearError("tools");
|
|
53260
|
+
listTools();
|
|
53261
|
+
},
|
|
52793
53262
|
callTool: async (name2, params) => {
|
|
52794
53263
|
const result = await callTool(name2, params);
|
|
52795
53264
|
return result;
|
|
@@ -2014,6 +2014,10 @@ video {
|
|
|
2014
2014
|
--tw-text-opacity: 1;
|
|
2015
2015
|
color: rgb(133 77 14 / var(--tw-text-opacity, 1));
|
|
2016
2016
|
}
|
|
2017
|
+
.text-yellow-900 {
|
|
2018
|
+
--tw-text-opacity: 1;
|
|
2019
|
+
color: rgb(113 63 18 / var(--tw-text-opacity, 1));
|
|
2020
|
+
}
|
|
2017
2021
|
.underline-offset-4 {
|
|
2018
2022
|
text-underline-offset: 4px;
|
|
2019
2023
|
}
|
package/client/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-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BsOrK-Nh.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Cz-lwW4x.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root" class="w-full"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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>",
|