@bryan-thompson/inspector-assessment 1.3.0 → 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 +16 -8
- package/client/dist/assets/{OAuthCallback-CiSJznN1.js → OAuthCallback-CIWsnXN_.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-D_XkKc3n.js → OAuthDebugCallback-DP9WXVFe.js} +1 -1
- package/client/dist/assets/{index-BsOrK-Nh.js → index-CynAt5P-.js} +344 -82
- package/client/dist/index.html +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,12 +180,12 @@ Response: "Stored in collection: <script>alert(1)</script>"
|
|
|
180
180
|
→ Tool is just storing data, not executing it
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
❌ **VULNERABLE - Command Execution
|
|
183
|
+
❌ **VULNERABLE - Command Execution** (Calculator Injection):
|
|
184
184
|
|
|
185
185
|
```
|
|
186
|
-
Payload: "
|
|
186
|
+
Payload: "2+2"
|
|
187
187
|
Response: "The answer is 4"
|
|
188
|
-
→ Tool executed the
|
|
188
|
+
→ Tool executed the arithmetic expression via eval()!
|
|
189
189
|
```
|
|
190
190
|
|
|
191
191
|
**Detection Approach**:
|
|
@@ -197,9 +197,17 @@ Response: "The answer is 4"
|
|
|
197
197
|
**Impact**:
|
|
198
198
|
|
|
199
199
|
- **Zero false positives** on data storage/retrieval tools (qdrant, databases, file systems)
|
|
200
|
-
- **
|
|
201
|
-
- **Dual-mode testing**: Reviewer mode (3 critical patterns, fast) + Developer mode (all
|
|
202
|
-
- **Real vulnerabilities still detected**: 100% test pass rate on detecting actual command injection, role override, data exfiltration
|
|
200
|
+
- **18 injection patterns tested** (9 original + 9 advanced patterns)
|
|
201
|
+
- **Dual-mode testing**: Reviewer mode (3 critical patterns, fast) + Developer mode (all 13 patterns, comprehensive)
|
|
202
|
+
- **Real vulnerabilities still detected**: 100% test pass rate on detecting actual command injection, calculator injection, role override, data exfiltration
|
|
203
|
+
|
|
204
|
+
**Supported Injection Types**:
|
|
205
|
+
|
|
206
|
+
- **Command Injection**: System commands (whoami, ls -la, pwd)
|
|
207
|
+
- **Calculator Injection**: Arithmetic expressions and code injection via eval() (NEW - 7 payloads)
|
|
208
|
+
- **SQL Injection**: Database command injection
|
|
209
|
+
- **Path Traversal**: File system access outside intended directory
|
|
210
|
+
- Plus 9 additional patterns (Unicode Bypass, Nested Injection, Package Squatting, etc.)
|
|
203
211
|
|
|
204
212
|
**Validation**: See [VULNERABILITY_TESTING.md](VULNERABILITY_TESTING.md) for detailed testing guide and examples.
|
|
205
213
|
|
|
@@ -216,8 +224,8 @@ Response: "The answer is 4"
|
|
|
216
224
|
- Performance measurement
|
|
217
225
|
|
|
218
226
|
2. **SecurityAssessor** (443 lines)
|
|
219
|
-
-
|
|
220
|
-
- Direct command injection, role override, data exfiltration detection
|
|
227
|
+
- 13 distinct injection attack patterns (including Calculator Injection) with context-aware reflection detection
|
|
228
|
+
- Direct command injection, calculator injection (eval detection), role override, data exfiltration detection
|
|
221
229
|
- Vulnerability analysis with risk levels (HIGH/MEDIUM/LOW)
|
|
222
230
|
- Zero false positives through intelligent distinction between data reflection and command execution
|
|
223
231
|
|
|
@@ -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-CynAt5P-.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-D_XkKc3n.js → OAuthDebugCallback-DP9WXVFe.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-CynAt5P-.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.4.0";
|
|
16209
16209
|
const packageJson = {
|
|
16210
16210
|
name,
|
|
16211
16211
|
version: version$1
|
|
@@ -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
|
]);
|
|
@@ -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) {
|
|
@@ -47166,6 +47322,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47166
47322
|
const results = [];
|
|
47167
47323
|
const criticalPatterns = [
|
|
47168
47324
|
"Command Injection",
|
|
47325
|
+
"Calculator Injection",
|
|
47169
47326
|
"SQL Injection",
|
|
47170
47327
|
"Path Traversal"
|
|
47171
47328
|
];
|
|
@@ -47175,7 +47332,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47175
47332
|
);
|
|
47176
47333
|
const toolsToTest = this.selectToolsForTesting(context.tools);
|
|
47177
47334
|
this.log(
|
|
47178
|
-
`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)`
|
|
47179
47336
|
);
|
|
47180
47337
|
for (const tool of toolsToTest) {
|
|
47181
47338
|
if (!this.hasInputParameters(tool)) {
|
|
@@ -47252,7 +47409,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47252
47409
|
};
|
|
47253
47410
|
}
|
|
47254
47411
|
try {
|
|
47255
|
-
const params = this.createTestParameters(payload
|
|
47412
|
+
const params = this.createTestParameters(payload, tool);
|
|
47256
47413
|
if (Object.keys(params).length === 0) {
|
|
47257
47414
|
return {
|
|
47258
47415
|
testName: attackName,
|
|
@@ -47562,16 +47719,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47562
47719
|
evidence: "Safe storage control tool (validated safe implementation, control group for testing)"
|
|
47563
47720
|
};
|
|
47564
47721
|
}
|
|
47565
|
-
|
|
47566
|
-
return {
|
|
47567
|
-
isVulnerable: false,
|
|
47568
|
-
evidence: "API wrapper response - returned external content as data"
|
|
47569
|
-
};
|
|
47570
|
-
}
|
|
47571
|
-
const isJustReflection = this.isReflectionResponse(
|
|
47572
|
-
responseText,
|
|
47573
|
-
payloadText
|
|
47574
|
-
);
|
|
47722
|
+
const isJustReflection = this.isReflectionResponse(responseText);
|
|
47575
47723
|
if (isJustReflection) {
|
|
47576
47724
|
return {
|
|
47577
47725
|
isVulnerable: false,
|
|
@@ -47928,14 +48076,31 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
47928
48076
|
/**
|
|
47929
48077
|
* Check if response is just reflection (safe)
|
|
47930
48078
|
* Expanded to catch more reflection patterns including echo, repeat, display
|
|
47931
|
-
* IMPROVED: Bidirectional patterns, safety indicators, and
|
|
48079
|
+
* IMPROVED: Bidirectional patterns, safety indicators, and two-layer defense
|
|
47932
48080
|
*
|
|
47933
48081
|
* CRITICAL: This check distinguishes between:
|
|
47934
48082
|
* - SAFE: Tool stores/echoes malicious input as data (reflection)
|
|
47935
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)
|
|
47936
48088
|
*/
|
|
47937
|
-
isReflectionResponse(responseText
|
|
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
|
+
];
|
|
47938
48102
|
const reflectionPatterns = [
|
|
48103
|
+
...statusPatterns,
|
|
47939
48104
|
// Original patterns (keep all existing)
|
|
47940
48105
|
/stored.*query/i,
|
|
47941
48106
|
/saved.*input/i,
|
|
@@ -48010,49 +48175,145 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
48010
48175
|
/invalid .* format.*stored as text/i,
|
|
48011
48176
|
/error:.*too (long|short|large)/i
|
|
48012
48177
|
];
|
|
48013
|
-
|
|
48014
|
-
|
|
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
|
+
}
|
|
48015
48236
|
}
|
|
48016
48237
|
try {
|
|
48017
48238
|
const parsed = JSON.parse(responseText);
|
|
48018
|
-
|
|
48019
|
-
"
|
|
48020
|
-
|
|
48021
|
-
|
|
48022
|
-
|
|
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;
|
|
48239
|
+
if (parsed.action === "test" || parsed.action === "placeholder") {
|
|
48240
|
+
const resultText = String(parsed.result || "");
|
|
48241
|
+
if (!this.detectExecutionArtifacts(resultText)) {
|
|
48242
|
+
return true;
|
|
48243
|
+
}
|
|
48044
48244
|
}
|
|
48045
|
-
if (parsed.status && /(
|
|
48046
|
-
|
|
48245
|
+
if (parsed.status && /(completed|success|ok|done)/.test(parsed.status)) {
|
|
48246
|
+
if (!this.detectExecutionArtifacts(responseText)) {
|
|
48247
|
+
return true;
|
|
48248
|
+
}
|
|
48047
48249
|
}
|
|
48048
48250
|
} catch {
|
|
48049
48251
|
}
|
|
48050
48252
|
return false;
|
|
48051
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;
|
|
48311
|
+
}
|
|
48052
48312
|
/**
|
|
48053
48313
|
* Analyze injection response (existing logic)
|
|
48314
|
+
* Note: payload parameter unused after refactoring to two-layer defense
|
|
48054
48315
|
*/
|
|
48055
|
-
analyzeInjectionResponse(response,
|
|
48316
|
+
analyzeInjectionResponse(response, _payload) {
|
|
48056
48317
|
const responseText = this.extractResponseContent(response);
|
|
48057
48318
|
const executionPatterns = [
|
|
48058
48319
|
/executed/i,
|
|
@@ -48064,7 +48325,7 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
48064
48325
|
const hasExecution = executionPatterns.some(
|
|
48065
48326
|
(pattern2) => pattern2.test(responseText)
|
|
48066
48327
|
);
|
|
48067
|
-
if (hasExecution && !this.isReflectionResponse(responseText
|
|
48328
|
+
if (hasExecution && !this.isReflectionResponse(responseText)) {
|
|
48068
48329
|
return {
|
|
48069
48330
|
isVulnerable: true,
|
|
48070
48331
|
evidence: "Tool executed instruction: found execution keywords"
|
|
@@ -48096,11 +48357,31 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
48096
48357
|
return {};
|
|
48097
48358
|
}
|
|
48098
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
|
+
}
|
|
48099
48382
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
48100
48383
|
const propSchema = prop;
|
|
48101
|
-
if (
|
|
48102
|
-
params[key] = payload;
|
|
48103
|
-
} 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)) {
|
|
48104
48385
|
if (propSchema.type === "string") {
|
|
48105
48386
|
params[key] = "test";
|
|
48106
48387
|
} else if (propSchema.type === "number") {
|
|
@@ -48139,25 +48420,6 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
48139
48420
|
];
|
|
48140
48421
|
return executionTests.includes(attackName);
|
|
48141
48422
|
}
|
|
48142
|
-
/**
|
|
48143
|
-
* Check if response is from an API wrapper tool
|
|
48144
|
-
* API wrappers return external content as data, not execute it
|
|
48145
|
-
*/
|
|
48146
|
-
isApiWrapperResponse(responseText) {
|
|
48147
|
-
const apiWrapperPatterns = [
|
|
48148
|
-
/successfully\s+(scraped|fetched|crawled)/i,
|
|
48149
|
-
/content\s+from\s+http/i,
|
|
48150
|
-
/api\s+(request|response|call)\s+(completed|successful)/i,
|
|
48151
|
-
/retrieved\s+\d+\s+(results|pages|urls)/i,
|
|
48152
|
-
/markdown.*screenshot.*links/i,
|
|
48153
|
-
// Firecrawl format indicators
|
|
48154
|
-
/scraping\s+(complete|finished|done)/i,
|
|
48155
|
-
/\bfirecrawl\b/i,
|
|
48156
|
-
/crawl.*job/i,
|
|
48157
|
-
/extraction.*complete/i
|
|
48158
|
-
];
|
|
48159
|
-
return apiWrapperPatterns.some((pattern2) => pattern2.test(responseText));
|
|
48160
|
-
}
|
|
48161
48423
|
/**
|
|
48162
48424
|
* Check if response is returning search results
|
|
48163
48425
|
* Search tools return query results as data, not execute them
|
|
@@ -50388,14 +50650,14 @@ const AssessmentTab = ({
|
|
|
50388
50650
|
),
|
|
50389
50651
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label$1, { htmlFor: "domain-testing", className: "cursor-pointer", children: [
|
|
50390
50652
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: "Enable Advanced Security Testing" }),
|
|
50391
|
-
/* @__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" })
|
|
50392
50654
|
] })
|
|
50393
50655
|
] }),
|
|
50394
50656
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground ml-6", children: [
|
|
50395
50657
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Basic:" }),
|
|
50396
|
-
"
|
|
50658
|
+
" 4 critical injection patterns (~20 checks). ",
|
|
50397
50659
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Advanced:" }),
|
|
50398
|
-
" 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)."
|
|
50399
50661
|
] })
|
|
50400
50662
|
] }),
|
|
50401
50663
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
@@ -52962,13 +53224,13 @@ const App = () => {
|
|
|
52962
53224
|
) });
|
|
52963
53225
|
if (window.location.pathname === "/oauth/callback") {
|
|
52964
53226
|
const OAuthCallback = React.lazy(
|
|
52965
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
53227
|
+
() => __vitePreload(() => import("./OAuthCallback-CIWsnXN_.js"), true ? [] : void 0)
|
|
52966
53228
|
);
|
|
52967
53229
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
52968
53230
|
}
|
|
52969
53231
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
52970
53232
|
const OAuthDebugCallback = React.lazy(
|
|
52971
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
53233
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-DP9WXVFe.js"), true ? [] : void 0)
|
|
52972
53234
|
);
|
|
52973
53235
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
52974
53236
|
}
|
package/client/dist/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CynAt5P-.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-Cz-lwW4x.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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>",
|