@maverick006/vibeguard 1.0.6 → 1.0.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/dist/formatter.js +30 -35
- package/package.json +1 -1
- package/src/formatter.ts +32 -37
package/dist/formatter.js
CHANGED
|
@@ -18,6 +18,8 @@ function visibleWidth(str) {
|
|
|
18
18
|
let width = 0;
|
|
19
19
|
for (const char of plain) {
|
|
20
20
|
const code = char.codePointAt(0) || 0;
|
|
21
|
+
if (code === 0xFE0F || code === 0xFE0E)
|
|
22
|
+
continue; // ignore variation selectors
|
|
21
23
|
// Emojis and certain symbols take 2 terminal columns
|
|
22
24
|
if (code > 0x1F000 || (code >= 0x2600 && code <= 0x27BF)) {
|
|
23
25
|
width += 2;
|
|
@@ -130,39 +132,32 @@ function renderDashboard(options) {
|
|
|
130
132
|
const riskColor = stats.riskLevel === 'LOW RISK' ? green : stats.riskLevel === 'MEDIUM RISK' ? yellow : red;
|
|
131
133
|
console.log('\n');
|
|
132
134
|
// Print Top Row: Clock aligned right
|
|
133
|
-
console.log(' '.repeat(
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`${darkBorder('└───────────────────────────────────────┘')}`
|
|
153
|
-
];
|
|
154
|
-
for (let i = 0; i < leftHeader.length; i++) {
|
|
155
|
-
const l = padVisible(leftHeader[i], 66);
|
|
156
|
-
const r = rightBox[i] || '';
|
|
157
|
-
console.log(`${l}${r}`);
|
|
158
|
-
}
|
|
135
|
+
console.log(' '.repeat(65) + cyan.bold(timeStr));
|
|
136
|
+
// 1. Header (Shield + VIBEGUARD)
|
|
137
|
+
console.log(`${cyan(shield[0])} ${cyan.bold(title[0])}`);
|
|
138
|
+
console.log(`${cyan(shield[1])} ${cyan.bold(title[1])}`);
|
|
139
|
+
console.log(`${cyan(shield[2])} ${cyan.bold(title[2])}`);
|
|
140
|
+
console.log(`${cyan(shield[3])} ${cyan.bold(title[3])}`);
|
|
141
|
+
console.log(`${cyan(shield[4])} ${cyan.bold(title[4])}`);
|
|
142
|
+
console.log(`${cyan(shield[5])} ${gray('AI-Powered DevSecOps Orchestrator')}`);
|
|
143
|
+
console.log(` ${cyan('Scanning. Analyzing. Protecting.')}\n`);
|
|
144
|
+
// 2. Risk Score Box (Placed cleanly BELOW VIBEGUARD Header)
|
|
145
|
+
const boxWidth = 74;
|
|
146
|
+
console.log(darkBorder(`┌${'─'.repeat(boxWidth)}┐`));
|
|
147
|
+
console.log(`${darkBorder('│')} ${cyan.bold('OVERALL RISK SCORE')}${' '.repeat(boxWidth - 20)}${darkBorder('│')}`);
|
|
148
|
+
console.log(`${darkBorder('│')}${' '.repeat(boxWidth)}${darkBorder('│')}`);
|
|
149
|
+
const scoreLine1 = ` ${scoreColor.bold(String(stats.score))} ${gray('/100')} ${red('CRITICAL')} ${String(stats.critical).padEnd(2, ' ')} ${orange('HIGH')} ${String(stats.high).padEnd(2, ' ')}`;
|
|
150
|
+
const scoreLine2 = ` ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${yellow('MEDIUM')} ${String(stats.medium).padEnd(2, ' ')} ${cyan('LOW')} ${String(stats.low).padEnd(2, ' ')}`;
|
|
151
|
+
console.log(`${darkBorder('│')}${padVisible(scoreLine1, boxWidth)}${darkBorder('│')}`);
|
|
152
|
+
console.log(`${darkBorder('│')}${padVisible(scoreLine2, boxWidth)}${darkBorder('│')}`);
|
|
153
|
+
console.log(darkBorder(`└${'─'.repeat(boxWidth)}┘`));
|
|
159
154
|
console.log('');
|
|
160
|
-
// 3
|
|
155
|
+
// 3. Multi-panel Grid (Pipeline & Findings)
|
|
161
156
|
const pipelineRows = [
|
|
162
|
-
|
|
157
|
+
`</> SAST (Semgrep) ${green('✓ OK')}`,
|
|
163
158
|
`📦 Dependency Check (Trivy) ${green('✓ OK')}`,
|
|
164
159
|
`🔑 Secrets Scan (Gitleaks) ${green('✓ OK')}`,
|
|
165
|
-
|
|
160
|
+
`🔒 IaC Scan (Checkov) ${green('✓ OK')}`,
|
|
166
161
|
`🐳 Container Scan (Trivy) ${green('✓ OK')}`,
|
|
167
162
|
`📑 Code Quality (ESLint) ${green('✓ OK')}`,
|
|
168
163
|
``,
|
|
@@ -175,7 +170,7 @@ function renderDashboard(options) {
|
|
|
175
170
|
];
|
|
176
171
|
// Table of findings (top 10)
|
|
177
172
|
const topFindings = findings.slice(0, 10);
|
|
178
|
-
const tableHeader = `${dimGray(padVisible('ID',
|
|
173
|
+
const tableHeader = `${dimGray(padVisible('ID', 13))} ${dimGray(padVisible('SEVERITY', 10))} ${dimGray(padVisible('TITLE', 28))} ${dimGray('FILE:LINE')}`;
|
|
179
174
|
const findingRows = [tableHeader];
|
|
180
175
|
if (topFindings.length === 0) {
|
|
181
176
|
findingRows.push(green(' ✓ No security vulnerabilities detected. Codebase is clean!'));
|
|
@@ -192,24 +187,24 @@ function renderDashboard(options) {
|
|
|
192
187
|
else if (sev === 'MEDIUM')
|
|
193
188
|
sevFormatted = yellow('MEDIUM ');
|
|
194
189
|
const rawTitle = f.title || 'Security Finding';
|
|
195
|
-
const titleTruncated = rawTitle.length >
|
|
190
|
+
const titleTruncated = rawTitle.length > 26 ? rawTitle.slice(0, 24) + '..' : rawTitle;
|
|
196
191
|
const titleFormatted = padVisible(white(titleTruncated), 28);
|
|
197
192
|
const fileLine = `${f.file || 'unknown'}:${f.line || 1}`;
|
|
198
|
-
const idFormatted = padVisible(cyan(id),
|
|
193
|
+
const idFormatted = padVisible(cyan(id), 13);
|
|
199
194
|
findingRows.push(`${idFormatted} ${sevFormatted} ${titleFormatted} ${dimGray(fileLine)}`);
|
|
200
195
|
}
|
|
201
196
|
}
|
|
202
197
|
// Print Section Headers with ANSI-safe padding
|
|
203
198
|
const headerCol1 = padVisible(cyan('▶ SCANNER PIPELINE'), 36);
|
|
204
199
|
const headerCol2 = cyan('▶ TOP FINDINGS');
|
|
205
|
-
console.log(
|
|
200
|
+
console.log(`${headerCol1} ${headerCol2}`);
|
|
206
201
|
const maxRows = Math.max(pipelineRows.length, findingRows.length);
|
|
207
202
|
for (let i = 0; i < maxRows; i++) {
|
|
208
203
|
const left = padVisible(pipelineRows[i] || '', 36);
|
|
209
204
|
const right = findingRows[i] || '';
|
|
210
205
|
console.log(`${left} ${right}`);
|
|
211
206
|
}
|
|
212
|
-
// AI Remediation Section
|
|
207
|
+
// 4. AI Remediation Section
|
|
213
208
|
if (remediation) {
|
|
214
209
|
console.log(`\n${cyan('▶ AI REMEDIATION (VG-AI)')}\n`);
|
|
215
210
|
console.log(`${cyan('Finding:')} ${red.bold(remediation.findingId)}\n`);
|
|
@@ -235,7 +230,7 @@ function renderDashboard(options) {
|
|
|
235
230
|
console.log(darkBorder('└────────────────────────────────────────────────────────────┘'));
|
|
236
231
|
console.log(`\n${cyan('Confidence:')} ${green.bold(remediation.confidence + '%')}`);
|
|
237
232
|
}
|
|
238
|
-
// Summary Bar (bottom capsule)
|
|
233
|
+
// 5. Summary Bar (bottom capsule)
|
|
239
234
|
console.log(`\n${cyan('▶ SUMMARY')}`);
|
|
240
235
|
const summaryText = `🛡️ Scan completed in ${duration} │ ${stats.total} findings │ 6 passed`;
|
|
241
236
|
const barTop = `┌${'─'.repeat(visibleWidth(summaryText) + 4)}┐`;
|
package/package.json
CHANGED
package/src/formatter.ts
CHANGED
|
@@ -22,6 +22,7 @@ function visibleWidth(str: string): number {
|
|
|
22
22
|
let width = 0;
|
|
23
23
|
for (const char of plain) {
|
|
24
24
|
const code = char.codePointAt(0) || 0;
|
|
25
|
+
if (code === 0xFE0F || code === 0xFE0E) continue; // ignore variation selectors
|
|
25
26
|
// Emojis and certain symbols take 2 terminal columns
|
|
26
27
|
if (code > 0x1F000 || (code >= 0x2600 && code <= 0x27BF)) {
|
|
27
28
|
width += 2;
|
|
@@ -152,43 +153,37 @@ export function renderDashboard(options: {
|
|
|
152
153
|
console.log('\n');
|
|
153
154
|
|
|
154
155
|
// Print Top Row: Clock aligned right
|
|
155
|
-
console.log(' '.repeat(
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
`${darkBorder('│')} ${yellow('MEDIUM')} ${String(stats.medium).padStart(2, ' ')} ${darkBorder('│')}`,
|
|
175
|
-
`${darkBorder('│')} ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${cyan('LOW')} ${String(stats.low).padStart(2, ' ')} ${darkBorder('│')}`,
|
|
176
|
-
`${darkBorder('└───────────────────────────────────────┘')}`
|
|
177
|
-
];
|
|
156
|
+
console.log(' '.repeat(65) + cyan.bold(timeStr));
|
|
157
|
+
|
|
158
|
+
// 1. Header (Shield + VIBEGUARD)
|
|
159
|
+
console.log(`${cyan(shield[0])} ${cyan.bold(title[0])}`);
|
|
160
|
+
console.log(`${cyan(shield[1])} ${cyan.bold(title[1])}`);
|
|
161
|
+
console.log(`${cyan(shield[2])} ${cyan.bold(title[2])}`);
|
|
162
|
+
console.log(`${cyan(shield[3])} ${cyan.bold(title[3])}`);
|
|
163
|
+
console.log(`${cyan(shield[4])} ${cyan.bold(title[4])}`);
|
|
164
|
+
console.log(`${cyan(shield[5])} ${gray('AI-Powered DevSecOps Orchestrator')}`);
|
|
165
|
+
console.log(` ${cyan('Scanning. Analyzing. Protecting.')}\n`);
|
|
166
|
+
|
|
167
|
+
// 2. Risk Score Box (Placed cleanly BELOW VIBEGUARD Header)
|
|
168
|
+
const boxWidth = 74;
|
|
169
|
+
console.log(darkBorder(`┌${'─'.repeat(boxWidth)}┐`));
|
|
170
|
+
console.log(`${darkBorder('│')} ${cyan.bold('OVERALL RISK SCORE')}${' '.repeat(boxWidth - 20)}${darkBorder('│')}`);
|
|
171
|
+
console.log(`${darkBorder('│')}${' '.repeat(boxWidth)}${darkBorder('│')}`);
|
|
172
|
+
|
|
173
|
+
const scoreLine1 = ` ${scoreColor.bold(String(stats.score))} ${gray('/100')} ${red('CRITICAL')} ${String(stats.critical).padEnd(2, ' ')} ${orange('HIGH')} ${String(stats.high).padEnd(2, ' ')}`;
|
|
174
|
+
const scoreLine2 = ` ${riskColor.bold(stats.riskLevel.padEnd(13, ' '))} ${yellow('MEDIUM')} ${String(stats.medium).padEnd(2, ' ')} ${cyan('LOW')} ${String(stats.low).padEnd(2, ' ')}`;
|
|
178
175
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
console.log(`${l}${r}`);
|
|
183
|
-
}
|
|
176
|
+
console.log(`${darkBorder('│')}${padVisible(scoreLine1, boxWidth)}${darkBorder('│')}`);
|
|
177
|
+
console.log(`${darkBorder('│')}${padVisible(scoreLine2, boxWidth)}${darkBorder('│')}`);
|
|
178
|
+
console.log(darkBorder(`└${'─'.repeat(boxWidth)}┘`));
|
|
184
179
|
console.log('');
|
|
185
180
|
|
|
186
|
-
// 3
|
|
181
|
+
// 3. Multi-panel Grid (Pipeline & Findings)
|
|
187
182
|
const pipelineRows = [
|
|
188
|
-
|
|
183
|
+
`</> SAST (Semgrep) ${green('✓ OK')}`,
|
|
189
184
|
`📦 Dependency Check (Trivy) ${green('✓ OK')}`,
|
|
190
185
|
`🔑 Secrets Scan (Gitleaks) ${green('✓ OK')}`,
|
|
191
|
-
|
|
186
|
+
`🔒 IaC Scan (Checkov) ${green('✓ OK')}`,
|
|
192
187
|
`🐳 Container Scan (Trivy) ${green('✓ OK')}`,
|
|
193
188
|
`📑 Code Quality (ESLint) ${green('✓ OK')}`,
|
|
194
189
|
``,
|
|
@@ -202,7 +197,7 @@ export function renderDashboard(options: {
|
|
|
202
197
|
|
|
203
198
|
// Table of findings (top 10)
|
|
204
199
|
const topFindings = findings.slice(0, 10);
|
|
205
|
-
const tableHeader = `${dimGray(padVisible('ID',
|
|
200
|
+
const tableHeader = `${dimGray(padVisible('ID', 13))} ${dimGray(padVisible('SEVERITY', 10))} ${dimGray(padVisible('TITLE', 28))} ${dimGray('FILE:LINE')}`;
|
|
206
201
|
|
|
207
202
|
const findingRows: string[] = [tableHeader];
|
|
208
203
|
|
|
@@ -218,11 +213,11 @@ export function renderDashboard(options: {
|
|
|
218
213
|
else if (sev === 'MEDIUM') sevFormatted = yellow('MEDIUM ');
|
|
219
214
|
|
|
220
215
|
const rawTitle = f.title || 'Security Finding';
|
|
221
|
-
const titleTruncated = rawTitle.length >
|
|
216
|
+
const titleTruncated = rawTitle.length > 26 ? rawTitle.slice(0, 24) + '..' : rawTitle;
|
|
222
217
|
const titleFormatted = padVisible(white(titleTruncated), 28);
|
|
223
218
|
|
|
224
219
|
const fileLine = `${f.file || 'unknown'}:${f.line || 1}`;
|
|
225
|
-
const idFormatted = padVisible(cyan(id),
|
|
220
|
+
const idFormatted = padVisible(cyan(id), 13);
|
|
226
221
|
findingRows.push(`${idFormatted} ${sevFormatted} ${titleFormatted} ${dimGray(fileLine)}`);
|
|
227
222
|
}
|
|
228
223
|
}
|
|
@@ -230,7 +225,7 @@ export function renderDashboard(options: {
|
|
|
230
225
|
// Print Section Headers with ANSI-safe padding
|
|
231
226
|
const headerCol1 = padVisible(cyan('▶ SCANNER PIPELINE'), 36);
|
|
232
227
|
const headerCol2 = cyan('▶ TOP FINDINGS');
|
|
233
|
-
console.log(
|
|
228
|
+
console.log(`${headerCol1} ${headerCol2}`);
|
|
234
229
|
|
|
235
230
|
const maxRows = Math.max(pipelineRows.length, findingRows.length);
|
|
236
231
|
for (let i = 0; i < maxRows; i++) {
|
|
@@ -239,7 +234,7 @@ export function renderDashboard(options: {
|
|
|
239
234
|
console.log(`${left} ${right}`);
|
|
240
235
|
}
|
|
241
236
|
|
|
242
|
-
// AI Remediation Section
|
|
237
|
+
// 4. AI Remediation Section
|
|
243
238
|
if (remediation) {
|
|
244
239
|
console.log(`\n${cyan('▶ AI REMEDIATION (VG-AI)')}\n`);
|
|
245
240
|
console.log(`${cyan('Finding:')} ${red.bold(remediation.findingId)}\n`);
|
|
@@ -265,7 +260,7 @@ export function renderDashboard(options: {
|
|
|
265
260
|
console.log(`\n${cyan('Confidence:')} ${green.bold(remediation.confidence + '%')}`);
|
|
266
261
|
}
|
|
267
262
|
|
|
268
|
-
// Summary Bar (bottom capsule)
|
|
263
|
+
// 5. Summary Bar (bottom capsule)
|
|
269
264
|
console.log(`\n${cyan('▶ SUMMARY')}`);
|
|
270
265
|
const summaryText = `🛡️ Scan completed in ${duration} │ ${stats.total} findings │ 6 passed`;
|
|
271
266
|
const barTop = `┌${'─'.repeat(visibleWidth(summaryText) + 4)}┐`;
|