@bryan-thompson/inspector-assessment-cli 1.26.6 → 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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bryan-thompson/inspector-assessment-cli",
3
- "version": "1.26.6",
3
+ "version": "1.26.7",
4
4
  "description": "CLI for the Enhanced MCP Inspector with assessment capabilities",
5
5
  "license": "MIT",
6
6
  "author": "Bryan Thompson <bryan@triepod.ai>",