@bryan-thompson/inspector-assessment-cli 1.26.5 → 1.26.7
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/build/__tests__/assessment-runner/assessment-executor.test.js +248 -0
- package/build/__tests__/assessment-runner/config-builder.test.js +289 -0
- package/build/__tests__/assessment-runner/index.test.js +41 -0
- package/build/__tests__/assessment-runner/server-config.test.js +249 -0
- package/build/__tests__/assessment-runner/server-connection.test.js +221 -0
- package/build/__tests__/assessment-runner/source-loader.test.js +341 -0
- package/build/__tests__/assessment-runner/tool-wrapper.test.js +114 -0
- package/build/__tests__/assessment-runner-facade.test.js +118 -0
- package/build/assess-full.js +26 -1254
- package/build/lib/assessment-runner/assessment-executor.js +323 -0
- package/build/lib/assessment-runner/config-builder.js +127 -0
- package/build/lib/assessment-runner/index.js +20 -0
- package/build/lib/assessment-runner/server-config.js +78 -0
- package/build/lib/assessment-runner/server-connection.js +80 -0
- package/build/lib/assessment-runner/source-loader.js +139 -0
- package/build/lib/assessment-runner/tool-wrapper.js +40 -0
- package/build/lib/assessment-runner/types.js +8 -0
- package/build/lib/assessment-runner.js +12 -0
- package/build/lib/cli-parser.js +419 -0
- package/build/lib/comparison-handler.js +84 -0
- package/build/lib/result-output.js +154 -0
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comparison Handler Module
|
|
3
|
+
*
|
|
4
|
+
* Handles assessment comparison/diff logic for comparing
|
|
5
|
+
* current results against a baseline.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/lib/comparison-handler
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import { compareAssessments, } from "../../../client/lib/lib/assessmentDiffer.js";
|
|
11
|
+
import { formatDiffAsMarkdown } from "../../../client/lib/lib/reportFormatters/DiffReportFormatter.js";
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Comparison Functions
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Handle comparison mode - compare current results against a baseline.
|
|
17
|
+
*
|
|
18
|
+
* @param results - Current assessment results
|
|
19
|
+
* @param options - CLI options including comparePath and diffOnly
|
|
20
|
+
* @returns ComparisonResult if comparison was performed, null if no comparison
|
|
21
|
+
*/
|
|
22
|
+
export function handleComparison(results, options) {
|
|
23
|
+
if (!options.comparePath) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (!fs.existsSync(options.comparePath)) {
|
|
27
|
+
console.error(`Error: Baseline file not found: ${options.comparePath}`);
|
|
28
|
+
// Return null to indicate comparison failed - caller handles null returns
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const baselineData = JSON.parse(fs.readFileSync(options.comparePath, "utf-8"));
|
|
32
|
+
// Validate baseline has expected structure
|
|
33
|
+
if (!baselineData.functionality || !baselineData.security) {
|
|
34
|
+
console.warn("Warning: Baseline file may be incomplete (missing functionality or security)");
|
|
35
|
+
}
|
|
36
|
+
const baseline = baselineData;
|
|
37
|
+
const diff = compareAssessments(baseline, results);
|
|
38
|
+
// Handle diff-only mode
|
|
39
|
+
if (options.diffOnly) {
|
|
40
|
+
let diffPath;
|
|
41
|
+
if (options.format === "markdown") {
|
|
42
|
+
diffPath =
|
|
43
|
+
options.outputPath || `/tmp/inspector-diff-${options.serverName}.md`;
|
|
44
|
+
fs.writeFileSync(diffPath, formatDiffAsMarkdown(diff));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
diffPath =
|
|
48
|
+
options.outputPath || `/tmp/inspector-diff-${options.serverName}.json`;
|
|
49
|
+
fs.writeFileSync(diffPath, JSON.stringify(diff, null, 2));
|
|
50
|
+
}
|
|
51
|
+
const exitCode = diff.summary.overallChange === "regressed" ? 1 : 0;
|
|
52
|
+
return { diff, exitCode, diffOutputPath: diffPath };
|
|
53
|
+
}
|
|
54
|
+
// Return comparison result for normal mode
|
|
55
|
+
const exitCode = diff.summary.overallChange === "regressed" ? 1 : 0;
|
|
56
|
+
return { diff, exitCode };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Display comparison summary to console.
|
|
60
|
+
*
|
|
61
|
+
* @param diff - Assessment diff to display
|
|
62
|
+
*/
|
|
63
|
+
export function displayComparisonSummary(diff) {
|
|
64
|
+
console.log("\n" + "=".repeat(70));
|
|
65
|
+
console.log("VERSION COMPARISON");
|
|
66
|
+
console.log("=".repeat(70));
|
|
67
|
+
console.log(`Baseline: ${diff.baseline.version || "N/A"} (${diff.baseline.date})`);
|
|
68
|
+
console.log(`Current: ${diff.current.version || "N/A"} (${diff.current.date})`);
|
|
69
|
+
console.log(`Overall Change: ${diff.summary.overallChange.toUpperCase()}`);
|
|
70
|
+
console.log(`Modules Improved: ${diff.summary.modulesImproved}`);
|
|
71
|
+
console.log(`Modules Regressed: ${diff.summary.modulesRegressed}`);
|
|
72
|
+
if (diff.securityDelta.newVulnerabilities.length > 0) {
|
|
73
|
+
console.log(`\n⚠️ NEW VULNERABILITIES: ${diff.securityDelta.newVulnerabilities.length}`);
|
|
74
|
+
}
|
|
75
|
+
if (diff.securityDelta.fixedVulnerabilities.length > 0) {
|
|
76
|
+
console.log(`✅ FIXED VULNERABILITIES: ${diff.securityDelta.fixedVulnerabilities.length}`);
|
|
77
|
+
}
|
|
78
|
+
if (diff.functionalityDelta.newBrokenTools.length > 0) {
|
|
79
|
+
console.log(`❌ NEW BROKEN TOOLS: ${diff.functionalityDelta.newBrokenTools.length}`);
|
|
80
|
+
}
|
|
81
|
+
if (diff.functionalityDelta.fixedTools.length > 0) {
|
|
82
|
+
console.log(`✅ FIXED TOOLS: ${diff.functionalityDelta.fixedTools.length}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Output Module
|
|
3
|
+
*
|
|
4
|
+
* Handles saving assessment results to files and displaying
|
|
5
|
+
* summaries to the console.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/lib/result-output
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import { ASSESSMENT_CATEGORY_METADATA, } from "../../../client/lib/lib/assessmentTypes.js";
|
|
11
|
+
import { createFormatter } from "../../../client/lib/lib/reportFormatters/index.js";
|
|
12
|
+
import { generatePolicyComplianceReport } from "../../../client/lib/services/assessment/PolicyComplianceGenerator.js";
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Result Output
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Save results to file with appropriate format
|
|
18
|
+
*/
|
|
19
|
+
export function saveResults(serverName, results, options) {
|
|
20
|
+
const format = options.format || "json";
|
|
21
|
+
// Generate policy compliance report if requested
|
|
22
|
+
const policyReport = options.includePolicy
|
|
23
|
+
? generatePolicyComplianceReport(results, serverName)
|
|
24
|
+
: undefined;
|
|
25
|
+
// Create formatter with options
|
|
26
|
+
const formatter = createFormatter({
|
|
27
|
+
format,
|
|
28
|
+
includePolicyMapping: options.includePolicy,
|
|
29
|
+
policyReport,
|
|
30
|
+
serverName,
|
|
31
|
+
includeDetails: true,
|
|
32
|
+
prettyPrint: true,
|
|
33
|
+
});
|
|
34
|
+
const fileExtension = formatter.getFileExtension();
|
|
35
|
+
const defaultPath = `/tmp/inspector-full-assessment-${serverName}${fileExtension}`;
|
|
36
|
+
const finalPath = options.outputPath || defaultPath;
|
|
37
|
+
// For JSON format, add metadata wrapper
|
|
38
|
+
if (format === "json") {
|
|
39
|
+
// Filter out undefined/skipped modules from results (--skip-modules support)
|
|
40
|
+
const filteredResults = Object.fromEntries(Object.entries(results).filter(([_, v]) => v !== undefined));
|
|
41
|
+
const output = {
|
|
42
|
+
timestamp: new Date().toISOString(),
|
|
43
|
+
assessmentType: "full",
|
|
44
|
+
...filteredResults,
|
|
45
|
+
...(policyReport ? { policyCompliance: policyReport } : {}),
|
|
46
|
+
};
|
|
47
|
+
fs.writeFileSync(finalPath, JSON.stringify(output, null, 2));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// For other formats (markdown), use the formatter
|
|
51
|
+
const content = formatter.format(results);
|
|
52
|
+
fs.writeFileSync(finalPath, content);
|
|
53
|
+
}
|
|
54
|
+
return finalPath;
|
|
55
|
+
}
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Summary Display
|
|
58
|
+
// ============================================================================
|
|
59
|
+
/**
|
|
60
|
+
* Display summary to console
|
|
61
|
+
*/
|
|
62
|
+
export function displaySummary(results) {
|
|
63
|
+
const { overallStatus, summary, totalTestsRun, executionTime,
|
|
64
|
+
// Destructuring order matches display order below
|
|
65
|
+
functionality, security, documentation, errorHandling, usability, mcpSpecCompliance, aupCompliance, toolAnnotations, prohibitedLibraries, manifestValidation, portability, externalAPIScanner, authentication, temporal, resources, prompts, crossCapability, } = results;
|
|
66
|
+
console.log("\n" + "=".repeat(70));
|
|
67
|
+
console.log("FULL ASSESSMENT RESULTS");
|
|
68
|
+
console.log("=".repeat(70));
|
|
69
|
+
console.log(`Server: ${results.serverName}`);
|
|
70
|
+
console.log(`Overall Status: ${overallStatus}`);
|
|
71
|
+
console.log(`Total Tests Run: ${totalTestsRun}`);
|
|
72
|
+
console.log(`Execution Time: ${executionTime}ms`);
|
|
73
|
+
console.log("-".repeat(70));
|
|
74
|
+
console.log("\n📊 MODULE STATUS:");
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
const modules = [
|
|
77
|
+
["Functionality", functionality, "functionality"],
|
|
78
|
+
["Security", security, "security"],
|
|
79
|
+
["Documentation", documentation, "documentation"],
|
|
80
|
+
["Error Handling", errorHandling, "errorHandling"],
|
|
81
|
+
["Usability", usability, "usability"],
|
|
82
|
+
["MCP Spec Compliance", mcpSpecCompliance, "mcpSpecCompliance"],
|
|
83
|
+
["AUP Compliance", aupCompliance, "aupCompliance"],
|
|
84
|
+
["Tool Annotations", toolAnnotations, "toolAnnotations"],
|
|
85
|
+
["Prohibited Libraries", prohibitedLibraries, "prohibitedLibraries"],
|
|
86
|
+
["Manifest Validation", manifestValidation, "manifestValidation"],
|
|
87
|
+
["Portability", portability, "portability"],
|
|
88
|
+
["External API Scanner", externalAPIScanner, "externalAPIScanner"],
|
|
89
|
+
["Authentication", authentication, "authentication"],
|
|
90
|
+
["Temporal", temporal, "temporal"],
|
|
91
|
+
["Resources", resources, "resources"],
|
|
92
|
+
["Prompts", prompts, "prompts"],
|
|
93
|
+
["Cross-Capability", crossCapability, "crossCapability"],
|
|
94
|
+
];
|
|
95
|
+
for (const [name, module, categoryKey] of modules) {
|
|
96
|
+
if (module) {
|
|
97
|
+
const metadata = ASSESSMENT_CATEGORY_METADATA[categoryKey];
|
|
98
|
+
const optionalMarker = metadata?.tier === "optional" ? " (optional)" : "";
|
|
99
|
+
const icon = module.status === "PASS"
|
|
100
|
+
? "✅"
|
|
101
|
+
: module.status === "FAIL"
|
|
102
|
+
? "❌"
|
|
103
|
+
: "⚠️";
|
|
104
|
+
console.log(` ${icon} ${name}${optionalMarker}: ${module.status}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
console.log("\n📋 KEY FINDINGS:");
|
|
108
|
+
console.log(` ${summary}`);
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
+
const securityModule = security;
|
|
111
|
+
if (securityModule?.vulnerabilities?.length > 0) {
|
|
112
|
+
const vulns = securityModule.vulnerabilities;
|
|
113
|
+
console.log(`\n🔒 SECURITY VULNERABILITIES (${vulns.length}):`);
|
|
114
|
+
for (const vuln of vulns.slice(0, 5)) {
|
|
115
|
+
console.log(` • ${vuln}`);
|
|
116
|
+
}
|
|
117
|
+
if (vulns.length > 5) {
|
|
118
|
+
console.log(` ... and ${vulns.length - 5} more`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
const aupModule = aupCompliance;
|
|
123
|
+
if (aupModule?.violations?.length > 0) {
|
|
124
|
+
const violations = aupModule.violations;
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
const critical = violations.filter((v) => v.severity === "CRITICAL");
|
|
127
|
+
console.log(`\n⚖️ AUP FINDINGS:`);
|
|
128
|
+
console.log(` Total flagged: ${violations.length}`);
|
|
129
|
+
if (critical.length > 0) {
|
|
130
|
+
console.log(` 🚨 CRITICAL violations: ${critical.length}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
const annotationsModule = toolAnnotations;
|
|
135
|
+
if (annotationsModule) {
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
|
+
const funcModule = functionality;
|
|
138
|
+
console.log(`\n🏷️ TOOL ANNOTATIONS:`);
|
|
139
|
+
console.log(` Annotated: ${annotationsModule.annotatedCount || 0}/${funcModule?.workingTools || 0}`);
|
|
140
|
+
if (annotationsModule.missingAnnotationsCount > 0) {
|
|
141
|
+
console.log(` Missing: ${annotationsModule.missingAnnotationsCount}`);
|
|
142
|
+
}
|
|
143
|
+
if (annotationsModule.misalignedAnnotationsCount > 0) {
|
|
144
|
+
console.log(` ⚠️ Misalignments: ${annotationsModule.misalignedAnnotationsCount}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (results.recommendations?.length > 0) {
|
|
148
|
+
console.log("\n💡 RECOMMENDATIONS:");
|
|
149
|
+
for (const rec of results.recommendations.slice(0, 5)) {
|
|
150
|
+
console.log(` • ${rec}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
console.log("\n" + "=".repeat(70));
|
|
154
|
+
}
|
package/package.json
CHANGED