@vibecheckai/cli 3.3.0 → 3.5.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/bin/registry.js +389 -269
- package/bin/runners/cli-utils.js +2 -33
- package/bin/runners/context/generators/cursor.js +49 -2
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +849 -0
- package/bin/runners/lib/analyzers.js +599 -142
- package/bin/runners/lib/audit-logger.js +532 -0
- package/bin/runners/lib/authority/authorities/architecture.js +364 -0
- package/bin/runners/lib/authority/authorities/compliance.js +341 -0
- package/bin/runners/lib/authority/authorities/human.js +343 -0
- package/bin/runners/lib/authority/authorities/quality.js +420 -0
- package/bin/runners/lib/authority/authorities/security.js +228 -0
- package/bin/runners/lib/authority/index.js +293 -0
- package/bin/runners/lib/authority-badge.js +425 -425
- package/bin/runners/lib/bundle/bundle-intelligence.js +846 -0
- package/bin/runners/lib/cli-charts.js +368 -0
- package/bin/runners/lib/cli-config-display.js +405 -0
- package/bin/runners/lib/cli-demo.js +275 -0
- package/bin/runners/lib/cli-errors.js +438 -0
- package/bin/runners/lib/cli-help-formatter.js +439 -0
- package/bin/runners/lib/cli-interactive-menu.js +509 -0
- package/bin/runners/lib/cli-prompts.js +441 -0
- package/bin/runners/lib/cli-scan-cards.js +362 -0
- package/bin/runners/lib/compliance-reporter.js +710 -0
- package/bin/runners/lib/conductor/index.js +671 -0
- package/bin/runners/lib/easy/README.md +123 -0
- package/bin/runners/lib/easy/index.js +140 -0
- package/bin/runners/lib/easy/interactive-wizard.js +788 -0
- package/bin/runners/lib/easy/one-click-firewall.js +564 -0
- package/bin/runners/lib/easy/zero-config-reality.js +714 -0
- package/bin/runners/lib/engines/accessibility-engine.js +218 -18
- package/bin/runners/lib/engines/api-consistency-engine.js +335 -30
- package/bin/runners/lib/engines/async-patterns-engine.js +444 -0
- package/bin/runners/lib/engines/bundle-size-engine.js +433 -0
- package/bin/runners/lib/engines/confidence-scoring.js +276 -0
- package/bin/runners/lib/engines/context-detection.js +264 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +292 -27
- package/bin/runners/lib/engines/database-patterns-engine.js +429 -0
- package/bin/runners/lib/engines/duplicate-code-engine.js +354 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +127 -17
- package/bin/runners/lib/engines/env-variables-engine.js +458 -0
- package/bin/runners/lib/engines/error-handling-engine.js +437 -0
- package/bin/runners/lib/engines/false-positive-prevention.js +630 -0
- package/bin/runners/lib/engines/framework-adapters/index.js +607 -0
- package/bin/runners/lib/engines/framework-detection.js +508 -0
- package/bin/runners/lib/engines/import-order-engine.js +429 -0
- package/bin/runners/lib/engines/mock-data-engine.js +53 -10
- package/bin/runners/lib/engines/naming-conventions-engine.js +544 -0
- package/bin/runners/lib/engines/noise-reduction-engine.js +452 -0
- package/bin/runners/lib/engines/orchestrator.js +334 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +176 -36
- package/bin/runners/lib/engines/react-patterns-engine.js +457 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +382 -54
- package/bin/runners/lib/engines/type-aware-engine.js +263 -39
- package/bin/runners/lib/engines/vibecheck-engines/index.js +122 -13
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +806 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +373 -73
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +577 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +543 -0
- package/bin/runners/lib/engines/vibecheck-engines.js +514 -0
- package/bin/runners/lib/enhanced-features/index.js +305 -0
- package/bin/runners/lib/enhanced-output.js +631 -0
- package/bin/runners/lib/enterprise.js +300 -0
- package/bin/runners/lib/entitlements-v2.js +161 -478
- package/bin/runners/lib/firewall/command-validator.js +351 -0
- package/bin/runners/lib/firewall/config.js +341 -0
- package/bin/runners/lib/firewall/content-validator.js +519 -0
- package/bin/runners/lib/firewall/index.js +101 -0
- package/bin/runners/lib/firewall/path-validator.js +256 -0
- package/bin/runners/lib/html-proof-report.js +350 -700
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +817 -0
- package/bin/runners/lib/mcp-utils.js +425 -0
- package/bin/runners/lib/missions/plan.js +46 -6
- package/bin/runners/lib/missions/templates.js +232 -0
- package/bin/runners/lib/output/index.js +1022 -0
- package/bin/runners/lib/policy-engine.js +652 -0
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +333 -0
- package/bin/runners/lib/polish/autofix/async-handlers.js +273 -0
- package/bin/runners/lib/polish/autofix/dead-code.js +280 -0
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +344 -0
- package/bin/runners/lib/polish/autofix/index.js +200 -0
- package/bin/runners/lib/polish/autofix/remove-consoles.js +209 -0
- package/bin/runners/lib/polish/autofix/strengthen-types.js +245 -0
- package/bin/runners/lib/polish/backend-checks.js +148 -0
- package/bin/runners/lib/polish/documentation-checks.js +111 -0
- package/bin/runners/lib/polish/frontend-checks.js +168 -0
- package/bin/runners/lib/polish/index.js +71 -0
- package/bin/runners/lib/polish/infrastructure-checks.js +131 -0
- package/bin/runners/lib/polish/library-detection.js +175 -0
- package/bin/runners/lib/polish/performance-checks.js +100 -0
- package/bin/runners/lib/polish/security-checks.js +148 -0
- package/bin/runners/lib/polish/utils.js +203 -0
- package/bin/runners/lib/prompt-builder.js +540 -0
- package/bin/runners/lib/proof-certificate.js +634 -0
- package/bin/runners/lib/reality/accessibility-audit.js +946 -0
- package/bin/runners/lib/reality/api-contract-validator.js +1012 -0
- package/bin/runners/lib/reality/chaos-engineering.js +1084 -0
- package/bin/runners/lib/reality/performance-tracker.js +1077 -0
- package/bin/runners/lib/reality/scenario-generator.js +1404 -0
- package/bin/runners/lib/reality/visual-regression.js +852 -0
- package/bin/runners/lib/reality-profiler.js +717 -0
- package/bin/runners/lib/replay/flight-recorder-viewer.js +1160 -0
- package/bin/runners/lib/review/ai-code-review.js +832 -0
- package/bin/runners/lib/rules/custom-rule-engine.js +985 -0
- package/bin/runners/lib/sbom-generator.js +641 -0
- package/bin/runners/lib/scan-output-enhanced.js +512 -0
- package/bin/runners/lib/scan-output.js +65 -19
- package/bin/runners/lib/security/owasp-scanner.js +939 -0
- package/bin/runners/lib/ship-output.js +18 -25
- package/bin/runners/lib/terminal-ui.js +113 -1
- package/bin/runners/lib/unified-cli-output.js +603 -430
- package/bin/runners/lib/upsell.js +90 -338
- package/bin/runners/lib/validators/contract-validator.js +283 -0
- package/bin/runners/lib/validators/dead-export-detector.js +279 -0
- package/bin/runners/lib/validators/dep-audit.js +245 -0
- package/bin/runners/lib/validators/env-validator.js +319 -0
- package/bin/runners/lib/validators/index.js +120 -0
- package/bin/runners/lib/validators/license-checker.js +252 -0
- package/bin/runners/lib/validators/route-validator.js +290 -0
- package/bin/runners/runAIAgent.js +5 -10
- package/bin/runners/runAgent.js +3 -0
- package/bin/runners/runApprove.js +1233 -1200
- package/bin/runners/runAuth.js +22 -1
- package/bin/runners/runAuthority.js +528 -0
- package/bin/runners/runCheckpoint.js +4 -24
- package/bin/runners/runClassify.js +862 -859
- package/bin/runners/runConductor.js +772 -0
- package/bin/runners/runContainer.js +366 -0
- package/bin/runners/runContext.js +3 -0
- package/bin/runners/runDoctor.js +28 -41
- package/bin/runners/runEasy.js +410 -0
- package/bin/runners/runFirewall.js +3 -0
- package/bin/runners/runFirewallHook.js +3 -0
- package/bin/runners/runFix.js +76 -66
- package/bin/runners/runGuard.js +411 -18
- package/bin/runners/runIaC.js +372 -0
- package/bin/runners/runInit.js +10 -60
- package/bin/runners/runMcp.js +11 -12
- package/bin/runners/runPolish.js +240 -64
- package/bin/runners/runPromptFirewall.js +5 -12
- package/bin/runners/runProve.js +20 -55
- package/bin/runners/runReality.js +68 -59
- package/bin/runners/runReport.js +31 -5
- package/bin/runners/runRuntime.js +5 -8
- package/bin/runners/runScan.js +194 -1273
- package/bin/runners/runShip.js +695 -47
- package/bin/runners/runTruth.js +3 -0
- package/bin/runners/runValidate.js +7 -11
- package/bin/runners/runVibe.js +791 -0
- package/bin/runners/runWatch.js +14 -23
- package/bin/vibecheck.js +179 -65
- package/mcp-server/index.js +202 -636
- package/mcp-server/lib/api-client.cjs +7 -299
- package/mcp-server/package.json +1 -1
- package/mcp-server/tier-auth.js +175 -574
- package/mcp-server/tools-v3.js +800 -505
- package/mcp-server/tools.js +495 -0
- package/package.json +1 -1
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
- package/mcp-server/index-v1.js +0 -698
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container Security Scanner CLI
|
|
3
|
+
*
|
|
4
|
+
* Scans Dockerfiles for security issues and best practices
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
|
|
12
|
+
const { EXIT } = require("./lib/exit-codes");
|
|
13
|
+
|
|
14
|
+
// Terminal UI
|
|
15
|
+
let terminalUI;
|
|
16
|
+
try {
|
|
17
|
+
terminalUI = require("./lib/terminal-ui");
|
|
18
|
+
} catch {
|
|
19
|
+
terminalUI = {
|
|
20
|
+
c: { reset: "", bold: "", dim: "", red: "", yellow: "", green: "", cyan: "" },
|
|
21
|
+
rgb: () => "",
|
|
22
|
+
icons: { check: "✓", cross: "✗", warning: "⚠", info: "ℹ" },
|
|
23
|
+
Spinner: class { start() {} succeed() {} fail() {} },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { c, rgb, icons, Spinner } = terminalUI;
|
|
28
|
+
|
|
29
|
+
// Unified Output System
|
|
30
|
+
const { output } = require("./lib/output/index.js");
|
|
31
|
+
|
|
32
|
+
// Colors
|
|
33
|
+
const colors = {
|
|
34
|
+
critical: rgb(255, 80, 80),
|
|
35
|
+
high: rgb(255, 150, 50),
|
|
36
|
+
medium: rgb(255, 200, 0),
|
|
37
|
+
low: rgb(100, 200, 255),
|
|
38
|
+
info: rgb(150, 150, 150),
|
|
39
|
+
success: rgb(0, 255, 150),
|
|
40
|
+
accent: rgb(0, 200, 255),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const BANNER = `
|
|
44
|
+
${rgb(0, 200, 255)} ╔═══════════════════════════════════════════════════════════════╗${c.reset}
|
|
45
|
+
${rgb(0, 180, 255)} ║ ${c.bold}CONTAINER SECURITY${c.reset}${rgb(0, 180, 255)} • Dockerfile Scanning ║${c.reset}
|
|
46
|
+
${rgb(0, 160, 255)} ╚═══════════════════════════════════════════════════════════════╝${c.reset}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
function printBanner() {
|
|
50
|
+
console.log(BANNER);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function printHelp() {
|
|
54
|
+
console.log(`
|
|
55
|
+
${c.bold}Usage:${c.reset} vibecheck container [options] [path]
|
|
56
|
+
|
|
57
|
+
${c.bold}Description:${c.reset}
|
|
58
|
+
Scan Dockerfiles for security issues and best practices.
|
|
59
|
+
|
|
60
|
+
${c.bold}Options:${c.reset}
|
|
61
|
+
${colors.accent}--path, -p${c.reset} Path to Dockerfile or directory (default: .)
|
|
62
|
+
${colors.accent}--json${c.reset} Output as JSON
|
|
63
|
+
${colors.accent}--sarif${c.reset} Output as SARIF for GitHub
|
|
64
|
+
${colors.accent}--ignore${c.reset} Ignore specific rule IDs (comma-separated)
|
|
65
|
+
${colors.accent}--min-severity${c.reset} Minimum severity to report (critical/high/medium/low)
|
|
66
|
+
${colors.accent}--verbose, -v${c.reset} Show detailed output
|
|
67
|
+
${colors.accent}--help, -h${c.reset} Show this help
|
|
68
|
+
|
|
69
|
+
${c.bold}Examples:${c.reset}
|
|
70
|
+
${c.dim}# Scan current directory${c.reset}
|
|
71
|
+
vibecheck container
|
|
72
|
+
|
|
73
|
+
${c.dim}# Scan specific Dockerfile${c.reset}
|
|
74
|
+
vibecheck container Dockerfile.prod
|
|
75
|
+
|
|
76
|
+
${c.dim}# JSON output for CI${c.reset}
|
|
77
|
+
vibecheck container --json
|
|
78
|
+
|
|
79
|
+
${c.dim}# Ignore specific rules${c.reset}
|
|
80
|
+
vibecheck container --ignore CIS-DI-0001,BP-003
|
|
81
|
+
`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseArgs(args) {
|
|
85
|
+
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
|
|
86
|
+
|
|
87
|
+
const opts = {
|
|
88
|
+
path: globalFlags.path || ".",
|
|
89
|
+
json: globalFlags.json || false,
|
|
90
|
+
sarif: false,
|
|
91
|
+
verbose: globalFlags.verbose || false,
|
|
92
|
+
help: globalFlags.help || false,
|
|
93
|
+
ignore: [],
|
|
94
|
+
minSeverity: null,
|
|
95
|
+
noBanner: globalFlags.noBanner || false,
|
|
96
|
+
ci: globalFlags.ci || false,
|
|
97
|
+
quiet: globalFlags.quiet || false,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < cleanArgs.length; i++) {
|
|
101
|
+
const arg = cleanArgs[i];
|
|
102
|
+
|
|
103
|
+
if (arg === "--sarif") opts.sarif = true;
|
|
104
|
+
else if (arg === "--ignore") {
|
|
105
|
+
opts.ignore = (cleanArgs[++i] || "").split(",").filter(Boolean);
|
|
106
|
+
}
|
|
107
|
+
else if (arg.startsWith("--ignore=")) {
|
|
108
|
+
opts.ignore = arg.split("=")[1].split(",").filter(Boolean);
|
|
109
|
+
}
|
|
110
|
+
else if (arg === "--min-severity") {
|
|
111
|
+
opts.minSeverity = cleanArgs[++i];
|
|
112
|
+
}
|
|
113
|
+
else if (arg.startsWith("--min-severity=")) {
|
|
114
|
+
opts.minSeverity = arg.split("=")[1];
|
|
115
|
+
}
|
|
116
|
+
else if (!arg.startsWith("-")) {
|
|
117
|
+
opts.path = arg;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return opts;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatFinding(finding, verbose = false) {
|
|
125
|
+
const severityColors = {
|
|
126
|
+
critical: colors.critical,
|
|
127
|
+
high: colors.high,
|
|
128
|
+
medium: colors.medium,
|
|
129
|
+
low: colors.low,
|
|
130
|
+
info: colors.info,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const severityIcons = {
|
|
134
|
+
critical: "🔴",
|
|
135
|
+
high: "🟠",
|
|
136
|
+
medium: "🟡",
|
|
137
|
+
low: "🔵",
|
|
138
|
+
info: "⚪",
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const color = severityColors[finding.severity] || colors.info;
|
|
142
|
+
const icon = severityIcons[finding.severity] || "⚪";
|
|
143
|
+
|
|
144
|
+
let output = ` ${icon} ${color}${finding.severity.toUpperCase()}${c.reset} ${finding.title}\n`;
|
|
145
|
+
output += ` ${c.dim}${finding.id}${c.reset}`;
|
|
146
|
+
|
|
147
|
+
if (finding.line) {
|
|
148
|
+
output += ` ${c.dim}• Line ${finding.line}${c.reset}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
output += "\n";
|
|
152
|
+
|
|
153
|
+
if (verbose) {
|
|
154
|
+
output += ` ${c.dim}${finding.description}${c.reset}\n`;
|
|
155
|
+
output += ` ${colors.accent}Fix:${c.reset} ${finding.recommendation}\n`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return output;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function runContainer(args) {
|
|
162
|
+
const opts = parseArgs(args);
|
|
163
|
+
|
|
164
|
+
if (opts.help) {
|
|
165
|
+
printHelp();
|
|
166
|
+
return EXIT.SUCCESS;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (shouldShowBanner(opts)) {
|
|
170
|
+
printBanner();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const spinner = new Spinner();
|
|
174
|
+
spinner.start("Scanning for Dockerfiles...");
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
// Dynamic import of the security package
|
|
178
|
+
let containerScanner;
|
|
179
|
+
try {
|
|
180
|
+
containerScanner = require("@vibecheck/security").scanDockerfile ||
|
|
181
|
+
require("@vibecheck/security").container?.scanDockerfile;
|
|
182
|
+
|
|
183
|
+
if (!containerScanner) {
|
|
184
|
+
// Try direct import
|
|
185
|
+
const security = require("../../packages/security/src/container/scanner");
|
|
186
|
+
containerScanner = security.scanDockerfile;
|
|
187
|
+
}
|
|
188
|
+
} catch (e) {
|
|
189
|
+
// Fallback: try relative path
|
|
190
|
+
try {
|
|
191
|
+
const security = require("../../packages/security/src/container/scanner");
|
|
192
|
+
containerScanner = security.scanDockerfile;
|
|
193
|
+
} catch {
|
|
194
|
+
spinner.fail("Container scanning module not available");
|
|
195
|
+
console.error(`\n ${colors.critical}${icons.cross}${c.reset} Container scanning requires @vibecheck/security package\n`);
|
|
196
|
+
return EXIT.INTERNAL_ERROR;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const targetPath = path.resolve(opts.path);
|
|
201
|
+
const stat = fs.statSync(targetPath);
|
|
202
|
+
|
|
203
|
+
let results = [];
|
|
204
|
+
|
|
205
|
+
if (stat.isDirectory()) {
|
|
206
|
+
// Find all Dockerfiles in directory
|
|
207
|
+
const dockerfiles = [];
|
|
208
|
+
|
|
209
|
+
function findDockerfiles(dir) {
|
|
210
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
211
|
+
for (const entry of entries) {
|
|
212
|
+
const fullPath = path.join(dir, entry.name);
|
|
213
|
+
if (entry.isDirectory() && !["node_modules", ".git", ".terraform"].includes(entry.name)) {
|
|
214
|
+
findDockerfiles(fullPath);
|
|
215
|
+
} else if (
|
|
216
|
+
entry.name === "Dockerfile" ||
|
|
217
|
+
entry.name.match(/^Dockerfile\.\w+$/) ||
|
|
218
|
+
entry.name.match(/\.dockerfile$/i)
|
|
219
|
+
) {
|
|
220
|
+
dockerfiles.push(fullPath);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
findDockerfiles(targetPath);
|
|
226
|
+
|
|
227
|
+
if (dockerfiles.length === 0) {
|
|
228
|
+
spinner.succeed("No Dockerfiles found");
|
|
229
|
+
console.log(`\n ${c.dim}No Dockerfiles found in ${targetPath}${c.reset}\n`);
|
|
230
|
+
return EXIT.SUCCESS;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
spinner.succeed(`Found ${dockerfiles.length} Dockerfile(s)`);
|
|
234
|
+
|
|
235
|
+
for (const dockerfile of dockerfiles) {
|
|
236
|
+
const result = await containerScanner({
|
|
237
|
+
dockerfilePath: dockerfile,
|
|
238
|
+
ignoreRules: opts.ignore,
|
|
239
|
+
minSeverity: opts.minSeverity,
|
|
240
|
+
});
|
|
241
|
+
results.push(result);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
// Single file
|
|
245
|
+
spinner.succeed("Scanning Dockerfile");
|
|
246
|
+
const result = await containerScanner({
|
|
247
|
+
dockerfilePath: targetPath,
|
|
248
|
+
ignoreRules: opts.ignore,
|
|
249
|
+
minSeverity: opts.minSeverity,
|
|
250
|
+
});
|
|
251
|
+
results.push(result);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Aggregate results
|
|
255
|
+
const totalFindings = results.flatMap(r => r.findings);
|
|
256
|
+
const totalScore = results.length > 0
|
|
257
|
+
? Math.round(results.reduce((sum, r) => sum + r.score, 0) / results.length)
|
|
258
|
+
: 100;
|
|
259
|
+
|
|
260
|
+
// JSON output
|
|
261
|
+
if (opts.json) {
|
|
262
|
+
console.log(JSON.stringify({
|
|
263
|
+
success: true,
|
|
264
|
+
results,
|
|
265
|
+
summary: {
|
|
266
|
+
filesScanned: results.length,
|
|
267
|
+
totalFindings: totalFindings.length,
|
|
268
|
+
score: totalScore,
|
|
269
|
+
critical: totalFindings.filter(f => f.severity === "critical").length,
|
|
270
|
+
high: totalFindings.filter(f => f.severity === "high").length,
|
|
271
|
+
medium: totalFindings.filter(f => f.severity === "medium").length,
|
|
272
|
+
low: totalFindings.filter(f => f.severity === "low").length,
|
|
273
|
+
},
|
|
274
|
+
}, null, 2));
|
|
275
|
+
|
|
276
|
+
return totalFindings.some(f => f.severity === "critical" || f.severity === "high")
|
|
277
|
+
? EXIT.BLOCKING
|
|
278
|
+
: totalFindings.length > 0 ? EXIT.WARNINGS : EXIT.SUCCESS;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// SARIF output
|
|
282
|
+
if (opts.sarif) {
|
|
283
|
+
const sarif = {
|
|
284
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
285
|
+
version: "2.1.0",
|
|
286
|
+
runs: [{
|
|
287
|
+
tool: {
|
|
288
|
+
driver: {
|
|
289
|
+
name: "vibecheck-container",
|
|
290
|
+
version: "1.0.0",
|
|
291
|
+
informationUri: "https://vibecheckai.dev",
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
results: totalFindings.map(f => ({
|
|
295
|
+
ruleId: f.id,
|
|
296
|
+
level: f.severity === "critical" || f.severity === "high" ? "error" : "warning",
|
|
297
|
+
message: { text: f.description },
|
|
298
|
+
locations: [{
|
|
299
|
+
physicalLocation: {
|
|
300
|
+
artifactLocation: { uri: f.file || opts.path },
|
|
301
|
+
region: f.line ? { startLine: f.line } : undefined,
|
|
302
|
+
},
|
|
303
|
+
}],
|
|
304
|
+
})),
|
|
305
|
+
}],
|
|
306
|
+
};
|
|
307
|
+
console.log(JSON.stringify(sarif, null, 2));
|
|
308
|
+
return EXIT.SUCCESS;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Human-readable output
|
|
312
|
+
console.log("");
|
|
313
|
+
|
|
314
|
+
for (const result of results) {
|
|
315
|
+
const relPath = path.relative(process.cwd(), result.dockerfile);
|
|
316
|
+
console.log(`${c.bold}📄 ${relPath}${c.reset}`);
|
|
317
|
+
console.log(` Score: ${result.score >= 80 ? colors.success : result.score >= 60 ? colors.medium : colors.critical}${result.score}/100${c.reset}`);
|
|
318
|
+
console.log("");
|
|
319
|
+
|
|
320
|
+
if (result.findings.length === 0) {
|
|
321
|
+
console.log(` ${colors.success}${icons.check}${c.reset} No issues found\n`);
|
|
322
|
+
} else {
|
|
323
|
+
// Group by severity
|
|
324
|
+
const bySeverity = {
|
|
325
|
+
critical: result.findings.filter(f => f.severity === "critical"),
|
|
326
|
+
high: result.findings.filter(f => f.severity === "high"),
|
|
327
|
+
medium: result.findings.filter(f => f.severity === "medium"),
|
|
328
|
+
low: result.findings.filter(f => f.severity === "low"),
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
for (const [severity, findings] of Object.entries(bySeverity)) {
|
|
332
|
+
if (findings.length > 0) {
|
|
333
|
+
for (const finding of findings) {
|
|
334
|
+
console.log(formatFinding(finding, opts.verbose));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Recommendations
|
|
341
|
+
if (result.recommendations && result.recommendations.length > 0 && opts.verbose) {
|
|
342
|
+
console.log(`${c.bold} 💡 Recommendations:${c.reset}`);
|
|
343
|
+
for (const rec of result.recommendations) {
|
|
344
|
+
console.log(` • ${rec}`);
|
|
345
|
+
}
|
|
346
|
+
console.log("");
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Summary
|
|
351
|
+
console.log(`${c.dim}─────────────────────────────────────────────────────────────${c.reset}`);
|
|
352
|
+
console.log(`${c.bold}Summary:${c.reset} ${results.length} file(s), ${totalFindings.length} finding(s), Score: ${totalScore}/100`);
|
|
353
|
+
console.log("");
|
|
354
|
+
|
|
355
|
+
return totalFindings.some(f => f.severity === "critical" || f.severity === "high")
|
|
356
|
+
? EXIT.BLOCKING
|
|
357
|
+
: totalFindings.length > 0 ? EXIT.WARNINGS : EXIT.SUCCESS;
|
|
358
|
+
|
|
359
|
+
} catch (error) {
|
|
360
|
+
spinner.fail("Scan failed");
|
|
361
|
+
console.error(`\n ${colors.critical}${icons.cross}${c.reset} ${error.message}\n`);
|
|
362
|
+
return EXIT.INTERNAL_ERROR;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
module.exports = { runContainer };
|
|
@@ -24,6 +24,9 @@ const {
|
|
|
24
24
|
getTierFromKey,
|
|
25
25
|
} = require("./lib/unified-cli-output");
|
|
26
26
|
|
|
27
|
+
// Unified Output System
|
|
28
|
+
const { output } = require("./lib/output/index.js");
|
|
29
|
+
|
|
27
30
|
async function runContext(args) {
|
|
28
31
|
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
|
|
29
32
|
const quiet = shouldSuppressOutput(globalFlags);
|
package/bin/runners/runDoctor.js
CHANGED
|
@@ -24,44 +24,15 @@ const { formatWorkflowUpsell } = require("./lib/upsell");
|
|
|
24
24
|
const { getApiKey } = require("./lib/auth");
|
|
25
25
|
|
|
26
26
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
27
|
-
//
|
|
27
|
+
// TERMINAL UI - Import from shared module
|
|
28
28
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
29
29
|
|
|
30
|
-
const c =
|
|
31
|
-
reset: '\x1b[0m',
|
|
32
|
-
bold: '\x1b[1m',
|
|
33
|
-
dim: '\x1b[2m',
|
|
34
|
-
italic: '\x1b[3m',
|
|
35
|
-
underline: '\x1b[4m',
|
|
36
|
-
// Colors
|
|
37
|
-
black: '\x1b[30m',
|
|
38
|
-
red: '\x1b[31m',
|
|
39
|
-
green: '\x1b[32m',
|
|
40
|
-
yellow: '\x1b[33m',
|
|
41
|
-
blue: '\x1b[34m',
|
|
42
|
-
magenta: '\x1b[35m',
|
|
43
|
-
cyan: '\x1b[36m',
|
|
44
|
-
white: '\x1b[37m',
|
|
45
|
-
// Bright colors
|
|
46
|
-
gray: '\x1b[90m',
|
|
47
|
-
brightRed: '\x1b[91m',
|
|
48
|
-
brightGreen: '\x1b[92m',
|
|
49
|
-
brightYellow: '\x1b[93m',
|
|
50
|
-
brightBlue: '\x1b[94m',
|
|
51
|
-
brightMagenta: '\x1b[95m',
|
|
52
|
-
brightCyan: '\x1b[96m',
|
|
53
|
-
brightWhite: '\x1b[97m',
|
|
54
|
-
// Cursor control
|
|
55
|
-
clearLine: '\x1b[2K',
|
|
56
|
-
hideCursor: '\x1b[?25l',
|
|
57
|
-
showCursor: '\x1b[?25h',
|
|
58
|
-
};
|
|
30
|
+
const { c, rgb, bgRgb, icons, Spinner } = require("./lib/terminal-ui");
|
|
59
31
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
|
|
32
|
+
// Unified Output System
|
|
33
|
+
const { output } = require("./lib/output/index.js");
|
|
63
34
|
|
|
64
|
-
//
|
|
35
|
+
// Extended color palette for doctor command
|
|
65
36
|
const colors = {
|
|
66
37
|
// Gradients for banner
|
|
67
38
|
gradient1: rgb(100, 200, 255), // Light blue
|
|
@@ -305,6 +276,13 @@ async function runDoctor(args, context = {}) {
|
|
|
305
276
|
const json = isJsonMode(opts);
|
|
306
277
|
const executionStart = Date.now();
|
|
307
278
|
|
|
279
|
+
// Configure unified output mode
|
|
280
|
+
output.setMode({
|
|
281
|
+
json: json,
|
|
282
|
+
quiet: quiet,
|
|
283
|
+
ci: opts.ci
|
|
284
|
+
});
|
|
285
|
+
|
|
308
286
|
if (opts.help) {
|
|
309
287
|
printHelp(shouldShowBanner(opts));
|
|
310
288
|
return EXIT.SUCCESS;
|
|
@@ -603,14 +581,20 @@ function runDoctorLegacy() {
|
|
|
603
581
|
|
|
604
582
|
// Get tier for upsell
|
|
605
583
|
const { key } = getApiKey();
|
|
606
|
-
const currentTier = key ? "
|
|
584
|
+
const currentTier = key ? "pro" : "free";
|
|
607
585
|
|
|
608
586
|
if (hasIssues) {
|
|
609
587
|
console.log(" ❌ Issues found. Fix them and run doctor again.");
|
|
610
588
|
console.log();
|
|
611
|
-
|
|
589
|
+
|
|
612
590
|
if (currentTier === "free") {
|
|
613
|
-
console.log(` ${c.
|
|
591
|
+
console.log(` ${c.gray}╭${'─'.repeat(58)}╮${c.reset}`);
|
|
592
|
+
console.log(` ${c.gray}│${c.reset} ${c.magenta}★ PRO${c.reset} Auto-fix environment issues with AI ${c.gray}│${c.reset}`);
|
|
593
|
+
console.log(` ${c.gray}│${c.reset} ${c.cyan}vibecheck fix${c.reset} can auto-repair many issues ${c.gray}│${c.reset}`);
|
|
594
|
+
console.log(` ${c.gray}│${c.reset} ${c.dim}https://vibecheckai.dev/pricing${c.reset} ${c.gray}│${c.reset}`);
|
|
595
|
+
console.log(` ${c.gray}╰${'─'.repeat(58)}╯${c.reset}`);
|
|
596
|
+
} else {
|
|
597
|
+
console.log(` ${c.dim}Need help?${c.reset} ${colors.accent}vibecheck fix${c.reset} ${c.dim}can auto-repair many issues${c.reset}`);
|
|
614
598
|
}
|
|
615
599
|
console.log();
|
|
616
600
|
return EXIT.BLOCKING;
|
|
@@ -618,10 +602,13 @@ function runDoctorLegacy() {
|
|
|
618
602
|
console.log(" ✅ Environment healthy!");
|
|
619
603
|
console.log();
|
|
620
604
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
console.log(` ${
|
|
605
|
+
if (currentTier === "free") {
|
|
606
|
+
console.log(` ${c.gray}╭${'─'.repeat(58)}╮${c.reset}`);
|
|
607
|
+
console.log(` ${c.gray}│${c.reset} ${c.green}✓${c.reset} Ready to scan! Try: ${c.cyan}vibecheck scan${c.reset} ${c.gray}│${c.reset}`);
|
|
608
|
+
console.log(` ${c.gray}│${c.reset} ${c.gray}│${c.reset}`);
|
|
609
|
+
console.log(` ${c.gray}│${c.reset} ${c.magenta}★ PRO${c.reset} AI-powered fixes, CI gates, ship badges ${c.gray}│${c.reset}`);
|
|
610
|
+
console.log(` ${c.gray}│${c.reset} ${c.dim}https://vibecheckai.dev/pricing${c.reset} ${c.gray}│${c.reset}`);
|
|
611
|
+
console.log(` ${c.gray}╰${'─'.repeat(58)}╯${c.reset}`);
|
|
625
612
|
} else {
|
|
626
613
|
console.log(` ${c.dim}Ready to scan?${c.reset} ${colors.accent}vibecheck scan${c.reset} ${c.dim}finds AI mistakes before they ship${c.reset}`);
|
|
627
614
|
}
|