@vibecheckai/cli 3.2.2 → 3.2.4
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/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,190 +1,525 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enterprise Scan Output - Premium Format
|
|
3
|
-
* Features:
|
|
4
|
-
* - "SCAN" ASCII Art
|
|
5
|
-
* - Fixed alignment tables
|
|
6
|
-
* - "Redacted" Upsell section for Scan context
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Scan Output - Premium Format
|
|
3
|
+
* Features:
|
|
4
|
+
* - "SCAN" ASCII Art
|
|
5
|
+
* - Fixed alignment tables
|
|
6
|
+
* - "Redacted" Upsell section for Scan context
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Use ANSI codes directly (chalk v5 is ESM-only, this is CommonJS)
|
|
10
|
+
const ESC = '\x1b';
|
|
11
|
+
const chalk = {
|
|
12
|
+
reset: `${ESC}[0m`,
|
|
13
|
+
bold: `${ESC}[1m`,
|
|
14
|
+
dim: `${ESC}[2m`,
|
|
15
|
+
red: `${ESC}[31m`,
|
|
16
|
+
green: `${ESC}[32m`,
|
|
17
|
+
yellow: `${ESC}[33m`,
|
|
18
|
+
cyan: `${ESC}[36m`,
|
|
19
|
+
magenta: `${ESC}[35m`,
|
|
20
|
+
white: `${ESC}[37m`,
|
|
21
|
+
gray: `${ESC}[90m`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
25
|
+
// CONFIGURATION
|
|
26
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
27
|
+
|
|
28
|
+
const WIDTH = 76;
|
|
29
|
+
|
|
30
|
+
const BOX = {
|
|
31
|
+
topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
|
|
32
|
+
horizontal: '═', vertical: '║',
|
|
33
|
+
teeRight: '╠', teeLeft: '╣',
|
|
34
|
+
|
|
35
|
+
// Table Borders (Light)
|
|
36
|
+
tTopLeft: '┌', tTopRight: '┐', tBottomLeft: '└', tBottomRight: '┘',
|
|
37
|
+
tHorizontal: '─', tVertical: '│',
|
|
38
|
+
tTeeTop: '┬', tTeeBottom: '┴', tTee: '┼', tTeeLeft: '├', tTeeRight: '┤'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const LOGO_SCAN = `
|
|
42
|
+
█████████ █████████ █████████ █████████
|
|
43
|
+
███░░░░░███ ███░░░░░███ ███░░░░░███ ███░░░░░███
|
|
44
|
+
░███ ░░░ ░███ ░░░ ░███ ░███ ░███ ░███
|
|
45
|
+
░░█████████ ░███ ░███████████ ░███ ░███
|
|
46
|
+
░░░░░░░░███░███ ░███░░░░░███ ░███ ░███
|
|
47
|
+
███ ░███░███ ███ ░███ ░███ ░███ ░███
|
|
48
|
+
░░█████████ ░░█████████ ░███ ░███ ░███ ░███
|
|
49
|
+
░░░░░░░░░ ░░░░░░░░░ ░░░░ ░░░░ ░░░░ ░░░░
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
// Column widths for the table (Must correspond to math below)
|
|
53
|
+
const COL_1 = 10; // Severity
|
|
54
|
+
const COL_2 = 13; // Component
|
|
55
|
+
const COL_3 = 41; // Message
|
|
56
|
+
|
|
57
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
58
|
+
// UTILITIES
|
|
59
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
60
|
+
|
|
61
|
+
function padCenter(str, width) {
|
|
62
|
+
const visibleLen = str.replace(/\u001b\[\d+m/g, '').length;
|
|
63
|
+
const padding = Math.max(0, width - visibleLen);
|
|
64
|
+
const left = Math.floor(padding / 2);
|
|
65
|
+
const right = padding - left;
|
|
66
|
+
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function padRight(str, len) {
|
|
70
|
+
const visibleLen = str.length; // Simplified for this usage
|
|
71
|
+
const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
|
|
72
|
+
return truncated + ' '.repeat(Math.max(0, len - truncated.length));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function padLogoBlock(ascii, width) {
|
|
76
|
+
const lines = ascii.trim().split('\n');
|
|
77
|
+
const maxContentWidth = Math.max(...lines.map(l => l.length));
|
|
78
|
+
|
|
79
|
+
return lines.map(line => {
|
|
80
|
+
const solidLine = line + ' '.repeat(maxContentWidth - line.length);
|
|
81
|
+
return padCenter(solidLine, width);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function renderProgressBar(score, width = 20) {
|
|
86
|
+
const filled = Math.round((score / 100) * width);
|
|
87
|
+
const bar = `${chalk.green}█${chalk.reset}`.repeat(filled) + `${chalk.gray}░${chalk.reset}`.repeat(width - filled);
|
|
88
|
+
return bar;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
92
|
+
// MAIN FORMATTER
|
|
93
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
94
|
+
|
|
95
|
+
function formatScanOutput(result, options = {}) {
|
|
96
|
+
// Extract data from various possible structures
|
|
97
|
+
let score = 0;
|
|
98
|
+
let findings = [];
|
|
99
|
+
let duration = 0;
|
|
100
|
+
let scannedFiles = 0;
|
|
101
|
+
|
|
102
|
+
// Helper function to calculate score from findings
|
|
103
|
+
function calculateScoreFromFindings(findings) {
|
|
104
|
+
if (!findings || findings.length === 0) return 100;
|
|
105
|
+
|
|
106
|
+
// Count by severity (handle various severity formats)
|
|
107
|
+
const severityCounts = {
|
|
108
|
+
critical: 0,
|
|
109
|
+
high: 0,
|
|
110
|
+
medium: 0,
|
|
111
|
+
low: 0,
|
|
112
|
+
info: 0,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
findings.forEach(f => {
|
|
116
|
+
const sev = (f.severity || '').toLowerCase();
|
|
117
|
+
if (sev === 'critical' || sev === 'block') {
|
|
118
|
+
severityCounts.critical++;
|
|
119
|
+
} else if (sev === 'high') {
|
|
120
|
+
severityCounts.high++;
|
|
121
|
+
} else if (sev === 'medium' || sev === 'warn' || sev === 'warning') {
|
|
122
|
+
severityCounts.medium++;
|
|
123
|
+
} else if (sev === 'low') {
|
|
124
|
+
severityCounts.low++;
|
|
125
|
+
} else if (sev === 'info') {
|
|
126
|
+
severityCounts.info++;
|
|
127
|
+
} else {
|
|
128
|
+
// Default unknown severities to medium
|
|
129
|
+
severityCounts.medium++;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Calculate score with proper weights
|
|
134
|
+
// Critical: 25 points each, High: 15, Medium: 5, Low: 2, Info: 0
|
|
135
|
+
const deductions =
|
|
136
|
+
(severityCounts.critical * 25) +
|
|
137
|
+
(severityCounts.high * 15) +
|
|
138
|
+
(severityCounts.medium * 5) +
|
|
139
|
+
(severityCounts.low * 2);
|
|
140
|
+
|
|
141
|
+
// Cap deductions to prevent negative scores, but allow score to go to 0
|
|
142
|
+
const calculatedScore = Math.max(0, 100 - deductions);
|
|
143
|
+
|
|
144
|
+
// If we have findings but score is still 100, something's wrong - recalculate more aggressively
|
|
145
|
+
if (findings.length > 0 && calculatedScore === 100 && deductions === 0) {
|
|
146
|
+
// All findings were info or unknown - still deduct something
|
|
147
|
+
return Math.max(50, 100 - (findings.length * 0.1));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return Math.round(calculatedScore);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Handle different input structures
|
|
154
|
+
if (result.verdict) {
|
|
155
|
+
// New unified output structure
|
|
156
|
+
if (typeof result.verdict === 'object') {
|
|
157
|
+
findings = result.findings || [];
|
|
158
|
+
|
|
159
|
+
// Always recalculate score from findings to ensure accuracy
|
|
160
|
+
score = calculateScoreFromFindings(findings);
|
|
161
|
+
|
|
162
|
+
// Only use provided score if it seems reasonable (not 100 when we have findings)
|
|
163
|
+
if (result.verdict.score && findings.length === 0) {
|
|
164
|
+
score = result.verdict.score;
|
|
165
|
+
} else if (result.verdict.score && result.verdict.score < score) {
|
|
166
|
+
// Use the lower (worse) score if provided score is worse
|
|
167
|
+
score = result.verdict.score;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
duration = result.timings?.total || result.duration || 0;
|
|
171
|
+
scannedFiles = result.timings?.filesScanned || result.scannedFiles || findings.length || 0;
|
|
172
|
+
} else {
|
|
173
|
+
// Legacy structure where verdict is just a string
|
|
174
|
+
findings = result.findings || [];
|
|
175
|
+
score = calculateScoreFromFindings(findings);
|
|
176
|
+
|
|
177
|
+
// Fallback to verdict string if no findings
|
|
178
|
+
if (!score && findings.length === 0) {
|
|
179
|
+
const verdictStr = result.verdict;
|
|
180
|
+
if (verdictStr === 'PASS' || verdictStr === 'SHIP') score = 100;
|
|
181
|
+
else if (verdictStr === 'WARN') score = 70;
|
|
182
|
+
else if (verdictStr === 'FAIL' || verdictStr === 'BLOCK') score = 40;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
duration = result.timings?.total || result.duration || 0;
|
|
186
|
+
scannedFiles = result.timings?.filesScanned || result.scannedFiles || findings.length || 0;
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
// Direct structure
|
|
190
|
+
findings = result.findings || [];
|
|
191
|
+
score = calculateScoreFromFindings(findings);
|
|
192
|
+
|
|
193
|
+
// Only use provided score if it seems reasonable
|
|
194
|
+
if (result.score && findings.length === 0) {
|
|
195
|
+
score = result.score;
|
|
196
|
+
} else if (result.score && result.score < score) {
|
|
197
|
+
score = result.score; // Use worse score
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
duration = result.duration || result.timings?.total || 0;
|
|
201
|
+
scannedFiles = result.scannedFiles || result.timings?.filesScanned || 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const hasIssues = findings.length > 0;
|
|
205
|
+
const heapMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
206
|
+
const lines = [];
|
|
207
|
+
|
|
208
|
+
// 1. OUTER FRAME TOP
|
|
209
|
+
lines.push(`${chalk.gray}${BOX.topLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.topRight}${chalk.reset}`);
|
|
210
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
211
|
+
|
|
212
|
+
// 2. LOGO (Cyan if clean, Red if issues)
|
|
213
|
+
const logoColorCode = hasIssues ? chalk.red : chalk.cyan;
|
|
214
|
+
padLogoBlock(LOGO_SCAN, WIDTH - 2).forEach(line => {
|
|
215
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${logoColorCode}${line}${chalk.reset}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const subTitle = hasIssues ? 'INTEGRITY SCAN • ISSUES DETECTED' : 'INTEGRITY SCAN • CLEAN';
|
|
219
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.bold}${chalk.white}${subTitle}${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
220
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
221
|
+
|
|
222
|
+
// 3. TELEMETRY
|
|
223
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.teeLeft}${chalk.reset}`);
|
|
224
|
+
const stats = `📡 TELEMETRY │ ⏱ ${duration}ms │ 📂 ${scannedFiles} Files │ 📦 ${heapMB}MB`;
|
|
225
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(stats, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
226
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.teeLeft}${chalk.reset}`);
|
|
227
|
+
|
|
228
|
+
if (!hasIssues) {
|
|
229
|
+
// -- CLEAN STATE --
|
|
230
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
231
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.green}✅ NO STATIC ISSUES FOUND${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
232
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
233
|
+
} else {
|
|
234
|
+
// -- ISSUES STATE --
|
|
235
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
236
|
+
const scoreBar = renderProgressBar(score, 20);
|
|
237
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`HEALTH SCORE [${scoreBar}] ${score} / 100`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
238
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
239
|
+
|
|
240
|
+
// 4. FINDINGS SUMMARY BY CATEGORY
|
|
241
|
+
const categoryCounts = {};
|
|
242
|
+
findings.forEach(f => {
|
|
243
|
+
const cat = f.category || f.type || f.ruleId || 'Other';
|
|
244
|
+
categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const categoryLabels = {
|
|
248
|
+
'EnvContract': '🔐 Env Vars',
|
|
249
|
+
'MissingRoute': '🔗 Routes',
|
|
250
|
+
'GhostAuth': '🛡️ Auth',
|
|
251
|
+
'FakeSuccess': '⚠️ Fake Success',
|
|
252
|
+
'MockData': '🎭 Mock Data',
|
|
253
|
+
'Secrets': '🔑 Secrets',
|
|
254
|
+
'ConsoleLog': '📝 Console Logs',
|
|
255
|
+
'TodoFixme': '📋 TODOs',
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const summaryItems = Object.entries(categoryCounts)
|
|
259
|
+
.sort((a, b) => b[1] - a[1])
|
|
260
|
+
.slice(0, 6)
|
|
261
|
+
.map(([cat, count]) => {
|
|
262
|
+
const label = categoryLabels[cat] || `📌 ${cat}`;
|
|
263
|
+
return `${label}: ${chalk.bold}${count}${chalk.reset}`;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
if (summaryItems.length > 0) {
|
|
267
|
+
// Split into two lines if too long
|
|
268
|
+
const categoryText = summaryItems.join(' • ');
|
|
269
|
+
const prefix = `${chalk.dim}Findings by Category:${chalk.reset} `;
|
|
270
|
+
const maxLineLength = WIDTH - 10; // Leave some margin
|
|
271
|
+
|
|
272
|
+
if (categoryText.length > maxLineLength - prefix.length) {
|
|
273
|
+
// Split into two lines
|
|
274
|
+
const midPoint = Math.floor(summaryItems.length / 2);
|
|
275
|
+
const line1 = summaryItems.slice(0, midPoint).join(' • ');
|
|
276
|
+
const line2 = summaryItems.slice(midPoint).join(' • ');
|
|
277
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${prefix}${line1}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
278
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${' '.repeat(prefix.length)}${line2}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
279
|
+
} else {
|
|
280
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${prefix}${categoryText}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
281
|
+
}
|
|
282
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 5. FINDINGS TABLE
|
|
286
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter('DETECTED VULNERABILITIES (STATIC)', WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
287
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
288
|
+
|
|
289
|
+
// Table Construction - Fixed alignment with proper spacing
|
|
290
|
+
const C1 = COL_1, C2 = COL_2, C3 = COL_3;
|
|
291
|
+
const tableContentWidth = C1 + C2 + C3 + 7; // 7 = borders (3 vertical + 4 spaces)
|
|
292
|
+
const tableLeftPad = Math.floor((WIDTH - 2 - tableContentWidth) / 2);
|
|
293
|
+
const tablePad = ' '.repeat(Math.max(1, tableLeftPad)); // At least 1 space padding
|
|
294
|
+
|
|
295
|
+
// Table borders without padding (we'll add padding when inserting into frame)
|
|
296
|
+
const tTop = `${BOX.tTopLeft}${BOX.tHorizontal.repeat(C1)}${BOX.tTeeTop}${BOX.tHorizontal.repeat(C2)}${BOX.tTeeTop}${BOX.tHorizontal.repeat(C3)}${BOX.tTopRight}`;
|
|
297
|
+
const tMid = `${BOX.tTeeLeft}${BOX.tHorizontal.repeat(C1)}${BOX.tTee}${BOX.tHorizontal.repeat(C2)}${BOX.tTee}${BOX.tHorizontal.repeat(C3)}${BOX.tTeeRight}`;
|
|
298
|
+
const tBot = `${BOX.tBottomLeft}${BOX.tHorizontal.repeat(C1)}${BOX.tTeeBottom}${BOX.tHorizontal.repeat(C2)}${BOX.tTeeBottom}${BOX.tHorizontal.repeat(C3)}${BOX.tBottomRight}`;
|
|
299
|
+
|
|
300
|
+
// Calculate right padding to fill the line
|
|
301
|
+
const totalTableWidth = tablePad.length + tableContentWidth;
|
|
302
|
+
const rightPad = WIDTH - 2 - totalTableWidth;
|
|
303
|
+
const rightPadStr = ' '.repeat(Math.max(0, rightPad));
|
|
304
|
+
|
|
305
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${tablePad}${chalk.gray}${tTop}${chalk.reset}${rightPadStr}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
306
|
+
const header = `${tablePad}${BOX.tVertical}${padRight(' SEVERITY', C1)}${BOX.tVertical}${padRight(' TYPE', C2)}${BOX.tVertical}${padRight(' FINDING', C3)}${BOX.tVertical}`;
|
|
307
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${chalk.bold}${header}${chalk.reset}${rightPadStr}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
308
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${tablePad}${chalk.gray}${tMid}${chalk.reset}${rightPadStr}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
309
|
+
|
|
310
|
+
// Rows - map findings to display format (show top 10)
|
|
311
|
+
const topItems = findings.slice(0, 10);
|
|
312
|
+
topItems.forEach(item => {
|
|
313
|
+
// Handle different finding structures
|
|
314
|
+
const severityText = item.severity === 'critical' || item.severity === 'BLOCK' || item.category === 'critical'
|
|
315
|
+
? '🛑 CRIT '
|
|
316
|
+
: item.severity === 'warning' || item.severity === 'WARN' || item.category === 'warning'
|
|
317
|
+
? '🟡 WARN '
|
|
318
|
+
: '🟡 WARN ';
|
|
319
|
+
|
|
320
|
+
const severityColor = item.severity === 'critical' || item.severity === 'BLOCK' || item.category === 'critical'
|
|
321
|
+
? chalk.red
|
|
322
|
+
: chalk.yellow;
|
|
323
|
+
|
|
324
|
+
const severity = `${severityColor}${severityText}${chalk.reset}`;
|
|
325
|
+
|
|
326
|
+
// Map category to readable type
|
|
327
|
+
const categoryMap = {
|
|
328
|
+
'EnvContract': 'EnvVar',
|
|
329
|
+
'MissingRoute': 'Route',
|
|
330
|
+
'GhostAuth': 'Auth',
|
|
331
|
+
'FakeSuccess': 'FakeSuccess',
|
|
332
|
+
'MockData': 'MockData',
|
|
333
|
+
'Secrets': 'Secret',
|
|
334
|
+
'ConsoleLog': 'Console',
|
|
335
|
+
'TodoFixme': 'TODO',
|
|
336
|
+
};
|
|
337
|
+
const typeName = categoryMap[item.category] || item.category || item.type || item.ruleId || 'Unknown';
|
|
338
|
+
const type = padRight(' ' + typeName, C2);
|
|
339
|
+
|
|
340
|
+
// Truncate description if too long
|
|
341
|
+
let desc = item.message || item.title || item.description || '';
|
|
342
|
+
if (desc.length > C3 - 1) {
|
|
343
|
+
desc = desc.substring(0, C3 - 4) + '...';
|
|
344
|
+
}
|
|
345
|
+
desc = padRight(' ' + desc, C3);
|
|
346
|
+
|
|
347
|
+
const row = `${tablePad}${chalk.gray}${BOX.tVertical}${chalk.reset}${padRight(severity, C1)}${chalk.gray}${BOX.tVertical}${chalk.reset}${type}${chalk.gray}${BOX.tVertical}${chalk.reset}${desc}${chalk.gray}${BOX.tVertical}${chalk.reset}`;
|
|
348
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${row}${rightPadStr}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${tablePad}${chalk.gray}${tBot}${chalk.reset}${rightPadStr}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
352
|
+
|
|
353
|
+
// Show count if more findings exist
|
|
354
|
+
if (findings.length > 10) {
|
|
355
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.dim}... and ${findings.length - 10} more findings${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 6. UPSELL SECTION - Autofix & Mission Packs
|
|
362
|
+
lines.push(`${chalk.gray}${BOX.teeRight}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.teeLeft}${chalk.reset}`);
|
|
363
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
364
|
+
|
|
365
|
+
// Autofix Upsell
|
|
366
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.bold}${chalk.cyan}🚀 AUTO-FIX AVAILABLE${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
367
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.dim}Fix ${findings.length} issues automatically with AI-powered autofix${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
368
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`Run ${chalk.cyan}vibecheck scan --autofix${chalk.reset} to apply fixes`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
369
|
+
|
|
370
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
371
|
+
|
|
372
|
+
// Mission Packs Upsell
|
|
373
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.bold}${chalk.magenta}📦 MISSION PACKS${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
374
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.dim}Get AI-generated fix plans grouped by feature area${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
375
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`Run ${chalk.cyan}vibecheck fix --packs${chalk.reset} to generate mission packs`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
376
|
+
|
|
377
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
378
|
+
|
|
379
|
+
// Upgrade CTA
|
|
380
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${chalk.bold}★ Upgrade for unlimited scans + auto-fix${chalk.reset} → ${chalk.cyan}https://vibecheckai.dev${chalk.reset}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
381
|
+
|
|
382
|
+
// BOTTOM FRAME
|
|
383
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
384
|
+
lines.push(`${chalk.gray}${BOX.bottomLeft}${BOX.horizontal.repeat(WIDTH - 2)}${BOX.bottomRight}${chalk.reset}`);
|
|
385
|
+
|
|
386
|
+
return lines.join('\n');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
390
|
+
// ADDITIONAL EXPORTS (for compatibility with runScan.js)
|
|
391
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
392
|
+
|
|
393
|
+
// Exit codes
|
|
394
|
+
const EXIT_CODES = {
|
|
395
|
+
SHIP: 0,
|
|
396
|
+
WARN: 1,
|
|
397
|
+
BLOCK: 2,
|
|
398
|
+
ERROR: 1,
|
|
399
|
+
MISCONFIG: 3,
|
|
400
|
+
INTERNAL: 1,
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// Severity weights for scoring
|
|
404
|
+
const SEVERITY_WEIGHTS = {
|
|
405
|
+
critical: 25,
|
|
406
|
+
high: 15,
|
|
407
|
+
medium: 5,
|
|
408
|
+
low: 1,
|
|
409
|
+
info: 0,
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Calculate overall score from severity counts
|
|
414
|
+
*/
|
|
415
|
+
function calculateScore(severityCounts, totalFindings = 0) {
|
|
416
|
+
if (totalFindings === 0) return 100;
|
|
417
|
+
|
|
418
|
+
const deductions =
|
|
419
|
+
(severityCounts.critical || 0) * SEVERITY_WEIGHTS.critical +
|
|
420
|
+
(severityCounts.high || 0) * SEVERITY_WEIGHTS.high +
|
|
421
|
+
(severityCounts.medium || 0) * SEVERITY_WEIGHTS.medium +
|
|
422
|
+
(severityCounts.low || 0) * SEVERITY_WEIGHTS.low;
|
|
423
|
+
|
|
424
|
+
// Cap deductions at 100
|
|
425
|
+
const score = Math.max(0, 100 - deductions);
|
|
426
|
+
return Math.round(score);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Get exit code from verdict
|
|
431
|
+
*/
|
|
432
|
+
function getExitCode(verdict) {
|
|
433
|
+
if (!verdict) return EXIT_CODES.BLOCK;
|
|
434
|
+
const v = typeof verdict === 'object' ? verdict.verdict : verdict;
|
|
435
|
+
if (v === 'SHIP' || v === 'PASS') return EXIT_CODES.SHIP;
|
|
436
|
+
if (v === 'WARN') return EXIT_CODES.WARN;
|
|
437
|
+
return EXIT_CODES.BLOCK;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Print error message
|
|
442
|
+
*/
|
|
443
|
+
function printError(error, context = '') {
|
|
444
|
+
const isConfig = error.message && (
|
|
445
|
+
error.message.includes('config') ||
|
|
446
|
+
error.message.includes('missing') ||
|
|
447
|
+
error.message.includes('not found')
|
|
448
|
+
);
|
|
449
|
+
const exitCode = isConfig ? EXIT_CODES.MISCONFIG : EXIT_CODES.INTERNAL;
|
|
450
|
+
|
|
451
|
+
console.error(`${chalk.red}✗ Error${chalk.reset}`);
|
|
452
|
+
if (context) console.error(` ${chalk.dim}${context}${chalk.reset}`);
|
|
453
|
+
console.error(` ${error.message || error}`);
|
|
454
|
+
if (error.stack && process.env.VIBECHECK_DEBUG) {
|
|
455
|
+
console.error(`${chalk.dim}${error.stack}${chalk.reset}`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return exitCode;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Format SARIF output (placeholder - full implementation in report-engine.js)
|
|
463
|
+
*/
|
|
464
|
+
function formatSARIF(findings, options = {}) {
|
|
465
|
+
// Basic SARIF structure - full implementation should be in report-engine.js
|
|
466
|
+
return JSON.stringify({
|
|
467
|
+
version: "2.1.0",
|
|
468
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema.json",
|
|
469
|
+
runs: [{
|
|
470
|
+
tool: {
|
|
471
|
+
driver: {
|
|
472
|
+
name: "vibecheck",
|
|
473
|
+
version: options.version || "1.0.0"
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
results: findings.map((f, i) => ({
|
|
477
|
+
ruleId: f.ruleId || f.category || `rule-${i}`,
|
|
478
|
+
message: {
|
|
479
|
+
text: f.message || f.title || ""
|
|
480
|
+
},
|
|
481
|
+
level: f.severity === 'critical' ? 'error' : f.severity === 'warning' ? 'warning' : 'note',
|
|
482
|
+
locations: f.file ? [{
|
|
483
|
+
physicalLocation: {
|
|
484
|
+
artifactLocation: {
|
|
485
|
+
uri: f.file
|
|
486
|
+
},
|
|
487
|
+
region: f.line ? {
|
|
488
|
+
startLine: f.line
|
|
489
|
+
} : undefined
|
|
490
|
+
}
|
|
491
|
+
}] : []
|
|
492
|
+
}))
|
|
493
|
+
}]
|
|
494
|
+
}, null, 2);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Placeholder functions for compatibility
|
|
498
|
+
function renderCoverageMap(coverageMap) {
|
|
499
|
+
return ''; // Implement if needed
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function renderBreakdown(breakdown) {
|
|
503
|
+
return ''; // Implement if needed
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function renderBlockers(blockers) {
|
|
507
|
+
return ''; // Implement if needed
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function renderLayers(layers) {
|
|
511
|
+
return ''; // Implement if needed
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
module.exports = {
|
|
515
|
+
formatScanOutput,
|
|
516
|
+
calculateScore,
|
|
517
|
+
getExitCode,
|
|
518
|
+
printError,
|
|
519
|
+
formatSARIF,
|
|
520
|
+
EXIT_CODES,
|
|
521
|
+
renderCoverageMap,
|
|
522
|
+
renderBreakdown,
|
|
523
|
+
renderBlockers,
|
|
524
|
+
renderLayers,
|
|
525
|
+
};
|