@vibgrate/cli 0.1.3 → 1.0.1
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/README.md +60 -2
- package/dist/{baseline-D5UDXOEJ.js → baseline-35XRSRAD.js} +2 -2
- package/dist/{chunk-3X3ZMVHI.js → chunk-NTRKEIKP.js} +1 -1
- package/dist/{chunk-VXEZ7APL.js → chunk-VMNBKARQ.js} +350 -81
- package/dist/{chunk-AMOJCCF5.js → chunk-VXZT34Y5.js} +4 -1
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,13 +56,19 @@ That's it. You'll see a full drift report in seconds.
|
|
|
56
56
|
## What You Get
|
|
57
57
|
|
|
58
58
|
```
|
|
59
|
+
╭───╮➜
|
|
60
|
+
╭┤◉ ◉├╮ V I B G R A T E
|
|
61
|
+
╰┤───├╯ Drift Intelligence Engine v1.x.x
|
|
62
|
+
╰───╯
|
|
63
|
+
|
|
59
64
|
╔══════════════════════════════════════════╗
|
|
60
65
|
║ Vibgrate Drift Report ║
|
|
61
66
|
╚══════════════════════════════════════════╝
|
|
62
67
|
|
|
63
68
|
Drift Score: 72/100
|
|
64
|
-
Risk Level:
|
|
69
|
+
Risk Level: LOW
|
|
65
70
|
Projects: 3
|
|
71
|
+
VCS: git main a1b2c3d
|
|
66
72
|
|
|
67
73
|
Score Breakdown
|
|
68
74
|
Runtime: ████████████████████ 100
|
|
@@ -77,9 +83,61 @@ That's it. You'll see a full drift report in seconds.
|
|
|
77
83
|
Dependencies:
|
|
78
84
|
42 current 8 1-behind 3 2+ behind
|
|
79
85
|
|
|
80
|
-
|
|
86
|
+
── web-app (node) src/web
|
|
87
|
+
Runtime: 20.11.0 (current)
|
|
88
|
+
Frameworks:
|
|
89
|
+
React: 18.2.0 → 19.0.0 (1 behind)
|
|
90
|
+
Dependencies:
|
|
91
|
+
31 current 5 1-behind 2 2+ behind
|
|
92
|
+
|
|
93
|
+
Tech Stack
|
|
94
|
+
Frontend: React, Tailwind CSS
|
|
95
|
+
Bundlers: Vite
|
|
96
|
+
Testing: Vitest, Playwright
|
|
97
|
+
Lint & Format: ESLint, Prettier
|
|
98
|
+
|
|
99
|
+
Services & Integrations
|
|
100
|
+
Cloud: AWS SDK v3
|
|
101
|
+
Databases: PostgreSQL
|
|
102
|
+
|
|
103
|
+
TypeScript
|
|
104
|
+
v5.4.2 · strict ✔ · ESM · target: ES2022
|
|
105
|
+
|
|
106
|
+
Build & Deploy
|
|
107
|
+
CI: GitHub Actions
|
|
108
|
+
Docker: 2 Dockerfiles (node:20-alpine)
|
|
109
|
+
Package Managers: pnpm
|
|
110
|
+
|
|
111
|
+
Security Posture
|
|
112
|
+
Lockfile ✔ · .env ✔ · node_modules ✔
|
|
113
|
+
|
|
114
|
+
Dependency Graph
|
|
115
|
+
pnpm-lock.yaml: 312 unique, 487 installed
|
|
116
|
+
5 duplicated packages
|
|
117
|
+
|
|
118
|
+
Findings (2 warnings, 1 note)
|
|
81
119
|
⚠ Framework "NestJS" is 1 major version(s) behind
|
|
120
|
+
framework/outdated in src/api/package.json
|
|
82
121
|
⚠ 12% of dependencies are 2+ major versions behind
|
|
122
|
+
dependency/outdated in src/api/package.json
|
|
123
|
+
ℹ TypeScript target is ES2022
|
|
124
|
+
ts/target in tsconfig.json
|
|
125
|
+
|
|
126
|
+
╔══════════════════════════════════════════╗
|
|
127
|
+
║ Top Priority Actions ║
|
|
128
|
+
╚══════════════════════════════════════════╝
|
|
129
|
+
|
|
130
|
+
1. Upgrade NestJS 10.3.0 → 11.0.0 in my-api
|
|
131
|
+
1 major version behind. Major framework drift increases
|
|
132
|
+
breaking change risk and blocks access to security fixes.
|
|
133
|
+
Impact: +5–15 points (framework score)
|
|
134
|
+
|
|
135
|
+
2. Reduce dependency rot in my-api (42% severely outdated)
|
|
136
|
+
3 of 53 dependencies are 2+ majors behind. Run `npm outdated`
|
|
137
|
+
and prioritise packages with known CVEs.
|
|
138
|
+
Impact: +5–10 points (dependency score)
|
|
139
|
+
|
|
140
|
+
Scanned at 2026-02-16T00:00:00.000Z · 1.2s · 48 files scanned
|
|
83
141
|
```
|
|
84
142
|
|
|
85
143
|
---
|
|
@@ -95,9 +95,9 @@ function clamp(val, min, max) {
|
|
|
95
95
|
return Math.min(max, Math.max(min, val));
|
|
96
96
|
}
|
|
97
97
|
function runtimeScore(projects) {
|
|
98
|
-
if (projects.length === 0) return
|
|
98
|
+
if (projects.length === 0) return null;
|
|
99
99
|
const lags = projects.map((p) => p.runtimeMajorsBehind).filter((v) => v !== void 0);
|
|
100
|
-
if (lags.length === 0) return
|
|
100
|
+
if (lags.length === 0) return null;
|
|
101
101
|
const maxLag = Math.max(...lags);
|
|
102
102
|
if (maxLag === 0) return 100;
|
|
103
103
|
if (maxLag === 1) return 80;
|
|
@@ -107,9 +107,9 @@ function runtimeScore(projects) {
|
|
|
107
107
|
}
|
|
108
108
|
function frameworkScore(projects) {
|
|
109
109
|
const allFrameworks = projects.flatMap((p) => p.frameworks);
|
|
110
|
-
if (allFrameworks.length === 0) return
|
|
110
|
+
if (allFrameworks.length === 0) return null;
|
|
111
111
|
const lags = allFrameworks.map((f) => f.majorsBehind).filter((v) => v !== null);
|
|
112
|
-
if (lags.length === 0) return
|
|
112
|
+
if (lags.length === 0) return null;
|
|
113
113
|
const maxLag = Math.max(...lags);
|
|
114
114
|
const avgLag = lags.reduce((a, b) => a + b, 0) / lags.length;
|
|
115
115
|
const maxPenalty = Math.min(maxLag * 20, 100);
|
|
@@ -128,13 +128,15 @@ function dependencyScore(projects) {
|
|
|
128
128
|
totalUnknown += p.dependencyAgeBuckets.unknown;
|
|
129
129
|
}
|
|
130
130
|
const total = totalCurrent + totalOne + totalTwo;
|
|
131
|
-
if (total === 0) return
|
|
131
|
+
if (total === 0) return null;
|
|
132
132
|
const currentPct = totalCurrent / total;
|
|
133
133
|
const onePct = totalOne / total;
|
|
134
134
|
const twoPct = totalTwo / total;
|
|
135
135
|
return clamp(Math.round(currentPct * 100 - onePct * 10 - twoPct * 40), 0, 100);
|
|
136
136
|
}
|
|
137
137
|
function eolScore(projects) {
|
|
138
|
+
const hasRuntimeData = projects.some((p) => p.runtimeMajorsBehind !== void 0);
|
|
139
|
+
if (!hasRuntimeData) return null;
|
|
138
140
|
let score = 100;
|
|
139
141
|
for (const p of projects) {
|
|
140
142
|
if (p.type === "node" && p.runtimeMajorsBehind !== void 0) {
|
|
@@ -155,20 +157,50 @@ function computeDriftScore(projects) {
|
|
|
155
157
|
const fs5 = frameworkScore(projects);
|
|
156
158
|
const ds = dependencyScore(projects);
|
|
157
159
|
const es = eolScore(projects);
|
|
158
|
-
const
|
|
160
|
+
const components = [
|
|
161
|
+
{ score: rs, weight: 0.25 },
|
|
162
|
+
{ score: fs5, weight: 0.25 },
|
|
163
|
+
{ score: ds, weight: 0.3 },
|
|
164
|
+
{ score: es, weight: 0.2 }
|
|
165
|
+
];
|
|
166
|
+
const active = components.filter((c) => c.score !== null);
|
|
167
|
+
if (active.length === 0) {
|
|
168
|
+
return {
|
|
169
|
+
score: 100,
|
|
170
|
+
riskLevel: "low",
|
|
171
|
+
components: {
|
|
172
|
+
runtimeScore: rs ?? 100,
|
|
173
|
+
frameworkScore: fs5 ?? 100,
|
|
174
|
+
dependencyScore: ds ?? 100,
|
|
175
|
+
eolScore: es ?? 100
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const totalActiveWeight = active.reduce((sum, c) => sum + c.weight, 0);
|
|
180
|
+
let score = 0;
|
|
181
|
+
for (const c of active) {
|
|
182
|
+
score += c.score * (c.weight / totalActiveWeight);
|
|
183
|
+
}
|
|
184
|
+
score = Math.round(score);
|
|
159
185
|
let riskLevel;
|
|
160
186
|
if (score >= 70) riskLevel = "low";
|
|
161
187
|
else if (score >= 40) riskLevel = "moderate";
|
|
162
188
|
else riskLevel = "high";
|
|
189
|
+
const measured = [];
|
|
190
|
+
if (rs !== null) measured.push("runtime");
|
|
191
|
+
if (fs5 !== null) measured.push("framework");
|
|
192
|
+
if (ds !== null) measured.push("dependency");
|
|
193
|
+
if (es !== null) measured.push("eol");
|
|
163
194
|
return {
|
|
164
195
|
score,
|
|
165
196
|
riskLevel,
|
|
166
197
|
components: {
|
|
167
|
-
runtimeScore: rs,
|
|
168
|
-
frameworkScore: fs5,
|
|
169
|
-
dependencyScore: ds,
|
|
170
|
-
eolScore: es
|
|
171
|
-
}
|
|
198
|
+
runtimeScore: rs ?? 100,
|
|
199
|
+
frameworkScore: fs5 ?? 100,
|
|
200
|
+
dependencyScore: ds ?? 100,
|
|
201
|
+
eolScore: es ?? 100
|
|
202
|
+
},
|
|
203
|
+
measured
|
|
172
204
|
};
|
|
173
205
|
}
|
|
174
206
|
function generateFindings(projects, config) {
|
|
@@ -243,11 +275,22 @@ function generateFindings(projects, config) {
|
|
|
243
275
|
return findings;
|
|
244
276
|
}
|
|
245
277
|
|
|
278
|
+
// src/version.ts
|
|
279
|
+
import { createRequire } from "module";
|
|
280
|
+
var require2 = createRequire(import.meta.url);
|
|
281
|
+
var pkg = require2("../package.json");
|
|
282
|
+
var VERSION = pkg.version;
|
|
283
|
+
|
|
246
284
|
// src/formatters/text.ts
|
|
247
285
|
import chalk from "chalk";
|
|
248
286
|
function formatText(artifact) {
|
|
249
287
|
const lines = [];
|
|
250
288
|
lines.push("");
|
|
289
|
+
lines.push(chalk.cyan(" \u256D\u2500\u2500\u2500\u256E") + chalk.greenBright("\u279C"));
|
|
290
|
+
lines.push(chalk.cyan(" \u256D\u2524") + chalk.greenBright("\u25C9 \u25C9") + chalk.cyan("\u251C\u256E") + " " + chalk.bold.white("V I B G R A T E"));
|
|
291
|
+
lines.push(chalk.cyan(" \u2570\u2524") + chalk.dim("\u2500\u2500\u2500") + chalk.cyan("\u251C\u256F") + " " + chalk.dim(`Drift Intelligence Engine v${VERSION}`));
|
|
292
|
+
lines.push(chalk.cyan(" \u2570\u2500\u2500\u2500\u256F"));
|
|
293
|
+
lines.push("");
|
|
251
294
|
lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
252
295
|
lines.push(chalk.bold.cyan("\u2551 Vibgrate Drift Report \u2551"));
|
|
253
296
|
lines.push(chalk.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
@@ -263,11 +306,12 @@ function formatText(artifact) {
|
|
|
263
306
|
lines.push(chalk.bold(" VCS: ") + vcsParts.join(" "));
|
|
264
307
|
}
|
|
265
308
|
lines.push("");
|
|
309
|
+
const m = new Set(artifact.drift.measured ?? ["runtime", "framework", "dependency", "eol"]);
|
|
266
310
|
lines.push(chalk.bold.underline(" Score Breakdown"));
|
|
267
|
-
lines.push(` Runtime: ${scoreBar(artifact.drift.components.runtimeScore)}`);
|
|
268
|
-
lines.push(` Frameworks: ${scoreBar(artifact.drift.components.frameworkScore)}`);
|
|
269
|
-
lines.push(` Dependencies: ${scoreBar(artifact.drift.components.dependencyScore)}`);
|
|
270
|
-
lines.push(` EOL Risk: ${scoreBar(artifact.drift.components.eolScore)}`);
|
|
311
|
+
lines.push(` Runtime: ${m.has("runtime") ? scoreBar(artifact.drift.components.runtimeScore) : chalk.dim("n/a")}`);
|
|
312
|
+
lines.push(` Frameworks: ${m.has("framework") ? scoreBar(artifact.drift.components.frameworkScore) : chalk.dim("n/a")}`);
|
|
313
|
+
lines.push(` Dependencies: ${m.has("dependency") ? scoreBar(artifact.drift.components.dependencyScore) : chalk.dim("n/a")}`);
|
|
314
|
+
lines.push(` EOL Risk: ${m.has("eol") ? scoreBar(artifact.drift.components.eolScore) : chalk.dim("n/a")}`);
|
|
271
315
|
lines.push("");
|
|
272
316
|
for (const project of artifact.projects) {
|
|
273
317
|
lines.push(chalk.bold(` \u2500\u2500 ${project.name} `) + chalk.dim(`(${project.type}) ${project.path}`));
|
|
@@ -293,8 +337,24 @@ function formatText(artifact) {
|
|
|
293
337
|
}
|
|
294
338
|
lines.push("");
|
|
295
339
|
}
|
|
340
|
+
if (artifact.delta !== void 0) {
|
|
341
|
+
const deltaStr = artifact.delta > 0 ? chalk.green(`+${artifact.delta}`) : artifact.delta < 0 ? chalk.red(`${artifact.delta}`) : chalk.dim("0");
|
|
342
|
+
lines.push(chalk.bold(" Drift Delta: ") + deltaStr + " (vs baseline)");
|
|
343
|
+
lines.push("");
|
|
344
|
+
}
|
|
345
|
+
if (artifact.extended) {
|
|
346
|
+
lines.push(...formatExtended(artifact.extended));
|
|
347
|
+
}
|
|
296
348
|
if (artifact.findings.length > 0) {
|
|
297
|
-
|
|
349
|
+
const errors = artifact.findings.filter((f) => f.level === "error");
|
|
350
|
+
const warnings = artifact.findings.filter((f) => f.level === "warning");
|
|
351
|
+
const notes = artifact.findings.filter((f) => f.level === "note");
|
|
352
|
+
const summary = [
|
|
353
|
+
errors.length > 0 ? chalk.red(`${errors.length} error${errors.length !== 1 ? "s" : ""}`) : "",
|
|
354
|
+
warnings.length > 0 ? chalk.yellow(`${warnings.length} warning${warnings.length !== 1 ? "s" : ""}`) : "",
|
|
355
|
+
notes.length > 0 ? chalk.blue(`${notes.length} note${notes.length !== 1 ? "s" : ""}`) : ""
|
|
356
|
+
].filter(Boolean).join(chalk.dim(", "));
|
|
357
|
+
lines.push(chalk.bold.underline(` Findings`) + chalk.dim(` (${summary})`));
|
|
298
358
|
for (const f of artifact.findings) {
|
|
299
359
|
const icon = f.level === "error" ? chalk.red("\u2716") : f.level === "warning" ? chalk.yellow("\u26A0") : chalk.blue("\u2139");
|
|
300
360
|
lines.push(` ${icon} ${f.message}`);
|
|
@@ -302,15 +362,30 @@ function formatText(artifact) {
|
|
|
302
362
|
}
|
|
303
363
|
lines.push("");
|
|
304
364
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
lines.push(chalk.bold("
|
|
365
|
+
const actions = generatePriorityActions(artifact);
|
|
366
|
+
if (actions.length > 0) {
|
|
367
|
+
lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
368
|
+
lines.push(chalk.bold.cyan("\u2551 Top Priority Actions \u2551"));
|
|
369
|
+
lines.push(chalk.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
308
370
|
lines.push("");
|
|
371
|
+
for (let i = 0; i < actions.length; i++) {
|
|
372
|
+
const a = actions[i];
|
|
373
|
+
const num = chalk.bold.cyan(` ${i + 1}.`);
|
|
374
|
+
lines.push(`${num} ${chalk.bold(a.title)}`);
|
|
375
|
+
lines.push(chalk.dim(` ${a.explanation}`));
|
|
376
|
+
if (a.impact) lines.push(` Impact: ${chalk.green(a.impact)}`);
|
|
377
|
+
lines.push("");
|
|
378
|
+
}
|
|
309
379
|
}
|
|
310
|
-
|
|
311
|
-
|
|
380
|
+
const scannedParts = [`Scanned at ${artifact.timestamp}`];
|
|
381
|
+
if (artifact.durationMs !== void 0) {
|
|
382
|
+
const secs = (artifact.durationMs / 1e3).toFixed(1);
|
|
383
|
+
scannedParts.push(`${secs}s`);
|
|
312
384
|
}
|
|
313
|
-
|
|
385
|
+
if (artifact.filesScanned !== void 0) {
|
|
386
|
+
scannedParts.push(`${artifact.filesScanned} file${artifact.filesScanned !== 1 ? "s" : ""} scanned`);
|
|
387
|
+
}
|
|
388
|
+
lines.push(chalk.dim(` ${scannedParts.join(" \xB7 ")}`));
|
|
314
389
|
lines.push("");
|
|
315
390
|
return lines.join("\n");
|
|
316
391
|
}
|
|
@@ -331,7 +406,7 @@ function scoreBar(score) {
|
|
|
331
406
|
const filled = Math.round(score / 100 * width);
|
|
332
407
|
const empty = width - filled;
|
|
333
408
|
const color = score >= 70 ? chalk.green : score >= 40 ? chalk.yellow : chalk.red;
|
|
334
|
-
return color("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(empty)) + ` ${score}`;
|
|
409
|
+
return color("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(empty)) + ` ${Math.round(score)}`;
|
|
335
410
|
}
|
|
336
411
|
var CATEGORY_LABELS = {
|
|
337
412
|
frontend: "Frontend",
|
|
@@ -471,6 +546,149 @@ function formatExtended(ext) {
|
|
|
471
546
|
}
|
|
472
547
|
return lines;
|
|
473
548
|
}
|
|
549
|
+
function generatePriorityActions(artifact) {
|
|
550
|
+
const actions = [];
|
|
551
|
+
const eolProjects = artifact.projects.filter(
|
|
552
|
+
(p) => p.runtimeMajorsBehind !== void 0 && p.runtimeMajorsBehind >= 3
|
|
553
|
+
);
|
|
554
|
+
if (eolProjects.length > 0) {
|
|
555
|
+
const names = eolProjects.map((p) => p.name).join(", ");
|
|
556
|
+
const runtimes = eolProjects.map((p) => `${p.runtime} \u2192 ${p.runtimeLatest}`).join(", ");
|
|
557
|
+
actions.push({
|
|
558
|
+
title: `Upgrade EOL runtime${eolProjects.length > 1 ? "s" : ""} in ${names}`,
|
|
559
|
+
explanation: `${runtimes}. End-of-life runtimes no longer receive security patches and block ecosystem upgrades.`,
|
|
560
|
+
impact: `+${Math.min(eolProjects.length * 10, 30)} points (runtime & EOL scores)`,
|
|
561
|
+
severity: 100
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
const severeFrameworks = [];
|
|
565
|
+
for (const p of artifact.projects) {
|
|
566
|
+
for (const fw of p.frameworks) {
|
|
567
|
+
if (fw.majorsBehind !== null && fw.majorsBehind >= 3) {
|
|
568
|
+
severeFrameworks.push({ name: fw.name, fw: `${fw.currentVersion} \u2192 ${fw.latestVersion}`, behind: fw.majorsBehind, project: p.name });
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (severeFrameworks.length > 0) {
|
|
573
|
+
const worst = severeFrameworks.sort((a, b) => b.behind - a.behind)[0];
|
|
574
|
+
const others = severeFrameworks.length > 1 ? ` (+${severeFrameworks.length - 1} more)` : "";
|
|
575
|
+
actions.push({
|
|
576
|
+
title: `Upgrade ${worst.name} ${worst.fw} in ${worst.project}${others}`,
|
|
577
|
+
explanation: `${worst.behind} major versions behind. Major framework drift increases breaking change risk and blocks access to security fixes and performance improvements.`,
|
|
578
|
+
impact: `+5\u201315 points (framework score)`,
|
|
579
|
+
severity: 90
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
for (const p of artifact.projects) {
|
|
583
|
+
const b = p.dependencyAgeBuckets;
|
|
584
|
+
const total = b.current + b.oneBehind + b.twoPlusBehind;
|
|
585
|
+
if (total === 0) continue;
|
|
586
|
+
const twoPlusPct = Math.round(b.twoPlusBehind / total * 100);
|
|
587
|
+
if (twoPlusPct >= 40) {
|
|
588
|
+
actions.push({
|
|
589
|
+
title: `Reduce dependency rot in ${p.name} (${twoPlusPct}% severely outdated)`,
|
|
590
|
+
explanation: `${b.twoPlusBehind} of ${total} dependencies are 2+ majors behind. Run \`npm outdated\` and prioritise packages with known CVEs or breaking API changes.`,
|
|
591
|
+
impact: `+5\u201310 points (dependency score)`,
|
|
592
|
+
severity: 80 + twoPlusPct / 10
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const twoMajorFrameworks = [];
|
|
597
|
+
for (const p of artifact.projects) {
|
|
598
|
+
for (const fw of p.frameworks) {
|
|
599
|
+
if (fw.majorsBehind === 2) {
|
|
600
|
+
twoMajorFrameworks.push({ name: fw.name, project: p.name, fw: `${fw.currentVersion} \u2192 ${fw.latestVersion}` });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const uniqueTwo = [...new Map(twoMajorFrameworks.map((f) => [f.name, f])).values()];
|
|
605
|
+
if (uniqueTwo.length > 0) {
|
|
606
|
+
const list = uniqueTwo.slice(0, 3).map((f) => `${f.name} (${f.fw})`).join(", ");
|
|
607
|
+
const moreCount = uniqueTwo.length > 3 ? ` +${uniqueTwo.length - 3} more` : "";
|
|
608
|
+
actions.push({
|
|
609
|
+
title: `Plan major framework upgrades: ${list}${moreCount}`,
|
|
610
|
+
explanation: `These frameworks are 2 major versions behind. Create upgrade tickets and check migration guides \u2014 the gap will widen with each new release.`,
|
|
611
|
+
impact: `+5\u201310 points (framework score)`,
|
|
612
|
+
severity: 60
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
if (artifact.extended?.breakingChangeExposure) {
|
|
616
|
+
const bc = artifact.extended.breakingChangeExposure;
|
|
617
|
+
const total = bc.deprecatedPackages.length + bc.legacyPolyfills.length;
|
|
618
|
+
if (total > 0) {
|
|
619
|
+
const items = [...bc.deprecatedPackages, ...bc.legacyPolyfills].slice(0, 5).join(", ");
|
|
620
|
+
const moreCount = total > 5 ? ` +${total - 5} more` : "";
|
|
621
|
+
actions.push({
|
|
622
|
+
title: `Replace deprecated/legacy packages: ${items}${moreCount}`,
|
|
623
|
+
explanation: `${total} package${total !== 1 ? "s" : ""} are deprecated or legacy polyfills. These receive no updates and may have known vulnerabilities.`,
|
|
624
|
+
severity: 55
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (artifact.extended?.dependencyGraph) {
|
|
629
|
+
const dg = artifact.extended.dependencyGraph;
|
|
630
|
+
const phantomCount = dg.phantomDependencies.length;
|
|
631
|
+
if (phantomCount >= 10) {
|
|
632
|
+
let detail = `Packages used in code but not declared in package.json. These rely on transitive installs and can break unpredictably when other packages update.`;
|
|
633
|
+
const details = dg.phantomDependencyDetails;
|
|
634
|
+
if (details && details.length > 0) {
|
|
635
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
636
|
+
for (const d of details) {
|
|
637
|
+
if (!byPath.has(d.sourcePath)) byPath.set(d.sourcePath, []);
|
|
638
|
+
byPath.get(d.sourcePath).push({ package: d.package, spec: d.spec });
|
|
639
|
+
}
|
|
640
|
+
const pathLines = [];
|
|
641
|
+
let shown = 0;
|
|
642
|
+
for (const [srcPath, pkgs] of byPath) {
|
|
643
|
+
if (shown >= 10) break;
|
|
644
|
+
pathLines.push(`
|
|
645
|
+
./${srcPath}`);
|
|
646
|
+
for (const pkg2 of pkgs) {
|
|
647
|
+
if (shown >= 10) break;
|
|
648
|
+
pathLines.push(` ${pkg2.package}: ${pkg2.spec}`);
|
|
649
|
+
shown++;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const remaining = phantomCount - shown;
|
|
653
|
+
detail += pathLines.join("");
|
|
654
|
+
if (remaining > 0) detail += `
|
|
655
|
+
... and ${remaining} more`;
|
|
656
|
+
}
|
|
657
|
+
actions.push({
|
|
658
|
+
title: `Fix ${phantomCount} phantom dependencies`,
|
|
659
|
+
explanation: detail,
|
|
660
|
+
severity: 45
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (artifact.extended?.securityPosture) {
|
|
665
|
+
const sec = artifact.extended.securityPosture;
|
|
666
|
+
if (sec.envFilesTracked || !sec.lockfilePresent) {
|
|
667
|
+
const issues = [];
|
|
668
|
+
if (sec.envFilesTracked) issues.push(".env files are tracked in git");
|
|
669
|
+
if (!sec.lockfilePresent) issues.push("no lockfile found");
|
|
670
|
+
actions.push({
|
|
671
|
+
title: `Fix security posture: ${issues.join(", ")}`,
|
|
672
|
+
explanation: sec.envFilesTracked ? "Environment files may contain secrets. Add them to .gitignore and rotate any exposed credentials immediately." : "Without a lockfile, installs are non-deterministic. Run the install command to generate one and commit it.",
|
|
673
|
+
severity: 95
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (artifact.extended?.dependencyGraph) {
|
|
678
|
+
const dupes = artifact.extended.dependencyGraph.duplicatedPackages;
|
|
679
|
+
const highImpactDupes = dupes.filter((d) => d.versions.length >= 3);
|
|
680
|
+
if (highImpactDupes.length >= 3) {
|
|
681
|
+
const names = highImpactDupes.slice(0, 4).map((d) => `${d.name} (${d.versions.length}v)`).join(", ");
|
|
682
|
+
actions.push({
|
|
683
|
+
title: `Deduplicate heavily-versioned packages`,
|
|
684
|
+
explanation: `${highImpactDupes.length} packages have 3+ versions installed: ${names}. Run \`npm dedupe\` to reduce bundle size and install time.`,
|
|
685
|
+
severity: 35
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
actions.sort((a, b) => b.severity - a.severity);
|
|
690
|
+
return actions.slice(0, 5);
|
|
691
|
+
}
|
|
474
692
|
|
|
475
693
|
// src/formatters/sarif.ts
|
|
476
694
|
function formatSarif(artifact) {
|
|
@@ -554,12 +772,6 @@ function toSarifResult(finding) {
|
|
|
554
772
|
};
|
|
555
773
|
}
|
|
556
774
|
|
|
557
|
-
// src/version.ts
|
|
558
|
-
import { createRequire } from "module";
|
|
559
|
-
var require2 = createRequire(import.meta.url);
|
|
560
|
-
var pkg = require2("../package.json");
|
|
561
|
-
var VERSION = pkg.version;
|
|
562
|
-
|
|
563
775
|
// src/commands/scan.ts
|
|
564
776
|
import * as path12 from "path";
|
|
565
777
|
import { Command } from "commander";
|
|
@@ -1350,7 +1562,7 @@ var ROBOT = [
|
|
|
1350
1562
|
];
|
|
1351
1563
|
var BRAND = [
|
|
1352
1564
|
chalk2.bold.white(" V I B G R A T E"),
|
|
1353
|
-
chalk2.dim(
|
|
1565
|
+
chalk2.dim(` Drift Intelligence Engine`) + chalk2.dim(` v${VERSION}`)
|
|
1354
1566
|
];
|
|
1355
1567
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1356
1568
|
var ScanProgress = class {
|
|
@@ -2021,9 +2233,11 @@ async function scanDependencyGraph(rootDir) {
|
|
|
2021
2233
|
const lockedNames = new Set(versionMap.keys());
|
|
2022
2234
|
const pkgFiles = await findPackageJsonFiles(rootDir);
|
|
2023
2235
|
const phantoms = /* @__PURE__ */ new Set();
|
|
2236
|
+
const phantomDetails = [];
|
|
2024
2237
|
for (const pjPath of pkgFiles) {
|
|
2025
2238
|
try {
|
|
2026
2239
|
const pj = await readJsonFile(pjPath);
|
|
2240
|
+
const relPath = path7.relative(rootDir, pjPath);
|
|
2027
2241
|
for (const section of ["dependencies", "devDependencies"]) {
|
|
2028
2242
|
const deps = pj[section];
|
|
2029
2243
|
if (!deps) continue;
|
|
@@ -2031,6 +2245,7 @@ async function scanDependencyGraph(rootDir) {
|
|
|
2031
2245
|
const ver = typeof version === "string" ? version : "";
|
|
2032
2246
|
if (!lockedNames.has(name) && !ver.startsWith("workspace:")) {
|
|
2033
2247
|
phantoms.add(name);
|
|
2248
|
+
phantomDetails.push({ package: name, spec: ver, sourcePath: relPath });
|
|
2034
2249
|
}
|
|
2035
2250
|
}
|
|
2036
2251
|
}
|
|
@@ -2038,6 +2253,7 @@ async function scanDependencyGraph(rootDir) {
|
|
|
2038
2253
|
}
|
|
2039
2254
|
}
|
|
2040
2255
|
result.phantomDependencies = [...phantoms].sort();
|
|
2256
|
+
result.phantomDependencyDetails = phantomDetails.sort((a, b) => a.sourcePath.localeCompare(b.sourcePath) || a.package.localeCompare(b.package));
|
|
2041
2257
|
return result;
|
|
2042
2258
|
}
|
|
2043
2259
|
|
|
@@ -3447,10 +3663,12 @@ function scanServiceDependencies(projects) {
|
|
|
3447
3663
|
|
|
3448
3664
|
// src/commands/scan.ts
|
|
3449
3665
|
async function runScan(rootDir, opts) {
|
|
3666
|
+
const scanStart = Date.now();
|
|
3450
3667
|
const config = await loadConfig(rootDir);
|
|
3451
3668
|
const sem = new Semaphore(opts.concurrency);
|
|
3452
3669
|
const npmCache = new NpmCache(rootDir, sem);
|
|
3453
3670
|
const scanners = config.scanners;
|
|
3671
|
+
let filesScanned = 0;
|
|
3454
3672
|
const progress = new ScanProgress(rootDir);
|
|
3455
3673
|
const steps = [
|
|
3456
3674
|
{ id: "config", label: "Loading configuration" },
|
|
@@ -3484,6 +3702,7 @@ async function runScan(rootDir, opts) {
|
|
|
3484
3702
|
progress.addDependencies(p.dependencies.length);
|
|
3485
3703
|
progress.addFrameworks(p.frameworks.length);
|
|
3486
3704
|
}
|
|
3705
|
+
filesScanned += nodeProjects.length;
|
|
3487
3706
|
progress.addProjects(nodeProjects.length);
|
|
3488
3707
|
progress.completeStep("node", `${nodeProjects.length} project${nodeProjects.length !== 1 ? "s" : ""}`, nodeProjects.length);
|
|
3489
3708
|
progress.startStep("dotnet");
|
|
@@ -3492,91 +3711,130 @@ async function runScan(rootDir, opts) {
|
|
|
3492
3711
|
progress.addDependencies(p.dependencies.length);
|
|
3493
3712
|
progress.addFrameworks(p.frameworks.length);
|
|
3494
3713
|
}
|
|
3714
|
+
filesScanned += dotnetProjects.length;
|
|
3495
3715
|
progress.addProjects(dotnetProjects.length);
|
|
3496
3716
|
progress.completeStep("dotnet", `${dotnetProjects.length} project${dotnetProjects.length !== 1 ? "s" : ""}`, dotnetProjects.length);
|
|
3497
3717
|
const allProjects = [...nodeProjects, ...dotnetProjects];
|
|
3498
3718
|
const extended = {};
|
|
3499
3719
|
if (scanners !== false) {
|
|
3720
|
+
const scannerTasks = [];
|
|
3500
3721
|
if (scanners?.platformMatrix?.enabled !== false) {
|
|
3501
3722
|
progress.startStep("platform");
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3723
|
+
scannerTasks.push(
|
|
3724
|
+
scanPlatformMatrix(rootDir).then((result) => {
|
|
3725
|
+
extended.platformMatrix = result;
|
|
3726
|
+
const nativeCount = result.nativeModules.length;
|
|
3727
|
+
const dockerCount = result.dockerBaseImages.length;
|
|
3728
|
+
const parts = [];
|
|
3729
|
+
if (nativeCount > 0) parts.push(`${nativeCount} native`);
|
|
3730
|
+
if (dockerCount > 0) parts.push(`${dockerCount} docker`);
|
|
3731
|
+
progress.completeStep("platform", parts.join(", ") || "clean", nativeCount + dockerCount);
|
|
3732
|
+
})
|
|
3733
|
+
);
|
|
3509
3734
|
}
|
|
3510
3735
|
if (scanners?.toolingInventory?.enabled !== false) {
|
|
3511
3736
|
progress.startStep("tooling");
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3737
|
+
scannerTasks.push(
|
|
3738
|
+
Promise.resolve().then(() => {
|
|
3739
|
+
extended.toolingInventory = scanToolingInventory(allProjects);
|
|
3740
|
+
const toolCount = Object.values(extended.toolingInventory).reduce((sum, arr) => sum + arr.length, 0);
|
|
3741
|
+
progress.completeStep("tooling", `${toolCount} tool${toolCount !== 1 ? "s" : ""} mapped`, toolCount);
|
|
3742
|
+
})
|
|
3743
|
+
);
|
|
3515
3744
|
}
|
|
3516
3745
|
if (scanners?.serviceDependencies?.enabled !== false) {
|
|
3517
3746
|
progress.startStep("services");
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3747
|
+
scannerTasks.push(
|
|
3748
|
+
Promise.resolve().then(() => {
|
|
3749
|
+
extended.serviceDependencies = scanServiceDependencies(allProjects);
|
|
3750
|
+
const svcCount = Object.values(extended.serviceDependencies).reduce((sum, arr) => sum + arr.length, 0);
|
|
3751
|
+
progress.completeStep("services", `${svcCount} service${svcCount !== 1 ? "s" : ""} detected`, svcCount);
|
|
3752
|
+
})
|
|
3753
|
+
);
|
|
3521
3754
|
}
|
|
3522
3755
|
if (scanners?.breakingChangeExposure?.enabled !== false) {
|
|
3523
3756
|
progress.startStep("breaking");
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3757
|
+
scannerTasks.push(
|
|
3758
|
+
Promise.resolve().then(() => {
|
|
3759
|
+
extended.breakingChangeExposure = scanBreakingChangeExposure(allProjects);
|
|
3760
|
+
const bc = extended.breakingChangeExposure;
|
|
3761
|
+
const bcTotal = bc.deprecatedPackages.length + bc.legacyPolyfills.length;
|
|
3762
|
+
progress.completeStep(
|
|
3763
|
+
"breaking",
|
|
3764
|
+
bcTotal > 0 ? `${bc.deprecatedPackages.length} deprecated, ${bc.legacyPolyfills.length} polyfills` : "none found",
|
|
3765
|
+
bcTotal
|
|
3766
|
+
);
|
|
3767
|
+
})
|
|
3531
3768
|
);
|
|
3532
3769
|
}
|
|
3533
3770
|
if (scanners?.securityPosture?.enabled !== false) {
|
|
3534
3771
|
progress.startStep("security");
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3772
|
+
scannerTasks.push(
|
|
3773
|
+
scanSecurityPosture(rootDir).then((result) => {
|
|
3774
|
+
extended.securityPosture = result;
|
|
3775
|
+
const secDetail = result.lockfilePresent ? `lockfile \u2714${result.gitignoreCoversEnv ? " \xB7 .env \u2714" : " \xB7 .env \u2716"}` : "no lockfile";
|
|
3776
|
+
progress.completeStep("security", secDetail);
|
|
3777
|
+
})
|
|
3778
|
+
);
|
|
3539
3779
|
}
|
|
3540
3780
|
if (scanners?.buildDeploy?.enabled !== false) {
|
|
3541
3781
|
progress.startStep("build");
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3782
|
+
scannerTasks.push(
|
|
3783
|
+
scanBuildDeploy(rootDir).then((result) => {
|
|
3784
|
+
extended.buildDeploy = result;
|
|
3785
|
+
const bdParts = [];
|
|
3786
|
+
if (result.ci.length > 0) bdParts.push(result.ci.join(", "));
|
|
3787
|
+
if (result.docker.dockerfileCount > 0) bdParts.push(`${result.docker.dockerfileCount} Dockerfile${result.docker.dockerfileCount !== 1 ? "s" : ""}`);
|
|
3788
|
+
progress.completeStep("build", bdParts.join(" \xB7 ") || "none detected");
|
|
3789
|
+
})
|
|
3790
|
+
);
|
|
3548
3791
|
}
|
|
3549
3792
|
if (scanners?.tsModernity?.enabled !== false) {
|
|
3550
3793
|
progress.startStep("ts");
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3794
|
+
scannerTasks.push(
|
|
3795
|
+
scanTsModernity(rootDir).then((result) => {
|
|
3796
|
+
extended.tsModernity = result;
|
|
3797
|
+
const tsParts = [];
|
|
3798
|
+
if (result.typescriptVersion) tsParts.push(`v${result.typescriptVersion}`);
|
|
3799
|
+
if (result.strict === true) tsParts.push("strict");
|
|
3800
|
+
if (result.moduleType) tsParts.push(result.moduleType.toUpperCase());
|
|
3801
|
+
progress.completeStep("ts", tsParts.join(" \xB7 ") || "no tsconfig");
|
|
3802
|
+
})
|
|
3803
|
+
);
|
|
3558
3804
|
}
|
|
3559
3805
|
if (scanners?.fileHotspots?.enabled !== false) {
|
|
3560
3806
|
progress.startStep("hotspots");
|
|
3561
|
-
|
|
3562
|
-
|
|
3807
|
+
scannerTasks.push(
|
|
3808
|
+
scanFileHotspots(rootDir).then((result) => {
|
|
3809
|
+
extended.fileHotspots = result;
|
|
3810
|
+
progress.completeStep("hotspots", `${result.totalFiles} files`, result.totalFiles);
|
|
3811
|
+
})
|
|
3812
|
+
);
|
|
3563
3813
|
}
|
|
3564
3814
|
if (scanners?.dependencyGraph?.enabled !== false) {
|
|
3565
3815
|
progress.startStep("depgraph");
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3816
|
+
scannerTasks.push(
|
|
3817
|
+
scanDependencyGraph(rootDir).then((result) => {
|
|
3818
|
+
extended.dependencyGraph = result;
|
|
3819
|
+
const dgDetail = result.lockfileType ? `${result.lockfileType} \xB7 ${result.totalUnique} unique` : "no lockfile";
|
|
3820
|
+
progress.completeStep("depgraph", dgDetail, result.totalUnique);
|
|
3821
|
+
})
|
|
3822
|
+
);
|
|
3570
3823
|
}
|
|
3571
3824
|
if (scanners?.dependencyRisk?.enabled !== false) {
|
|
3572
3825
|
progress.startStep("deprisk");
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3826
|
+
scannerTasks.push(
|
|
3827
|
+
Promise.resolve().then(() => {
|
|
3828
|
+
extended.dependencyRisk = scanDependencyRisk(allProjects);
|
|
3829
|
+
const dr = extended.dependencyRisk;
|
|
3830
|
+
const drParts = [];
|
|
3831
|
+
if (dr.deprecatedPackages.length > 0) drParts.push(`${dr.deprecatedPackages.length} deprecated`);
|
|
3832
|
+
if (dr.nativeModulePackages.length > 0) drParts.push(`${dr.nativeModulePackages.length} native`);
|
|
3833
|
+
progress.completeStep("deprisk", drParts.join(", ") || "low risk");
|
|
3834
|
+
})
|
|
3835
|
+
);
|
|
3579
3836
|
}
|
|
3837
|
+
await Promise.all(scannerTasks);
|
|
3580
3838
|
}
|
|
3581
3839
|
progress.startStep("drift");
|
|
3582
3840
|
const drift = computeDriftScore(allProjects);
|
|
@@ -3596,6 +3854,15 @@ async function runScan(rootDir, opts) {
|
|
|
3596
3854
|
if (allProjects.length === 0) {
|
|
3597
3855
|
console.log(chalk3.yellow("No projects found."));
|
|
3598
3856
|
}
|
|
3857
|
+
if (extended.fileHotspots) filesScanned += extended.fileHotspots.totalFiles;
|
|
3858
|
+
if (extended.securityPosture) filesScanned += 1;
|
|
3859
|
+
if (extended.tsModernity?.typescriptVersion) filesScanned += 1;
|
|
3860
|
+
if (extended.dependencyGraph?.lockfileType) filesScanned += 1;
|
|
3861
|
+
if (extended.buildDeploy) {
|
|
3862
|
+
filesScanned += extended.buildDeploy.docker.dockerfileCount;
|
|
3863
|
+
filesScanned += extended.buildDeploy.ci.length;
|
|
3864
|
+
}
|
|
3865
|
+
const durationMs = Date.now() - scanStart;
|
|
3599
3866
|
const artifact = {
|
|
3600
3867
|
schemaVersion: "1.0",
|
|
3601
3868
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3605,7 +3872,9 @@ async function runScan(rootDir, opts) {
|
|
|
3605
3872
|
projects: allProjects,
|
|
3606
3873
|
drift,
|
|
3607
3874
|
findings,
|
|
3608
|
-
...Object.keys(extended).length > 0 ? { extended } : {}
|
|
3875
|
+
...Object.keys(extended).length > 0 ? { extended } : {},
|
|
3876
|
+
durationMs,
|
|
3877
|
+
filesScanned
|
|
3609
3878
|
};
|
|
3610
3879
|
if (opts.baseline) {
|
|
3611
3880
|
const baselinePath = path12.resolve(opts.baseline);
|
|
@@ -3689,9 +3958,9 @@ export {
|
|
|
3689
3958
|
writeDefaultConfig,
|
|
3690
3959
|
computeDriftScore,
|
|
3691
3960
|
generateFindings,
|
|
3961
|
+
VERSION,
|
|
3692
3962
|
formatText,
|
|
3693
3963
|
formatSarif,
|
|
3694
|
-
VERSION,
|
|
3695
3964
|
runScan,
|
|
3696
3965
|
scanCommand
|
|
3697
3966
|
};
|
|
@@ -8,7 +8,10 @@ function formatMarkdown(artifact) {
|
|
|
8
8
|
lines.push(`| **Drift Score** | ${artifact.drift.score}/100 |`);
|
|
9
9
|
lines.push(`| **Risk Level** | ${artifact.drift.riskLevel.toUpperCase()} |`);
|
|
10
10
|
lines.push(`| **Projects** | ${artifact.projects.length} |`);
|
|
11
|
-
|
|
11
|
+
const scannedMeta = [artifact.timestamp];
|
|
12
|
+
if (artifact.durationMs !== void 0) scannedMeta.push(`${(artifact.durationMs / 1e3).toFixed(1)}s`);
|
|
13
|
+
if (artifact.filesScanned !== void 0) scannedMeta.push(`${artifact.filesScanned} files`);
|
|
14
|
+
lines.push(`| **Scanned** | ${scannedMeta.join(" \xB7 ")} |`);
|
|
12
15
|
if (artifact.vcs) {
|
|
13
16
|
lines.push(`| **VCS** | ${artifact.vcs.type} |`);
|
|
14
17
|
if (artifact.vcs.branch) lines.push(`| **Branch** | ${artifact.vcs.branch} |`);
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
formatMarkdown
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VXZT34Y5.js";
|
|
5
5
|
import {
|
|
6
6
|
baselineCommand
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-NTRKEIKP.js";
|
|
8
8
|
import {
|
|
9
9
|
VERSION,
|
|
10
10
|
ensureDir,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
scanCommand,
|
|
16
16
|
writeDefaultConfig,
|
|
17
17
|
writeTextFile
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-VMNBKARQ.js";
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
21
|
import { Command as Command6 } from "commander";
|
|
@@ -38,7 +38,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
38
38
|
console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
|
|
39
39
|
}
|
|
40
40
|
if (opts.baseline) {
|
|
41
|
-
const { runBaseline } = await import("./baseline-
|
|
41
|
+
const { runBaseline } = await import("./baseline-35XRSRAD.js");
|
|
42
42
|
await runBaseline(rootDir);
|
|
43
43
|
}
|
|
44
44
|
console.log("");
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ interface DriftScore {
|
|
|
43
43
|
dependencyScore: number;
|
|
44
44
|
eolScore: number;
|
|
45
45
|
};
|
|
46
|
+
/** Which components had sufficient data to score. Missing = no data available. */
|
|
47
|
+
measured?: ('runtime' | 'framework' | 'dependency' | 'eol')[];
|
|
46
48
|
}
|
|
47
49
|
interface Finding {
|
|
48
50
|
ruleId: string;
|
|
@@ -70,6 +72,10 @@ interface ScanArtifact {
|
|
|
70
72
|
baseline?: string;
|
|
71
73
|
delta?: number;
|
|
72
74
|
extended?: ExtendedScanResults;
|
|
75
|
+
/** Scan wall-clock duration in milliseconds */
|
|
76
|
+
durationMs?: number;
|
|
77
|
+
/** Number of manifest/config files scanned */
|
|
78
|
+
filesScanned?: number;
|
|
73
79
|
}
|
|
74
80
|
interface ScanOptions {
|
|
75
81
|
out?: string;
|
|
@@ -130,12 +136,18 @@ interface DuplicatedPackage {
|
|
|
130
136
|
versions: string[];
|
|
131
137
|
consumers: number;
|
|
132
138
|
}
|
|
139
|
+
interface PhantomDependency {
|
|
140
|
+
package: string;
|
|
141
|
+
spec: string;
|
|
142
|
+
sourcePath: string;
|
|
143
|
+
}
|
|
133
144
|
interface DependencyGraphResult {
|
|
134
145
|
lockfileType: string | null;
|
|
135
146
|
totalUnique: number;
|
|
136
147
|
totalInstalled: number;
|
|
137
148
|
duplicatedPackages: DuplicatedPackage[];
|
|
138
149
|
phantomDependencies: string[];
|
|
150
|
+
phantomDependencyDetails?: PhantomDependency[];
|
|
139
151
|
}
|
|
140
152
|
interface InventoryItem {
|
|
141
153
|
name: string;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatMarkdown
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VXZT34Y5.js";
|
|
4
4
|
import {
|
|
5
5
|
computeDriftScore,
|
|
6
6
|
formatSarif,
|
|
7
7
|
formatText,
|
|
8
8
|
generateFindings,
|
|
9
9
|
runScan
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VMNBKARQ.js";
|
|
11
11
|
export {
|
|
12
12
|
computeDriftScore,
|
|
13
13
|
formatMarkdown,
|