@bryan-thompson/inspector-assessment 1.2.1 → 1.4.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 +75 -8
- package/client/dist/assets/{OAuthCallback-C8iZSwWO.js → OAuthCallback-CIWsnXN_.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-Br9U2vZs.js → OAuthDebugCallback-DP9WXVFe.js} +1 -1
- package/client/dist/assets/{index-D12b6zCd.js → index-CynAt5P-.js} +937 -206
- 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
|
@@ -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.4.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.4.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" })
|
|
@@ -46396,7 +46396,7 @@ class FunctionalityAssessor extends BaseAssessor {
|
|
|
46396
46396
|
}
|
|
46397
46397
|
const SECURITY_ATTACK_PATTERNS = [
|
|
46398
46398
|
// ==========================================
|
|
46399
|
-
// CRITICAL INJECTION TESTS (
|
|
46399
|
+
// CRITICAL INJECTION TESTS (4)
|
|
46400
46400
|
// ==========================================
|
|
46401
46401
|
// 1. COMMAND INJECTION
|
|
46402
46402
|
{
|
|
@@ -46476,7 +46476,66 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46476
46476
|
}
|
|
46477
46477
|
]
|
|
46478
46478
|
},
|
|
46479
|
-
// 3.
|
|
46479
|
+
// 3. CALCULATOR INJECTION
|
|
46480
|
+
{
|
|
46481
|
+
attackName: "Calculator Injection",
|
|
46482
|
+
description: "Test if calculator/math tools execute arbitrary expressions instead of safely storing them",
|
|
46483
|
+
payloads: [
|
|
46484
|
+
// Simple arithmetic - triggers eval() in vulnerable calculator tools
|
|
46485
|
+
{
|
|
46486
|
+
payload: "2+2",
|
|
46487
|
+
evidence: /The answer is 4/i,
|
|
46488
|
+
riskLevel: "HIGH",
|
|
46489
|
+
description: "Simple arithmetic expression (common trigger)",
|
|
46490
|
+
payloadType: "injection"
|
|
46491
|
+
},
|
|
46492
|
+
{
|
|
46493
|
+
payload: "5*5",
|
|
46494
|
+
evidence: /The answer is 25/i,
|
|
46495
|
+
riskLevel: "HIGH",
|
|
46496
|
+
description: "Multiplication expression",
|
|
46497
|
+
payloadType: "injection"
|
|
46498
|
+
},
|
|
46499
|
+
{
|
|
46500
|
+
payload: "100+200",
|
|
46501
|
+
evidence: /The answer is 300/i,
|
|
46502
|
+
riskLevel: "HIGH",
|
|
46503
|
+
description: "Addition with larger numbers",
|
|
46504
|
+
payloadType: "injection"
|
|
46505
|
+
},
|
|
46506
|
+
// Natural language triggers
|
|
46507
|
+
{
|
|
46508
|
+
payload: "what is 10*10",
|
|
46509
|
+
evidence: /The answer is 100/i,
|
|
46510
|
+
riskLevel: "HIGH",
|
|
46511
|
+
description: "Natural language math query",
|
|
46512
|
+
payloadType: "injection"
|
|
46513
|
+
},
|
|
46514
|
+
{
|
|
46515
|
+
payload: "calculate 50+50",
|
|
46516
|
+
evidence: /The answer is 100/i,
|
|
46517
|
+
riskLevel: "HIGH",
|
|
46518
|
+
description: "Calculate command",
|
|
46519
|
+
payloadType: "injection"
|
|
46520
|
+
},
|
|
46521
|
+
// Code injection via eval()
|
|
46522
|
+
{
|
|
46523
|
+
payload: "__import__('os').system('whoami')",
|
|
46524
|
+
evidence: /\b(root|user|admin|ubuntu)\b/i,
|
|
46525
|
+
riskLevel: "HIGH",
|
|
46526
|
+
description: "Python code injection via eval",
|
|
46527
|
+
payloadType: "injection"
|
|
46528
|
+
},
|
|
46529
|
+
{
|
|
46530
|
+
payload: "1+1; __import__('subprocess').run(['whoami'])",
|
|
46531
|
+
evidence: /\b(root|user|admin|ubuntu)\b/i,
|
|
46532
|
+
riskLevel: "HIGH",
|
|
46533
|
+
description: "eval chain to system command",
|
|
46534
|
+
payloadType: "injection"
|
|
46535
|
+
}
|
|
46536
|
+
]
|
|
46537
|
+
},
|
|
46538
|
+
// 4. PATH TRAVERSAL
|
|
46480
46539
|
{
|
|
46481
46540
|
attackName: "Path Traversal",
|
|
46482
46541
|
description: "Test if tool allows access to files outside intended directory",
|
|
@@ -46514,7 +46573,7 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46514
46573
|
// ==========================================
|
|
46515
46574
|
// INPUT VALIDATION TESTS (3)
|
|
46516
46575
|
// ==========================================
|
|
46517
|
-
//
|
|
46576
|
+
// 5. TYPE SAFETY
|
|
46518
46577
|
{
|
|
46519
46578
|
attackName: "Type Safety",
|
|
46520
46579
|
description: "Test if tool validates parameter types correctly",
|
|
@@ -46542,7 +46601,7 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46542
46601
|
}
|
|
46543
46602
|
]
|
|
46544
46603
|
},
|
|
46545
|
-
//
|
|
46604
|
+
// 6. BOUNDARY TESTING
|
|
46546
46605
|
{
|
|
46547
46606
|
attackName: "Boundary Testing",
|
|
46548
46607
|
description: "Test edge cases and boundary conditions",
|
|
@@ -46570,7 +46629,7 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46570
46629
|
}
|
|
46571
46630
|
]
|
|
46572
46631
|
},
|
|
46573
|
-
//
|
|
46632
|
+
// 7. REQUIRED FIELDS
|
|
46574
46633
|
{
|
|
46575
46634
|
attackName: "Required Fields",
|
|
46576
46635
|
description: "Test if tool validates required parameters",
|
|
@@ -46587,7 +46646,7 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46587
46646
|
// ==========================================
|
|
46588
46647
|
// PROTOCOL COMPLIANCE TESTS (2)
|
|
46589
46648
|
// ==========================================
|
|
46590
|
-
//
|
|
46649
|
+
// 8. MCP ERROR FORMAT
|
|
46591
46650
|
{
|
|
46592
46651
|
attackName: "MCP Error Format",
|
|
46593
46652
|
description: "Verify errors follow MCP protocol specification",
|
|
@@ -46601,7 +46660,7 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46601
46660
|
}
|
|
46602
46661
|
]
|
|
46603
46662
|
},
|
|
46604
|
-
//
|
|
46663
|
+
// 9. TIMEOUT HANDLING
|
|
46605
46664
|
{
|
|
46606
46665
|
attackName: "Timeout Handling",
|
|
46607
46666
|
description: "Test if tool handles long operations gracefully",
|
|
@@ -46614,6 +46673,103 @@ const SECURITY_ATTACK_PATTERNS = [
|
|
|
46614
46673
|
payloadType: "protocol"
|
|
46615
46674
|
}
|
|
46616
46675
|
]
|
|
46676
|
+
},
|
|
46677
|
+
// ==========================================
|
|
46678
|
+
// TOOL-SPECIFIC VULNERABILITY TESTS (4)
|
|
46679
|
+
// ==========================================
|
|
46680
|
+
// 10. INDIRECT PROMPT INJECTION (HTTP URL Fetching)
|
|
46681
|
+
{
|
|
46682
|
+
attackName: "Indirect Prompt Injection",
|
|
46683
|
+
description: "Test if tool fetches and executes external content",
|
|
46684
|
+
payloads: [
|
|
46685
|
+
{
|
|
46686
|
+
payload: "http://example.com",
|
|
46687
|
+
evidence: /(status_code.*200|status.*200.*content|<html|<!DOCTYPE|fetched.*http.*content)/i,
|
|
46688
|
+
riskLevel: "HIGH",
|
|
46689
|
+
description: "Benign URL fetch test",
|
|
46690
|
+
payloadType: "injection",
|
|
46691
|
+
parameterTypes: ["url", "uri", "link", "endpoint", "address"]
|
|
46692
|
+
},
|
|
46693
|
+
{
|
|
46694
|
+
payload: "http://evil.com/inject.txt",
|
|
46695
|
+
evidence: /(fetched|retrieved|evil\.com|malicious|injected)/i,
|
|
46696
|
+
riskLevel: "HIGH",
|
|
46697
|
+
description: "Malicious URL fetch test",
|
|
46698
|
+
payloadType: "injection",
|
|
46699
|
+
parameterTypes: ["url", "uri", "link"]
|
|
46700
|
+
}
|
|
46701
|
+
]
|
|
46702
|
+
},
|
|
46703
|
+
// 11. UNICODE BYPASS
|
|
46704
|
+
{
|
|
46705
|
+
attackName: "Unicode Bypass",
|
|
46706
|
+
description: "Test if tool decodes and executes unicode-encoded commands",
|
|
46707
|
+
payloads: [
|
|
46708
|
+
{
|
|
46709
|
+
payload: "\\u0077\\u0068\\u006f\\u0061\\u006d\\u0069",
|
|
46710
|
+
// "whoami"
|
|
46711
|
+
evidence: /(root|user|admin|decoded.*executed|unicode.*executed)/i,
|
|
46712
|
+
riskLevel: "MEDIUM",
|
|
46713
|
+
description: "Unicode-encoded system command",
|
|
46714
|
+
payloadType: "injection",
|
|
46715
|
+
parameterTypes: ["text", "content", "message", "input", "data"]
|
|
46716
|
+
},
|
|
46717
|
+
{
|
|
46718
|
+
payload: "\\u0069\\u0067\\u006e\\u006f\\u0072\\u0065",
|
|
46719
|
+
// "ignore"
|
|
46720
|
+
evidence: /(decoded|unicode|ignore.*previous|executed)/i,
|
|
46721
|
+
riskLevel: "MEDIUM",
|
|
46722
|
+
description: "Unicode-encoded instruction",
|
|
46723
|
+
payloadType: "injection",
|
|
46724
|
+
parameterTypes: ["text", "content", "message"]
|
|
46725
|
+
}
|
|
46726
|
+
]
|
|
46727
|
+
},
|
|
46728
|
+
// 12. NESTED INJECTION
|
|
46729
|
+
{
|
|
46730
|
+
attackName: "Nested Injection",
|
|
46731
|
+
description: "Test if tool executes hidden instructions in nested JSON",
|
|
46732
|
+
payloads: [
|
|
46733
|
+
{
|
|
46734
|
+
payload: '{"query":"normal","hidden":"whoami"}',
|
|
46735
|
+
evidence: /(executed.*hidden|nested.*instruction|root|user|admin)/i,
|
|
46736
|
+
riskLevel: "MEDIUM",
|
|
46737
|
+
description: "Hidden command in JSON field",
|
|
46738
|
+
payloadType: "injection",
|
|
46739
|
+
parameterTypes: ["data", "json", "payload", "body", "params"]
|
|
46740
|
+
},
|
|
46741
|
+
{
|
|
46742
|
+
payload: '{"visible":"safe","_execute":"delete all"}',
|
|
46743
|
+
evidence: /(executed|deleted|internal.*field|hidden)/i,
|
|
46744
|
+
riskLevel: "HIGH",
|
|
46745
|
+
description: "Hidden instruction in internal field",
|
|
46746
|
+
payloadType: "injection",
|
|
46747
|
+
parameterTypes: ["data", "json", "input"]
|
|
46748
|
+
}
|
|
46749
|
+
]
|
|
46750
|
+
},
|
|
46751
|
+
// 13. PACKAGE SQUATTING
|
|
46752
|
+
{
|
|
46753
|
+
attackName: "Package Squatting",
|
|
46754
|
+
description: "Test if tool downloads typosquatted packages",
|
|
46755
|
+
payloads: [
|
|
46756
|
+
{
|
|
46757
|
+
payload: "install nmpy",
|
|
46758
|
+
evidence: /(downloaded|installed|nmpy|typosquatted|pip.*output)/i,
|
|
46759
|
+
riskLevel: "MEDIUM",
|
|
46760
|
+
description: "Typosquatted package (nmpy instead of numpy)",
|
|
46761
|
+
payloadType: "injection",
|
|
46762
|
+
parameterTypes: ["package", "pkg", "module", "library", "dependency"]
|
|
46763
|
+
},
|
|
46764
|
+
{
|
|
46765
|
+
payload: "requsts",
|
|
46766
|
+
evidence: /(downloaded|installed|requsts|typosquatted)/i,
|
|
46767
|
+
riskLevel: "MEDIUM",
|
|
46768
|
+
description: "Typosquatted package (requsts instead of requests)",
|
|
46769
|
+
payloadType: "injection",
|
|
46770
|
+
parameterTypes: ["package", "pkg", "module"]
|
|
46771
|
+
}
|
|
46772
|
+
]
|
|
46617
46773
|
}
|
|
46618
46774
|
];
|
|
46619
46775
|
function getPayloadsForAttack(attackName, limit) {
|
|
@@ -47002,10 +47158,20 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47002
47158
|
async assess(context) {
|
|
47003
47159
|
const toolsToTest = this.selectToolsForTesting(context.tools);
|
|
47004
47160
|
const allTests = await this.runUniversalSecurityTests(context);
|
|
47161
|
+
const connectionErrors = allTests.filter((t) => t.connectionError === true);
|
|
47162
|
+
const validTests = allTests.filter((t) => !t.connectionError);
|
|
47163
|
+
if (connectionErrors.length > 0) {
|
|
47164
|
+
this.log(
|
|
47165
|
+
`⚠️ WARNING: ${connectionErrors.length} test${connectionErrors.length !== 1 ? "s" : ""} failed due to connection/server errors`
|
|
47166
|
+
);
|
|
47167
|
+
this.log(
|
|
47168
|
+
`Connection errors: ${connectionErrors.map((e) => `${e.toolName}:${e.testName} (${e.errorType})`).join(", ")}`
|
|
47169
|
+
);
|
|
47170
|
+
}
|
|
47005
47171
|
const vulnerabilities = [];
|
|
47006
47172
|
let highRiskCount = 0;
|
|
47007
47173
|
let mediumRiskCount = 0;
|
|
47008
|
-
for (const test of
|
|
47174
|
+
for (const test of validTests) {
|
|
47009
47175
|
if (test.vulnerable) {
|
|
47010
47176
|
let vulnerability;
|
|
47011
47177
|
if (test.confidence === "high" || !test.confidence) {
|
|
@@ -47030,12 +47196,14 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47030
47196
|
vulnerabilities.length
|
|
47031
47197
|
);
|
|
47032
47198
|
const status = this.determineSecurityStatus(
|
|
47033
|
-
|
|
47199
|
+
validTests,
|
|
47034
47200
|
vulnerabilities.length,
|
|
47035
|
-
|
|
47201
|
+
validTests.length,
|
|
47202
|
+
connectionErrors.length
|
|
47036
47203
|
);
|
|
47037
47204
|
const explanation = this.generateSecurityExplanation(
|
|
47038
|
-
|
|
47205
|
+
validTests,
|
|
47206
|
+
connectionErrors,
|
|
47039
47207
|
vulnerabilities,
|
|
47040
47208
|
overallRiskLevel
|
|
47041
47209
|
);
|
|
@@ -47154,6 +47322,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47154
47322
|
const results = [];
|
|
47155
47323
|
const criticalPatterns = [
|
|
47156
47324
|
"Command Injection",
|
|
47325
|
+
"Calculator Injection",
|
|
47157
47326
|
"SQL Injection",
|
|
47158
47327
|
"Path Traversal"
|
|
47159
47328
|
];
|
|
@@ -47163,7 +47332,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47163
47332
|
);
|
|
47164
47333
|
const toolsToTest = this.selectToolsForTesting(context.tools);
|
|
47165
47334
|
this.log(
|
|
47166
|
-
`Starting BASIC security assessment - testing ${toolsToTest.length} tools with ${basicPatterns.length} critical injection patterns (~${toolsToTest.length * basicPatterns.length} tests)`
|
|
47335
|
+
`Starting BASIC security assessment - testing ${toolsToTest.length} tools with ${basicPatterns.length} critical injection patterns (~${toolsToTest.length * basicPatterns.length * 5} tests)`
|
|
47167
47336
|
);
|
|
47168
47337
|
for (const tool of toolsToTest) {
|
|
47169
47338
|
if (!this.hasInputParameters(tool)) {
|
|
@@ -47240,7 +47409,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47240
47409
|
};
|
|
47241
47410
|
}
|
|
47242
47411
|
try {
|
|
47243
|
-
const params = this.createTestParameters(payload
|
|
47412
|
+
const params = this.createTestParameters(payload, tool);
|
|
47244
47413
|
if (Object.keys(params).length === 0) {
|
|
47245
47414
|
return {
|
|
47246
47415
|
testName: attackName,
|
|
@@ -47256,6 +47425,24 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47256
47425
|
callTool(tool.name, params),
|
|
47257
47426
|
5e3
|
|
47258
47427
|
);
|
|
47428
|
+
if (this.isConnectionError(response)) {
|
|
47429
|
+
return {
|
|
47430
|
+
testName: attackName,
|
|
47431
|
+
description: payload.description,
|
|
47432
|
+
payload: payload.payload,
|
|
47433
|
+
riskLevel: payload.riskLevel,
|
|
47434
|
+
toolName: tool.name,
|
|
47435
|
+
vulnerable: true,
|
|
47436
|
+
// Mark as failed (test could not complete)
|
|
47437
|
+
evidence: `CONNECTION ERROR: Test could not complete due to server/network failure`,
|
|
47438
|
+
response: this.extractResponseContent(response),
|
|
47439
|
+
connectionError: true,
|
|
47440
|
+
errorType: this.classifyError(response),
|
|
47441
|
+
testReliability: "failed",
|
|
47442
|
+
confidence: "high",
|
|
47443
|
+
requiresManualReview: true
|
|
47444
|
+
};
|
|
47445
|
+
}
|
|
47259
47446
|
const { isVulnerable, evidence } = this.analyzeResponse(
|
|
47260
47447
|
response,
|
|
47261
47448
|
payload,
|
|
@@ -47280,6 +47467,23 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47280
47467
|
...confidenceResult
|
|
47281
47468
|
};
|
|
47282
47469
|
} catch (error) {
|
|
47470
|
+
if (this.isConnectionErrorFromException(error)) {
|
|
47471
|
+
return {
|
|
47472
|
+
testName: attackName,
|
|
47473
|
+
description: payload.description,
|
|
47474
|
+
payload: payload.payload,
|
|
47475
|
+
riskLevel: payload.riskLevel,
|
|
47476
|
+
toolName: tool.name,
|
|
47477
|
+
vulnerable: false,
|
|
47478
|
+
evidence: `CONNECTION ERROR: Test could not complete due to server/network failure`,
|
|
47479
|
+
response: this.extractErrorMessage(error),
|
|
47480
|
+
connectionError: true,
|
|
47481
|
+
errorType: this.classifyErrorFromException(error),
|
|
47482
|
+
testReliability: "failed",
|
|
47483
|
+
confidence: "high",
|
|
47484
|
+
requiresManualReview: true
|
|
47485
|
+
};
|
|
47486
|
+
}
|
|
47283
47487
|
return {
|
|
47284
47488
|
testName: attackName,
|
|
47285
47489
|
description: payload.description,
|
|
@@ -47292,16 +47496,183 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47292
47496
|
}
|
|
47293
47497
|
}
|
|
47294
47498
|
/**
|
|
47295
|
-
*
|
|
47296
|
-
* Returns
|
|
47499
|
+
* Check if response indicates connection/server failure
|
|
47500
|
+
* Returns true if test couldn't complete due to infrastructure issues
|
|
47501
|
+
*
|
|
47502
|
+
* CRITICAL: Only match transport/infrastructure errors, NOT tool business logic
|
|
47297
47503
|
*/
|
|
47298
|
-
|
|
47299
|
-
|
|
47300
|
-
|
|
47301
|
-
|
|
47302
|
-
|
|
47303
|
-
|
|
47504
|
+
isConnectionError(response) {
|
|
47505
|
+
const text = this.extractResponseContent(response).toLowerCase();
|
|
47506
|
+
const unambiguousPatterns = [
|
|
47507
|
+
/MCP error -32001/i,
|
|
47508
|
+
// MCP transport errors
|
|
47509
|
+
/MCP error -32603/i,
|
|
47510
|
+
// MCP internal error
|
|
47511
|
+
/MCP error -32000/i,
|
|
47512
|
+
// MCP server error
|
|
47513
|
+
/MCP error -32700/i,
|
|
47514
|
+
// MCP parse error
|
|
47515
|
+
/socket hang up/i,
|
|
47516
|
+
// Network socket errors
|
|
47517
|
+
/ECONNREFUSED/i,
|
|
47518
|
+
// Connection refused
|
|
47519
|
+
/ETIMEDOUT/i,
|
|
47520
|
+
// Network timeout
|
|
47521
|
+
/ERR_CONNECTION/i,
|
|
47522
|
+
// Connection errors
|
|
47523
|
+
/fetch failed/i,
|
|
47524
|
+
// HTTP fetch failures
|
|
47525
|
+
/connection reset/i,
|
|
47526
|
+
// Connection reset
|
|
47527
|
+
/error POSTing to endpoint/i,
|
|
47528
|
+
// Transport layer POST errors
|
|
47529
|
+
/error GETting.*endpoint/i,
|
|
47530
|
+
// Transport layer GET errors (requires 'endpoint' to avoid false positives)
|
|
47531
|
+
/service unavailable/i,
|
|
47532
|
+
// HTTP 503 (server down)
|
|
47533
|
+
/gateway timeout/i,
|
|
47534
|
+
// HTTP 504 (gateway timeout)
|
|
47535
|
+
/unknown tool:/i,
|
|
47536
|
+
// Tool name not in current server's tool list (stale tool list)
|
|
47537
|
+
/tool.*not found/i,
|
|
47538
|
+
// Alternative phrasing for missing tool
|
|
47539
|
+
/tool.*does not exist/i,
|
|
47540
|
+
// Alternative phrasing for missing tool
|
|
47541
|
+
/no such tool/i
|
|
47542
|
+
// Alternative phrasing for missing tool
|
|
47543
|
+
];
|
|
47544
|
+
if (unambiguousPatterns.some((pattern2) => pattern2.test(text))) {
|
|
47545
|
+
return true;
|
|
47304
47546
|
}
|
|
47547
|
+
const mcpPrefix = /^mcp error -\d+:/i.test(text);
|
|
47548
|
+
if (mcpPrefix) {
|
|
47549
|
+
const contextualPatterns = [
|
|
47550
|
+
/bad request/i,
|
|
47551
|
+
// HTTP 400 (only if in MCP error)
|
|
47552
|
+
/unauthorized/i,
|
|
47553
|
+
// HTTP 401 (only if in MCP error)
|
|
47554
|
+
/forbidden/i,
|
|
47555
|
+
// HTTP 403 (only if in MCP error)
|
|
47556
|
+
/no valid session/i,
|
|
47557
|
+
// Session errors (only if in MCP error)
|
|
47558
|
+
/session.*expired/i,
|
|
47559
|
+
// Session expiration (only if in MCP error)
|
|
47560
|
+
/internal server error/i,
|
|
47561
|
+
// HTTP 500 (only if in MCP error)
|
|
47562
|
+
/HTTP [45]\d\d/i
|
|
47563
|
+
// HTTP status codes (only if in MCP error)
|
|
47564
|
+
];
|
|
47565
|
+
return contextualPatterns.some((pattern2) => pattern2.test(text));
|
|
47566
|
+
}
|
|
47567
|
+
return false;
|
|
47568
|
+
}
|
|
47569
|
+
/**
|
|
47570
|
+
* Check if caught exception indicates connection/server failure
|
|
47571
|
+
* CRITICAL: Only match transport/infrastructure errors, NOT tool business logic
|
|
47572
|
+
*/
|
|
47573
|
+
isConnectionErrorFromException(error) {
|
|
47574
|
+
if (error instanceof Error) {
|
|
47575
|
+
const message = error.message.toLowerCase();
|
|
47576
|
+
const unambiguousPatterns = [
|
|
47577
|
+
/MCP error -32001/i,
|
|
47578
|
+
// MCP transport errors
|
|
47579
|
+
/MCP error -32603/i,
|
|
47580
|
+
// MCP internal error
|
|
47581
|
+
/MCP error -32000/i,
|
|
47582
|
+
// MCP server error
|
|
47583
|
+
/MCP error -32700/i,
|
|
47584
|
+
// MCP parse error
|
|
47585
|
+
/socket hang up/i,
|
|
47586
|
+
// Network socket errors
|
|
47587
|
+
/ECONNREFUSED/i,
|
|
47588
|
+
// Connection refused
|
|
47589
|
+
/ETIMEDOUT/i,
|
|
47590
|
+
// Network timeout
|
|
47591
|
+
/network error/i,
|
|
47592
|
+
// Generic network errors
|
|
47593
|
+
/ERR_CONNECTION/i,
|
|
47594
|
+
// Connection errors
|
|
47595
|
+
/fetch failed/i,
|
|
47596
|
+
// HTTP fetch failures
|
|
47597
|
+
/connection reset/i,
|
|
47598
|
+
// Connection reset
|
|
47599
|
+
/error POSTing to endpoint/i,
|
|
47600
|
+
// Transport layer POST errors
|
|
47601
|
+
/error GETting/i,
|
|
47602
|
+
// Transport layer GET errors
|
|
47603
|
+
/service unavailable/i,
|
|
47604
|
+
// HTTP 503 (server down)
|
|
47605
|
+
/gateway timeout/i,
|
|
47606
|
+
// HTTP 504 (gateway timeout)
|
|
47607
|
+
/unknown tool:/i,
|
|
47608
|
+
// Tool name not in current server's tool list (stale tool list)
|
|
47609
|
+
/tool.*not found/i,
|
|
47610
|
+
// Alternative phrasing for missing tool
|
|
47611
|
+
/tool.*does not exist/i,
|
|
47612
|
+
// Alternative phrasing for missing tool
|
|
47613
|
+
/no such tool/i
|
|
47614
|
+
// Alternative phrasing for missing tool
|
|
47615
|
+
];
|
|
47616
|
+
if (unambiguousPatterns.some((pattern2) => pattern2.test(message))) {
|
|
47617
|
+
return true;
|
|
47618
|
+
}
|
|
47619
|
+
const mcpPrefix = /^mcp error -\d+:/i.test(message);
|
|
47620
|
+
if (mcpPrefix) {
|
|
47621
|
+
const contextualPatterns = [
|
|
47622
|
+
/bad request/i,
|
|
47623
|
+
/unauthorized/i,
|
|
47624
|
+
/forbidden/i,
|
|
47625
|
+
/no valid session/i,
|
|
47626
|
+
/session.*expired/i,
|
|
47627
|
+
/internal server error/i,
|
|
47628
|
+
/HTTP [45]\d\d/i
|
|
47629
|
+
];
|
|
47630
|
+
return contextualPatterns.some((pattern2) => pattern2.test(message));
|
|
47631
|
+
}
|
|
47632
|
+
}
|
|
47633
|
+
return false;
|
|
47634
|
+
}
|
|
47635
|
+
/**
|
|
47636
|
+
* Classify error type for reporting
|
|
47637
|
+
*/
|
|
47638
|
+
classifyError(response) {
|
|
47639
|
+
const text = this.extractResponseContent(response).toLowerCase();
|
|
47640
|
+
if (/socket|ECONNREFUSED|ETIMEDOUT|network|fetch failed|connection reset/i.test(
|
|
47641
|
+
text
|
|
47642
|
+
)) {
|
|
47643
|
+
return "connection";
|
|
47644
|
+
}
|
|
47645
|
+
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(
|
|
47646
|
+
text
|
|
47647
|
+
)) {
|
|
47648
|
+
return "server";
|
|
47649
|
+
}
|
|
47650
|
+
if (/-32001/i.test(text)) {
|
|
47651
|
+
return "protocol";
|
|
47652
|
+
}
|
|
47653
|
+
return "protocol";
|
|
47654
|
+
}
|
|
47655
|
+
/**
|
|
47656
|
+
* Classify error type from caught exception
|
|
47657
|
+
*/
|
|
47658
|
+
classifyErrorFromException(error) {
|
|
47659
|
+
if (error instanceof Error) {
|
|
47660
|
+
const message = error.message.toLowerCase();
|
|
47661
|
+
if (/socket|ECONNREFUSED|ETIMEDOUT|network|fetch failed|connection reset/i.test(
|
|
47662
|
+
message
|
|
47663
|
+
)) {
|
|
47664
|
+
return "connection";
|
|
47665
|
+
}
|
|
47666
|
+
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(
|
|
47667
|
+
message
|
|
47668
|
+
)) {
|
|
47669
|
+
return "server";
|
|
47670
|
+
}
|
|
47671
|
+
if (/-32001/i.test(message)) {
|
|
47672
|
+
return "protocol";
|
|
47673
|
+
}
|
|
47674
|
+
}
|
|
47675
|
+
return "protocol";
|
|
47305
47676
|
}
|
|
47306
47677
|
/**
|
|
47307
47678
|
* Analyze response with evidence-based detection
|
|
@@ -47310,27 +47681,6 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47310
47681
|
analyzeResponse(response, payload, tool) {
|
|
47311
47682
|
const responseText = this.extractResponseContent(response).toLowerCase();
|
|
47312
47683
|
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
47684
|
const errorInfo = this.extractErrorInfo(response);
|
|
47335
47685
|
if (this.isMCPValidationError(errorInfo, responseText)) {
|
|
47336
47686
|
const errorCode = errorInfo.code ? ` (${errorInfo.code})` : "";
|
|
@@ -47363,20 +47713,24 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47363
47713
|
evidence: "Read-only info tool returned user/workspace data (intended data exposure, not vulnerability)"
|
|
47364
47714
|
};
|
|
47365
47715
|
}
|
|
47366
|
-
if (
|
|
47716
|
+
if (classification.categories.includes(ToolCategory.SAFE_STORAGE)) {
|
|
47367
47717
|
return {
|
|
47368
47718
|
isVulnerable: false,
|
|
47369
|
-
evidence: "
|
|
47719
|
+
evidence: "Safe storage control tool (validated safe implementation, control group for testing)"
|
|
47370
47720
|
};
|
|
47371
47721
|
}
|
|
47372
|
-
|
|
47373
|
-
|
|
47374
|
-
|
|
47375
|
-
|
|
47376
|
-
|
|
47377
|
-
|
|
47378
|
-
|
|
47379
|
-
|
|
47722
|
+
const isJustReflection = this.isReflectionResponse(responseText);
|
|
47723
|
+
if (isJustReflection) {
|
|
47724
|
+
return {
|
|
47725
|
+
isVulnerable: false,
|
|
47726
|
+
evidence: "Tool safely reflected input without execution"
|
|
47727
|
+
};
|
|
47728
|
+
}
|
|
47729
|
+
if (this.isValidationRejection(response)) {
|
|
47730
|
+
return {
|
|
47731
|
+
isVulnerable: false,
|
|
47732
|
+
evidence: "Tool correctly rejected invalid input with validation error"
|
|
47733
|
+
};
|
|
47380
47734
|
}
|
|
47381
47735
|
if (payload.evidence && payload.evidence.test(responseText)) {
|
|
47382
47736
|
const evidenceMatchesInput = payload.evidence.test(payloadText);
|
|
@@ -47402,6 +47756,54 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47402
47756
|
}
|
|
47403
47757
|
return this.analyzeInjectionResponse(response, payload.payload);
|
|
47404
47758
|
}
|
|
47759
|
+
/**
|
|
47760
|
+
* Check if tool explicitly rejected input with validation error (SAFE)
|
|
47761
|
+
* Returns true if tool properly rejected invalid input (SECURE)
|
|
47762
|
+
*
|
|
47763
|
+
* Distinguishes between:
|
|
47764
|
+
* - SAFE: {"valid": false, "errors": ["Input too long"]} (rejection)
|
|
47765
|
+
* - VULNERABLE: {"result": "deleted 5 rows"} (execution)
|
|
47766
|
+
*/
|
|
47767
|
+
isValidationRejection(response) {
|
|
47768
|
+
const responseText = this.extractResponseContent(response);
|
|
47769
|
+
try {
|
|
47770
|
+
const parsed = JSON.parse(responseText);
|
|
47771
|
+
if (parsed.valid === false || parsed.error === true || parsed.error === "true" || // Handle string boolean
|
|
47772
|
+
parsed.error && parsed.error !== false || // Any truthy error value
|
|
47773
|
+
parsed.status === "rejected" || parsed.status === "invalid" || parsed.status === "failed") {
|
|
47774
|
+
return true;
|
|
47775
|
+
}
|
|
47776
|
+
if (parsed.errors && Array.isArray(parsed.errors) && parsed.errors.length > 0) {
|
|
47777
|
+
return true;
|
|
47778
|
+
}
|
|
47779
|
+
if (parsed.error && typeof parsed.error === "string") {
|
|
47780
|
+
return true;
|
|
47781
|
+
}
|
|
47782
|
+
if (typeof parsed.result === "string") {
|
|
47783
|
+
const resultRejectionPatterns = [
|
|
47784
|
+
/validation (failed|error)/i,
|
|
47785
|
+
/rejected/i,
|
|
47786
|
+
/not.*approved/i,
|
|
47787
|
+
/not.*in.*list/i,
|
|
47788
|
+
/invalid.*input/i,
|
|
47789
|
+
/error:.*invalid/i
|
|
47790
|
+
];
|
|
47791
|
+
if (resultRejectionPatterns.some((p2) => p2.test(parsed.result))) {
|
|
47792
|
+
return true;
|
|
47793
|
+
}
|
|
47794
|
+
}
|
|
47795
|
+
} catch {
|
|
47796
|
+
}
|
|
47797
|
+
const rejectionPatterns = [
|
|
47798
|
+
/validation failed/i,
|
|
47799
|
+
/rejected/i,
|
|
47800
|
+
/not.*approved/i,
|
|
47801
|
+
/not.*in.*list/i,
|
|
47802
|
+
/invalid.*input/i,
|
|
47803
|
+
/error:.*invalid/i
|
|
47804
|
+
];
|
|
47805
|
+
return rejectionPatterns.some((pattern2) => pattern2.test(responseText));
|
|
47806
|
+
}
|
|
47405
47807
|
/**
|
|
47406
47808
|
* Check if response is an MCP validation error (safe rejection)
|
|
47407
47809
|
* Returns true if tool rejected input before processing (SECURE)
|
|
@@ -47535,7 +47937,8 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47535
47937
|
/**
|
|
47536
47938
|
* Determine security status based on confidence levels
|
|
47537
47939
|
*/
|
|
47538
|
-
determineSecurityStatus(tests, vulnerabilityCount, testCount) {
|
|
47940
|
+
determineSecurityStatus(tests, vulnerabilityCount, testCount, connectionErrorCount = 0) {
|
|
47941
|
+
if (connectionErrorCount > 0) return "FAIL";
|
|
47539
47942
|
if (testCount === 0) return "NEED_MORE_INFO";
|
|
47540
47943
|
if (vulnerabilityCount === 0) return "PASS";
|
|
47541
47944
|
const hasHighConfidence = tests.some(
|
|
@@ -47547,30 +47950,38 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47547
47950
|
/**
|
|
47548
47951
|
* Generate security explanation
|
|
47549
47952
|
*/
|
|
47550
|
-
generateSecurityExplanation(
|
|
47953
|
+
generateSecurityExplanation(validTests, connectionErrors, vulnerabilities, riskLevel) {
|
|
47551
47954
|
const vulnCount = vulnerabilities.length;
|
|
47552
|
-
const testCount =
|
|
47553
|
-
|
|
47955
|
+
const testCount = validTests.length;
|
|
47956
|
+
const errorCount = connectionErrors.length;
|
|
47957
|
+
let explanation = "";
|
|
47958
|
+
if (errorCount > 0) {
|
|
47959
|
+
explanation += `⚠️ ${errorCount} test${errorCount !== 1 ? "s" : ""} failed due to connection/server errors. `;
|
|
47960
|
+
}
|
|
47961
|
+
if (testCount === 0 && errorCount > 0) {
|
|
47962
|
+
return explanation + `No valid tests completed. Check server connectivity and retry assessment.`;
|
|
47963
|
+
}
|
|
47964
|
+
if (testCount === 0 && errorCount === 0) {
|
|
47554
47965
|
return `No tools selected for security testing. Select tools to run security assessments.`;
|
|
47555
47966
|
}
|
|
47556
47967
|
if (vulnCount === 0) {
|
|
47557
|
-
return `Tested ${testCount} security patterns across selected tools. No vulnerabilities detected. All tools properly handle malicious inputs.`;
|
|
47968
|
+
return explanation + `Tested ${testCount} security patterns across selected tools. No vulnerabilities detected. All tools properly handle malicious inputs.`;
|
|
47558
47969
|
}
|
|
47559
|
-
const highConfidenceCount =
|
|
47970
|
+
const highConfidenceCount = validTests.filter(
|
|
47560
47971
|
(t) => t.vulnerable && (!t.confidence || t.confidence === "high")
|
|
47561
47972
|
).length;
|
|
47562
|
-
const mediumConfidenceCount =
|
|
47973
|
+
const mediumConfidenceCount = validTests.filter(
|
|
47563
47974
|
(t) => t.vulnerable && t.confidence === "medium"
|
|
47564
47975
|
).length;
|
|
47565
|
-
const lowConfidenceCount =
|
|
47976
|
+
const lowConfidenceCount = validTests.filter(
|
|
47566
47977
|
(t) => t.vulnerable && t.confidence === "low"
|
|
47567
47978
|
).length;
|
|
47568
47979
|
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.`;
|
|
47980
|
+
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
47981
|
} 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.`;
|
|
47982
|
+
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
47983
|
} 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.`;
|
|
47984
|
+
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
47985
|
}
|
|
47575
47986
|
}
|
|
47576
47987
|
/**
|
|
@@ -47665,10 +48076,31 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47665
48076
|
/**
|
|
47666
48077
|
* Check if response is just reflection (safe)
|
|
47667
48078
|
* Expanded to catch more reflection patterns including echo, repeat, display
|
|
47668
|
-
* IMPROVED: Bidirectional patterns
|
|
48079
|
+
* IMPROVED: Bidirectional patterns, safety indicators, and two-layer defense
|
|
48080
|
+
*
|
|
48081
|
+
* CRITICAL: This check distinguishes between:
|
|
48082
|
+
* - SAFE: Tool stores/echoes malicious input as data (reflection)
|
|
48083
|
+
* - VULNERABLE: Tool executes malicious input and returns results (execution)
|
|
48084
|
+
*
|
|
48085
|
+
* Two-layer defense:
|
|
48086
|
+
* Layer 1: Match reflection/status patterns
|
|
48087
|
+
* Layer 2: Verify NO execution evidence (defense-in-depth)
|
|
47669
48088
|
*/
|
|
47670
48089
|
isReflectionResponse(responseText) {
|
|
48090
|
+
console.log("[DIAG] isReflectionResponse called");
|
|
48091
|
+
console.log("[DIAG] Response preview:", responseText.substring(0, 200));
|
|
48092
|
+
const statusPatterns = [
|
|
48093
|
+
// "Action executed successfully: <anything>" (generic status message)
|
|
48094
|
+
/action\s+executed\s+successfully:/i,
|
|
48095
|
+
/command\s+executed\s+successfully:/i,
|
|
48096
|
+
// "Action executed successfully" (generic status - in JSON or standalone)
|
|
48097
|
+
/"result":\s*"action\s+executed\s+successfully"/i,
|
|
48098
|
+
/result.*action\s+executed\s+successfully/i,
|
|
48099
|
+
/successfully\s+(executed|completed|processed):/i,
|
|
48100
|
+
/successfully\s+(executed|completed|processed)"/i
|
|
48101
|
+
];
|
|
47671
48102
|
const reflectionPatterns = [
|
|
48103
|
+
...statusPatterns,
|
|
47672
48104
|
// Original patterns (keep all existing)
|
|
47673
48105
|
/stored.*query/i,
|
|
47674
48106
|
/saved.*input/i,
|
|
@@ -47685,16 +48117,34 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47685
48117
|
/safely.*as.*data/i,
|
|
47686
48118
|
// NEW: Bidirectional patterns (catch "Query stored" and "stored query")
|
|
47687
48119
|
/query.*stored/i,
|
|
48120
|
+
/stored.*query/i,
|
|
48121
|
+
// Bidirectional: "Stored query"
|
|
47688
48122
|
/input.*saved/i,
|
|
47689
48123
|
/parameter.*received/i,
|
|
47690
48124
|
/command.*stored/i,
|
|
48125
|
+
/stored.*command/i,
|
|
48126
|
+
// Bidirectional: "Stored command"
|
|
47691
48127
|
/data.*stored/i,
|
|
48128
|
+
/stored.*data/i,
|
|
48129
|
+
// Bidirectional: "Stored data"
|
|
47692
48130
|
/action.*stored/i,
|
|
48131
|
+
/stored.*action/i,
|
|
48132
|
+
// Bidirectional: "Stored action"
|
|
47693
48133
|
/text.*stored/i,
|
|
48134
|
+
/stored.*text/i,
|
|
48135
|
+
// Bidirectional: "Stored text"
|
|
47694
48136
|
/setting.*stored/i,
|
|
48137
|
+
/stored.*setting/i,
|
|
48138
|
+
// Bidirectional: "Stored setting"
|
|
47695
48139
|
/instruction.*stored/i,
|
|
48140
|
+
/stored.*instruction/i,
|
|
48141
|
+
// Bidirectional: "Stored instruction"
|
|
47696
48142
|
/url.*stored/i,
|
|
48143
|
+
/stored.*url/i,
|
|
48144
|
+
// Bidirectional: "Stored URL"
|
|
47697
48145
|
/package.*stored/i,
|
|
48146
|
+
/stored.*package/i,
|
|
48147
|
+
// Bidirectional: "Stored package"
|
|
47698
48148
|
// NEW: Safety indicators (common in hardened implementations)
|
|
47699
48149
|
/stored.*safely/i,
|
|
47700
48150
|
/safely.*stored/i,
|
|
@@ -47708,12 +48158,160 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47708
48158
|
/stored.*successfully/i,
|
|
47709
48159
|
/saved.*to/i,
|
|
47710
48160
|
/recorded\s+in/i,
|
|
47711
|
-
/added\s+to/i
|
|
48161
|
+
/added\s+to/i,
|
|
48162
|
+
// NEW: Storage/logging confirmations (high confidence)
|
|
48163
|
+
/logged successfully:/i,
|
|
48164
|
+
/queued for processing:/i,
|
|
48165
|
+
/saved (for|successfully)/i,
|
|
48166
|
+
/stored for (admin review|configuration|processing)/i,
|
|
48167
|
+
// NEW: Processing confirmations (high confidence)
|
|
48168
|
+
/processed successfully/i,
|
|
48169
|
+
/validated successfully/i,
|
|
48170
|
+
/parsed successfully/i,
|
|
48171
|
+
/(validation|processing) (passed|completed)/i,
|
|
48172
|
+
// NEW: Error messages with input reflection (common safe pattern)
|
|
48173
|
+
/error:.*not (found|in approved list|recognized)/i,
|
|
48174
|
+
/error getting info for ['"].*['"]/i,
|
|
48175
|
+
/invalid .* format.*stored as text/i,
|
|
48176
|
+
/error:.*too (long|short|large)/i
|
|
47712
48177
|
];
|
|
47713
|
-
|
|
48178
|
+
const matchedPatterns = [];
|
|
48179
|
+
const hasReflection = reflectionPatterns.some((pattern2) => {
|
|
48180
|
+
const matches = pattern2.test(responseText);
|
|
48181
|
+
if (matches) {
|
|
48182
|
+
matchedPatterns.push(pattern2.source.substring(0, 50));
|
|
48183
|
+
}
|
|
48184
|
+
return matches;
|
|
48185
|
+
});
|
|
48186
|
+
console.log("[DIAG] Has reflection:", hasReflection);
|
|
48187
|
+
if (matchedPatterns.length > 0) {
|
|
48188
|
+
console.log(
|
|
48189
|
+
"[DIAG] Matched reflection patterns:",
|
|
48190
|
+
matchedPatterns.join(", ")
|
|
48191
|
+
);
|
|
48192
|
+
}
|
|
48193
|
+
if (hasReflection) {
|
|
48194
|
+
try {
|
|
48195
|
+
const parsed = JSON.parse(responseText);
|
|
48196
|
+
const resultText = String(parsed.result || "");
|
|
48197
|
+
const outputFields = [
|
|
48198
|
+
parsed.stdout,
|
|
48199
|
+
parsed.stderr,
|
|
48200
|
+
parsed.output,
|
|
48201
|
+
parsed.contents,
|
|
48202
|
+
parsed.execution_log,
|
|
48203
|
+
parsed.command_output
|
|
48204
|
+
].filter(Boolean).join(" ");
|
|
48205
|
+
const resultIsStatusOnly = statusPatterns.some(
|
|
48206
|
+
(pattern2) => pattern2.test(resultText)
|
|
48207
|
+
);
|
|
48208
|
+
const hasExecutionInOutput = resultIsStatusOnly ? this.detectExecutionArtifacts(outputFields) : this.detectExecutionArtifacts(resultText) || this.detectExecutionArtifacts(outputFields);
|
|
48209
|
+
console.log(
|
|
48210
|
+
"[DIAG] JSON mode - checking execution in result/output fields only"
|
|
48211
|
+
);
|
|
48212
|
+
console.log("[DIAG] Has execution in output:", hasExecutionInOutput);
|
|
48213
|
+
if (hasExecutionInOutput) {
|
|
48214
|
+
console.log(
|
|
48215
|
+
"[DIAG] RESULT: Reflection + Execution in output = VULNERABLE (false)"
|
|
48216
|
+
);
|
|
48217
|
+
return false;
|
|
48218
|
+
}
|
|
48219
|
+
console.log("[DIAG] RESULT: Reflection + clean output = SAFE (true)");
|
|
48220
|
+
return true;
|
|
48221
|
+
} catch {
|
|
48222
|
+
const hasExecution = this.detectExecutionArtifacts(responseText);
|
|
48223
|
+
console.log(
|
|
48224
|
+
"[DIAG] Text mode - Has execution artifacts:",
|
|
48225
|
+
hasExecution
|
|
48226
|
+
);
|
|
48227
|
+
if (hasExecution) {
|
|
48228
|
+
console.log(
|
|
48229
|
+
"[DIAG] RESULT: Reflection + Execution = VULNERABLE (false)"
|
|
48230
|
+
);
|
|
48231
|
+
return false;
|
|
48232
|
+
}
|
|
48233
|
+
console.log("[DIAG] RESULT: Reflection only = SAFE (true)");
|
|
48234
|
+
return true;
|
|
48235
|
+
}
|
|
48236
|
+
}
|
|
48237
|
+
try {
|
|
48238
|
+
const parsed = JSON.parse(responseText);
|
|
48239
|
+
if (parsed.action === "test" || parsed.action === "placeholder") {
|
|
48240
|
+
const resultText = String(parsed.result || "");
|
|
48241
|
+
if (!this.detectExecutionArtifacts(resultText)) {
|
|
48242
|
+
return true;
|
|
48243
|
+
}
|
|
48244
|
+
}
|
|
48245
|
+
if (parsed.status && /(completed|success|ok|done)/.test(parsed.status)) {
|
|
48246
|
+
if (!this.detectExecutionArtifacts(responseText)) {
|
|
48247
|
+
return true;
|
|
48248
|
+
}
|
|
48249
|
+
}
|
|
48250
|
+
} catch {
|
|
48251
|
+
}
|
|
48252
|
+
return false;
|
|
48253
|
+
}
|
|
48254
|
+
/**
|
|
48255
|
+
* Detect execution artifacts in response
|
|
48256
|
+
* Returns true if response contains evidence of actual code execution
|
|
48257
|
+
*
|
|
48258
|
+
* HIGH confidence: System files, commands, directory listings
|
|
48259
|
+
* MEDIUM confidence: Contextual patterns (root alone, paths)
|
|
48260
|
+
*/
|
|
48261
|
+
detectExecutionArtifacts(responseText) {
|
|
48262
|
+
console.log("[DIAG] detectExecutionArtifacts called");
|
|
48263
|
+
const executionIndicators = [
|
|
48264
|
+
// HIGH CONFIDENCE - System files (requires format)
|
|
48265
|
+
/[a-z]+:x:\d+:\d+:/i,
|
|
48266
|
+
// passwd: "root:x:0:0:"
|
|
48267
|
+
/uid=\d+\([^)]+\)\s+gid=\d+/i,
|
|
48268
|
+
// id: "uid=0(root) gid=0(root)"
|
|
48269
|
+
// HIGH CONFIDENCE - Directory listings (full format)
|
|
48270
|
+
/[d-][rwx-]{9}\s+\d+\s+[a-z]+/i,
|
|
48271
|
+
// ls -la: "drwxr-xr-x 2 root"
|
|
48272
|
+
/total\s+\d+\s*$/m,
|
|
48273
|
+
// ls total line
|
|
48274
|
+
// HIGH CONFIDENCE - Command execution fields
|
|
48275
|
+
/command_executed:\s*[^"\s]/i,
|
|
48276
|
+
// "command_executed: whoami"
|
|
48277
|
+
/stdout:\s*["']?[^"'\s]/i,
|
|
48278
|
+
// "stdout: root"
|
|
48279
|
+
/(execution|output)_log:/i,
|
|
48280
|
+
// Log fields
|
|
48281
|
+
// HIGH CONFIDENCE - Shell paths
|
|
48282
|
+
/\/bin\/(bash|sh|zsh|dash)/i,
|
|
48283
|
+
// Shell executables
|
|
48284
|
+
// MEDIUM CONFIDENCE - System identity (contextual only)
|
|
48285
|
+
/\b(root|administrator)\s*$/im,
|
|
48286
|
+
// "root" alone on line (whoami)
|
|
48287
|
+
/\/root\//i,
|
|
48288
|
+
// Path: "/root/"
|
|
48289
|
+
/\/etc\/passwd/i,
|
|
48290
|
+
// Sensitive file
|
|
48291
|
+
// MEDIUM CONFIDENCE - Process info
|
|
48292
|
+
/PID:\s*\d{3,}/i
|
|
48293
|
+
// Process ID
|
|
48294
|
+
];
|
|
48295
|
+
const matchedExecutionPatterns = [];
|
|
48296
|
+
const found = executionIndicators.some((pattern2) => {
|
|
48297
|
+
const matches = pattern2.test(responseText);
|
|
48298
|
+
if (matches) {
|
|
48299
|
+
matchedExecutionPatterns.push(pattern2.source.substring(0, 50));
|
|
48300
|
+
}
|
|
48301
|
+
return matches;
|
|
48302
|
+
});
|
|
48303
|
+
if (matchedExecutionPatterns.length > 0) {
|
|
48304
|
+
console.log(
|
|
48305
|
+
"[DIAG] Matched execution patterns:",
|
|
48306
|
+
matchedExecutionPatterns.join(", ")
|
|
48307
|
+
);
|
|
48308
|
+
}
|
|
48309
|
+
console.log("[DIAG] Execution artifacts found:", found);
|
|
48310
|
+
return found;
|
|
47714
48311
|
}
|
|
47715
48312
|
/**
|
|
47716
48313
|
* Analyze injection response (existing logic)
|
|
48314
|
+
* Note: payload parameter unused after refactoring to two-layer defense
|
|
47717
48315
|
*/
|
|
47718
48316
|
analyzeInjectionResponse(response, _payload) {
|
|
47719
48317
|
const responseText = this.extractResponseContent(response);
|
|
@@ -47759,11 +48357,31 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47759
48357
|
return {};
|
|
47760
48358
|
}
|
|
47761
48359
|
const params = {};
|
|
48360
|
+
const targetParamTypes = payload.parameterTypes || [];
|
|
48361
|
+
let payloadInjected = false;
|
|
48362
|
+
if (targetParamTypes.length > 0) {
|
|
48363
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
48364
|
+
const propSchema = prop;
|
|
48365
|
+
const paramNameLower = key.toLowerCase();
|
|
48366
|
+
if (propSchema.type === "string" && targetParamTypes.some((type2) => paramNameLower.includes(type2))) {
|
|
48367
|
+
params[key] = payload.payload;
|
|
48368
|
+
payloadInjected = true;
|
|
48369
|
+
break;
|
|
48370
|
+
}
|
|
48371
|
+
}
|
|
48372
|
+
} else {
|
|
48373
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
48374
|
+
const propSchema = prop;
|
|
48375
|
+
if (propSchema.type === "string" && !payloadInjected) {
|
|
48376
|
+
params[key] = payload.payload;
|
|
48377
|
+
payloadInjected = true;
|
|
48378
|
+
break;
|
|
48379
|
+
}
|
|
48380
|
+
}
|
|
48381
|
+
}
|
|
47762
48382
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
47763
48383
|
const propSchema = prop;
|
|
47764
|
-
if (
|
|
47765
|
-
params[key] = payload;
|
|
47766
|
-
} else if ((_b = schema.required) == null ? void 0 : _b.includes(key)) {
|
|
48384
|
+
if (((_b = schema.required) == null ? void 0 : _b.includes(key)) && !(key in params)) {
|
|
47767
48385
|
if (propSchema.type === "string") {
|
|
47768
48386
|
params[key] = "test";
|
|
47769
48387
|
} else if (propSchema.type === "number") {
|
|
@@ -47802,25 +48420,6 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47802
48420
|
];
|
|
47803
48421
|
return executionTests.includes(attackName);
|
|
47804
48422
|
}
|
|
47805
|
-
/**
|
|
47806
|
-
* Check if response is from an API wrapper tool
|
|
47807
|
-
* API wrappers return external content as data, not execute it
|
|
47808
|
-
*/
|
|
47809
|
-
isApiWrapperResponse(responseText) {
|
|
47810
|
-
const apiWrapperPatterns = [
|
|
47811
|
-
/successfully\s+(scraped|fetched|crawled)/i,
|
|
47812
|
-
/content\s+from\s+http/i,
|
|
47813
|
-
/api\s+(request|response|call)\s+(completed|successful)/i,
|
|
47814
|
-
/retrieved\s+\d+\s+(results|pages|urls)/i,
|
|
47815
|
-
/markdown.*screenshot.*links/i,
|
|
47816
|
-
// Firecrawl format indicators
|
|
47817
|
-
/scraping\s+(complete|finished|done)/i,
|
|
47818
|
-
/\bfirecrawl\b/i,
|
|
47819
|
-
/crawl.*job/i,
|
|
47820
|
-
/extraction.*complete/i
|
|
47821
|
-
];
|
|
47822
|
-
return apiWrapperPatterns.some((pattern2) => pattern2.test(responseText));
|
|
47823
|
-
}
|
|
47824
48423
|
/**
|
|
47825
48424
|
* Check if response is returning search results
|
|
47826
48425
|
* Search tools return query results as data, not execute them
|
|
@@ -49744,6 +50343,7 @@ function ToolSelector({
|
|
|
49744
50343
|
const AssessmentTab = ({
|
|
49745
50344
|
tools,
|
|
49746
50345
|
isLoadingTools = false,
|
|
50346
|
+
listTools,
|
|
49747
50347
|
callTool,
|
|
49748
50348
|
serverName = "MCP Server"
|
|
49749
50349
|
}) => {
|
|
@@ -49761,6 +50361,7 @@ const AssessmentTab = ({
|
|
|
49761
50361
|
const [showJson, setShowJson] = reactExports.useState(false);
|
|
49762
50362
|
const [collapsedTools, setCollapsedTools] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
49763
50363
|
const [allToolsCollapsed, setAllToolsCollapsed] = reactExports.useState(false);
|
|
50364
|
+
const [showOnlyErrors, setShowOnlyErrors] = reactExports.useState(false);
|
|
49764
50365
|
const [expandedToolDescriptions, setExpandedToolDescriptions] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
49765
50366
|
const [categoryFilter, setCategoryFilter] = reactExports.useState({
|
|
49766
50367
|
functionality: true,
|
|
@@ -49780,11 +50381,28 @@ const AssessmentTab = ({
|
|
|
49780
50381
|
[config]
|
|
49781
50382
|
);
|
|
49782
50383
|
reactExports.useEffect(() => {
|
|
49783
|
-
if (tools.length
|
|
50384
|
+
if (tools.length === 0) {
|
|
50385
|
+
return;
|
|
50386
|
+
}
|
|
50387
|
+
const currentToolNames = tools.map((t) => t.name);
|
|
50388
|
+
if (!config.selectedToolsForTesting) {
|
|
49784
50389
|
setConfig({
|
|
49785
50390
|
...config,
|
|
49786
|
-
selectedToolsForTesting:
|
|
50391
|
+
selectedToolsForTesting: currentToolNames
|
|
49787
50392
|
});
|
|
50393
|
+
} else {
|
|
50394
|
+
const existingSelections = config.selectedToolsForTesting.filter(
|
|
50395
|
+
(name2) => currentToolNames.includes(name2)
|
|
50396
|
+
);
|
|
50397
|
+
const newTools = currentToolNames.filter(
|
|
50398
|
+
(name2) => !config.selectedToolsForTesting.includes(name2)
|
|
50399
|
+
);
|
|
50400
|
+
if (newTools.length > 0 || existingSelections.length !== config.selectedToolsForTesting.length) {
|
|
50401
|
+
setConfig({
|
|
50402
|
+
...config,
|
|
50403
|
+
selectedToolsForTesting: [...existingSelections, ...newTools]
|
|
50404
|
+
});
|
|
50405
|
+
}
|
|
49788
50406
|
}
|
|
49789
50407
|
}, [tools, config, setConfig]);
|
|
49790
50408
|
const calculateFilteredOverallStatus = reactExports.useCallback(
|
|
@@ -49955,7 +50573,23 @@ const AssessmentTab = ({
|
|
|
49955
50573
|
)
|
|
49956
50574
|
] }),
|
|
49957
50575
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
49958
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
50576
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
50577
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Label$1, { htmlFor: "tool-selector", children: "Select tools for testing:" }),
|
|
50578
|
+
listTools && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50579
|
+
Button,
|
|
50580
|
+
{
|
|
50581
|
+
variant: "ghost",
|
|
50582
|
+
size: "sm",
|
|
50583
|
+
onClick: () => listTools(),
|
|
50584
|
+
disabled: isLoadingTools || isRunning,
|
|
50585
|
+
className: "h-7 px-2",
|
|
50586
|
+
children: [
|
|
50587
|
+
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" }),
|
|
50588
|
+
"Refresh"
|
|
50589
|
+
]
|
|
50590
|
+
}
|
|
50591
|
+
)
|
|
50592
|
+
] }),
|
|
49959
50593
|
isLoadingTools ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
49960
50594
|
/* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "w-4 h-4 animate-spin" }),
|
|
49961
50595
|
"Loading tools..."
|
|
@@ -50016,14 +50650,14 @@ const AssessmentTab = ({
|
|
|
50016
50650
|
),
|
|
50017
50651
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label$1, { htmlFor: "domain-testing", className: "cursor-pointer", children: [
|
|
50018
50652
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: "Enable Advanced Security Testing" }),
|
|
50019
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2 text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-0.5 rounded", children: "
|
|
50653
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2 text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-0.5 rounded", children: "13 Patterns" })
|
|
50020
50654
|
] })
|
|
50021
50655
|
] }),
|
|
50022
50656
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground ml-6", children: [
|
|
50023
50657
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Basic:" }),
|
|
50024
|
-
"
|
|
50658
|
+
" 4 critical injection patterns (~20 checks). ",
|
|
50025
50659
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Advanced:" }),
|
|
50026
|
-
" All
|
|
50660
|
+
" All 13 patterns (~37 checks per tool). Tests for injection vulnerabilities (Command, Calculator, SQL, Path Traversal), input validation (Type Safety, Boundary, Required Fields), protocol compliance (MCP Error Format, Timeout Handling), and tool-specific vulnerabilities (Indirect Injection, Unicode Bypass, Nested Injection, Package Squatting)."
|
|
50027
50661
|
] })
|
|
50028
50662
|
] }),
|
|
50029
50663
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
@@ -50232,74 +50866,129 @@ const AssessmentTab = ({
|
|
|
50232
50866
|
children: [
|
|
50233
50867
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm mb-2", children: assessment.security.explanation }),
|
|
50234
50868
|
(() => {
|
|
50235
|
-
var _a2, _b2, _c;
|
|
50236
|
-
const
|
|
50869
|
+
var _a2, _b2, _c, _d;
|
|
50870
|
+
const connectionErrors = ((_a2 = assessment.security.promptInjectionTests) == null ? void 0 : _a2.filter(
|
|
50871
|
+
(t) => t.connectionError === true
|
|
50872
|
+
)) || [];
|
|
50873
|
+
const highConfidenceCount = ((_b2 = assessment.security.promptInjectionTests) == null ? void 0 : _b2.filter(
|
|
50237
50874
|
(t) => t.vulnerable && (!t.confidence || t.confidence === "high")
|
|
50238
50875
|
).length) || 0;
|
|
50239
|
-
const mediumConfidenceCount = ((
|
|
50876
|
+
const mediumConfidenceCount = ((_c = assessment.security.promptInjectionTests) == null ? void 0 : _c.filter(
|
|
50240
50877
|
(t) => t.vulnerable && t.confidence === "medium"
|
|
50241
50878
|
).length) || 0;
|
|
50242
|
-
const lowConfidenceCount = ((
|
|
50879
|
+
const lowConfidenceCount = ((_d = assessment.security.promptInjectionTests) == null ? void 0 : _d.filter(
|
|
50243
50880
|
(t) => t.vulnerable && t.confidence === "low"
|
|
50244
50881
|
).length) || 0;
|
|
50245
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50246
|
-
|
|
50247
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
50248
|
-
|
|
50249
|
-
|
|
50250
|
-
|
|
50251
|
-
|
|
50252
|
-
|
|
50253
|
-
|
|
50254
|
-
|
|
50255
|
-
|
|
50256
|
-
|
|
50257
|
-
|
|
50258
|
-
|
|
50259
|
-
|
|
50882
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
50883
|
+
connectionErrors.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-yellow-50 border-l-4 border-yellow-500 p-4 mb-4 rounded", children: [
|
|
50884
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
50885
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "w-5 h-5 text-yellow-600" }),
|
|
50886
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("h5", { className: "text-sm font-semibold text-yellow-900", children: [
|
|
50887
|
+
"Connection Errors (",
|
|
50888
|
+
connectionErrors.length,
|
|
50889
|
+
")"
|
|
50890
|
+
] })
|
|
50891
|
+
] }),
|
|
50892
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-yellow-800 mb-2", children: [
|
|
50893
|
+
connectionErrors.length,
|
|
50894
|
+
" test",
|
|
50895
|
+
connectionErrors.length !== 1 ? "s" : "",
|
|
50896
|
+
" could not complete due to server/network failures. These tests are excluded from vulnerability counts."
|
|
50897
|
+
] }),
|
|
50898
|
+
/* @__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(
|
|
50899
|
+
"div",
|
|
50900
|
+
{
|
|
50901
|
+
className: "flex items-start gap-2",
|
|
50902
|
+
children: [
|
|
50903
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-yellow-600", children: "•" }),
|
|
50904
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
50905
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: err.testName }),
|
|
50906
|
+
" on",
|
|
50907
|
+
" ",
|
|
50908
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "bg-yellow-100 px-1 rounded", children: err.toolName }),
|
|
50909
|
+
": ",
|
|
50910
|
+
err.errorType
|
|
50911
|
+
] })
|
|
50912
|
+
]
|
|
50913
|
+
},
|
|
50914
|
+
i
|
|
50915
|
+
)) }),
|
|
50916
|
+
/* @__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
50917
|
] }),
|
|
50261
|
-
|
|
50918
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm space-y-1", children: [
|
|
50919
|
+
highConfidenceCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-red-700", children: [
|
|
50920
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Confirmed Issues:" }),
|
|
50921
|
+
" ",
|
|
50922
|
+
highConfidenceCount
|
|
50923
|
+
] }),
|
|
50924
|
+
mediumConfidenceCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-amber-700", children: [
|
|
50925
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Need Review:" }),
|
|
50926
|
+
" ",
|
|
50927
|
+
mediumConfidenceCount
|
|
50928
|
+
] }),
|
|
50929
|
+
lowConfidenceCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-blue-700", children: [
|
|
50930
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Uncertain (Verification Needed):" }),
|
|
50931
|
+
" ",
|
|
50932
|
+
lowConfidenceCount
|
|
50933
|
+
] }),
|
|
50934
|
+
highConfidenceCount === 0 && mediumConfidenceCount === 0 && lowConfidenceCount === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-green-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "All tests passed" }) })
|
|
50935
|
+
] })
|
|
50262
50936
|
] });
|
|
50263
50937
|
})(),
|
|
50264
50938
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2", children: [
|
|
50265
50939
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
50266
50940
|
/* @__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
|
-
|
|
50941
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
50942
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50943
|
+
Button,
|
|
50944
|
+
{
|
|
50945
|
+
variant: showOnlyErrors ? "default" : "outline",
|
|
50946
|
+
size: "sm",
|
|
50947
|
+
className: "text-xs h-6 px-2",
|
|
50948
|
+
onClick: () => setShowOnlyErrors(!showOnlyErrors),
|
|
50949
|
+
children: [
|
|
50950
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { className: "h-3 w-3 mr-1" }),
|
|
50951
|
+
showOnlyErrors ? "Show All" : "Filter Errors"
|
|
50952
|
+
]
|
|
50953
|
+
}
|
|
50954
|
+
),
|
|
50955
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
50956
|
+
Button,
|
|
50957
|
+
{
|
|
50958
|
+
variant: "outline",
|
|
50959
|
+
size: "sm",
|
|
50960
|
+
className: "text-xs h-6 px-2",
|
|
50961
|
+
onClick: () => {
|
|
50962
|
+
const toolGroups = /* @__PURE__ */ new Map();
|
|
50963
|
+
assessment.security.promptInjectionTests.forEach(
|
|
50964
|
+
(testResult) => {
|
|
50965
|
+
const toolName = testResult.toolName || "Unknown Tool";
|
|
50966
|
+
if (!toolGroups.has(toolName)) {
|
|
50967
|
+
toolGroups.set(toolName, []);
|
|
50968
|
+
}
|
|
50280
50969
|
}
|
|
50281
|
-
}
|
|
50282
|
-
);
|
|
50283
|
-
if (allToolsCollapsed) {
|
|
50284
|
-
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
50285
|
-
setAllToolsCollapsed(false);
|
|
50286
|
-
} else {
|
|
50287
|
-
const allToolNames = Array.from(
|
|
50288
|
-
toolGroups.keys()
|
|
50289
50970
|
);
|
|
50290
|
-
|
|
50291
|
-
|
|
50292
|
-
|
|
50293
|
-
|
|
50294
|
-
|
|
50295
|
-
|
|
50296
|
-
|
|
50297
|
-
|
|
50298
|
-
|
|
50299
|
-
|
|
50300
|
-
|
|
50301
|
-
|
|
50302
|
-
|
|
50971
|
+
if (allToolsCollapsed) {
|
|
50972
|
+
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
50973
|
+
setAllToolsCollapsed(false);
|
|
50974
|
+
} else {
|
|
50975
|
+
const allToolNames = Array.from(
|
|
50976
|
+
toolGroups.keys()
|
|
50977
|
+
);
|
|
50978
|
+
setCollapsedTools(new Set(allToolNames));
|
|
50979
|
+
setAllToolsCollapsed(true);
|
|
50980
|
+
}
|
|
50981
|
+
},
|
|
50982
|
+
children: allToolsCollapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
50983
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-3 w-3 mr-1" }),
|
|
50984
|
+
"Expand All"
|
|
50985
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
50986
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-3 w-3 mr-1" }),
|
|
50987
|
+
"Collapse All"
|
|
50988
|
+
] })
|
|
50989
|
+
}
|
|
50990
|
+
)
|
|
50991
|
+
] })
|
|
50303
50992
|
] }),
|
|
50304
50993
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 space-y-1", children: (() => {
|
|
50305
50994
|
const toolGroups = /* @__PURE__ */ new Map();
|
|
@@ -50324,18 +51013,26 @@ const AssessmentTab = ({
|
|
|
50324
51013
|
newCollapsed.size === toolGroups.size
|
|
50325
51014
|
);
|
|
50326
51015
|
};
|
|
50327
|
-
|
|
50328
|
-
|
|
50329
|
-
|
|
50330
|
-
{
|
|
50331
|
-
|
|
50332
|
-
|
|
50333
|
-
|
|
50334
|
-
|
|
50335
|
-
|
|
50336
|
-
|
|
50337
|
-
|
|
50338
|
-
|
|
51016
|
+
let filteredGroups = Array.from(toolGroups.entries());
|
|
51017
|
+
if (showOnlyErrors) {
|
|
51018
|
+
filteredGroups = filteredGroups.filter(
|
|
51019
|
+
([, toolTests]) => {
|
|
51020
|
+
return toolTests.some(
|
|
51021
|
+
(test) => test.vulnerable === true
|
|
51022
|
+
);
|
|
51023
|
+
}
|
|
51024
|
+
);
|
|
51025
|
+
}
|
|
51026
|
+
return filteredGroups.map(([toolName, toolTests]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
51027
|
+
CollapsibleToolSection,
|
|
51028
|
+
{
|
|
51029
|
+
toolName,
|
|
51030
|
+
toolTests,
|
|
51031
|
+
isCollapsed: collapsedTools.has(toolName),
|
|
51032
|
+
onToggle: handleToggleTool
|
|
51033
|
+
},
|
|
51034
|
+
toolName
|
|
51035
|
+
));
|
|
50339
51036
|
})() }),
|
|
50340
51037
|
assessment.security.vulnerabilities.length > 0 && (() => {
|
|
50341
51038
|
var _a2, _b2, _c;
|
|
@@ -50589,43 +51286,58 @@ const AssessmentTab = ({
|
|
|
50589
51286
|
assessment.errorHandling.metrics.testDetails && assessment.errorHandling.metrics.testDetails.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 border-t pt-3", children: [
|
|
50590
51287
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
50591
51288
|
/* @__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
|
-
|
|
51289
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
51290
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
51291
|
+
Button,
|
|
51292
|
+
{
|
|
51293
|
+
variant: showOnlyErrors ? "default" : "outline",
|
|
51294
|
+
size: "sm",
|
|
51295
|
+
className: "text-xs h-6 px-2",
|
|
51296
|
+
onClick: () => setShowOnlyErrors(!showOnlyErrors),
|
|
51297
|
+
children: [
|
|
51298
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Funnel, { className: "h-3 w-3 mr-1" }),
|
|
51299
|
+
showOnlyErrors ? "Show All" : "Filter Errors"
|
|
51300
|
+
]
|
|
51301
|
+
}
|
|
51302
|
+
),
|
|
51303
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
51304
|
+
Button,
|
|
51305
|
+
{
|
|
51306
|
+
variant: "outline",
|
|
51307
|
+
size: "sm",
|
|
51308
|
+
className: "text-xs h-6 px-2",
|
|
51309
|
+
onClick: () => {
|
|
51310
|
+
var _a2;
|
|
51311
|
+
const toolGroups = /* @__PURE__ */ new Map();
|
|
51312
|
+
(_a2 = assessment.errorHandling.metrics.testDetails) == null ? void 0 : _a2.forEach(
|
|
51313
|
+
(testResult) => {
|
|
51314
|
+
const toolName = testResult.toolName || "Unknown Tool";
|
|
51315
|
+
if (!toolGroups.has(toolName)) {
|
|
51316
|
+
toolGroups.set(toolName, []);
|
|
51317
|
+
}
|
|
50606
51318
|
}
|
|
50607
|
-
}
|
|
50608
|
-
);
|
|
50609
|
-
if (allToolsCollapsed) {
|
|
50610
|
-
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
50611
|
-
setAllToolsCollapsed(false);
|
|
50612
|
-
} else {
|
|
50613
|
-
const allToolNames = Array.from(
|
|
50614
|
-
toolGroups.keys()
|
|
50615
51319
|
);
|
|
50616
|
-
|
|
50617
|
-
|
|
50618
|
-
|
|
50619
|
-
|
|
50620
|
-
|
|
50621
|
-
|
|
50622
|
-
|
|
50623
|
-
|
|
50624
|
-
|
|
50625
|
-
|
|
50626
|
-
|
|
50627
|
-
|
|
50628
|
-
|
|
51320
|
+
if (allToolsCollapsed) {
|
|
51321
|
+
setCollapsedTools(/* @__PURE__ */ new Set());
|
|
51322
|
+
setAllToolsCollapsed(false);
|
|
51323
|
+
} else {
|
|
51324
|
+
const allToolNames = Array.from(
|
|
51325
|
+
toolGroups.keys()
|
|
51326
|
+
);
|
|
51327
|
+
setCollapsedTools(new Set(allToolNames));
|
|
51328
|
+
setAllToolsCollapsed(true);
|
|
51329
|
+
}
|
|
51330
|
+
},
|
|
51331
|
+
children: allToolsCollapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
51332
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-3 w-3 mr-1" }),
|
|
51333
|
+
"Expand All"
|
|
51334
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
51335
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-3 w-3 mr-1" }),
|
|
51336
|
+
"Collapse All"
|
|
51337
|
+
] })
|
|
51338
|
+
}
|
|
51339
|
+
)
|
|
51340
|
+
] })
|
|
50629
51341
|
] }),
|
|
50630
51342
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 space-y-1", children: (() => {
|
|
50631
51343
|
var _a2;
|
|
@@ -50651,12 +51363,27 @@ const AssessmentTab = ({
|
|
|
50651
51363
|
newCollapsed.size === toolGroups.size
|
|
50652
51364
|
);
|
|
50653
51365
|
};
|
|
50654
|
-
|
|
51366
|
+
let filteredGroups = Array.from(
|
|
51367
|
+
toolGroups.entries()
|
|
51368
|
+
);
|
|
51369
|
+
if (showOnlyErrors) {
|
|
51370
|
+
filteredGroups = filteredGroups.filter(
|
|
51371
|
+
([, toolTests]) => {
|
|
51372
|
+
return toolTests.some(
|
|
51373
|
+
(test) => test.passed === false
|
|
51374
|
+
);
|
|
51375
|
+
}
|
|
51376
|
+
);
|
|
51377
|
+
}
|
|
51378
|
+
return filteredGroups.map(
|
|
50655
51379
|
([toolName, toolTests]) => {
|
|
50656
|
-
const
|
|
51380
|
+
const scoredTests = toolTests.filter(
|
|
51381
|
+
(t) => t.testType !== "invalid_values"
|
|
51382
|
+
);
|
|
51383
|
+
const passedCount = scoredTests.filter(
|
|
50657
51384
|
(t) => t.passed
|
|
50658
51385
|
).length;
|
|
50659
|
-
const totalCount =
|
|
51386
|
+
const totalCount = scoredTests.length;
|
|
50660
51387
|
const allPassed = passedCount === totalCount;
|
|
50661
51388
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
50662
51389
|
"div",
|
|
@@ -52497,13 +53224,13 @@ const App = () => {
|
|
|
52497
53224
|
) });
|
|
52498
53225
|
if (window.location.pathname === "/oauth/callback") {
|
|
52499
53226
|
const OAuthCallback = React.lazy(
|
|
52500
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
53227
|
+
() => __vitePreload(() => import("./OAuthCallback-CIWsnXN_.js"), true ? [] : void 0)
|
|
52501
53228
|
);
|
|
52502
53229
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
52503
53230
|
}
|
|
52504
53231
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
52505
53232
|
const OAuthDebugCallback = React.lazy(
|
|
52506
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
53233
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-DP9WXVFe.js"), true ? [] : void 0)
|
|
52507
53234
|
);
|
|
52508
53235
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
52509
53236
|
}
|
|
@@ -52790,6 +53517,10 @@ const App = () => {
|
|
|
52790
53517
|
{
|
|
52791
53518
|
tools,
|
|
52792
53519
|
isLoadingTools,
|
|
53520
|
+
listTools: () => {
|
|
53521
|
+
clearError("tools");
|
|
53522
|
+
listTools();
|
|
53523
|
+
},
|
|
52793
53524
|
callTool: async (name2, params) => {
|
|
52794
53525
|
const result = await callTool(name2, params);
|
|
52795
53526
|
return result;
|