@bryan-thompson/inspector-assessment-client 1.12.0 → 1.13.1
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/dist/assets/{OAuthCallback-DD8JgGmx.js → OAuthCallback-Dg3beipA.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-CGeg00AP.js → OAuthDebugCallback-zRUPyR0T.js} +1 -1
- package/dist/assets/{index-sUICDw7A.js → index-DtKbQaUh.js} +136 -8
- package/dist/index.html +1 -1
- package/lib/lib/assessmentTypes.d.ts +33 -0
- package/lib/lib/assessmentTypes.d.ts.map +1 -1
- package/lib/lib/assessmentTypes.js +5 -0
- package/lib/lib/policyMapping.d.ts +183 -0
- package/lib/lib/policyMapping.d.ts.map +1 -0
- package/lib/lib/policyMapping.js +442 -0
- package/lib/lib/reportFormatters/MarkdownReportFormatter.d.ts +91 -0
- package/lib/lib/reportFormatters/MarkdownReportFormatter.d.ts.map +1 -0
- package/lib/lib/reportFormatters/MarkdownReportFormatter.js +498 -0
- package/lib/lib/reportFormatters/index.d.ts +50 -0
- package/lib/lib/reportFormatters/index.d.ts.map +1 -0
- package/lib/lib/reportFormatters/index.js +81 -0
- package/lib/lib/securityPatterns.d.ts +3 -3
- package/lib/lib/securityPatterns.d.ts.map +1 -1
- package/lib/lib/securityPatterns.js +129 -4
- package/lib/services/assessment/AssessmentOrchestrator.d.ts +1 -0
- package/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +22 -1
- package/lib/services/assessment/PolicyComplianceGenerator.d.ts +119 -0
- package/lib/services/assessment/PolicyComplianceGenerator.d.ts.map +1 -0
- package/lib/services/assessment/PolicyComplianceGenerator.js +632 -0
- package/lib/services/assessment/modules/ExternalAPIScannerAssessor.d.ts +58 -0
- package/lib/services/assessment/modules/ExternalAPIScannerAssessor.d.ts.map +1 -0
- package/lib/services/assessment/modules/ExternalAPIScannerAssessor.js +248 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +6 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ToolAnnotationAssessor.js +77 -20
- package/lib/services/assessment/modules/index.d.ts +1 -0
- package/lib/services/assessment/modules/index.d.ts.map +1 -1
- package/lib/services/assessment/modules/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Report Formatter
|
|
3
|
+
*
|
|
4
|
+
* Generates human-readable markdown reports from MCP assessment results.
|
|
5
|
+
* Designed for reviewers, auditors, and developers.
|
|
6
|
+
*
|
|
7
|
+
* @module MarkdownReportFormatter
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Formats assessment results as Markdown
|
|
11
|
+
*/
|
|
12
|
+
export class MarkdownReportFormatter {
|
|
13
|
+
options;
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.options = {
|
|
16
|
+
includeDetails: true,
|
|
17
|
+
includeRecommendations: true,
|
|
18
|
+
...options,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Format assessment results as markdown
|
|
23
|
+
*/
|
|
24
|
+
format(assessment) {
|
|
25
|
+
const sections = [];
|
|
26
|
+
// Header
|
|
27
|
+
sections.push(this.formatHeader(assessment));
|
|
28
|
+
// Executive Summary
|
|
29
|
+
sections.push(this.formatExecutiveSummary(assessment));
|
|
30
|
+
// Module Status Table
|
|
31
|
+
sections.push(this.formatModuleStatusTable(assessment));
|
|
32
|
+
// Key Findings
|
|
33
|
+
sections.push(this.formatKeyFindings(assessment));
|
|
34
|
+
// Policy Compliance (if enabled)
|
|
35
|
+
if (this.options.includePolicy && this.options.policyReport) {
|
|
36
|
+
sections.push(this.formatPolicyCompliance(this.options.policyReport));
|
|
37
|
+
}
|
|
38
|
+
// Recommendations
|
|
39
|
+
if (this.options.includeRecommendations) {
|
|
40
|
+
sections.push(this.formatRecommendations(assessment));
|
|
41
|
+
}
|
|
42
|
+
// Detailed Results (if enabled)
|
|
43
|
+
if (this.options.includeDetails) {
|
|
44
|
+
sections.push(this.formatDetailedResults(assessment));
|
|
45
|
+
}
|
|
46
|
+
// Footer
|
|
47
|
+
sections.push(this.formatFooter(assessment));
|
|
48
|
+
return sections.filter(Boolean).join("\n\n---\n\n");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Format header section
|
|
52
|
+
*/
|
|
53
|
+
formatHeader(assessment) {
|
|
54
|
+
const serverName = this.options.serverName || assessment.serverName;
|
|
55
|
+
const statusEmoji = this.getStatusEmoji(assessment.overallStatus);
|
|
56
|
+
return `# MCP Server Assessment Report
|
|
57
|
+
|
|
58
|
+
**Server**: ${serverName}
|
|
59
|
+
**Date**: ${new Date(assessment.assessmentDate).toLocaleString()}
|
|
60
|
+
**Status**: ${statusEmoji} ${assessment.overallStatus}
|
|
61
|
+
**Version**: ${assessment.assessorVersion}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Format executive summary
|
|
65
|
+
*/
|
|
66
|
+
formatExecutiveSummary(assessment) {
|
|
67
|
+
const lines = ["## Executive Summary", ""];
|
|
68
|
+
lines.push("| Metric | Value |");
|
|
69
|
+
lines.push("|--------|-------|");
|
|
70
|
+
// Tools count
|
|
71
|
+
const workingCount = assessment.functionality?.workingTools ?? 0;
|
|
72
|
+
const brokenCount = assessment.functionality?.brokenTools?.length ?? 0;
|
|
73
|
+
lines.push(`| Tools Tested | ${workingCount + brokenCount} |`);
|
|
74
|
+
// Success rate
|
|
75
|
+
const successRate = assessment.functionality?.coveragePercentage ??
|
|
76
|
+
(workingCount > 0
|
|
77
|
+
? Math.round((workingCount / (workingCount + brokenCount)) * 100)
|
|
78
|
+
: 0);
|
|
79
|
+
lines.push(`| Tool Success Rate | ${successRate}% |`);
|
|
80
|
+
// Vulnerabilities
|
|
81
|
+
const vulnCount = assessment.security?.vulnerabilities?.length ?? 0;
|
|
82
|
+
lines.push(`| Security Vulnerabilities | ${vulnCount === 0 ? "None" : vulnCount} |`);
|
|
83
|
+
// Total tests
|
|
84
|
+
lines.push(`| Total Tests Run | ${assessment.totalTestsRun} |`);
|
|
85
|
+
// Execution time
|
|
86
|
+
const execTime = (assessment.executionTime / 1000).toFixed(1);
|
|
87
|
+
lines.push(`| Execution Time | ${execTime}s |`);
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format module status table
|
|
92
|
+
*/
|
|
93
|
+
formatModuleStatusTable(assessment) {
|
|
94
|
+
const lines = ["## Module Status", ""];
|
|
95
|
+
lines.push("| Module | Status | Key Finding |");
|
|
96
|
+
lines.push("|--------|--------|-------------|");
|
|
97
|
+
// Core modules
|
|
98
|
+
lines.push(this.formatModuleRow("Functionality", assessment.functionality?.status, this.getFunctionalityFinding(assessment)));
|
|
99
|
+
lines.push(this.formatModuleRow("Security", assessment.security?.status, this.getSecurityFinding(assessment)));
|
|
100
|
+
lines.push(this.formatModuleRow("Error Handling", assessment.errorHandling?.status, this.getErrorHandlingFinding(assessment)));
|
|
101
|
+
lines.push(this.formatModuleRow("Documentation", assessment.documentation?.status, this.getDocumentationFinding(assessment)));
|
|
102
|
+
lines.push(this.formatModuleRow("Usability", assessment.usability?.status, this.getUsabilityFinding(assessment)));
|
|
103
|
+
// Extended modules (if present)
|
|
104
|
+
if (assessment.mcpSpecCompliance) {
|
|
105
|
+
lines.push(this.formatModuleRow("MCP Spec Compliance", assessment.mcpSpecCompliance.status, this.getMCPSpecFinding(assessment)));
|
|
106
|
+
}
|
|
107
|
+
if (assessment.aupCompliance) {
|
|
108
|
+
lines.push(this.formatModuleRow("AUP Compliance", assessment.aupCompliance.status, this.getAUPFinding(assessment)));
|
|
109
|
+
}
|
|
110
|
+
if (assessment.toolAnnotations) {
|
|
111
|
+
lines.push(this.formatModuleRow("Tool Annotations", assessment.toolAnnotations.status, this.getAnnotationFinding(assessment)));
|
|
112
|
+
}
|
|
113
|
+
return lines.join("\n");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Format a single module row
|
|
117
|
+
*/
|
|
118
|
+
formatModuleRow(name, status, finding) {
|
|
119
|
+
const statusEmoji = this.getStatusEmoji(status);
|
|
120
|
+
const displayStatus = status || "NOT_RUN";
|
|
121
|
+
return `| ${name} | ${statusEmoji} ${displayStatus} | ${finding} |`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Format key findings section
|
|
125
|
+
*/
|
|
126
|
+
formatKeyFindings(assessment) {
|
|
127
|
+
const lines = ["## Key Findings", ""];
|
|
128
|
+
// Critical Issues
|
|
129
|
+
const criticalIssues = this.getCriticalIssues(assessment);
|
|
130
|
+
if (criticalIssues.length > 0) {
|
|
131
|
+
lines.push("### Critical Issues");
|
|
132
|
+
for (const issue of criticalIssues) {
|
|
133
|
+
lines.push(`- ${issue}`);
|
|
134
|
+
}
|
|
135
|
+
lines.push("");
|
|
136
|
+
}
|
|
137
|
+
// Warnings
|
|
138
|
+
const warnings = this.getWarnings(assessment);
|
|
139
|
+
if (warnings.length > 0) {
|
|
140
|
+
lines.push("### Warnings");
|
|
141
|
+
for (const warning of warnings) {
|
|
142
|
+
lines.push(`- ${warning}`);
|
|
143
|
+
}
|
|
144
|
+
lines.push("");
|
|
145
|
+
}
|
|
146
|
+
// Positive findings
|
|
147
|
+
const positives = this.getPositiveFindings(assessment);
|
|
148
|
+
if (positives.length > 0) {
|
|
149
|
+
lines.push("### Positive Findings");
|
|
150
|
+
for (const positive of positives) {
|
|
151
|
+
lines.push(`- ${positive}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return lines.join("\n");
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Format policy compliance section
|
|
158
|
+
*/
|
|
159
|
+
formatPolicyCompliance(report) {
|
|
160
|
+
const lines = ["## Policy Compliance", ""];
|
|
161
|
+
// Summary
|
|
162
|
+
lines.push("### Compliance Summary");
|
|
163
|
+
lines.push("");
|
|
164
|
+
lines.push("| Metric | Value |");
|
|
165
|
+
lines.push("|--------|-------|");
|
|
166
|
+
lines.push(`| Total Requirements | ${report.summary.totalRequirements} |`);
|
|
167
|
+
lines.push(`| Passed | ${report.summary.passed} |`);
|
|
168
|
+
lines.push(`| Failed | ${report.summary.failed} |`);
|
|
169
|
+
lines.push(`| Needs Review | ${report.summary.needsReview} |`);
|
|
170
|
+
lines.push(`| Compliance Score | ${report.summary.complianceScore}% |`);
|
|
171
|
+
lines.push(`| Overall Status | ${this.getStatusEmoji(this.complianceToAssessment(report.summary.overallStatus))} ${report.summary.overallStatus} |`);
|
|
172
|
+
lines.push("");
|
|
173
|
+
// Category breakdown
|
|
174
|
+
lines.push("### By Category");
|
|
175
|
+
lines.push("");
|
|
176
|
+
lines.push("| Category | Status | Passed | Failed |");
|
|
177
|
+
lines.push("|----------|--------|--------|--------|");
|
|
178
|
+
for (const [, category] of Object.entries(report.byCategory)) {
|
|
179
|
+
const statusEmoji = this.getComplianceStatusEmoji(category.status);
|
|
180
|
+
lines.push(`| ${category.categoryName} | ${statusEmoji} ${category.status} | ${category.passed}/${category.total} | ${category.failed} |`);
|
|
181
|
+
}
|
|
182
|
+
lines.push("");
|
|
183
|
+
// Critical issues from policy
|
|
184
|
+
if (report.criticalIssues.length > 0) {
|
|
185
|
+
lines.push("### Critical Policy Issues");
|
|
186
|
+
lines.push("");
|
|
187
|
+
for (const issue of report.criticalIssues.slice(0, 5)) {
|
|
188
|
+
lines.push(`- **${issue.requirement.id}**: ${issue.requirement.name} - ${issue.status}`);
|
|
189
|
+
if (issue.recommendation) {
|
|
190
|
+
lines.push(` - ${issue.recommendation}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
lines.push("");
|
|
194
|
+
}
|
|
195
|
+
// Action items
|
|
196
|
+
if (report.actionItems.length > 0) {
|
|
197
|
+
lines.push("### Action Items");
|
|
198
|
+
lines.push("");
|
|
199
|
+
for (const item of report.actionItems) {
|
|
200
|
+
lines.push(`- ${item}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return lines.join("\n");
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Format recommendations section
|
|
207
|
+
*/
|
|
208
|
+
formatRecommendations(assessment) {
|
|
209
|
+
const lines = ["## Recommendations", ""];
|
|
210
|
+
const recs = assessment.recommendations || [];
|
|
211
|
+
if (recs.length === 0) {
|
|
212
|
+
lines.push("No recommendations at this time.");
|
|
213
|
+
return lines.join("\n");
|
|
214
|
+
}
|
|
215
|
+
// Group by priority
|
|
216
|
+
const high = recs.filter((r) => r.toLowerCase().includes("critical") ||
|
|
217
|
+
r.toLowerCase().includes("security"));
|
|
218
|
+
const medium = recs.filter((r) => !high.includes(r) &&
|
|
219
|
+
(r.toLowerCase().includes("required") ||
|
|
220
|
+
r.toLowerCase().includes("must")));
|
|
221
|
+
const low = recs.filter((r) => !high.includes(r) && !medium.includes(r));
|
|
222
|
+
if (high.length > 0) {
|
|
223
|
+
lines.push("### High Priority");
|
|
224
|
+
for (const rec of high) {
|
|
225
|
+
lines.push(`1. ${rec}`);
|
|
226
|
+
}
|
|
227
|
+
lines.push("");
|
|
228
|
+
}
|
|
229
|
+
if (medium.length > 0) {
|
|
230
|
+
lines.push("### Medium Priority");
|
|
231
|
+
for (const rec of medium) {
|
|
232
|
+
lines.push(`1. ${rec}`);
|
|
233
|
+
}
|
|
234
|
+
lines.push("");
|
|
235
|
+
}
|
|
236
|
+
if (low.length > 0) {
|
|
237
|
+
lines.push("### Other");
|
|
238
|
+
for (const rec of low.slice(0, 5)) {
|
|
239
|
+
lines.push(`1. ${rec}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return lines.join("\n");
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Format detailed results section
|
|
246
|
+
*/
|
|
247
|
+
formatDetailedResults(assessment) {
|
|
248
|
+
const lines = ["## Detailed Results", ""];
|
|
249
|
+
// Functionality details
|
|
250
|
+
if (assessment.functionality) {
|
|
251
|
+
lines.push("### Functionality");
|
|
252
|
+
lines.push("");
|
|
253
|
+
lines.push(`**Status**: ${assessment.functionality.status}`);
|
|
254
|
+
lines.push("");
|
|
255
|
+
const workingCount = assessment.functionality.workingTools ?? 0;
|
|
256
|
+
const brokenTools = assessment.functionality.brokenTools || [];
|
|
257
|
+
if (workingCount > 0) {
|
|
258
|
+
lines.push(`**Working Tools**: ${workingCount} tool(s) functioning correctly`);
|
|
259
|
+
}
|
|
260
|
+
if (brokenTools.length > 0) {
|
|
261
|
+
lines.push("");
|
|
262
|
+
lines.push(`**Broken Tools** (${brokenTools.length}):`);
|
|
263
|
+
for (const tool of brokenTools.slice(0, 5)) {
|
|
264
|
+
lines.push(`- ${tool}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
lines.push("");
|
|
268
|
+
}
|
|
269
|
+
// Security details
|
|
270
|
+
if (assessment.security) {
|
|
271
|
+
lines.push("### Security");
|
|
272
|
+
lines.push("");
|
|
273
|
+
lines.push(`**Status**: ${assessment.security.status}`);
|
|
274
|
+
lines.push("");
|
|
275
|
+
const vulns = assessment.security.vulnerabilities || [];
|
|
276
|
+
if (vulns.length > 0) {
|
|
277
|
+
lines.push(`**Vulnerabilities Found** (${vulns.length}):`);
|
|
278
|
+
for (const vuln of vulns.slice(0, 5)) {
|
|
279
|
+
lines.push(`- ${vuln}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
lines.push("No vulnerabilities detected.");
|
|
284
|
+
}
|
|
285
|
+
lines.push("");
|
|
286
|
+
}
|
|
287
|
+
// Tool Annotations details
|
|
288
|
+
if (assessment.toolAnnotations) {
|
|
289
|
+
lines.push("### Tool Annotations");
|
|
290
|
+
lines.push("");
|
|
291
|
+
lines.push(`**Status**: ${assessment.toolAnnotations.status}`);
|
|
292
|
+
lines.push(`**Coverage**: ${assessment.toolAnnotations.annotatedCount} annotated, ${assessment.toolAnnotations.missingAnnotationsCount} missing`);
|
|
293
|
+
if (assessment.toolAnnotations.annotationSources) {
|
|
294
|
+
const sources = assessment.toolAnnotations.annotationSources;
|
|
295
|
+
lines.push("");
|
|
296
|
+
lines.push("**Annotation Sources**:");
|
|
297
|
+
lines.push(`- MCP Protocol: ${sources.mcp}`);
|
|
298
|
+
lines.push(`- Source Code: ${sources.sourceCode}`);
|
|
299
|
+
lines.push(`- Inferred: ${sources.inferred}`);
|
|
300
|
+
lines.push(`- None: ${sources.none}`);
|
|
301
|
+
}
|
|
302
|
+
lines.push("");
|
|
303
|
+
}
|
|
304
|
+
return lines.join("\n");
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Format footer section
|
|
308
|
+
*/
|
|
309
|
+
formatFooter(assessment) {
|
|
310
|
+
return `## Report Metadata
|
|
311
|
+
|
|
312
|
+
- **Generated**: ${new Date().toISOString()}
|
|
313
|
+
- **Assessor Version**: ${assessment.assessorVersion}
|
|
314
|
+
- **MCP Protocol Version**: ${assessment.mcpProtocolVersion || "N/A"}
|
|
315
|
+
- **Total Tests**: ${assessment.totalTestsRun}
|
|
316
|
+
- **Execution Time**: ${(assessment.executionTime / 1000).toFixed(2)}s
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
*Generated by MCP Inspector Assessment CLI*`;
|
|
321
|
+
}
|
|
322
|
+
// ============================================================================
|
|
323
|
+
// Helper Methods
|
|
324
|
+
// ============================================================================
|
|
325
|
+
getStatusEmoji(status) {
|
|
326
|
+
switch (status) {
|
|
327
|
+
case "PASS":
|
|
328
|
+
return "✅";
|
|
329
|
+
case "FAIL":
|
|
330
|
+
return "❌";
|
|
331
|
+
case "NEED_MORE_INFO":
|
|
332
|
+
return "⚠️";
|
|
333
|
+
default:
|
|
334
|
+
return "❓";
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
getComplianceStatusEmoji(status) {
|
|
338
|
+
switch (status) {
|
|
339
|
+
case "PASS":
|
|
340
|
+
return "✅";
|
|
341
|
+
case "FAIL":
|
|
342
|
+
return "❌";
|
|
343
|
+
case "FLAG":
|
|
344
|
+
return "🚩";
|
|
345
|
+
case "REVIEW":
|
|
346
|
+
return "🔍";
|
|
347
|
+
case "NOT_APPLICABLE":
|
|
348
|
+
return "➖";
|
|
349
|
+
case "NOT_TESTED":
|
|
350
|
+
return "❓";
|
|
351
|
+
default:
|
|
352
|
+
return "❓";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
complianceToAssessment(status) {
|
|
356
|
+
switch (status) {
|
|
357
|
+
case "COMPLIANT":
|
|
358
|
+
return "PASS";
|
|
359
|
+
case "NON_COMPLIANT":
|
|
360
|
+
return "FAIL";
|
|
361
|
+
case "NEEDS_REVIEW":
|
|
362
|
+
return "NEED_MORE_INFO";
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
getFunctionalityFinding(assessment) {
|
|
366
|
+
const working = assessment.functionality?.workingTools ?? 0;
|
|
367
|
+
const broken = assessment.functionality?.brokenTools?.length ?? 0;
|
|
368
|
+
const total = working + broken;
|
|
369
|
+
if (total === 0)
|
|
370
|
+
return "No tools tested";
|
|
371
|
+
if (broken === 0)
|
|
372
|
+
return `All ${total} tools working`;
|
|
373
|
+
return `${working}/${total} tools working, ${broken} failing`;
|
|
374
|
+
}
|
|
375
|
+
getSecurityFinding(assessment) {
|
|
376
|
+
const vulns = assessment.security?.vulnerabilities?.length ?? 0;
|
|
377
|
+
if (vulns === 0)
|
|
378
|
+
return "No vulnerabilities detected";
|
|
379
|
+
return `${vulns} vulnerability(ies) detected`;
|
|
380
|
+
}
|
|
381
|
+
getErrorHandlingFinding(assessment) {
|
|
382
|
+
const metrics = assessment.errorHandling?.metrics;
|
|
383
|
+
if (!metrics)
|
|
384
|
+
return "Not tested";
|
|
385
|
+
const passRate = metrics.validationCoverage?.overallPassRate ?? 0;
|
|
386
|
+
return `${passRate}% pass rate`;
|
|
387
|
+
}
|
|
388
|
+
getDocumentationFinding(assessment) {
|
|
389
|
+
const status = assessment.documentation?.status;
|
|
390
|
+
if (!status)
|
|
391
|
+
return "Not assessed";
|
|
392
|
+
return status === "PASS"
|
|
393
|
+
? "Documentation adequate"
|
|
394
|
+
: "Documentation needs improvement";
|
|
395
|
+
}
|
|
396
|
+
getUsabilityFinding(assessment) {
|
|
397
|
+
const status = assessment.usability?.status;
|
|
398
|
+
if (!status)
|
|
399
|
+
return "Not assessed";
|
|
400
|
+
return status === "PASS" ? "Good usability" : "Usability issues found";
|
|
401
|
+
}
|
|
402
|
+
getMCPSpecFinding(assessment) {
|
|
403
|
+
const status = assessment.mcpSpecCompliance?.status;
|
|
404
|
+
if (!status)
|
|
405
|
+
return "Not tested";
|
|
406
|
+
return status === "PASS" ? "Protocol compliant" : "Protocol issues found";
|
|
407
|
+
}
|
|
408
|
+
getAUPFinding(assessment) {
|
|
409
|
+
const violations = assessment.aupCompliance?.violations?.length ?? 0;
|
|
410
|
+
if (violations === 0)
|
|
411
|
+
return "No AUP violations";
|
|
412
|
+
return `${violations} AUP violation(s) detected`;
|
|
413
|
+
}
|
|
414
|
+
getAnnotationFinding(assessment) {
|
|
415
|
+
const annotated = assessment.toolAnnotations?.annotatedCount ?? 0;
|
|
416
|
+
const missing = assessment.toolAnnotations?.missingAnnotationsCount ?? 0;
|
|
417
|
+
if (annotated === 0 && missing === 0)
|
|
418
|
+
return "No tools assessed";
|
|
419
|
+
if (missing === 0)
|
|
420
|
+
return `All ${annotated} tools annotated`;
|
|
421
|
+
return `${annotated} annotated, ${missing} missing`;
|
|
422
|
+
}
|
|
423
|
+
getCriticalIssues(assessment) {
|
|
424
|
+
const issues = [];
|
|
425
|
+
// Security vulnerabilities
|
|
426
|
+
const vulns = assessment.security?.vulnerabilities || [];
|
|
427
|
+
if (vulns.length > 0) {
|
|
428
|
+
issues.push(`${vulns.length} security vulnerability(ies) detected`);
|
|
429
|
+
}
|
|
430
|
+
// AUP violations
|
|
431
|
+
const aupViolations = assessment.aupCompliance?.violations || [];
|
|
432
|
+
const critical = aupViolations.filter((v) => typeof v === "object" &&
|
|
433
|
+
v !== null &&
|
|
434
|
+
v.severity === "CRITICAL");
|
|
435
|
+
if (critical.length > 0) {
|
|
436
|
+
issues.push(`${critical.length} critical AUP violation(s)`);
|
|
437
|
+
}
|
|
438
|
+
// Broken tools
|
|
439
|
+
const broken = assessment.functionality?.brokenTools || [];
|
|
440
|
+
if (broken.length > 0) {
|
|
441
|
+
issues.push(`${broken.length} tool(s) not functioning correctly`);
|
|
442
|
+
}
|
|
443
|
+
return issues;
|
|
444
|
+
}
|
|
445
|
+
getWarnings(assessment) {
|
|
446
|
+
const warnings = [];
|
|
447
|
+
// Missing annotations
|
|
448
|
+
const missing = assessment.toolAnnotations?.missingAnnotationsCount ?? 0;
|
|
449
|
+
if (missing > 0) {
|
|
450
|
+
warnings.push(`${missing} tool(s) missing required annotations`);
|
|
451
|
+
}
|
|
452
|
+
// Misaligned annotations
|
|
453
|
+
const misaligned = assessment.toolAnnotations?.misalignedAnnotationsCount ?? 0;
|
|
454
|
+
if (misaligned > 0) {
|
|
455
|
+
warnings.push(`${misaligned} tool(s) with potentially misaligned annotations`);
|
|
456
|
+
}
|
|
457
|
+
// Error handling issues
|
|
458
|
+
const errorMetrics = assessment.errorHandling?.metrics;
|
|
459
|
+
const passRate = errorMetrics?.validationCoverage?.overallPassRate ?? 100;
|
|
460
|
+
if (errorMetrics && passRate < 80) {
|
|
461
|
+
warnings.push(`Low error handling pass rate (${passRate}%)`);
|
|
462
|
+
}
|
|
463
|
+
return warnings;
|
|
464
|
+
}
|
|
465
|
+
getPositiveFindings(assessment) {
|
|
466
|
+
const positives = [];
|
|
467
|
+
// All tools working
|
|
468
|
+
const working = assessment.functionality?.workingTools ?? 0;
|
|
469
|
+
const broken = assessment.functionality?.brokenTools?.length ?? 0;
|
|
470
|
+
if (working > 0 && broken === 0) {
|
|
471
|
+
positives.push(`All ${working} tools functioning correctly`);
|
|
472
|
+
}
|
|
473
|
+
// No security vulnerabilities
|
|
474
|
+
const vulns = assessment.security?.vulnerabilities?.length ?? 0;
|
|
475
|
+
if (vulns === 0) {
|
|
476
|
+
positives.push("No security vulnerabilities detected");
|
|
477
|
+
}
|
|
478
|
+
// Full annotation coverage
|
|
479
|
+
const annotated = assessment.toolAnnotations?.annotatedCount ?? 0;
|
|
480
|
+
const missingAnnotations = assessment.toolAnnotations?.missingAnnotationsCount ?? 0;
|
|
481
|
+
if (annotated > 0 && missingAnnotations === 0) {
|
|
482
|
+
positives.push(`All ${annotated} tools have required annotations`);
|
|
483
|
+
}
|
|
484
|
+
// Good error handling
|
|
485
|
+
const errorMetrics2 = assessment.errorHandling?.metrics;
|
|
486
|
+
const passRate2 = errorMetrics2?.validationCoverage?.overallPassRate ?? 0;
|
|
487
|
+
if (errorMetrics2 && passRate2 >= 90) {
|
|
488
|
+
positives.push("Excellent error handling coverage");
|
|
489
|
+
}
|
|
490
|
+
return positives;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Create a markdown formatter with options
|
|
495
|
+
*/
|
|
496
|
+
export function createMarkdownFormatter(options) {
|
|
497
|
+
return new MarkdownReportFormatter(options);
|
|
498
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report Formatters
|
|
3
|
+
*
|
|
4
|
+
* Factory module for creating report formatters in different output formats.
|
|
5
|
+
*
|
|
6
|
+
* @module reportFormatters
|
|
7
|
+
*/
|
|
8
|
+
import type { MCPDirectoryAssessment } from "../assessmentTypes.js";
|
|
9
|
+
import type { PolicyComplianceReport } from "../policyMapping.js";
|
|
10
|
+
import { MarkdownReportFormatter, type MarkdownReportOptions } from "./MarkdownReportFormatter.js";
|
|
11
|
+
/**
|
|
12
|
+
* Supported output formats
|
|
13
|
+
*/
|
|
14
|
+
export type ReportFormat = "json" | "markdown";
|
|
15
|
+
/**
|
|
16
|
+
* Base formatter interface
|
|
17
|
+
*/
|
|
18
|
+
export interface ReportFormatter {
|
|
19
|
+
/** Format the assessment results */
|
|
20
|
+
format(assessment: MCPDirectoryAssessment): string;
|
|
21
|
+
/** Get the file extension for this format */
|
|
22
|
+
getFileExtension(): string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Options for creating a formatter
|
|
26
|
+
*/
|
|
27
|
+
export interface FormatterOptions {
|
|
28
|
+
/** Output format */
|
|
29
|
+
format: ReportFormat;
|
|
30
|
+
/** Include policy compliance mapping */
|
|
31
|
+
includePolicyMapping?: boolean;
|
|
32
|
+
/** Policy compliance report (generated separately) */
|
|
33
|
+
policyReport?: PolicyComplianceReport;
|
|
34
|
+
/** Server name override */
|
|
35
|
+
serverName?: string;
|
|
36
|
+
/** Include detailed results */
|
|
37
|
+
includeDetails?: boolean;
|
|
38
|
+
/** Pretty print JSON */
|
|
39
|
+
prettyPrint?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a formatter based on options
|
|
43
|
+
*/
|
|
44
|
+
export declare function createFormatter(options: FormatterOptions): ReportFormatter;
|
|
45
|
+
/**
|
|
46
|
+
* Quick format utility
|
|
47
|
+
*/
|
|
48
|
+
export declare function formatAssessmentReport(assessment: MCPDirectoryAssessment, format?: ReportFormat, options?: Partial<FormatterOptions>): string;
|
|
49
|
+
export { MarkdownReportFormatter, type MarkdownReportOptions };
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/reportFormatters/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,EAC3B,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,MAAM,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,CAAC;IACnD,6CAA6C;IAC7C,gBAAgB,IAAI,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oBAAoB;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,sDAAsD;IACtD,YAAY,CAAC,EAAE,sBAAsB,CAAC;IACtC,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA8DD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAQ1E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,sBAAsB,EAClC,MAAM,GAAE,YAAqB,EAC7B,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAClC,MAAM,CAMR;AAGD,OAAO,EAAE,uBAAuB,EAAE,KAAK,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report Formatters
|
|
3
|
+
*
|
|
4
|
+
* Factory module for creating report formatters in different output formats.
|
|
5
|
+
*
|
|
6
|
+
* @module reportFormatters
|
|
7
|
+
*/
|
|
8
|
+
import { MarkdownReportFormatter, } from "./MarkdownReportFormatter.js";
|
|
9
|
+
/**
|
|
10
|
+
* JSON formatter implementation
|
|
11
|
+
*/
|
|
12
|
+
class JSONReportFormatter {
|
|
13
|
+
options;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
format(assessment) {
|
|
18
|
+
const output = {
|
|
19
|
+
...assessment,
|
|
20
|
+
};
|
|
21
|
+
// Add policy compliance if included
|
|
22
|
+
if (this.options.includePolicyMapping && this.options.policyReport) {
|
|
23
|
+
output.policyCompliance = this.options.policyReport;
|
|
24
|
+
}
|
|
25
|
+
// Override server name if provided
|
|
26
|
+
if (this.options.serverName) {
|
|
27
|
+
output.serverName = this.options.serverName;
|
|
28
|
+
}
|
|
29
|
+
const indent = this.options.prettyPrint !== false ? 2 : undefined;
|
|
30
|
+
return JSON.stringify(output, null, indent);
|
|
31
|
+
}
|
|
32
|
+
getFileExtension() {
|
|
33
|
+
return ".json";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Markdown formatter wrapper
|
|
38
|
+
*/
|
|
39
|
+
class MarkdownFormatterWrapper {
|
|
40
|
+
formatter;
|
|
41
|
+
constructor(options) {
|
|
42
|
+
const mdOptions = {
|
|
43
|
+
includePolicy: options.includePolicyMapping,
|
|
44
|
+
policyReport: options.policyReport,
|
|
45
|
+
includeDetails: options.includeDetails ?? true,
|
|
46
|
+
includeRecommendations: true,
|
|
47
|
+
serverName: options.serverName,
|
|
48
|
+
};
|
|
49
|
+
this.formatter = new MarkdownReportFormatter(mdOptions);
|
|
50
|
+
}
|
|
51
|
+
format(assessment) {
|
|
52
|
+
return this.formatter.format(assessment);
|
|
53
|
+
}
|
|
54
|
+
getFileExtension() {
|
|
55
|
+
return ".md";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a formatter based on options
|
|
60
|
+
*/
|
|
61
|
+
export function createFormatter(options) {
|
|
62
|
+
switch (options.format) {
|
|
63
|
+
case "markdown":
|
|
64
|
+
return new MarkdownFormatterWrapper(options);
|
|
65
|
+
case "json":
|
|
66
|
+
default:
|
|
67
|
+
return new JSONReportFormatter(options);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Quick format utility
|
|
72
|
+
*/
|
|
73
|
+
export function formatAssessmentReport(assessment, format = "json", options) {
|
|
74
|
+
const formatter = createFormatter({
|
|
75
|
+
format,
|
|
76
|
+
...options,
|
|
77
|
+
});
|
|
78
|
+
return formatter.format(assessment);
|
|
79
|
+
}
|
|
80
|
+
// Re-export types and classes
|
|
81
|
+
export { MarkdownReportFormatter };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Backend API Security Patterns
|
|
3
|
-
* Tests MCP server API security with
|
|
3
|
+
* Tests MCP server API security with 16 focused patterns
|
|
4
4
|
*
|
|
5
5
|
* Architecture: Attack-Type with Specific Payloads
|
|
6
6
|
* - Critical Injection (4 patterns): Command, Calculator, SQL, Path Traversal
|
|
7
7
|
* - Input Validation (3 patterns): Type Safety, Boundary Testing, Required Fields
|
|
8
8
|
* - Protocol Compliance (2 patterns): MCP Error Format, Timeout Handling
|
|
9
|
-
* - Tool-Specific Vulnerabilities (
|
|
9
|
+
* - Tool-Specific Vulnerabilities (7 patterns): Indirect Injection, Unicode Bypass, Nested Injection, Package Squatting, Data Exfiltration, Configuration Drift, Tool Shadowing
|
|
10
10
|
*
|
|
11
11
|
* Scope: Backend API Security ONLY
|
|
12
12
|
* - Tests structured data inputs to API endpoints
|
|
@@ -38,7 +38,7 @@ export interface AttackPattern {
|
|
|
38
38
|
* BACKEND API SECURITY PATTERNS
|
|
39
39
|
* ========================================
|
|
40
40
|
*
|
|
41
|
-
*
|
|
41
|
+
* 16 focused patterns for MCP server API security
|
|
42
42
|
*/
|
|
43
43
|
export declare const SECURITY_ATTACK_PATTERNS: AttackPattern[];
|
|
44
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"securityPatterns.d.ts","sourceRoot":"","sources":["../../src/lib/securityPatterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"securityPatterns.d.ts","sourceRoot":"","sources":["../../src/lib/securityPatterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,aAAa,EA+hBnD,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,eAAe,EAAE,CAQnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,EAAE,CAEtD;AAED;;GAEG;AACH,wBAAgB,oBAAoB;;;;;;;;EA8BnC"}
|