@bryan-thompson/inspector-assessment 1.43.2 → 1.43.4
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 +1062 -224
- package/cli/build/assess-full.js +532 -106
- package/cli/build/assess-security.js +54 -90
- package/cli/build/lib/cli-parser.js +14 -1
- package/cli/build/lib/cli-parserSchemas.js +1 -0
- package/cli/build/lib/result-output.js +21 -0
- package/cli/build/profiles.js +20 -0
- package/cli/build/validate-testbed.js +0 -0
- package/cli/package.json +1 -1
- package/client/dist/assets/{OAuthCallback-BS8-A1sU.js → OAuthCallback-Chi58kRc.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-025_TM2i.js → OAuthDebugCallback-BluD_Wxg.js} +1 -1
- package/client/dist/assets/{index-DEhlIjy-.js → index-KW2LwGdp.js} +4 -4
- package/client/dist/index.html +1 -1
- package/client/lib/lib/assessment/configSchemas.d.ts +64 -64
- package/client/lib/lib/assessment/jsonlEventSchemas.d.ts +286 -286
- package/client/lib/lib/assessment/resultTypes.d.ts +10 -0
- package/client/lib/lib/assessment/resultTypes.d.ts.map +1 -1
- package/client/lib/lib/assessmentTypes.d.ts +1 -20
- package/client/lib/lib/assessmentTypes.d.ts.map +1 -1
- package/client/lib/lib/assessmentTypes.js +1 -20
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +57 -104
- package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/client/lib/services/assessment/AssessmentOrchestrator.js +298 -133
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -0
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.deprecated.js +1 -1
- package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +564 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts +5 -0
- package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/SecurityAssessor.js +62 -0
- package/client/lib/services/assessment/modules/index.d.ts +1 -1
- package/client/lib/services/assessment/modules/index.js +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts +15 -0
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/SecurityPayloadTester.js +72 -0
- package/client/lib/services/assessment/modules/securityTests/factory.d.ts +2 -0
- package/client/lib/services/assessment/modules/securityTests/factory.d.ts.map +1 -1
- package/client/lib/services/assessment/modules/securityTests/factory.js +1 -0
- package/client/lib/services/assessment/registry/AssessorDefinitions.js +1 -1
- package/client/lib/services/assessment/responseValidatorSchemas.d.ts +12 -12
- package/client/package.json +3 -3
- package/package.json +4 -2
- package/server/package.json +1 -1
- package/cli/build/lib/__tests__/zodErrorFormatter.test.js +0 -282
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts +0 -109
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.d.ts.map +0 -1
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.deprecated.d.ts +0 -109
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.deprecated.d.ts.map +0 -1
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.deprecated.js +0 -852
- package/client/lib/services/assessment/modules/ProtocolComplianceAssessor.js +0 -852
|
@@ -50,7 +50,7 @@ export { TemporalAssessor } from "./TemporalAssessor.js";
|
|
|
50
50
|
* The `errorHandling` result field is preserved for backward compatibility.
|
|
51
51
|
* @see GitHub Issue #188
|
|
52
52
|
*/
|
|
53
|
-
export { ProtocolComplianceAssessor, type UnifiedProtocolComplianceAssessment, } from "./ProtocolComplianceAssessor.js";
|
|
53
|
+
export { ProtocolComplianceAssessor, type UnifiedProtocolComplianceAssessment, } from "./ProtocolComplianceAssessor/index.js";
|
|
54
54
|
export { AUPComplianceAssessor } from "./AUPComplianceAssessor.js";
|
|
55
55
|
export { ToolAnnotationAssessor } from "./ToolAnnotationAssessor.js";
|
|
56
56
|
export { ProhibitedLibrariesAssessor } from "./ProhibitedLibrariesAssessor.js";
|
|
@@ -54,7 +54,7 @@ export { TemporalAssessor } from "./TemporalAssessor.js";
|
|
|
54
54
|
* The `errorHandling` result field is preserved for backward compatibility.
|
|
55
55
|
* @see GitHub Issue #188
|
|
56
56
|
*/
|
|
57
|
-
export { ProtocolComplianceAssessor, } from "./ProtocolComplianceAssessor.js";
|
|
57
|
+
export { ProtocolComplianceAssessor, } from "./ProtocolComplianceAssessor/index.js";
|
|
58
58
|
export { AUPComplianceAssessor } from "./AUPComplianceAssessor.js";
|
|
59
59
|
// ============================================================================
|
|
60
60
|
// Tier 2: Compliance (MCP Directory)
|
|
@@ -36,6 +36,11 @@ export interface PayloadTestConfig {
|
|
|
36
36
|
* When provided, enables annotation-aware false positive reduction
|
|
37
37
|
*/
|
|
38
38
|
toolAnnotationsContext?: ToolAnnotationsContext;
|
|
39
|
+
/**
|
|
40
|
+
* Transport type for context-aware test skipping
|
|
41
|
+
* When "http" or "sse", skip filesystem-only tests (e.g., Path Traversal)
|
|
42
|
+
*/
|
|
43
|
+
transportType?: "stdio" | "http" | "sse";
|
|
39
44
|
}
|
|
40
45
|
/**
|
|
41
46
|
* Logger interface for test execution
|
|
@@ -61,6 +66,16 @@ export declare class SecurityPayloadTester {
|
|
|
61
66
|
* Call before running tests to enable annotation-aware false positive reduction
|
|
62
67
|
*/
|
|
63
68
|
setToolAnnotationsContext(context: ToolAnnotationsContext | undefined): void;
|
|
69
|
+
/**
|
|
70
|
+
* Set transport type for context-aware test skipping (FP reduction)
|
|
71
|
+
* Call before running tests to skip irrelevant patterns
|
|
72
|
+
*/
|
|
73
|
+
setTransportType(transportType: "stdio" | "http" | "sse" | undefined): void;
|
|
74
|
+
/**
|
|
75
|
+
* Check if an attack pattern should be skipped for a given tool.
|
|
76
|
+
* Reduces false positives by skipping irrelevant test categories.
|
|
77
|
+
*/
|
|
78
|
+
private shouldSkipPattern;
|
|
64
79
|
/**
|
|
65
80
|
* Run comprehensive security tests (advanced mode)
|
|
66
81
|
* Tests selected tools with ALL 23 security patterns using diverse payloads
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPayloadTester.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPayloadTester.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAIjB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,2BAA2B,EAC3B,IAAI,EACL,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGL,eAAe,EAChB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"SecurityPayloadTester.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/SecurityPayloadTester.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAIjB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,2BAA2B,EAC3B,IAAI,EACL,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAGL,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAShC;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED;;GAEG;AACH,qBAAa,qBAAqB;IAO9B,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,kBAAkB;IAR5B,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,SAAS,CAAK;gBAGZ,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,UAAU,EAClB,kBAAkB,EAAE,CAAC,CAAC,EAC5B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,CAAC,CAAC;IAOjB;;;OAGG;IACH,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,SAAS,GAAG,IAAI;IAI5E;;;OAGG;IACH,gBAAgB,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,IAAI;IAI3E;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;;OAGG;IACG,yBAAyB,CAC7B,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,EACzC,UAAU,CAAC,EAAE,oBAAoB,GAChC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IA6NhC;;;OAGG;IACG,qBAAqB,CACzB,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,EACzC,UAAU,CAAC,EAAE,oBAAoB,GAChC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAwNhC;;OAEG;IACG,WAAW,CACf,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,GACxC,OAAO,CAAC,kBAAkB,CAAC;IAqT9B;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,CACR,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,2BAA2B,CAAC,GACxC,OAAO,CAAC,kBAAkB,CAAC;IAqD9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -13,6 +13,7 @@ import { SecurityPayloadGenerator } from "./SecurityPayloadGenerator.js";
|
|
|
13
13
|
import { SanitizationDetector } from "./SanitizationDetector.js";
|
|
14
14
|
import { DEFAULT_PERFORMANCE_CONFIG } from "../../config/performanceConfig.js";
|
|
15
15
|
import { isTransientErrorPattern } from "./SecurityPatternLibrary.js";
|
|
16
|
+
import { ToolClassifier } from "../../ToolClassifier.js";
|
|
16
17
|
/**
|
|
17
18
|
* Executes security tests with payloads against MCP tools
|
|
18
19
|
*/
|
|
@@ -39,6 +40,36 @@ export class SecurityPayloadTester {
|
|
|
39
40
|
setToolAnnotationsContext(context) {
|
|
40
41
|
this.config.toolAnnotationsContext = context;
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Set transport type for context-aware test skipping (FP reduction)
|
|
45
|
+
* Call before running tests to skip irrelevant patterns
|
|
46
|
+
*/
|
|
47
|
+
setTransportType(transportType) {
|
|
48
|
+
this.config.transportType = transportType;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if an attack pattern should be skipped for a given tool.
|
|
52
|
+
* Reduces false positives by skipping irrelevant test categories.
|
|
53
|
+
*/
|
|
54
|
+
shouldSkipPattern(tool, attackName) {
|
|
55
|
+
// Skip "Calculator Injection" for non-calculator tools
|
|
56
|
+
if (attackName === "Calculator Injection") {
|
|
57
|
+
const classifier = new ToolClassifier();
|
|
58
|
+
const result = classifier.classify(tool.name, tool.description);
|
|
59
|
+
const hasCalcCategory = result.categories.some((c) => c === "calculator" || c === "code_executor");
|
|
60
|
+
if (!hasCalcCategory) {
|
|
61
|
+
return "Tool is not a calculator/code executor - Calculator Injection not applicable";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Skip "Path Traversal" for HTTP/SSE transport (no local filesystem)
|
|
65
|
+
if (attackName === "Path Traversal") {
|
|
66
|
+
const transport = this.config.transportType;
|
|
67
|
+
if (transport === "http" || transport === "sse") {
|
|
68
|
+
return "Remote transport (HTTP/SSE) - Path Traversal not applicable to remote servers";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
42
73
|
/**
|
|
43
74
|
* Run comprehensive security tests (advanced mode)
|
|
44
75
|
* Tests selected tools with ALL 23 security patterns using diverse payloads
|
|
@@ -120,6 +151,25 @@ export class SecurityPayloadTester {
|
|
|
120
151
|
}
|
|
121
152
|
this.logger.log(`Testing ${tool.name} with all attack patterns`);
|
|
122
153
|
for (const attackPattern of attackPatterns) {
|
|
154
|
+
// Skip irrelevant attack patterns to reduce false positives
|
|
155
|
+
const skipReason = this.shouldSkipPattern(tool, attackPattern.attackName);
|
|
156
|
+
if (skipReason) {
|
|
157
|
+
const payloads = getPayloadsForAttack(attackPattern.attackName);
|
|
158
|
+
for (const payload of payloads) {
|
|
159
|
+
toolResults.push({
|
|
160
|
+
testName: attackPattern.attackName,
|
|
161
|
+
description: payload.description,
|
|
162
|
+
payload: payload.payload,
|
|
163
|
+
riskLevel: payload.riskLevel,
|
|
164
|
+
toolName: tool.name,
|
|
165
|
+
vulnerable: false,
|
|
166
|
+
evidence: skipReason,
|
|
167
|
+
});
|
|
168
|
+
completedTests++;
|
|
169
|
+
batchCount++;
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
123
173
|
const payloads = getPayloadsForAttack(attackPattern.attackName);
|
|
124
174
|
for (const payload of payloads) {
|
|
125
175
|
this.testCount++;
|
|
@@ -269,6 +319,28 @@ export class SecurityPayloadTester {
|
|
|
269
319
|
}
|
|
270
320
|
this.logger.log(`Testing ${tool.name} with ${basicPatterns.length} critical patterns`);
|
|
271
321
|
for (const attackPattern of basicPatterns) {
|
|
322
|
+
// Skip irrelevant attack patterns to reduce false positives
|
|
323
|
+
const skipReason = this.shouldSkipPattern(tool, attackPattern.attackName);
|
|
324
|
+
if (skipReason) {
|
|
325
|
+
const allPayloads = getPayloadsForAttack(attackPattern.attackName);
|
|
326
|
+
const payload = allPayloads[0];
|
|
327
|
+
if (payload) {
|
|
328
|
+
const skippedResult = {
|
|
329
|
+
testName: attackPattern.attackName,
|
|
330
|
+
description: payload.description,
|
|
331
|
+
payload: payload.payload,
|
|
332
|
+
riskLevel: payload.riskLevel,
|
|
333
|
+
toolName: tool.name,
|
|
334
|
+
vulnerable: false,
|
|
335
|
+
evidence: skipReason,
|
|
336
|
+
};
|
|
337
|
+
results.push(skippedResult);
|
|
338
|
+
toolResults.push(skippedResult);
|
|
339
|
+
completedTests++;
|
|
340
|
+
batchCount++;
|
|
341
|
+
}
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
272
344
|
const allPayloads = getPayloadsForAttack(attackPattern.attackName);
|
|
273
345
|
const payload = allPayloads[0];
|
|
274
346
|
if (!payload)
|
|
@@ -48,6 +48,8 @@ export interface SecurityTestersConfig {
|
|
|
48
48
|
logger: TestLogger;
|
|
49
49
|
/** Timeout execution wrapper from BaseAssessor */
|
|
50
50
|
executeWithTimeout: <T>(promise: Promise<T>, timeout: number) => Promise<T>;
|
|
51
|
+
/** Transport type for context-aware test skipping */
|
|
52
|
+
transportType?: "stdio" | "http" | "sse";
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* Create security testers with dependencies injected
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EACL,qBAAqB,EAErB,UAAU,EACX,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,aAAa,EAAE,qBAAqB,CAAC;IAErC,mEAAmE;IACnE,gBAAgB,EAAE,wBAAwB,CAAC;IAE3C,wEAAwE;IACxE,oBAAoB,EAAE,oBAAoB,CAAC;IAE3C,kEAAkE;IAClE,WAAW,EAAE,oBAAoB,CAAC;IAElC,wEAAwE;IACxE,gBAAgB,EAAE,oBAAoB,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,yDAAyD;IACzD,gBAAgB,EAAE,uBAAuB,CAAC;IAE1C,wCAAwC;IACxC,MAAM,EAAE,UAAU,CAAC;IAEnB,kDAAkD;IAClD,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../../src/services/assessment/modules/securityTests/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EACL,qBAAqB,EAErB,UAAU,EACX,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,aAAa,EAAE,qBAAqB,CAAC;IAErC,mEAAmE;IACnE,gBAAgB,EAAE,wBAAwB,CAAC;IAE3C,wEAAwE;IACxE,oBAAoB,EAAE,oBAAoB,CAAC;IAE3C,kEAAkE;IAClE,WAAW,EAAE,oBAAoB,CAAC;IAElC,wEAAwE;IACxE,gBAAgB,EAAE,oBAAoB,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,yDAAyD;IACzD,gBAAgB,EAAE,uBAAuB,CAAC;IAE1C,wCAAwC;IACxC,MAAM,EAAE,UAAU,CAAC;IAEnB,kDAAkD;IAClD,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAE5E,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,qBAAqB,GACnC,eAAe,CA+BjB;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,cAAc,EAAE,OAAO,CAAC,eAAe,CAAC,EACxC,aAAa,EAAE,qBAAqB,GACnC,eAAe,CAOjB"}
|
|
@@ -46,6 +46,7 @@ export function createSecurityTesters(factoryConfig) {
|
|
|
46
46
|
maxParallelTests: assessmentConfig.maxParallelTests,
|
|
47
47
|
securityTestTimeout: assessmentConfig.securityTestTimeout,
|
|
48
48
|
selectedToolsForTesting: assessmentConfig.selectedToolsForTesting,
|
|
49
|
+
transportType: factoryConfig.transportType,
|
|
49
50
|
};
|
|
50
51
|
return {
|
|
51
52
|
payloadTester: new SecurityPayloadTester(payloadConfig, logger, executeWithTimeout),
|
|
@@ -14,7 +14,7 @@ import { FunctionalityAssessor } from "../modules/FunctionalityAssessor.js";
|
|
|
14
14
|
import { SecurityAssessor } from "../modules/SecurityAssessor.js";
|
|
15
15
|
import { DocumentationAssessor } from "../modules/DocumentationAssessor.js";
|
|
16
16
|
// ErrorHandlingAssessor merged into ProtocolComplianceAssessor (Issue #188)
|
|
17
|
-
// import { ErrorHandlingAssessor } from "../modules/ErrorHandlingAssessor";
|
|
17
|
+
// import { ErrorHandlingAssessor } from "../modules/ErrorHandlingAssessor.js";
|
|
18
18
|
import { UsabilityAssessor } from "../modules/UsabilityAssessor.js";
|
|
19
19
|
// Protocol compliance (unified module with error handling - Issue #188)
|
|
20
20
|
import { ProtocolComplianceAssessor } from "../modules/ProtocolComplianceAssessor/ProtocolComplianceAssessor.js";
|
|
@@ -369,9 +369,9 @@ export declare const ValidationResultSchema: z.ZodObject<{
|
|
|
369
369
|
evidence?: string[];
|
|
370
370
|
confidence?: number;
|
|
371
371
|
isError?: boolean;
|
|
372
|
-
issues?: string[];
|
|
373
372
|
isValid?: boolean;
|
|
374
|
-
|
|
373
|
+
issues?: string[];
|
|
374
|
+
classification?: "error" | "fully_working" | "partially_working" | "connectivity_only" | "broken";
|
|
375
375
|
responseMetadata?: {
|
|
376
376
|
contentTypes?: ("text" | "image" | "audio" | "resource_link" | "resource")[];
|
|
377
377
|
hasStructuredContent?: boolean;
|
|
@@ -389,9 +389,9 @@ export declare const ValidationResultSchema: z.ZodObject<{
|
|
|
389
389
|
evidence?: string[];
|
|
390
390
|
confidence?: number;
|
|
391
391
|
isError?: boolean;
|
|
392
|
-
issues?: string[];
|
|
393
392
|
isValid?: boolean;
|
|
394
|
-
|
|
393
|
+
issues?: string[];
|
|
394
|
+
classification?: "error" | "fully_working" | "partially_working" | "connectivity_only" | "broken";
|
|
395
395
|
responseMetadata?: {
|
|
396
396
|
contentTypes?: ("text" | "image" | "audio" | "resource_link" | "resource")[];
|
|
397
397
|
hasStructuredContent?: boolean;
|
|
@@ -480,7 +480,6 @@ export declare const MCPToolCallResultSchema: z.ZodObject<{
|
|
|
480
480
|
structuredContent: z.ZodOptional<z.ZodUnknown>;
|
|
481
481
|
_meta: z.ZodOptional<z.ZodUnknown>;
|
|
482
482
|
}, "strip", z.ZodTypeAny, {
|
|
483
|
-
_meta?: unknown;
|
|
484
483
|
content?: ({
|
|
485
484
|
text?: string;
|
|
486
485
|
type?: "text";
|
|
@@ -504,9 +503,9 @@ export declare const MCPToolCallResultSchema: z.ZodObject<{
|
|
|
504
503
|
uri?: string;
|
|
505
504
|
})[];
|
|
506
505
|
isError?: boolean;
|
|
506
|
+
_meta?: unknown;
|
|
507
507
|
structuredContent?: unknown;
|
|
508
508
|
}, {
|
|
509
|
-
_meta?: unknown;
|
|
510
509
|
content?: ({
|
|
511
510
|
text?: string;
|
|
512
511
|
type?: "text";
|
|
@@ -530,6 +529,7 @@ export declare const MCPToolCallResultSchema: z.ZodObject<{
|
|
|
530
529
|
uri?: string;
|
|
531
530
|
})[];
|
|
532
531
|
isError?: boolean;
|
|
532
|
+
_meta?: unknown;
|
|
533
533
|
structuredContent?: unknown;
|
|
534
534
|
}>;
|
|
535
535
|
export type MCPToolCallResultParsed = z.infer<typeof MCPToolCallResultSchema>;
|
|
@@ -596,7 +596,6 @@ export declare function safeParseContentArray(content: unknown): z.SafeParseRetu
|
|
|
596
596
|
* @returns SafeParseResult with success status and data/error
|
|
597
597
|
*/
|
|
598
598
|
export declare function safeParseMCPToolCallResult(result: unknown): z.SafeParseReturnType<{
|
|
599
|
-
_meta?: unknown;
|
|
600
599
|
content?: ({
|
|
601
600
|
text?: string;
|
|
602
601
|
type?: "text";
|
|
@@ -620,9 +619,9 @@ export declare function safeParseMCPToolCallResult(result: unknown): z.SafeParse
|
|
|
620
619
|
uri?: string;
|
|
621
620
|
})[];
|
|
622
621
|
isError?: boolean;
|
|
622
|
+
_meta?: unknown;
|
|
623
623
|
structuredContent?: unknown;
|
|
624
624
|
}, {
|
|
625
|
-
_meta?: unknown;
|
|
626
625
|
content?: ({
|
|
627
626
|
text?: string;
|
|
628
627
|
type?: "text";
|
|
@@ -646,6 +645,7 @@ export declare function safeParseMCPToolCallResult(result: unknown): z.SafeParse
|
|
|
646
645
|
uri?: string;
|
|
647
646
|
})[];
|
|
648
647
|
isError?: boolean;
|
|
648
|
+
_meta?: unknown;
|
|
649
649
|
structuredContent?: unknown;
|
|
650
650
|
}>;
|
|
651
651
|
/**
|
|
@@ -673,9 +673,9 @@ export declare function safeParseValidationResult(result: unknown): z.SafeParseR
|
|
|
673
673
|
evidence?: string[];
|
|
674
674
|
confidence?: number;
|
|
675
675
|
isError?: boolean;
|
|
676
|
-
issues?: string[];
|
|
677
676
|
isValid?: boolean;
|
|
678
|
-
|
|
677
|
+
issues?: string[];
|
|
678
|
+
classification?: "error" | "fully_working" | "partially_working" | "connectivity_only" | "broken";
|
|
679
679
|
responseMetadata?: {
|
|
680
680
|
contentTypes?: ("text" | "image" | "audio" | "resource_link" | "resource")[];
|
|
681
681
|
hasStructuredContent?: boolean;
|
|
@@ -693,9 +693,9 @@ export declare function safeParseValidationResult(result: unknown): z.SafeParseR
|
|
|
693
693
|
evidence?: string[];
|
|
694
694
|
confidence?: number;
|
|
695
695
|
isError?: boolean;
|
|
696
|
-
issues?: string[];
|
|
697
696
|
isValid?: boolean;
|
|
698
|
-
|
|
697
|
+
issues?: string[];
|
|
698
|
+
classification?: "error" | "fully_working" | "partially_working" | "connectivity_only" | "broken";
|
|
699
699
|
responseMetadata?: {
|
|
700
700
|
contentTypes?: ("text" | "image" | "audio" | "resource_link" | "resource")[];
|
|
701
701
|
hasStructuredContent?: boolean;
|
package/client/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-client",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.4",
|
|
4
4
|
"description": "Client-side application for the Enhanced MCP Inspector with assessment capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
],
|
|
37
37
|
"scripts": {
|
|
38
38
|
"dev": "vite --port 6274",
|
|
39
|
-
"build": "
|
|
40
|
-
"build:lib": "tsc -p tsconfig.lib.json && npx tsc-alias -p tsconfig.lib.json --resolve-full-paths",
|
|
39
|
+
"build": "vite build && npm run build:lib",
|
|
40
|
+
"build:lib": "tsc -p tsconfig.lib.json --noCheck && npx tsc-alias -p tsconfig.lib.json --resolve-full-paths",
|
|
41
41
|
"lint": "eslint .",
|
|
42
42
|
"preview": "vite preview --port 6274",
|
|
43
43
|
"test": "jest --config jest.config.cjs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.4",
|
|
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>",
|
|
@@ -89,8 +89,10 @@
|
|
|
89
89
|
],
|
|
90
90
|
"scripts": {
|
|
91
91
|
"build": "npm run build-server && npm run build-client && npm run build-cli",
|
|
92
|
+
"build:docker": "npm run build-server && npm run build-client:docker && npm run build-cli",
|
|
92
93
|
"build-server": "cd server && npm run build",
|
|
93
94
|
"build-client": "cd client && npm run build",
|
|
95
|
+
"build-client:docker": "docker run --rm -v \"$PWD\":/app -w /app/client node:22-slim sh -c 'npm install --prefer-offline 2>/dev/null; npm run build'",
|
|
94
96
|
"build-cli": "cd cli && npm run build",
|
|
95
97
|
"clean": "rimraf ./node_modules ./client/node_modules ./cli/node_modules ./build ./client/dist ./server/build ./cli/build ./package-lock.json && npm install",
|
|
96
98
|
"dev": "node client/bin/start.js --dev",
|
|
@@ -119,7 +121,7 @@
|
|
|
119
121
|
"prettier-fix": "prettier --write .",
|
|
120
122
|
"prettier-check": "prettier --check .",
|
|
121
123
|
"lint": "prettier --check . && cd client && npm run lint",
|
|
122
|
-
"prepare": "husky
|
|
124
|
+
"prepare": "husky",
|
|
123
125
|
"prepublishOnly": "node scripts/validate-publish.js",
|
|
124
126
|
"publish-all": "npm publish --workspaces --access public && npm publish --access public",
|
|
125
127
|
"update-version": "node scripts/update-version.js",
|
package/server/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bryan-thompson/inspector-assessment-server",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.4",
|
|
4
4
|
"description": "Server-side application for the Enhanced MCP Inspector with assessment capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bryan Thompson <bryan@triepod.ai>",
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Zod Error Formatting Utilities
|
|
3
|
-
*
|
|
4
|
-
* Validates the error formatting functions for CLI-friendly output.
|
|
5
|
-
*
|
|
6
|
-
* @module cli/lib/__tests__/zodErrorFormatter
|
|
7
|
-
*/
|
|
8
|
-
// Uses Jest globals (describe, test, expect)
|
|
9
|
-
import { jest } from "@jest/globals";
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
import { formatZodIssue, formatZodError, formatZodErrorIndented, printZodErrorForCli, zodErrorToArray, formatZodErrorForJson, formatUserFriendlyError, } from "../zodErrorFormatter.js";
|
|
12
|
-
// Helper to create a ZodError with specific issues
|
|
13
|
-
function createZodError(issues) {
|
|
14
|
-
// Create a schema that will fail and then manipulate the error
|
|
15
|
-
const schema = z.object({ dummy: z.string() });
|
|
16
|
-
const result = schema.safeParse({ dummy: 123 });
|
|
17
|
-
if (!result.success) {
|
|
18
|
-
// Replace the errors with our custom issues
|
|
19
|
-
result.error.issues = issues.map((issue) => ({
|
|
20
|
-
code: issue.code || "custom",
|
|
21
|
-
path: issue.path,
|
|
22
|
-
message: issue.message,
|
|
23
|
-
}));
|
|
24
|
-
return result.error;
|
|
25
|
-
}
|
|
26
|
-
throw new Error("Failed to create ZodError");
|
|
27
|
-
}
|
|
28
|
-
describe("zodErrorFormatter", () => {
|
|
29
|
-
describe("formatZodIssue", () => {
|
|
30
|
-
test("formats issue with empty path (message only)", () => {
|
|
31
|
-
const issue = { code: "custom", path: [], message: "Required" };
|
|
32
|
-
const result = formatZodIssue(issue);
|
|
33
|
-
expect(result).toBe("Required");
|
|
34
|
-
});
|
|
35
|
-
test("formats issue with single-level path", () => {
|
|
36
|
-
const issue = {
|
|
37
|
-
code: "custom",
|
|
38
|
-
path: ["field"],
|
|
39
|
-
message: "Invalid value",
|
|
40
|
-
};
|
|
41
|
-
const result = formatZodIssue(issue);
|
|
42
|
-
expect(result).toBe("field: Invalid value");
|
|
43
|
-
});
|
|
44
|
-
test("formats issue with nested path", () => {
|
|
45
|
-
const issue = {
|
|
46
|
-
code: "custom",
|
|
47
|
-
path: ["config", "nested", "field"],
|
|
48
|
-
message: "Must be a string",
|
|
49
|
-
};
|
|
50
|
-
const result = formatZodIssue(issue);
|
|
51
|
-
expect(result).toBe("config.nested.field: Must be a string");
|
|
52
|
-
});
|
|
53
|
-
test("formats issue with array index in path", () => {
|
|
54
|
-
const issue = {
|
|
55
|
-
code: "custom",
|
|
56
|
-
path: ["items", 0, "name"],
|
|
57
|
-
message: "Required",
|
|
58
|
-
};
|
|
59
|
-
const result = formatZodIssue(issue);
|
|
60
|
-
expect(result).toBe("items.0.name: Required");
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
describe("formatZodError", () => {
|
|
64
|
-
test("formats single error", () => {
|
|
65
|
-
const error = createZodError([
|
|
66
|
-
{ path: ["field"], message: "Invalid value" },
|
|
67
|
-
]);
|
|
68
|
-
const result = formatZodError(error);
|
|
69
|
-
expect(result).toBe("field: Invalid value");
|
|
70
|
-
});
|
|
71
|
-
test("formats multiple errors with newlines", () => {
|
|
72
|
-
const error = createZodError([
|
|
73
|
-
{ path: ["field1"], message: "Error 1" },
|
|
74
|
-
{ path: ["field2"], message: "Error 2" },
|
|
75
|
-
{ path: ["field3"], message: "Error 3" },
|
|
76
|
-
]);
|
|
77
|
-
const result = formatZodError(error);
|
|
78
|
-
expect(result).toBe("field1: Error 1\nfield2: Error 2\nfield3: Error 3");
|
|
79
|
-
});
|
|
80
|
-
test("handles error with empty path", () => {
|
|
81
|
-
const error = createZodError([{ path: [], message: "Global error" }]);
|
|
82
|
-
const result = formatZodError(error);
|
|
83
|
-
expect(result).toBe("Global error");
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe("formatZodErrorIndented", () => {
|
|
87
|
-
test("uses default indentation (two spaces)", () => {
|
|
88
|
-
const error = createZodError([
|
|
89
|
-
{ path: ["field"], message: "Invalid value" },
|
|
90
|
-
]);
|
|
91
|
-
const result = formatZodErrorIndented(error);
|
|
92
|
-
expect(result).toBe(" field: Invalid value");
|
|
93
|
-
});
|
|
94
|
-
test("uses custom indentation", () => {
|
|
95
|
-
const error = createZodError([
|
|
96
|
-
{ path: ["field"], message: "Invalid value" },
|
|
97
|
-
]);
|
|
98
|
-
const result = formatZodErrorIndented(error, "\t");
|
|
99
|
-
expect(result).toBe("\tfield: Invalid value");
|
|
100
|
-
});
|
|
101
|
-
test("uses four spaces indentation", () => {
|
|
102
|
-
const error = createZodError([
|
|
103
|
-
{ path: ["field"], message: "Invalid value" },
|
|
104
|
-
]);
|
|
105
|
-
const result = formatZodErrorIndented(error, " ");
|
|
106
|
-
expect(result).toBe(" field: Invalid value");
|
|
107
|
-
});
|
|
108
|
-
test("applies indent to each line for multiple errors", () => {
|
|
109
|
-
const error = createZodError([
|
|
110
|
-
{ path: ["field1"], message: "Error 1" },
|
|
111
|
-
{ path: ["field2"], message: "Error 2" },
|
|
112
|
-
]);
|
|
113
|
-
const result = formatZodErrorIndented(error, ">> ");
|
|
114
|
-
expect(result).toBe(">> field1: Error 1\n>> field2: Error 2");
|
|
115
|
-
});
|
|
116
|
-
test("uses empty indentation", () => {
|
|
117
|
-
const error = createZodError([
|
|
118
|
-
{ path: ["field"], message: "Invalid value" },
|
|
119
|
-
]);
|
|
120
|
-
const result = formatZodErrorIndented(error, "");
|
|
121
|
-
expect(result).toBe("field: Invalid value");
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
describe("printZodErrorForCli", () => {
|
|
125
|
-
let consoleSpy;
|
|
126
|
-
beforeEach(() => {
|
|
127
|
-
consoleSpy = jest.spyOn(console, "error").mockImplementation(() => { });
|
|
128
|
-
});
|
|
129
|
-
afterEach(() => {
|
|
130
|
-
consoleSpy.mockRestore();
|
|
131
|
-
});
|
|
132
|
-
test("prints with context prefix", () => {
|
|
133
|
-
const error = createZodError([
|
|
134
|
-
{ path: ["field"], message: "Invalid value" },
|
|
135
|
-
]);
|
|
136
|
-
printZodErrorForCli(error, "config file");
|
|
137
|
-
expect(consoleSpy).toHaveBeenCalledWith("Error in config file:\n field: Invalid value");
|
|
138
|
-
});
|
|
139
|
-
test("prints without context (default prefix)", () => {
|
|
140
|
-
const error = createZodError([
|
|
141
|
-
{ path: ["field"], message: "Invalid value" },
|
|
142
|
-
]);
|
|
143
|
-
printZodErrorForCli(error);
|
|
144
|
-
expect(consoleSpy).toHaveBeenCalledWith("Validation error:\n field: Invalid value");
|
|
145
|
-
});
|
|
146
|
-
test("outputs multiple errors with indentation", () => {
|
|
147
|
-
const error = createZodError([
|
|
148
|
-
{ path: ["field1"], message: "Error 1" },
|
|
149
|
-
{ path: ["field2"], message: "Error 2" },
|
|
150
|
-
]);
|
|
151
|
-
printZodErrorForCli(error, "CLI arguments");
|
|
152
|
-
expect(consoleSpy).toHaveBeenCalledWith("Error in CLI arguments:\n field1: Error 1\n field2: Error 2");
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
describe("zodErrorToArray", () => {
|
|
156
|
-
test("converts single error to array", () => {
|
|
157
|
-
const error = createZodError([
|
|
158
|
-
{ path: ["field"], message: "Invalid value" },
|
|
159
|
-
]);
|
|
160
|
-
const result = zodErrorToArray(error);
|
|
161
|
-
expect(result).toEqual(["field: Invalid value"]);
|
|
162
|
-
});
|
|
163
|
-
test("converts multiple errors to array", () => {
|
|
164
|
-
const error = createZodError([
|
|
165
|
-
{ path: ["field1"], message: "Error 1" },
|
|
166
|
-
{ path: ["field2"], message: "Error 2" },
|
|
167
|
-
{ path: ["field3"], message: "Error 3" },
|
|
168
|
-
]);
|
|
169
|
-
const result = zodErrorToArray(error);
|
|
170
|
-
expect(result).toEqual([
|
|
171
|
-
"field1: Error 1",
|
|
172
|
-
"field2: Error 2",
|
|
173
|
-
"field3: Error 3",
|
|
174
|
-
]);
|
|
175
|
-
});
|
|
176
|
-
test("returns array preserving order", () => {
|
|
177
|
-
const error = createZodError([
|
|
178
|
-
{ path: ["z"], message: "Z error" },
|
|
179
|
-
{ path: ["a"], message: "A error" },
|
|
180
|
-
{ path: ["m"], message: "M error" },
|
|
181
|
-
]);
|
|
182
|
-
const result = zodErrorToArray(error);
|
|
183
|
-
expect(result).toEqual(["z: Z error", "a: A error", "m: M error"]);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
describe("formatZodErrorForJson", () => {
|
|
187
|
-
test("includes message field", () => {
|
|
188
|
-
const error = createZodError([
|
|
189
|
-
{ path: ["field"], message: "Invalid value" },
|
|
190
|
-
]);
|
|
191
|
-
const result = formatZodErrorForJson(error);
|
|
192
|
-
expect(result.message).toBe("Validation failed");
|
|
193
|
-
});
|
|
194
|
-
test("includes errors array with path, message, code", () => {
|
|
195
|
-
const error = createZodError([
|
|
196
|
-
{ path: ["field"], message: "Invalid value", code: "invalid_type" },
|
|
197
|
-
]);
|
|
198
|
-
const result = formatZodErrorForJson(error);
|
|
199
|
-
expect(result.errors).toHaveLength(1);
|
|
200
|
-
expect(result.errors[0]).toEqual({
|
|
201
|
-
path: ["field"],
|
|
202
|
-
message: "Invalid value",
|
|
203
|
-
code: "invalid_type",
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
test("preserves path as array with numbers and strings", () => {
|
|
207
|
-
const error = createZodError([
|
|
208
|
-
{ path: ["items", 0, "nested", 1, "field"], message: "Error" },
|
|
209
|
-
]);
|
|
210
|
-
const result = formatZodErrorForJson(error);
|
|
211
|
-
expect(result.errors[0].path).toEqual(["items", 0, "nested", 1, "field"]);
|
|
212
|
-
});
|
|
213
|
-
test("handles multiple errors", () => {
|
|
214
|
-
const error = createZodError([
|
|
215
|
-
{ path: ["field1"], message: "Error 1", code: "too_small" },
|
|
216
|
-
{ path: ["field2"], message: "Error 2", code: "invalid_type" },
|
|
217
|
-
]);
|
|
218
|
-
const result = formatZodErrorForJson(error);
|
|
219
|
-
expect(result.errors).toHaveLength(2);
|
|
220
|
-
expect(result.errors[0].code).toBe("too_small");
|
|
221
|
-
expect(result.errors[1].code).toBe("invalid_type");
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
describe("formatUserFriendlyError", () => {
|
|
225
|
-
test("single error returns plain message", () => {
|
|
226
|
-
const error = createZodError([
|
|
227
|
-
{ path: ["username"], message: "Required" },
|
|
228
|
-
]);
|
|
229
|
-
const result = formatUserFriendlyError(error);
|
|
230
|
-
expect(result).toBe("username: Required");
|
|
231
|
-
});
|
|
232
|
-
test("single error with empty path returns message only", () => {
|
|
233
|
-
const error = createZodError([{ path: [], message: "Invalid input" }]);
|
|
234
|
-
const result = formatUserFriendlyError(error);
|
|
235
|
-
expect(result).toBe("Invalid input");
|
|
236
|
-
});
|
|
237
|
-
test("multiple errors returns bulleted list", () => {
|
|
238
|
-
const error = createZodError([
|
|
239
|
-
{ path: ["field1"], message: "Error 1" },
|
|
240
|
-
{ path: ["field2"], message: "Error 2" },
|
|
241
|
-
]);
|
|
242
|
-
const result = formatUserFriendlyError(error);
|
|
243
|
-
expect(result).toBe("Multiple validation errors:\n - field1: Error 1\n - field2: Error 2");
|
|
244
|
-
});
|
|
245
|
-
test("applies field label mapping", () => {
|
|
246
|
-
const error = createZodError([
|
|
247
|
-
{ path: ["serverName"], message: "Required" },
|
|
248
|
-
]);
|
|
249
|
-
const labels = { serverName: "Server Name" };
|
|
250
|
-
const result = formatUserFriendlyError(error, labels);
|
|
251
|
-
expect(result).toBe("Server Name: Required");
|
|
252
|
-
});
|
|
253
|
-
test("applies labels for multiple errors", () => {
|
|
254
|
-
const error = createZodError([
|
|
255
|
-
{ path: ["serverName"], message: "Required" },
|
|
256
|
-
{ path: ["configPath"], message: "Invalid path" },
|
|
257
|
-
]);
|
|
258
|
-
const labels = {
|
|
259
|
-
serverName: "Server Name",
|
|
260
|
-
configPath: "Configuration Path",
|
|
261
|
-
};
|
|
262
|
-
const result = formatUserFriendlyError(error, labels);
|
|
263
|
-
expect(result).toBe("Multiple validation errors:\n - Server Name: Required\n - Configuration Path: Invalid path");
|
|
264
|
-
});
|
|
265
|
-
test("falls back to path when no matching label", () => {
|
|
266
|
-
const error = createZodError([
|
|
267
|
-
{ path: ["unknownField"], message: "Error" },
|
|
268
|
-
]);
|
|
269
|
-
const labels = { otherField: "Other Field" };
|
|
270
|
-
const result = formatUserFriendlyError(error, labels);
|
|
271
|
-
expect(result).toBe("unknownField: Error");
|
|
272
|
-
});
|
|
273
|
-
test("handles nested path with labels", () => {
|
|
274
|
-
const error = createZodError([
|
|
275
|
-
{ path: ["config", "nested"], message: "Invalid" },
|
|
276
|
-
]);
|
|
277
|
-
const labels = { "config.nested": "Nested Config" };
|
|
278
|
-
const result = formatUserFriendlyError(error, labels);
|
|
279
|
-
expect(result).toBe("Nested Config: Invalid");
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
});
|