@bryan-thompson/inspector-assessment-client 1.11.1 → 1.13.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/dist/assets/{OAuthCallback-DA2koy6X.js → OAuthCallback-D8KW6pFf.js} +1 -1
- package/dist/assets/{OAuthDebugCallback-Bx60PQTT.js → OAuthDebugCallback-D15nNAOl.js} +1 -1
- package/dist/assets/{index-kJ0jPd4m.js → index-cVkEgqCc.js} +130 -5
- package/dist/index.html +1 -1
- package/lib/lib/assessmentTypes.d.ts +72 -1
- package/lib/lib/assessmentTypes.d.ts.map +1 -1
- 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.map +1 -1
- package/lib/services/assessment/AssessmentOrchestrator.js +8 -0
- 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/config/annotationPatterns.d.ts +70 -0
- package/lib/services/assessment/config/annotationPatterns.d.ts.map +1 -0
- package/lib/services/assessment/config/annotationPatterns.js +305 -0
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +22 -2
- package/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -1
- package/lib/services/assessment/modules/ToolAnnotationAssessor.js +289 -152
- package/package.json +1 -1
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Compliance Generator
|
|
3
|
+
*
|
|
4
|
+
* Maps MCP Inspector assessment results to Anthropic's Software Directory
|
|
5
|
+
* Policy requirements (30 total). Generates a structured compliance report
|
|
6
|
+
* that can be used for directory submission review.
|
|
7
|
+
*
|
|
8
|
+
* @module PolicyComplianceGenerator
|
|
9
|
+
*/
|
|
10
|
+
import { ANTHROPIC_POLICY_REQUIREMENTS, getCategoryDisplayName, } from "../../lib/policyMapping.js";
|
|
11
|
+
/**
|
|
12
|
+
* Generator for policy compliance reports
|
|
13
|
+
*/
|
|
14
|
+
export class PolicyComplianceGenerator {
|
|
15
|
+
version;
|
|
16
|
+
constructor(version = "1.0.0") {
|
|
17
|
+
this.version = version;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generate a full policy compliance report from assessment results
|
|
21
|
+
*/
|
|
22
|
+
generate(assessment, serverName) {
|
|
23
|
+
const results = this.evaluateAllRequirements(assessment);
|
|
24
|
+
const byCategory = this.groupByCategory(results);
|
|
25
|
+
const summary = this.calculateSummary(results);
|
|
26
|
+
const criticalIssues = this.identifyCriticalIssues(results);
|
|
27
|
+
const actionItems = this.generateActionItems(results);
|
|
28
|
+
return {
|
|
29
|
+
serverName: serverName || assessment.serverName,
|
|
30
|
+
generatedAt: new Date().toISOString(),
|
|
31
|
+
assessorVersion: this.version,
|
|
32
|
+
summary,
|
|
33
|
+
byCategory,
|
|
34
|
+
criticalIssues,
|
|
35
|
+
actionItems,
|
|
36
|
+
sourceAssessment: {
|
|
37
|
+
totalTestsRun: assessment.totalTestsRun,
|
|
38
|
+
executionTime: assessment.executionTime,
|
|
39
|
+
modulesRun: this.getRunModules(assessment),
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Evaluate all 30 policy requirements against assessment results
|
|
45
|
+
*/
|
|
46
|
+
evaluateAllRequirements(assessment) {
|
|
47
|
+
return ANTHROPIC_POLICY_REQUIREMENTS.map((req) => this.evaluateRequirement(req, assessment));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Evaluate a single policy requirement
|
|
51
|
+
*/
|
|
52
|
+
evaluateRequirement(requirement, assessment) {
|
|
53
|
+
const evidence = [];
|
|
54
|
+
const moduleResults = [];
|
|
55
|
+
// Collect evidence from each source module
|
|
56
|
+
for (const moduleName of requirement.moduleSource) {
|
|
57
|
+
const moduleData = this.getModuleData(assessment, moduleName);
|
|
58
|
+
if (moduleData) {
|
|
59
|
+
const findings = this.extractRelevantFindings(moduleName, moduleData, requirement.id);
|
|
60
|
+
moduleResults.push({
|
|
61
|
+
module: moduleName,
|
|
62
|
+
status: moduleData.status || "UNKNOWN",
|
|
63
|
+
relevantFindings: findings,
|
|
64
|
+
});
|
|
65
|
+
evidence.push(...findings);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Determine compliance status
|
|
69
|
+
const status = this.determineComplianceStatus(requirement, moduleResults, evidence);
|
|
70
|
+
// Generate recommendation if needed
|
|
71
|
+
const recommendation = status !== "PASS" && status !== "NOT_APPLICABLE"
|
|
72
|
+
? this.generateRecommendation(requirement, status, evidence)
|
|
73
|
+
: undefined;
|
|
74
|
+
// Determine if manual review is required
|
|
75
|
+
const manualReviewRequired = !requirement.automatable ||
|
|
76
|
+
status === "FLAG" ||
|
|
77
|
+
status === "REVIEW" ||
|
|
78
|
+
status === "NOT_TESTED";
|
|
79
|
+
return {
|
|
80
|
+
requirement,
|
|
81
|
+
status,
|
|
82
|
+
evidence,
|
|
83
|
+
moduleResults,
|
|
84
|
+
recommendation,
|
|
85
|
+
manualReviewRequired,
|
|
86
|
+
manualReviewGuidance: manualReviewRequired
|
|
87
|
+
? this.getManualReviewGuidance(requirement, status)
|
|
88
|
+
: undefined,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get module data from assessment by module name
|
|
93
|
+
*/
|
|
94
|
+
getModuleData(assessment, moduleName) {
|
|
95
|
+
const moduleMap = {
|
|
96
|
+
aupCompliance: assessment.aupCompliance,
|
|
97
|
+
security: assessment.security,
|
|
98
|
+
functionality: assessment.functionality,
|
|
99
|
+
errorHandling: assessment.errorHandling,
|
|
100
|
+
usability: assessment.usability,
|
|
101
|
+
documentation: assessment.documentation,
|
|
102
|
+
mcpSpecCompliance: assessment.mcpSpecCompliance,
|
|
103
|
+
toolAnnotations: assessment.toolAnnotations,
|
|
104
|
+
prohibitedLibraries: assessment.prohibitedLibraries,
|
|
105
|
+
manifestValidation: assessment.manifestValidation,
|
|
106
|
+
portability: assessment.portability,
|
|
107
|
+
};
|
|
108
|
+
const data = moduleMap[moduleName];
|
|
109
|
+
if (data && typeof data === "object") {
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extract relevant findings from a module for a specific requirement
|
|
116
|
+
*/
|
|
117
|
+
extractRelevantFindings(moduleName, moduleData, requirementId) {
|
|
118
|
+
const findings = [];
|
|
119
|
+
// Extract based on module type
|
|
120
|
+
switch (moduleName) {
|
|
121
|
+
case "aupCompliance":
|
|
122
|
+
findings.push(...this.extractAUPFindings(moduleData, requirementId));
|
|
123
|
+
break;
|
|
124
|
+
case "security":
|
|
125
|
+
findings.push(...this.extractSecurityFindings(moduleData, requirementId));
|
|
126
|
+
break;
|
|
127
|
+
case "functionality":
|
|
128
|
+
findings.push(...this.extractFunctionalityFindings(moduleData, requirementId));
|
|
129
|
+
break;
|
|
130
|
+
case "errorHandling":
|
|
131
|
+
findings.push(...this.extractErrorHandlingFindings(moduleData, requirementId));
|
|
132
|
+
break;
|
|
133
|
+
case "toolAnnotations":
|
|
134
|
+
findings.push(...this.extractToolAnnotationFindings(moduleData, requirementId));
|
|
135
|
+
break;
|
|
136
|
+
case "documentation":
|
|
137
|
+
findings.push(...this.extractDocumentationFindings(moduleData, requirementId));
|
|
138
|
+
break;
|
|
139
|
+
case "mcpSpecCompliance":
|
|
140
|
+
findings.push(...this.extractMCPSpecFindings(moduleData, requirementId));
|
|
141
|
+
break;
|
|
142
|
+
case "prohibitedLibraries":
|
|
143
|
+
findings.push(...this.extractProhibitedLibraryFindings(moduleData, requirementId));
|
|
144
|
+
break;
|
|
145
|
+
case "manifestValidation":
|
|
146
|
+
findings.push(...this.extractManifestFindings(moduleData, requirementId));
|
|
147
|
+
break;
|
|
148
|
+
case "portability":
|
|
149
|
+
findings.push(...this.extractPortabilityFindings(moduleData, requirementId));
|
|
150
|
+
break;
|
|
151
|
+
default:
|
|
152
|
+
// Generic extraction
|
|
153
|
+
if (moduleData.status) {
|
|
154
|
+
findings.push(`${moduleName} status: ${moduleData.status}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return findings;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Extract AUP compliance findings
|
|
161
|
+
*/
|
|
162
|
+
extractAUPFindings(data, requirementId) {
|
|
163
|
+
const findings = [];
|
|
164
|
+
if (data.status) {
|
|
165
|
+
findings.push(`AUP compliance status: ${data.status}`);
|
|
166
|
+
}
|
|
167
|
+
// Check for violations
|
|
168
|
+
const violations = data.violations;
|
|
169
|
+
if (Array.isArray(violations) && violations.length > 0) {
|
|
170
|
+
const violationCount = violations.length;
|
|
171
|
+
findings.push(`${violationCount} AUP violation(s) detected`);
|
|
172
|
+
// Add specific violation categories
|
|
173
|
+
if (requirementId.startsWith("SAFETY-")) {
|
|
174
|
+
const critical = violations.filter((v) => typeof v === "object" && v !== null && "severity" in v);
|
|
175
|
+
if (critical.length > 0) {
|
|
176
|
+
findings.push(`${critical.length} critical safety violation(s)`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Check overall score
|
|
181
|
+
const score = data.overallScore;
|
|
182
|
+
if (typeof score === "number") {
|
|
183
|
+
findings.push(`AUP compliance score: ${score}%`);
|
|
184
|
+
}
|
|
185
|
+
return findings;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Extract security findings
|
|
189
|
+
*/
|
|
190
|
+
extractSecurityFindings(data, _requirementId) {
|
|
191
|
+
const findings = [];
|
|
192
|
+
if (data.status) {
|
|
193
|
+
findings.push(`Security assessment status: ${data.status}`);
|
|
194
|
+
}
|
|
195
|
+
// Check for vulnerabilities
|
|
196
|
+
const vulnerabilities = data.vulnerabilities;
|
|
197
|
+
if (Array.isArray(vulnerabilities)) {
|
|
198
|
+
if (vulnerabilities.length === 0) {
|
|
199
|
+
findings.push("No security vulnerabilities detected");
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
findings.push(`${vulnerabilities.length} security vulnerability(ies) detected`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Check for prompt injection tests
|
|
206
|
+
const promptTests = data.promptInjectionTests;
|
|
207
|
+
if (Array.isArray(promptTests)) {
|
|
208
|
+
const vulnerable = promptTests.filter((t) => typeof t === "object" &&
|
|
209
|
+
t !== null &&
|
|
210
|
+
t.vulnerable);
|
|
211
|
+
if (vulnerable.length > 0) {
|
|
212
|
+
findings.push(`${vulnerable.length} tool(s) vulnerable to injection`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return findings;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Extract functionality findings
|
|
219
|
+
*/
|
|
220
|
+
extractFunctionalityFindings(data, _requirementId) {
|
|
221
|
+
const findings = [];
|
|
222
|
+
if (data.status) {
|
|
223
|
+
findings.push(`Functionality status: ${data.status}`);
|
|
224
|
+
}
|
|
225
|
+
// Check working/broken tools
|
|
226
|
+
const workingTools = data.workingTools;
|
|
227
|
+
const brokenTools = data.brokenTools;
|
|
228
|
+
if (Array.isArray(workingTools)) {
|
|
229
|
+
findings.push(`${workingTools.length} tool(s) working correctly`);
|
|
230
|
+
}
|
|
231
|
+
if (Array.isArray(brokenTools) && brokenTools.length > 0) {
|
|
232
|
+
findings.push(`${brokenTools.length} tool(s) with issues`);
|
|
233
|
+
}
|
|
234
|
+
// Check overall success rate
|
|
235
|
+
const summary = data.summary;
|
|
236
|
+
if (typeof summary === "object" && summary !== null) {
|
|
237
|
+
const s = summary;
|
|
238
|
+
if (typeof s.successRate === "number") {
|
|
239
|
+
findings.push(`Tool success rate: ${s.successRate.toFixed(1)}%`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return findings;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Extract error handling findings
|
|
246
|
+
*/
|
|
247
|
+
extractErrorHandlingFindings(data, _requirementId) {
|
|
248
|
+
const findings = [];
|
|
249
|
+
if (data.status) {
|
|
250
|
+
findings.push(`Error handling status: ${data.status}`);
|
|
251
|
+
}
|
|
252
|
+
const metrics = data.metrics;
|
|
253
|
+
if (typeof metrics === "object" && metrics !== null) {
|
|
254
|
+
if (typeof metrics.totalTests === "number") {
|
|
255
|
+
findings.push(`${metrics.totalTests} error handling tests executed`);
|
|
256
|
+
}
|
|
257
|
+
if (typeof metrics.passRate === "number") {
|
|
258
|
+
findings.push(`Error handling pass rate: ${metrics.passRate}%`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return findings;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Extract tool annotation findings
|
|
265
|
+
*/
|
|
266
|
+
extractToolAnnotationFindings(data, _requirementId) {
|
|
267
|
+
const findings = [];
|
|
268
|
+
if (data.status) {
|
|
269
|
+
findings.push(`Tool annotations status: ${data.status}`);
|
|
270
|
+
}
|
|
271
|
+
// Check annotation coverage
|
|
272
|
+
const annotatedCount = data.annotatedCount;
|
|
273
|
+
const missingCount = data.missingAnnotationsCount;
|
|
274
|
+
const misalignedCount = data.misalignedAnnotationsCount;
|
|
275
|
+
if (typeof annotatedCount === "number") {
|
|
276
|
+
findings.push(`${annotatedCount} tool(s) with annotations`);
|
|
277
|
+
}
|
|
278
|
+
if (typeof missingCount === "number" && missingCount > 0) {
|
|
279
|
+
findings.push(`${missingCount} tool(s) missing annotations`);
|
|
280
|
+
}
|
|
281
|
+
if (typeof misalignedCount === "number" && misalignedCount > 0) {
|
|
282
|
+
findings.push(`${misalignedCount} tool(s) with misaligned annotations`);
|
|
283
|
+
}
|
|
284
|
+
// Check annotation sources
|
|
285
|
+
const sources = data.annotationSources;
|
|
286
|
+
if (typeof sources === "object" && sources !== null) {
|
|
287
|
+
if (sources.mcp > 0) {
|
|
288
|
+
findings.push(`${sources.mcp} tool(s) with MCP protocol annotations`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return findings;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Extract documentation findings
|
|
295
|
+
*/
|
|
296
|
+
extractDocumentationFindings(data, _requirementId) {
|
|
297
|
+
const findings = [];
|
|
298
|
+
if (data.status) {
|
|
299
|
+
findings.push(`Documentation status: ${data.status}`);
|
|
300
|
+
}
|
|
301
|
+
// Check documentation quality
|
|
302
|
+
const quality = data.quality;
|
|
303
|
+
if (typeof quality === "object" && quality !== null) {
|
|
304
|
+
if (typeof quality.overallScore === "number") {
|
|
305
|
+
findings.push(`Documentation quality score: ${quality.overallScore}%`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return findings;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Extract MCP spec compliance findings
|
|
312
|
+
*/
|
|
313
|
+
extractMCPSpecFindings(data, _requirementId) {
|
|
314
|
+
const findings = [];
|
|
315
|
+
if (data.status) {
|
|
316
|
+
findings.push(`MCP spec compliance status: ${data.status}`);
|
|
317
|
+
}
|
|
318
|
+
// Check protocol compliance
|
|
319
|
+
const summary = data.summary;
|
|
320
|
+
if (typeof summary === "object" && summary !== null) {
|
|
321
|
+
if (typeof summary.totalChecks === "number") {
|
|
322
|
+
findings.push(`${summary.totalChecks} protocol checks performed`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return findings;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Extract prohibited library findings
|
|
329
|
+
*/
|
|
330
|
+
extractProhibitedLibraryFindings(data, _requirementId) {
|
|
331
|
+
const findings = [];
|
|
332
|
+
if (data.status) {
|
|
333
|
+
findings.push(`Prohibited libraries status: ${data.status}`);
|
|
334
|
+
}
|
|
335
|
+
const detected = data.prohibitedLibrariesDetected;
|
|
336
|
+
if (Array.isArray(detected)) {
|
|
337
|
+
if (detected.length === 0) {
|
|
338
|
+
findings.push("No prohibited libraries detected");
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
findings.push(`${detected.length} prohibited library(ies) detected`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return findings;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Extract manifest findings
|
|
348
|
+
*/
|
|
349
|
+
extractManifestFindings(data, _requirementId) {
|
|
350
|
+
const findings = [];
|
|
351
|
+
if (data.status) {
|
|
352
|
+
findings.push(`Manifest validation status: ${data.status}`);
|
|
353
|
+
}
|
|
354
|
+
const valid = data.isValid;
|
|
355
|
+
if (typeof valid === "boolean") {
|
|
356
|
+
findings.push(valid ? "Manifest is valid" : "Manifest validation failed");
|
|
357
|
+
}
|
|
358
|
+
return findings;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Extract portability findings
|
|
362
|
+
*/
|
|
363
|
+
extractPortabilityFindings(data, _requirementId) {
|
|
364
|
+
const findings = [];
|
|
365
|
+
if (data.status) {
|
|
366
|
+
findings.push(`Portability status: ${data.status}`);
|
|
367
|
+
}
|
|
368
|
+
const issues = data.issues;
|
|
369
|
+
if (Array.isArray(issues)) {
|
|
370
|
+
if (issues.length === 0) {
|
|
371
|
+
findings.push("No portability issues detected");
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
findings.push(`${issues.length} portability issue(s) detected`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return findings;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Determine compliance status based on module results and evidence
|
|
381
|
+
*/
|
|
382
|
+
determineComplianceStatus(requirement, moduleResults, evidence) {
|
|
383
|
+
// If no modules were run, mark as NOT_TESTED
|
|
384
|
+
if (moduleResults.length === 0) {
|
|
385
|
+
return "NOT_TESTED";
|
|
386
|
+
}
|
|
387
|
+
// Check if all modules passed
|
|
388
|
+
const allPassed = moduleResults.every((r) => r.status === "PASS" || r.status === "NEED_MORE_INFO");
|
|
389
|
+
const anyFailed = moduleResults.some((r) => r.status === "FAIL");
|
|
390
|
+
const anyNeedInfo = moduleResults.some((r) => r.status === "NEED_MORE_INFO");
|
|
391
|
+
// Check for critical findings in evidence
|
|
392
|
+
const hasCriticalFindings = evidence.some((e) => e.toLowerCase().includes("critical") ||
|
|
393
|
+
e.toLowerCase().includes("vulnerability") ||
|
|
394
|
+
e.toLowerCase().includes("violation"));
|
|
395
|
+
// Determine status based on severity and findings
|
|
396
|
+
if (anyFailed) {
|
|
397
|
+
if (requirement.severity === "CRITICAL" || hasCriticalFindings) {
|
|
398
|
+
return "FAIL";
|
|
399
|
+
}
|
|
400
|
+
return "FLAG";
|
|
401
|
+
}
|
|
402
|
+
if (anyNeedInfo) {
|
|
403
|
+
return "REVIEW";
|
|
404
|
+
}
|
|
405
|
+
if (allPassed && !hasCriticalFindings) {
|
|
406
|
+
return "PASS";
|
|
407
|
+
}
|
|
408
|
+
// Default to review if unclear
|
|
409
|
+
return "REVIEW";
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Generate a recommendation for non-passing requirements
|
|
413
|
+
*/
|
|
414
|
+
generateRecommendation(requirement, status, evidence) {
|
|
415
|
+
const prefix = status === "FAIL"
|
|
416
|
+
? "[REQUIRED]"
|
|
417
|
+
: status === "FLAG"
|
|
418
|
+
? "[RECOMMENDED]"
|
|
419
|
+
: "[REVIEW]";
|
|
420
|
+
// Generate specific recommendations based on requirement
|
|
421
|
+
const recommendations = {
|
|
422
|
+
"SAFETY-1": "Ensure server complies with Anthropic's Acceptable Use Policy",
|
|
423
|
+
"SAFETY-2": "Fix security vulnerabilities before submission",
|
|
424
|
+
"SAFETY-3": "Remove any malicious or deceptive functionality",
|
|
425
|
+
"SAFETY-4": "Implement proper input validation and sanitization",
|
|
426
|
+
"SAFETY-5": "Secure sensitive data handling and storage",
|
|
427
|
+
"SAFETY-6": "Implement proper authentication and authorization",
|
|
428
|
+
"COMPAT-1": "Ensure full MCP protocol compliance",
|
|
429
|
+
"COMPAT-2": "Support required MCP message types",
|
|
430
|
+
"COMPAT-3": "Remove or replace prohibited dependencies",
|
|
431
|
+
"COMPAT-4": "Fix compatibility issues with Claude clients",
|
|
432
|
+
"COMPAT-5": "Test across supported platforms",
|
|
433
|
+
"COMPAT-6": "Ensure cross-platform portability",
|
|
434
|
+
"FUNC-1": "Ensure all tools function as documented",
|
|
435
|
+
"FUNC-2": "Improve tool reliability and consistency",
|
|
436
|
+
"FUNC-3": "Implement proper error handling",
|
|
437
|
+
"FUNC-4": "Improve tool documentation",
|
|
438
|
+
"FUNC-5": "Add required tool annotations (readOnlyHint, destructiveHint)",
|
|
439
|
+
"FUNC-6": "Ensure tools produce meaningful results",
|
|
440
|
+
"FUNC-7": "Improve usability and user experience",
|
|
441
|
+
"DEV-1": "Provide valid manifest.json",
|
|
442
|
+
"DEV-2": "Include license information",
|
|
443
|
+
"DEV-3": "Add comprehensive README documentation",
|
|
444
|
+
"DEV-4": "Provide installation instructions",
|
|
445
|
+
"DEV-5": "Include usage examples",
|
|
446
|
+
"DEV-6": "Document configuration options",
|
|
447
|
+
"DEV-7": "Add contribution guidelines",
|
|
448
|
+
"DEV-8": "Maintain changelog",
|
|
449
|
+
"UNSUPP-1": "Remove cryptocurrency/blockchain functionality",
|
|
450
|
+
"UNSUPP-2": "Remove gambling functionality",
|
|
451
|
+
"UNSUPP-3": "Remove adult content functionality",
|
|
452
|
+
};
|
|
453
|
+
const baseRec = recommendations[requirement.id] || `Address ${requirement.name} issues`;
|
|
454
|
+
// Add evidence-specific context if available
|
|
455
|
+
const relevantEvidence = evidence.slice(0, 2).join("; ");
|
|
456
|
+
if (relevantEvidence) {
|
|
457
|
+
return `${prefix} ${baseRec}. Evidence: ${relevantEvidence}`;
|
|
458
|
+
}
|
|
459
|
+
return `${prefix} ${baseRec}`;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Get manual review guidance for a requirement
|
|
463
|
+
*/
|
|
464
|
+
getManualReviewGuidance(requirement, status) {
|
|
465
|
+
if (status === "NOT_TESTED") {
|
|
466
|
+
return `Run the following assessment modules to evaluate this requirement: ${requirement.moduleSource.join(", ")}`;
|
|
467
|
+
}
|
|
468
|
+
if (status === "FLAG" || status === "REVIEW") {
|
|
469
|
+
return `Manually verify: ${requirement.description}. Automated assessment found potential issues that require human judgment.`;
|
|
470
|
+
}
|
|
471
|
+
return `Review ${requirement.name} manually as this requirement cannot be fully automated.`;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Group results by category
|
|
475
|
+
*/
|
|
476
|
+
groupByCategory(results) {
|
|
477
|
+
const categories = [
|
|
478
|
+
"safety_security",
|
|
479
|
+
"compatibility",
|
|
480
|
+
"functionality",
|
|
481
|
+
"developer_requirements",
|
|
482
|
+
"unsupported_use_cases",
|
|
483
|
+
];
|
|
484
|
+
const byCategory = {};
|
|
485
|
+
for (const category of categories) {
|
|
486
|
+
const categoryResults = results.filter((r) => r.requirement.category === category);
|
|
487
|
+
const passed = categoryResults.filter((r) => r.status === "PASS").length;
|
|
488
|
+
const failed = categoryResults.filter((r) => r.status === "FAIL" || r.status === "FLAG").length;
|
|
489
|
+
let categoryStatus = "PASS";
|
|
490
|
+
if (failed > 0) {
|
|
491
|
+
const hasCriticalFail = categoryResults.some((r) => r.status === "FAIL" && r.requirement.severity === "CRITICAL");
|
|
492
|
+
categoryStatus = hasCriticalFail ? "FAIL" : "FLAG";
|
|
493
|
+
}
|
|
494
|
+
else if (categoryResults.some((r) => r.status === "REVIEW")) {
|
|
495
|
+
categoryStatus = "REVIEW";
|
|
496
|
+
}
|
|
497
|
+
else if (categoryResults.every((r) => r.status === "NOT_TESTED")) {
|
|
498
|
+
categoryStatus = "NOT_TESTED";
|
|
499
|
+
}
|
|
500
|
+
byCategory[category] = {
|
|
501
|
+
category,
|
|
502
|
+
categoryName: getCategoryDisplayName(category),
|
|
503
|
+
total: categoryResults.length,
|
|
504
|
+
passed,
|
|
505
|
+
failed,
|
|
506
|
+
status: categoryStatus,
|
|
507
|
+
requirements: categoryResults,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
return byCategory;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Calculate summary statistics
|
|
514
|
+
*/
|
|
515
|
+
calculateSummary(results) {
|
|
516
|
+
const passed = results.filter((r) => r.status === "PASS").length;
|
|
517
|
+
const failed = results.filter((r) => r.status === "FAIL").length;
|
|
518
|
+
const flagged = results.filter((r) => r.status === "FLAG").length;
|
|
519
|
+
const needsReview = results.filter((r) => r.status === "REVIEW").length;
|
|
520
|
+
const notApplicable = results.filter((r) => r.status === "NOT_APPLICABLE").length;
|
|
521
|
+
const notTested = results.filter((r) => r.status === "NOT_TESTED").length;
|
|
522
|
+
const applicableTotal = results.length - notApplicable - notTested;
|
|
523
|
+
const complianceScore = applicableTotal > 0 ? Math.round((passed / applicableTotal) * 100) : 0;
|
|
524
|
+
// Determine overall status
|
|
525
|
+
let overallStatus = "COMPLIANT";
|
|
526
|
+
if (failed > 0) {
|
|
527
|
+
// Check if any critical requirements failed
|
|
528
|
+
const criticalFailed = results.some((r) => r.status === "FAIL" && r.requirement.severity === "CRITICAL");
|
|
529
|
+
overallStatus = criticalFailed ? "NON_COMPLIANT" : "NEEDS_REVIEW";
|
|
530
|
+
}
|
|
531
|
+
else if (flagged > 0 || needsReview > 0) {
|
|
532
|
+
overallStatus = "NEEDS_REVIEW";
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
totalRequirements: results.length,
|
|
536
|
+
passed,
|
|
537
|
+
failed,
|
|
538
|
+
flagged,
|
|
539
|
+
needsReview,
|
|
540
|
+
notApplicable,
|
|
541
|
+
notTested,
|
|
542
|
+
complianceScore,
|
|
543
|
+
overallStatus,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Identify critical issues
|
|
548
|
+
*/
|
|
549
|
+
identifyCriticalIssues(results) {
|
|
550
|
+
return results.filter((r) => (r.status === "FAIL" || r.status === "FLAG") &&
|
|
551
|
+
(r.requirement.severity === "CRITICAL" ||
|
|
552
|
+
r.requirement.severity === "HIGH"));
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Generate prioritized action items
|
|
556
|
+
*/
|
|
557
|
+
generateActionItems(results) {
|
|
558
|
+
const actionItems = [];
|
|
559
|
+
// Group by severity
|
|
560
|
+
const critical = results.filter((r) => r.status === "FAIL" && r.requirement.severity === "CRITICAL");
|
|
561
|
+
const high = results.filter((r) => (r.status === "FAIL" && r.requirement.severity === "HIGH") ||
|
|
562
|
+
(r.status === "FLAG" && r.requirement.severity === "CRITICAL"));
|
|
563
|
+
const medium = results.filter((r) => (r.status === "FAIL" && r.requirement.severity === "MEDIUM") ||
|
|
564
|
+
(r.status === "FLAG" && r.requirement.severity === "HIGH"));
|
|
565
|
+
// Add critical items first
|
|
566
|
+
for (const result of critical) {
|
|
567
|
+
if (result.recommendation) {
|
|
568
|
+
actionItems.push(`[CRITICAL] ${result.recommendation}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
// Add high priority items
|
|
572
|
+
for (const result of high) {
|
|
573
|
+
if (result.recommendation) {
|
|
574
|
+
actionItems.push(`[HIGH] ${result.recommendation}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// Add medium priority items (limit to avoid overwhelming)
|
|
578
|
+
for (const result of medium.slice(0, 5)) {
|
|
579
|
+
if (result.recommendation) {
|
|
580
|
+
actionItems.push(`[MEDIUM] ${result.recommendation}`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// Add review items summary if many exist
|
|
584
|
+
const reviewCount = results.filter((r) => r.status === "REVIEW" || r.status === "NOT_TESTED").length;
|
|
585
|
+
if (reviewCount > 0) {
|
|
586
|
+
actionItems.push(`[INFO] ${reviewCount} requirement(s) need manual review or additional testing`);
|
|
587
|
+
}
|
|
588
|
+
return actionItems;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Get list of modules that were run in the assessment
|
|
592
|
+
*/
|
|
593
|
+
getRunModules(assessment) {
|
|
594
|
+
const modules = [];
|
|
595
|
+
if (assessment.functionality)
|
|
596
|
+
modules.push("functionality");
|
|
597
|
+
if (assessment.security)
|
|
598
|
+
modules.push("security");
|
|
599
|
+
if (assessment.documentation)
|
|
600
|
+
modules.push("documentation");
|
|
601
|
+
if (assessment.errorHandling)
|
|
602
|
+
modules.push("errorHandling");
|
|
603
|
+
if (assessment.usability)
|
|
604
|
+
modules.push("usability");
|
|
605
|
+
if (assessment.mcpSpecCompliance)
|
|
606
|
+
modules.push("mcpSpecCompliance");
|
|
607
|
+
if (assessment.aupCompliance)
|
|
608
|
+
modules.push("aupCompliance");
|
|
609
|
+
if (assessment.toolAnnotations)
|
|
610
|
+
modules.push("toolAnnotations");
|
|
611
|
+
if (assessment.prohibitedLibraries)
|
|
612
|
+
modules.push("prohibitedLibraries");
|
|
613
|
+
if (assessment.manifestValidation)
|
|
614
|
+
modules.push("manifestValidation");
|
|
615
|
+
if (assessment.portability)
|
|
616
|
+
modules.push("portability");
|
|
617
|
+
return modules;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Factory function to create a policy compliance generator
|
|
622
|
+
*/
|
|
623
|
+
export function createPolicyComplianceGenerator(version) {
|
|
624
|
+
return new PolicyComplianceGenerator(version);
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Quick utility to generate a compliance report
|
|
628
|
+
*/
|
|
629
|
+
export function generatePolicyComplianceReport(assessment, serverName) {
|
|
630
|
+
const generator = createPolicyComplianceGenerator();
|
|
631
|
+
return generator.generate(assessment, serverName);
|
|
632
|
+
}
|