@bryan-thompson/inspector-assessment 1.0.1 → 1.2.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 +12 -0
- package/client/dist/assets/{OAuthCallback-pJpyrBg5.js → OAuthCallback-CS0hHvzr.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-BGti8QhX.js → OAuthDebugCallback-CRsLrDJk.js} +1 -1
- package/client/dist/assets/{index-DYiWOife.css → index-Bc4MVSgQ.css} +31 -0
- package/client/dist/assets/{index-BEmDUKhR.js → index-CkSRacMw.js} +215 -40
- package/client/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/build/index.js +0 -0
package/README.md
CHANGED
|
@@ -45,6 +45,18 @@ bunx @bryan-thompson/inspector-assessment
|
|
|
45
45
|
|
|
46
46
|
The web interface will open at http://localhost:6274
|
|
47
47
|
|
|
48
|
+
## For MCP Directory Reviewers
|
|
49
|
+
|
|
50
|
+
If you're reviewing MCP servers for the Anthropic MCP Directory, see our **[Reviewer Quick Start Guide](docs/REVIEWER_QUICK_START.md)** for:
|
|
51
|
+
|
|
52
|
+
- **60-second fast screening** workflow for approve/reject decisions
|
|
53
|
+
- **5-minute detailed review** process for borderline cases
|
|
54
|
+
- **Common pitfalls** explanation (false positives in security, informational vs scored tests)
|
|
55
|
+
- **Decision matrix** with clear approval criteria
|
|
56
|
+
- **Fast CLI analysis** commands for troubleshooting
|
|
57
|
+
|
|
58
|
+
The quick start guide is optimized for fast reviewer onboarding and provides clear guidance on interpreting assessment results.
|
|
59
|
+
|
|
48
60
|
## About This Fork
|
|
49
61
|
|
|
50
62
|
This is an enhanced fork of [Anthropic's MCP Inspector](https://github.com/modelcontextprotocol/inspector) with significantly expanded assessment capabilities for MCP server validation and testing.
|
|
@@ -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-CkSRacMw.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-BGti8QhX.js → OAuthDebugCallback-CRsLrDJk.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-CkSRacMw.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -1332,6 +1332,9 @@ video {
|
|
|
1332
1332
|
.whitespace-nowrap {
|
|
1333
1333
|
white-space: nowrap;
|
|
1334
1334
|
}
|
|
1335
|
+
.whitespace-pre-line {
|
|
1336
|
+
white-space: pre-line;
|
|
1337
|
+
}
|
|
1335
1338
|
.whitespace-pre-wrap {
|
|
1336
1339
|
white-space: pre-wrap;
|
|
1337
1340
|
}
|
|
@@ -1383,6 +1386,10 @@ video {
|
|
|
1383
1386
|
.border-t {
|
|
1384
1387
|
border-top-width: 1px;
|
|
1385
1388
|
}
|
|
1389
|
+
.border-amber-500 {
|
|
1390
|
+
--tw-border-opacity: 1;
|
|
1391
|
+
border-color: rgb(245 158 11 / var(--tw-border-opacity, 1));
|
|
1392
|
+
}
|
|
1386
1393
|
.border-blue-200 {
|
|
1387
1394
|
--tw-border-opacity: 1;
|
|
1388
1395
|
border-color: rgb(191 219 254 / var(--tw-border-opacity, 1));
|
|
@@ -1474,6 +1481,14 @@ video {
|
|
|
1474
1481
|
.bg-accent {
|
|
1475
1482
|
background-color: hsl(var(--accent));
|
|
1476
1483
|
}
|
|
1484
|
+
.bg-amber-100 {
|
|
1485
|
+
--tw-bg-opacity: 1;
|
|
1486
|
+
background-color: rgb(254 243 199 / var(--tw-bg-opacity, 1));
|
|
1487
|
+
}
|
|
1488
|
+
.bg-amber-50 {
|
|
1489
|
+
--tw-bg-opacity: 1;
|
|
1490
|
+
background-color: rgb(255 251 235 / var(--tw-bg-opacity, 1));
|
|
1491
|
+
}
|
|
1477
1492
|
.bg-background {
|
|
1478
1493
|
background-color: hsl(var(--background));
|
|
1479
1494
|
}
|
|
@@ -1550,6 +1565,10 @@ video {
|
|
|
1550
1565
|
.bg-muted\/50 {
|
|
1551
1566
|
background-color: hsl(var(--muted) / 0.5);
|
|
1552
1567
|
}
|
|
1568
|
+
.bg-orange-100 {
|
|
1569
|
+
--tw-bg-opacity: 1;
|
|
1570
|
+
background-color: rgb(255 237 213 / var(--tw-bg-opacity, 1));
|
|
1571
|
+
}
|
|
1553
1572
|
.bg-orange-50 {
|
|
1554
1573
|
--tw-bg-opacity: 1;
|
|
1555
1574
|
background-color: rgb(255 247 237 / var(--tw-bg-opacity, 1));
|
|
@@ -1818,6 +1837,14 @@ video {
|
|
|
1818
1837
|
--tw-text-opacity: 1;
|
|
1819
1838
|
color: rgb(217 119 6 / var(--tw-text-opacity, 1));
|
|
1820
1839
|
}
|
|
1840
|
+
.text-amber-800 {
|
|
1841
|
+
--tw-text-opacity: 1;
|
|
1842
|
+
color: rgb(146 64 14 / var(--tw-text-opacity, 1));
|
|
1843
|
+
}
|
|
1844
|
+
.text-amber-900 {
|
|
1845
|
+
--tw-text-opacity: 1;
|
|
1846
|
+
color: rgb(120 53 15 / var(--tw-text-opacity, 1));
|
|
1847
|
+
}
|
|
1821
1848
|
.text-blue-500 {
|
|
1822
1849
|
--tw-text-opacity: 1;
|
|
1823
1850
|
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
|
@@ -1912,6 +1939,10 @@ video {
|
|
|
1912
1939
|
--tw-text-opacity: 1;
|
|
1913
1940
|
color: rgb(234 88 12 / var(--tw-text-opacity, 1));
|
|
1914
1941
|
}
|
|
1942
|
+
.text-orange-800 {
|
|
1943
|
+
--tw-text-opacity: 1;
|
|
1944
|
+
color: rgb(154 52 18 / var(--tw-text-opacity, 1));
|
|
1945
|
+
}
|
|
1915
1946
|
.text-popover-foreground {
|
|
1916
1947
|
color: hsl(var(--popover-foreground));
|
|
1917
1948
|
}
|
|
@@ -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.0
|
|
16208
|
+
const version$1 = "1.2.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.0
|
|
41739
|
+
const version = "1.2.0";
|
|
41740
41740
|
var [createTooltipContext] = createContextScope("Tooltip", [
|
|
41741
41741
|
createPopperScope
|
|
41742
41742
|
]);
|
|
@@ -44133,6 +44133,8 @@ const ToolsTab = ({
|
|
|
44133
44133
|
const DEFAULT_ASSESSMENT_CONFIG = {
|
|
44134
44134
|
testTimeout: 3e4,
|
|
44135
44135
|
// 30 seconds per tool
|
|
44136
|
+
delayBetweenTests: 0,
|
|
44137
|
+
// No delay by default
|
|
44136
44138
|
skipBrokenTools: false,
|
|
44137
44139
|
reviewerMode: false,
|
|
44138
44140
|
enableExtendedAssessment: true,
|
|
@@ -44158,6 +44160,8 @@ const DEFAULT_ASSESSMENT_CONFIG = {
|
|
|
44158
44160
|
const REVIEWER_MODE_CONFIG = {
|
|
44159
44161
|
testTimeout: 1e4,
|
|
44160
44162
|
// 10 seconds per tool (faster)
|
|
44163
|
+
delayBetweenTests: 100,
|
|
44164
|
+
// Small delay for rate limiting
|
|
44161
44165
|
skipBrokenTools: true,
|
|
44162
44166
|
// Skip broken tools to save time
|
|
44163
44167
|
reviewerMode: true,
|
|
@@ -44188,6 +44192,8 @@ const REVIEWER_MODE_CONFIG = {
|
|
|
44188
44192
|
const DEVELOPER_MODE_CONFIG = {
|
|
44189
44193
|
testTimeout: 3e4,
|
|
44190
44194
|
// 30 seconds per tool
|
|
44195
|
+
delayBetweenTests: 500,
|
|
44196
|
+
// Moderate delay for thorough testing
|
|
44191
44197
|
skipBrokenTools: false,
|
|
44192
44198
|
reviewerMode: false,
|
|
44193
44199
|
enableExtendedAssessment: true,
|
|
@@ -44783,6 +44789,9 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44783
44789
|
);
|
|
44784
44790
|
testDetails.push(...toolTests);
|
|
44785
44791
|
passedTests += toolTests.filter((t) => t.passed).length;
|
|
44792
|
+
if (this.config.delayBetweenTests && this.config.delayBetweenTests > 0) {
|
|
44793
|
+
await this.sleep(this.config.delayBetweenTests);
|
|
44794
|
+
}
|
|
44786
44795
|
}
|
|
44787
44796
|
this.testCount = testDetails.length;
|
|
44788
44797
|
const metrics = this.calculateMetrics(testDetails, passedTests);
|
|
@@ -44837,7 +44846,7 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44837
44846
|
return tests;
|
|
44838
44847
|
}
|
|
44839
44848
|
async testMissingParameters(tool, callTool) {
|
|
44840
|
-
var _a;
|
|
44849
|
+
var _a, _b, _c;
|
|
44841
44850
|
const testInput = {};
|
|
44842
44851
|
const schema = this.getToolSchema(tool);
|
|
44843
44852
|
const hasRequiredParams = (schema == null ? void 0 : schema.required) && Array.isArray(schema.required) && schema.required.length > 0;
|
|
@@ -44883,9 +44892,9 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44883
44892
|
reason: isError ? void 0 : "Tool did not reject missing parameters"
|
|
44884
44893
|
};
|
|
44885
44894
|
} catch (error) {
|
|
44886
|
-
const
|
|
44887
|
-
const messageLower = (
|
|
44888
|
-
const isMeaningfulError = messageLower.includes("required") || messageLower.includes("missing") || messageLower.includes("parameter") || messageLower.includes("must") || messageLower.includes("invalid") || messageLower.includes("validation") || (
|
|
44895
|
+
const errorInfo = this.extractErrorInfo(error);
|
|
44896
|
+
const messageLower = ((_b = errorInfo.message) == null ? void 0 : _b.toLowerCase()) ?? "";
|
|
44897
|
+
const isMeaningfulError = messageLower.includes("required") || messageLower.includes("missing") || messageLower.includes("parameter") || messageLower.includes("must") || messageLower.includes("invalid") || messageLower.includes("validation") || (((_c = errorInfo.message) == null ? void 0 : _c.length) ?? 0) > 20;
|
|
44889
44898
|
return {
|
|
44890
44899
|
toolName: tool.name,
|
|
44891
44900
|
testType: "missing_required",
|
|
@@ -44893,7 +44902,8 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44893
44902
|
expectedError: "Missing required parameters",
|
|
44894
44903
|
actualResponse: {
|
|
44895
44904
|
isError: true,
|
|
44896
|
-
|
|
44905
|
+
errorCode: errorInfo.code,
|
|
44906
|
+
errorMessage: errorInfo.message,
|
|
44897
44907
|
rawResponse: error
|
|
44898
44908
|
},
|
|
44899
44909
|
passed: isMeaningfulError,
|
|
@@ -44902,7 +44912,7 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44902
44912
|
}
|
|
44903
44913
|
}
|
|
44904
44914
|
async testWrongTypes(tool, callTool) {
|
|
44905
|
-
var _a;
|
|
44915
|
+
var _a, _b, _c;
|
|
44906
44916
|
const schema = this.getToolSchema(tool);
|
|
44907
44917
|
const testInput = this.generateWrongTypeParams(schema);
|
|
44908
44918
|
try {
|
|
@@ -44932,9 +44942,9 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44932
44942
|
reason: isError ? void 0 : "Tool accepted wrong parameter types"
|
|
44933
44943
|
};
|
|
44934
44944
|
} catch (error) {
|
|
44935
|
-
const
|
|
44936
|
-
const messageLower = (
|
|
44937
|
-
const isMeaningfulError = messageLower.includes("type") || messageLower.includes("invalid") || messageLower.includes("expected") || messageLower.includes("must be") || messageLower.includes("validation") || messageLower.includes("string") || messageLower.includes("number") || (
|
|
44945
|
+
const errorInfo = this.extractErrorInfo(error);
|
|
44946
|
+
const messageLower = ((_b = errorInfo.message) == null ? void 0 : _b.toLowerCase()) ?? "";
|
|
44947
|
+
const isMeaningfulError = messageLower.includes("type") || messageLower.includes("invalid") || messageLower.includes("expected") || messageLower.includes("must be") || messageLower.includes("validation") || messageLower.includes("string") || messageLower.includes("number") || (((_c = errorInfo.message) == null ? void 0 : _c.length) ?? 0) > 20;
|
|
44938
44948
|
return {
|
|
44939
44949
|
toolName: tool.name,
|
|
44940
44950
|
testType: "wrong_type",
|
|
@@ -44942,7 +44952,8 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44942
44952
|
expectedError: "Type validation error",
|
|
44943
44953
|
actualResponse: {
|
|
44944
44954
|
isError: true,
|
|
44945
|
-
|
|
44955
|
+
errorCode: errorInfo.code,
|
|
44956
|
+
errorMessage: errorInfo.message,
|
|
44946
44957
|
rawResponse: error
|
|
44947
44958
|
},
|
|
44948
44959
|
passed: isMeaningfulError,
|
|
@@ -44951,6 +44962,7 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44951
44962
|
}
|
|
44952
44963
|
}
|
|
44953
44964
|
async testInvalidValues(tool, callTool) {
|
|
44965
|
+
var _a, _b;
|
|
44954
44966
|
const schema = this.getToolSchema(tool);
|
|
44955
44967
|
const testInput = this.generateInvalidValueParams(schema);
|
|
44956
44968
|
try {
|
|
@@ -44975,9 +44987,9 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44975
44987
|
reason: isError ? void 0 : "Tool accepted invalid values"
|
|
44976
44988
|
};
|
|
44977
44989
|
} catch (error) {
|
|
44978
|
-
const
|
|
44979
|
-
const messageLower = (
|
|
44980
|
-
const isMeaningfulError = messageLower.includes("invalid") || messageLower.includes("not allowed") || messageLower.includes("must") || messageLower.includes("cannot") || messageLower.includes("validation") || messageLower.includes("error") || (
|
|
44990
|
+
const errorInfo = this.extractErrorInfo(error);
|
|
44991
|
+
const messageLower = ((_a = errorInfo.message) == null ? void 0 : _a.toLowerCase()) ?? "";
|
|
44992
|
+
const isMeaningfulError = messageLower.includes("invalid") || messageLower.includes("not allowed") || messageLower.includes("must") || messageLower.includes("cannot") || messageLower.includes("validation") || messageLower.includes("error") || (((_b = errorInfo.message) == null ? void 0 : _b.length) ?? 0) > 15;
|
|
44981
44993
|
return {
|
|
44982
44994
|
toolName: tool.name,
|
|
44983
44995
|
testType: "invalid_values",
|
|
@@ -44985,7 +44997,8 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44985
44997
|
expectedError: "Invalid parameter values",
|
|
44986
44998
|
actualResponse: {
|
|
44987
44999
|
isError: true,
|
|
44988
|
-
|
|
45000
|
+
errorCode: errorInfo.code,
|
|
45001
|
+
errorMessage: errorInfo.message,
|
|
44989
45002
|
rawResponse: error
|
|
44990
45003
|
},
|
|
44991
45004
|
passed: isMeaningfulError,
|
|
@@ -44994,6 +45007,7 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
44994
45007
|
}
|
|
44995
45008
|
}
|
|
44996
45009
|
async testExcessiveInput(tool, callTool) {
|
|
45010
|
+
var _a, _b;
|
|
44997
45011
|
const largeString = "x".repeat(1e5);
|
|
44998
45012
|
const testInput = this.generateParamsWithValue(tool, largeString);
|
|
44999
45013
|
try {
|
|
@@ -45020,9 +45034,9 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
45020
45034
|
reason: !isError && !response ? "Tool crashed on large input" : void 0
|
|
45021
45035
|
};
|
|
45022
45036
|
} catch (error) {
|
|
45023
|
-
const
|
|
45024
|
-
const messageLower = (
|
|
45025
|
-
const isMeaningfulError = messageLower.includes("size") || messageLower.includes("large") || messageLower.includes("limit") || messageLower.includes("exceed") || messageLower.includes("too") || messageLower.includes("maximum") || (
|
|
45037
|
+
const errorInfo = this.extractErrorInfo(error);
|
|
45038
|
+
const messageLower = ((_a = errorInfo.message) == null ? void 0 : _a.toLowerCase()) ?? "";
|
|
45039
|
+
const isMeaningfulError = messageLower.includes("size") || messageLower.includes("large") || messageLower.includes("limit") || messageLower.includes("exceed") || messageLower.includes("too") || messageLower.includes("maximum") || (((_b = errorInfo.message) == null ? void 0 : _b.length) ?? 0) > 10;
|
|
45026
45040
|
return {
|
|
45027
45041
|
toolName: tool.name,
|
|
45028
45042
|
testType: "excessive_input",
|
|
@@ -45030,7 +45044,8 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
45030
45044
|
expectedError: "Input size limit exceeded",
|
|
45031
45045
|
actualResponse: {
|
|
45032
45046
|
isError: true,
|
|
45033
|
-
|
|
45047
|
+
errorCode: errorInfo.code,
|
|
45048
|
+
errorMessage: errorInfo.message,
|
|
45034
45049
|
rawResponse: "[error details omitted]"
|
|
45035
45050
|
},
|
|
45036
45051
|
passed: isMeaningfulError,
|
|
@@ -45178,12 +45193,17 @@ class ErrorHandlingAssessor extends BaseAssessor {
|
|
|
45178
45193
|
else if (score >= 70) quality = "good";
|
|
45179
45194
|
else if (score >= 50) quality = "fair";
|
|
45180
45195
|
else quality = "poor";
|
|
45181
|
-
const
|
|
45196
|
+
const actualErrors = tests.filter((t) => t.actualResponse.isError);
|
|
45197
|
+
const errorsWithCodes = actualErrors.filter(
|
|
45182
45198
|
(t) => t.actualResponse.errorCode !== void 0
|
|
45183
|
-
);
|
|
45184
|
-
const
|
|
45199
|
+
).length;
|
|
45200
|
+
const errorsWithMessages = actualErrors.filter(
|
|
45185
45201
|
(t) => t.actualResponse.errorMessage && t.actualResponse.errorMessage.length > 10
|
|
45186
|
-
);
|
|
45202
|
+
).length;
|
|
45203
|
+
const hasProperErrorCodes = actualErrors.length === 0 || // If no errors were triggered, we can't assess this
|
|
45204
|
+
errorsWithCodes / actualErrors.length >= 0.5;
|
|
45205
|
+
const hasDescriptiveMessages = actualErrors.length === 0 || // If no errors were triggered, we can't assess this
|
|
45206
|
+
errorsWithMessages / actualErrors.length >= 0.5;
|
|
45187
45207
|
const validatesInputs = tests.filter((t) => ["missing_required", "wrong_type"].includes(t.testType)).some((t) => t.passed);
|
|
45188
45208
|
return {
|
|
45189
45209
|
mcpComplianceScore: score,
|
|
@@ -46358,6 +46378,13 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
46358
46378
|
response,
|
|
46359
46379
|
payload
|
|
46360
46380
|
);
|
|
46381
|
+
const confidenceResult = this.calculateConfidence(
|
|
46382
|
+
tool,
|
|
46383
|
+
isVulnerable,
|
|
46384
|
+
evidence || "",
|
|
46385
|
+
this.extractResponseContent(response),
|
|
46386
|
+
payload
|
|
46387
|
+
);
|
|
46361
46388
|
return {
|
|
46362
46389
|
testName: attackName,
|
|
46363
46390
|
description: payload.description,
|
|
@@ -46366,7 +46393,8 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
46366
46393
|
toolName: tool.name,
|
|
46367
46394
|
vulnerable: isVulnerable,
|
|
46368
46395
|
evidence,
|
|
46369
|
-
response: this.extractResponseContent(response)
|
|
46396
|
+
response: this.extractResponseContent(response),
|
|
46397
|
+
...confidenceResult
|
|
46370
46398
|
};
|
|
46371
46399
|
} catch (error) {
|
|
46372
46400
|
return {
|
|
@@ -46501,6 +46529,95 @@ class SecurityAssessor extends BaseAssessor {
|
|
|
46501
46529
|
).length;
|
|
46502
46530
|
return `Found ${vulnCount} vulnerabilities (${criticalCount} critical, ${moderateCount} moderate) across ${testCount} security tests. Risk level: ${riskLevel}. Tools may execute malicious commands or leak sensitive data.`;
|
|
46503
46531
|
}
|
|
46532
|
+
/**
|
|
46533
|
+
* Calculate confidence level and manual review requirements
|
|
46534
|
+
* Detects ambiguous patterns that need human verification
|
|
46535
|
+
*/
|
|
46536
|
+
calculateConfidence(tool, isVulnerable, evidence, responseText, payload) {
|
|
46537
|
+
var _a;
|
|
46538
|
+
const toolDescription = (tool.description || "").toLowerCase();
|
|
46539
|
+
const toolName = tool.name.toLowerCase();
|
|
46540
|
+
const responseLower = responseText.toLowerCase();
|
|
46541
|
+
const payloadLower = payload.payload.toLowerCase();
|
|
46542
|
+
if (!isVulnerable && (evidence.includes("safely reflected") || evidence.includes("API wrapper") || evidence.includes("safe: true"))) {
|
|
46543
|
+
return {
|
|
46544
|
+
confidence: "high",
|
|
46545
|
+
requiresManualReview: false
|
|
46546
|
+
};
|
|
46547
|
+
}
|
|
46548
|
+
if (isVulnerable && evidence.includes("executed") && !this.isStructuredDataTool(toolName, toolDescription)) {
|
|
46549
|
+
return {
|
|
46550
|
+
confidence: "high",
|
|
46551
|
+
requiresManualReview: false
|
|
46552
|
+
};
|
|
46553
|
+
}
|
|
46554
|
+
if (isVulnerable) {
|
|
46555
|
+
const isDataTool = this.isStructuredDataTool(toolName, toolDescription);
|
|
46556
|
+
const hasStructuredData = /title:|name:|description:|trust score:|id:|snippets:/i.test(
|
|
46557
|
+
responseText
|
|
46558
|
+
) || /^\s*-\s+/m.test(responseText) || // Bullet points
|
|
46559
|
+
/"[^"]+"\s*:\s*"[^"]+"/g.test(responseText);
|
|
46560
|
+
const patternInInput = (_a = payload.evidence) == null ? void 0 : _a.test(payloadLower);
|
|
46561
|
+
const echosInput = responseLower.includes(payloadLower);
|
|
46562
|
+
if (isDataTool && (hasStructuredData || echosInput) && patternInInput) {
|
|
46563
|
+
return {
|
|
46564
|
+
confidence: "low",
|
|
46565
|
+
requiresManualReview: true,
|
|
46566
|
+
manualReviewReason: "Pattern matched in structured data response. Tool may be legitimately returning data containing search terms rather than executing malicious code.",
|
|
46567
|
+
reviewGuidance: `Verify: 1) Does the tool actually execute/compute the input? 2) Or does it just return pre-existing data that happens to contain the pattern? 3) Check if '${payload.evidence}' appears in legitimate tool output vs. execution results.`
|
|
46568
|
+
};
|
|
46569
|
+
}
|
|
46570
|
+
if (payload.evidence && /\b\d\b/.test(payload.evidence.toString()) && /\b(score|count|trust|rating|id|version)\b/i.test(responseText)) {
|
|
46571
|
+
return {
|
|
46572
|
+
confidence: "low",
|
|
46573
|
+
requiresManualReview: true,
|
|
46574
|
+
manualReviewReason: "Numeric pattern found in response with numeric metadata (scores, counts, etc.). May be coincidental data rather than arithmetic execution.",
|
|
46575
|
+
reviewGuidance: "Verify: 1) Did the tool actually compute an arithmetic result? 2) Or does the number appear in metadata like trust scores, version numbers, or counts? 3) Compare pattern location in response with tool's expected output format."
|
|
46576
|
+
};
|
|
46577
|
+
}
|
|
46578
|
+
if (/admin|role|privilege|elevated/i.test(payload.payload) && /\b(library|search|documentation|api|wrapper)\b/i.test(toolDescription)) {
|
|
46579
|
+
return {
|
|
46580
|
+
confidence: "low",
|
|
46581
|
+
requiresManualReview: true,
|
|
46582
|
+
manualReviewReason: "Admin-related keywords found in search/retrieval tool results. Tool may be returning data about admin-related libraries/APIs rather than elevating privileges.",
|
|
46583
|
+
reviewGuidance: "Verify: 1) Did the tool actually change behavior or assume admin role? 2) Or did it return search results for admin-related content? 3) Test if tool behavior actually changed after this request."
|
|
46584
|
+
};
|
|
46585
|
+
}
|
|
46586
|
+
}
|
|
46587
|
+
if (isVulnerable && evidence.includes("executed")) {
|
|
46588
|
+
return {
|
|
46589
|
+
confidence: "medium",
|
|
46590
|
+
requiresManualReview: true,
|
|
46591
|
+
manualReviewReason: "Execution indicators found but context suggests possible ambiguity.",
|
|
46592
|
+
reviewGuidance: "Verify: 1) Review the full response to confirm actual code execution. 2) Check if tool's intended function involves execution. 3) Test with variations to confirm consistency."
|
|
46593
|
+
};
|
|
46594
|
+
}
|
|
46595
|
+
return {
|
|
46596
|
+
confidence: "high",
|
|
46597
|
+
requiresManualReview: false
|
|
46598
|
+
};
|
|
46599
|
+
}
|
|
46600
|
+
/**
|
|
46601
|
+
* Check if tool is a structured data tool (search, lookup, retrieval)
|
|
46602
|
+
* These tools naturally echo input patterns in their results
|
|
46603
|
+
*/
|
|
46604
|
+
isStructuredDataTool(toolName, toolDescription) {
|
|
46605
|
+
const dataToolPatterns = [
|
|
46606
|
+
/search/i,
|
|
46607
|
+
/find/i,
|
|
46608
|
+
/lookup/i,
|
|
46609
|
+
/query/i,
|
|
46610
|
+
/retrieve/i,
|
|
46611
|
+
/fetch/i,
|
|
46612
|
+
/get/i,
|
|
46613
|
+
/list/i,
|
|
46614
|
+
/resolve/i,
|
|
46615
|
+
/discover/i,
|
|
46616
|
+
/browse/i
|
|
46617
|
+
];
|
|
46618
|
+
const combined = `${toolName} ${toolDescription}`;
|
|
46619
|
+
return dataToolPatterns.some((pattern2) => pattern2.test(combined));
|
|
46620
|
+
}
|
|
46504
46621
|
/**
|
|
46505
46622
|
* Check if response is just reflection (safe)
|
|
46506
46623
|
* Expanded to catch more reflection patterns including echo, repeat, display
|
|
@@ -48008,9 +48125,17 @@ class ResponseValidator {
|
|
|
48008
48125
|
}
|
|
48009
48126
|
}
|
|
48010
48127
|
class TestScenarioEngine {
|
|
48011
|
-
constructor(testTimeout = 5e3) {
|
|
48128
|
+
constructor(testTimeout = 5e3, delayBetweenTests = 0) {
|
|
48012
48129
|
__publicField(this, "testTimeout");
|
|
48130
|
+
__publicField(this, "delayBetweenTests");
|
|
48013
48131
|
this.testTimeout = testTimeout;
|
|
48132
|
+
this.delayBetweenTests = delayBetweenTests;
|
|
48133
|
+
}
|
|
48134
|
+
/**
|
|
48135
|
+
* Sleep for specified milliseconds (for rate limiting)
|
|
48136
|
+
*/
|
|
48137
|
+
async sleep(ms) {
|
|
48138
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
48014
48139
|
}
|
|
48015
48140
|
/**
|
|
48016
48141
|
* Test tool with progressive complexity to identify failure points
|
|
@@ -48163,6 +48288,9 @@ class TestScenarioEngine {
|
|
|
48163
48288
|
callTool
|
|
48164
48289
|
);
|
|
48165
48290
|
result.scenarioResults.push(scenarioResult);
|
|
48291
|
+
if (this.delayBetweenTests > 0) {
|
|
48292
|
+
await this.sleep(this.delayBetweenTests);
|
|
48293
|
+
}
|
|
48166
48294
|
if (scenarioResult.executed) {
|
|
48167
48295
|
result.scenariosExecuted++;
|
|
48168
48296
|
if (scenarioResult.validation.isValid) {
|
|
@@ -48573,7 +48701,10 @@ class MCPAssessmentService {
|
|
|
48573
48701
|
* Enhanced functionality assessment with multi-scenario testing
|
|
48574
48702
|
*/
|
|
48575
48703
|
async assessFunctionalityEnhanced(tools, callTool) {
|
|
48576
|
-
const engine = new TestScenarioEngine(
|
|
48704
|
+
const engine = new TestScenarioEngine(
|
|
48705
|
+
this.config.testTimeout,
|
|
48706
|
+
this.config.delayBetweenTests ?? 0
|
|
48707
|
+
);
|
|
48577
48708
|
const toolResults = [];
|
|
48578
48709
|
const enhancedResults = [];
|
|
48579
48710
|
let workingCount = 0;
|
|
@@ -49744,11 +49875,11 @@ const ReviewerAssessmentView = ({
|
|
|
49744
49875
|
{
|
|
49745
49876
|
id: "documentation",
|
|
49746
49877
|
name: "3. Documentation Quality",
|
|
49747
|
-
quickCheck: `${assessment.documentation.metrics.hasReadme ? "Has README" : "Missing README"}, ${assessment.documentation.metrics.exampleCount} examples`,
|
|
49878
|
+
quickCheck: `${assessment.documentation.metrics.hasReadme ? "Has README" : "Missing README"}, ${assessment.documentation.metrics.exampleCount} functional examples found`,
|
|
49748
49879
|
verdict: assessment.documentation.status === "PASS" ? "PASS" : assessment.documentation.metrics.exampleCount < 3 ? "FAIL" : "REVIEW_NEEDED",
|
|
49749
49880
|
evidence: [
|
|
49750
49881
|
`Has README: ${assessment.documentation.metrics.hasReadme ? "Yes" : "No"}`,
|
|
49751
|
-
`
|
|
49882
|
+
`Functional examples: ${assessment.documentation.metrics.exampleCount} found (3+ required)`,
|
|
49752
49883
|
`Has installation instructions: ${assessment.documentation.metrics.hasInstallInstructions ? "Yes" : "No"}`,
|
|
49753
49884
|
`Has usage guide: ${assessment.documentation.metrics.hasUsageGuide ? "Yes" : "No"}`
|
|
49754
49885
|
],
|
|
@@ -50718,6 +50849,30 @@ const AssessmentTab = ({
|
|
|
50718
50849
|
),
|
|
50719
50850
|
!isLoadingTools && tools.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Select which tools to test for error handling (the most intensive tests). All tools are selected by default." })
|
|
50720
50851
|
] }),
|
|
50852
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
50853
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Label$1, { htmlFor: "delay-between-tests", children: "Delay between tests (milliseconds)" }),
|
|
50854
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
50855
|
+
Input,
|
|
50856
|
+
{
|
|
50857
|
+
id: "delay-between-tests",
|
|
50858
|
+
type: "number",
|
|
50859
|
+
min: "0",
|
|
50860
|
+
max: "5000",
|
|
50861
|
+
step: "100",
|
|
50862
|
+
value: config.delayBetweenTests ?? 0,
|
|
50863
|
+
onChange: (e) => {
|
|
50864
|
+
const value = parseInt(e.target.value, 10);
|
|
50865
|
+
setConfig({
|
|
50866
|
+
...config,
|
|
50867
|
+
delayBetweenTests: isNaN(value) ? 0 : value
|
|
50868
|
+
});
|
|
50869
|
+
},
|
|
50870
|
+
disabled: isRunning,
|
|
50871
|
+
placeholder: "0"
|
|
50872
|
+
}
|
|
50873
|
+
),
|
|
50874
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Add a delay between tests to avoid rate limiting. Recommended: 500-1000ms for rate-limited APIs. Set to 0 for no delay (default)." })
|
|
50875
|
+
] }),
|
|
50721
50876
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 border rounded-lg p-4 bg-blue-50 dark:bg-blue-950", children: [
|
|
50722
50877
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
50723
50878
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -50734,15 +50889,15 @@ const AssessmentTab = ({
|
|
|
50734
50889
|
),
|
|
50735
50890
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Label$1, { htmlFor: "domain-testing", className: "cursor-pointer", children: [
|
|
50736
50891
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold", children: "Enable Advanced Security Testing" }),
|
|
50737
|
-
/* @__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: "18
|
|
50892
|
+
/* @__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: "18 Patterns" })
|
|
50738
50893
|
] })
|
|
50739
50894
|
] }),
|
|
50740
50895
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground ml-6", children: [
|
|
50741
50896
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Basic:" }),
|
|
50742
|
-
" 3
|
|
50897
|
+
" 3 common patterns (~48 checks).",
|
|
50743
50898
|
" ",
|
|
50744
50899
|
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Advanced:" }),
|
|
50745
|
-
" All 18 patterns.
|
|
50900
|
+
" All 18 patterns. Designed to identify potential issues like Direct Command Injection, Role Override, Data Exfiltration, System Commands, Tool Shadowing, Context Escape, and 12+ other common attack vectors."
|
|
50746
50901
|
] })
|
|
50747
50902
|
] }),
|
|
50748
50903
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
@@ -51068,9 +51223,7 @@ const AssessmentTab = ({
|
|
|
51068
51223
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
51069
51224
|
"Examples:",
|
|
51070
51225
|
" ",
|
|
51071
|
-
assessment.documentation.metrics.exampleCount
|
|
51072
|
-
"/",
|
|
51073
|
-
assessment.documentation.metrics.requiredExamples
|
|
51226
|
+
assessment.documentation.metrics.exampleCount >= assessment.documentation.metrics.requiredExamples ? `${assessment.documentation.metrics.exampleCount} found (${assessment.documentation.metrics.requiredExamples}+ required) ✓` : `${assessment.documentation.metrics.exampleCount}/${assessment.documentation.metrics.requiredExamples} (need more)`
|
|
51074
51227
|
] }),
|
|
51075
51228
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
51076
51229
|
"Install Guide:",
|
|
@@ -51857,7 +52010,8 @@ const ErrorTestItem = ({ test }) => {
|
|
|
51857
52010
|
{
|
|
51858
52011
|
className: "text-xs border-l-2 pl-3 py-1",
|
|
51859
52012
|
style: {
|
|
51860
|
-
borderColor: test.passed ? "rgb(34 197 94)" : "rgb(239 68 68)"
|
|
52013
|
+
borderColor: test.testType === "invalid_values" ? "rgb(234 179 8)" : test.passed ? "rgb(34 197 94)" : "rgb(239 68 68)"
|
|
52014
|
+
// red-500 for FAIL
|
|
51861
52015
|
},
|
|
51862
52016
|
children: [
|
|
51863
52017
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -52106,9 +52260,30 @@ const SecurityVulnerabilityItem = ({ testResult, toolName }) => {
|
|
|
52106
52260
|
testResult.vulnerable ? "🚨 VULNERABLE - Tool executed malicious input!" : "✅ SECURE - Tool properly rejected malicious input"
|
|
52107
52261
|
]
|
|
52108
52262
|
}
|
|
52109
|
-
)
|
|
52263
|
+
),
|
|
52264
|
+
testResult.confidence && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
52265
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Confidence:" }),
|
|
52266
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
52267
|
+
"span",
|
|
52268
|
+
{
|
|
52269
|
+
className: `text-xs px-2 py-1 rounded font-semibold ${testResult.confidence === "high" ? "bg-green-100 text-green-800" : testResult.confidence === "medium" ? "bg-yellow-100 text-yellow-800" : "bg-orange-100 text-orange-800"}`,
|
|
52270
|
+
children: testResult.confidence.toUpperCase()
|
|
52271
|
+
}
|
|
52272
|
+
)
|
|
52273
|
+
] })
|
|
52110
52274
|
] })
|
|
52111
52275
|
] }),
|
|
52276
|
+
testResult.requiresManualReview && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-amber-50 border-l-4 border-amber-500 p-3 rounded", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2", children: [
|
|
52277
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "h-5 w-5 text-amber-600 flex-shrink-0 mt-0.5" }),
|
|
52278
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
|
|
52279
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-semibold text-amber-900 mb-1", children: "⚠️ Manual Review Required" }),
|
|
52280
|
+
testResult.manualReviewReason && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-amber-800 mb-2", children: testResult.manualReviewReason }),
|
|
52281
|
+
testResult.reviewGuidance && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-amber-100 p-2 rounded text-xs text-amber-900", children: [
|
|
52282
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Review Steps:" }),
|
|
52283
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 whitespace-pre-line", children: testResult.reviewGuidance })
|
|
52284
|
+
] })
|
|
52285
|
+
] })
|
|
52286
|
+
] }) }),
|
|
52112
52287
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
52113
52288
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1", children: "Test Payload" }),
|
|
52114
52289
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-black text-green-400 p-2 rounded text-xs font-mono whitespace-pre-wrap break-all", children: testResult.payload })
|
|
@@ -52269,7 +52444,7 @@ const generateTextReport = (assessment, filteredStatus, categoryFilter) => {
|
|
|
52269
52444
|
`Status: ${assessment.documentation.status}`,
|
|
52270
52445
|
assessment.documentation.explanation,
|
|
52271
52446
|
`- Has README: ${assessment.documentation.metrics.hasReadme ? "Yes" : "No"}`,
|
|
52272
|
-
`- Examples: ${assessment.documentation.metrics.exampleCount}
|
|
52447
|
+
`- Examples: ${assessment.documentation.metrics.exampleCount} functional examples found (${assessment.documentation.metrics.requiredExamples}+ required)`,
|
|
52273
52448
|
`- Installation Guide: ${assessment.documentation.metrics.hasInstallInstructions ? "Yes" : "No"}`,
|
|
52274
52449
|
`- Usage Guide: ${assessment.documentation.metrics.hasUsageGuide ? "Yes" : "No"}`
|
|
52275
52450
|
);
|
|
@@ -53106,13 +53281,13 @@ const App = () => {
|
|
|
53106
53281
|
) });
|
|
53107
53282
|
if (window.location.pathname === "/oauth/callback") {
|
|
53108
53283
|
const OAuthCallback = React.lazy(
|
|
53109
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
53284
|
+
() => __vitePreload(() => import("./OAuthCallback-CS0hHvzr.js"), true ? [] : void 0)
|
|
53110
53285
|
);
|
|
53111
53286
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
53112
53287
|
}
|
|
53113
53288
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
53114
53289
|
const OAuthDebugCallback = React.lazy(
|
|
53115
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
53290
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-CRsLrDJk.js"), true ? [] : void 0)
|
|
53116
53291
|
);
|
|
53117
53292
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
53118
53293
|
}
|
package/client/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CkSRacMw.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bc4MVSgQ.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root" class="w-full"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.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>",
|
package/server/build/index.js
CHANGED
|
File without changes
|