@vibgrate/cli 1.0.70 → 1.0.72
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/DOCS.md +1 -23
- package/README.md +0 -1
- package/dist/baseline-DXFJEV4M.js +10 -0
- package/dist/{chunk-L37LOGK2.js → chunk-7MXWH3XU.js} +2 -2
- package/dist/{chunk-RTKLZCLZ.js → chunk-DSKISXFM.js} +4281 -937
- package/dist/{chunk-TBE6NQ5Z.js → chunk-JQHUH6A3.js} +54 -5
- package/dist/cli.js +4 -4
- package/dist/{fs-B2ZS4NOP.js → fs-D24ONFXR.js} +1 -1
- package/dist/index.d.ts +10 -55
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/baseline-MRLWTPFD.js +0 -10
|
@@ -220,12 +220,16 @@ var FileCache = class _FileCache {
|
|
|
220
220
|
existsCache = /* @__PURE__ */ new Map();
|
|
221
221
|
/** User-configured exclude predicate (compiled from glob patterns) */
|
|
222
222
|
excludePredicate = null;
|
|
223
|
-
/** Directories that were auto-skipped because they were stuck
|
|
223
|
+
/** Directories that were auto-skipped because they were stuck */
|
|
224
224
|
_stuckPaths = [];
|
|
225
225
|
/** Files skipped because they exceed maxFileSizeToScan */
|
|
226
226
|
_skippedLargeFiles = [];
|
|
227
227
|
/** Maximum file size (bytes) we will read. 0 = unlimited. */
|
|
228
228
|
_maxFileSize = 0;
|
|
229
|
+
/** Per-project / per-directory scan timeout in ms. */
|
|
230
|
+
_projectScanTimeout = 18e4;
|
|
231
|
+
/** Whether we have already shown the "increase projectScanTimeout" hint */
|
|
232
|
+
_timeoutHintShown = false;
|
|
229
233
|
/** Root dir for relative-path computation (set by the first walkDir call) */
|
|
230
234
|
_rootDir = null;
|
|
231
235
|
/** Cached tree summary captured during the shared walk */
|
|
@@ -240,10 +244,29 @@ var FileCache = class _FileCache {
|
|
|
240
244
|
setMaxFileSize(bytes) {
|
|
241
245
|
this._maxFileSize = bytes;
|
|
242
246
|
}
|
|
247
|
+
/** Set the per-project scan timeout (milliseconds). Scanners use this
|
|
248
|
+
* instead of a hard-coded constant so the user can override it via config. */
|
|
249
|
+
setProjectScanTimeout(ms) {
|
|
250
|
+
this._projectScanTimeout = ms;
|
|
251
|
+
}
|
|
252
|
+
/** Current per-project scan timeout in milliseconds */
|
|
253
|
+
get projectScanTimeout() {
|
|
254
|
+
return this._projectScanTimeout;
|
|
255
|
+
}
|
|
243
256
|
/** Record a path that timed out or was stuck during scanning */
|
|
244
257
|
addStuckPath(relPath) {
|
|
245
258
|
this._stuckPaths.push(relPath);
|
|
246
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Returns true the first time it is called, false thereafter.
|
|
262
|
+
* Used by scanners to print the "increase projectScanTimeout" hint
|
|
263
|
+
* only once per scan run.
|
|
264
|
+
*/
|
|
265
|
+
shouldShowTimeoutHint() {
|
|
266
|
+
if (this._timeoutHintShown) return false;
|
|
267
|
+
this._timeoutHintShown = true;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
247
270
|
/** Get all paths that were auto-skipped due to being stuck (dirs + scanner files) */
|
|
248
271
|
get stuckPaths() {
|
|
249
272
|
return this._stuckPaths;
|
|
@@ -287,7 +310,7 @@ var FileCache = class _FileCache {
|
|
|
287
310
|
let lastReported = 0;
|
|
288
311
|
const REPORT_INTERVAL = 50;
|
|
289
312
|
const sem = new Semaphore(maxConcurrentReads);
|
|
290
|
-
const STUCK_TIMEOUT_MS =
|
|
313
|
+
const STUCK_TIMEOUT_MS = this._projectScanTimeout;
|
|
291
314
|
const extraSkip = _FileCache.EXTRA_SKIP;
|
|
292
315
|
const isExcluded = this.excludePredicate;
|
|
293
316
|
const stuckDirs = this._stuckPaths;
|
|
@@ -300,12 +323,15 @@ var FileCache = class _FileCache {
|
|
|
300
323
|
try {
|
|
301
324
|
entries = await sem.run(async () => {
|
|
302
325
|
const readPromise = fs.readdir(dir, { withFileTypes: true });
|
|
326
|
+
let stuckTimer;
|
|
303
327
|
const result = await Promise.race([
|
|
304
328
|
readPromise.then((e) => ({ ok: true, entries: e })),
|
|
305
|
-
new Promise(
|
|
306
|
-
|
|
307
|
-
|
|
329
|
+
new Promise((resolve2) => {
|
|
330
|
+
stuckTimer = setTimeout(() => resolve2({ ok: false }), STUCK_TIMEOUT_MS);
|
|
331
|
+
stuckTimer.unref();
|
|
332
|
+
})
|
|
308
333
|
]);
|
|
334
|
+
clearTimeout(stuckTimer);
|
|
309
335
|
if (!result.ok) {
|
|
310
336
|
stuckDirs.push(relDir || dir);
|
|
311
337
|
return null;
|
|
@@ -378,6 +404,29 @@ var FileCache = class _FileCache {
|
|
|
378
404
|
const entries = await this.walkDir(rootDir);
|
|
379
405
|
return entries.filter((e) => e.isFile && e.name.endsWith(".sln")).map((e) => e.absPath);
|
|
380
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* Count files under a given directory using the cached walk data.
|
|
409
|
+
* Avoids a redundant recursive readdir that can be slow on large
|
|
410
|
+
* project trees (the main cause of per-project timeout hits).
|
|
411
|
+
* Falls back to the standalone `countFilesInDir` if the walk hasn't
|
|
412
|
+
* been populated yet.
|
|
413
|
+
*/
|
|
414
|
+
async countFilesUnder(rootDir, dir) {
|
|
415
|
+
const entries = this.walkCache.get(rootDir);
|
|
416
|
+
if (!entries) {
|
|
417
|
+
return countFilesInDir(dir);
|
|
418
|
+
}
|
|
419
|
+
const resolved = await entries;
|
|
420
|
+
const prefix = dir.endsWith(path2.sep) ? dir : dir + path2.sep;
|
|
421
|
+
let count = 0;
|
|
422
|
+
for (const e of resolved) {
|
|
423
|
+
if (!e.isFile) continue;
|
|
424
|
+
if (e.absPath === dir || e.absPath.startsWith(prefix)) {
|
|
425
|
+
count++;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return count;
|
|
429
|
+
}
|
|
381
430
|
// ── File content reading ──
|
|
382
431
|
/**
|
|
383
432
|
* Read a text file. Files ≤ 1 MB are cached so subsequent calls from
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
baselineCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7MXWH3XU.js";
|
|
5
5
|
import {
|
|
6
6
|
VERSION,
|
|
7
7
|
dsnCommand,
|
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
pushCommand,
|
|
11
11
|
scanCommand,
|
|
12
12
|
writeDefaultConfig
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DSKISXFM.js";
|
|
14
14
|
import {
|
|
15
15
|
ensureDir,
|
|
16
16
|
pathExists,
|
|
17
17
|
readJsonFile,
|
|
18
18
|
writeTextFile
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-JQHUH6A3.js";
|
|
20
20
|
|
|
21
21
|
// src/cli.ts
|
|
22
22
|
import { Command as Command6 } from "commander";
|
|
@@ -39,7 +39,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
39
39
|
console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
|
|
40
40
|
}
|
|
41
41
|
if (opts.baseline) {
|
|
42
|
-
const { runBaseline } = await import("./baseline-
|
|
42
|
+
const { runBaseline } = await import("./baseline-DXFJEV4M.js");
|
|
43
43
|
await runBaseline(rootDir);
|
|
44
44
|
}
|
|
45
45
|
console.log("");
|
package/dist/index.d.ts
CHANGED
|
@@ -55,6 +55,8 @@ interface ProjectScan {
|
|
|
55
55
|
fileCount?: number;
|
|
56
56
|
/** Project-level architecture layer diagram (Mermaid flowchart) */
|
|
57
57
|
architectureMermaid?: string;
|
|
58
|
+
/** Project-level architecture detection result (layers, archetype, file counts) */
|
|
59
|
+
architecture?: ArchitectureResult;
|
|
58
60
|
/** Project-level relationship diagram (first-level parents + children) */
|
|
59
61
|
relationshipDiagram?: MermaidDiagram;
|
|
60
62
|
/** Compacted UI purpose evidence for this project */
|
|
@@ -75,6 +77,8 @@ interface SolutionScan {
|
|
|
75
77
|
projectPaths: string[];
|
|
76
78
|
/** Aggregate drift score for all resolved projects in this solution */
|
|
77
79
|
drift?: DriftScore;
|
|
80
|
+
/** Aggregate architecture result for all projects in this solution */
|
|
81
|
+
architecture?: ArchitectureResult;
|
|
78
82
|
/** Solution relationship diagram with top-level solution node and project links */
|
|
79
83
|
relationshipDiagram?: MermaidDiagram;
|
|
80
84
|
}
|
|
@@ -158,8 +162,6 @@ interface ScanOptions {
|
|
|
158
162
|
region?: string;
|
|
159
163
|
/** Fail on push errors (like --strict on push command) */
|
|
160
164
|
strict?: boolean;
|
|
161
|
-
/** Auto-install missing security tools via Homebrew */
|
|
162
|
-
installTools?: boolean;
|
|
163
165
|
/** Enable optional UI-purpose evidence extraction (slower, richer context for dashboard) */
|
|
164
166
|
uiPurpose?: boolean;
|
|
165
167
|
/** Prevent writing .vibgrate JSON artifacts to disk */
|
|
@@ -174,14 +176,12 @@ interface ScanOptions {
|
|
|
174
176
|
driftBudget?: number;
|
|
175
177
|
/** Fail when drift worsens by more than this percentage vs baseline */
|
|
176
178
|
driftWorseningPercent?: number;
|
|
179
|
+
/** Per-project scan timeout override (seconds). Takes precedence over config file. */
|
|
180
|
+
projectScanTimeout?: number;
|
|
177
181
|
}
|
|
178
182
|
interface ScannerToggle {
|
|
179
183
|
enabled: boolean;
|
|
180
184
|
}
|
|
181
|
-
type OwaspScannerMode = 'fast' | 'cache-input';
|
|
182
|
-
interface OwaspScannerConfig extends ScannerToggle {
|
|
183
|
-
mode?: OwaspScannerMode;
|
|
184
|
-
}
|
|
185
185
|
interface ScannersConfig {
|
|
186
186
|
platformMatrix?: ScannerToggle;
|
|
187
187
|
dependencyRisk?: ScannerToggle;
|
|
@@ -192,11 +192,9 @@ interface ScannersConfig {
|
|
|
192
192
|
breakingChangeExposure?: ScannerToggle;
|
|
193
193
|
fileHotspots?: ScannerToggle;
|
|
194
194
|
securityPosture?: ScannerToggle;
|
|
195
|
-
securityScanners?: ScannerToggle;
|
|
196
195
|
serviceDependencies?: ScannerToggle;
|
|
197
196
|
architecture?: ScannerToggle;
|
|
198
197
|
codeQuality?: ScannerToggle;
|
|
199
|
-
owaspCategoryMapping?: OwaspScannerConfig;
|
|
200
198
|
uiPurpose?: ScannerToggle;
|
|
201
199
|
runtimeConfiguration?: ScannerToggle;
|
|
202
200
|
dataStores?: ScannerToggle;
|
|
@@ -211,6 +209,10 @@ interface VibgrateConfig {
|
|
|
211
209
|
/** Maximum file size (bytes) the CLI will read during a scan. Files larger
|
|
212
210
|
* than this are silently skipped. Default: 5 242 880 (5 MB). */
|
|
213
211
|
maxFileSizeToScan?: number;
|
|
212
|
+
/** Per-project scan timeout in seconds. If a single project takes
|
|
213
|
+
* longer than this the project is skipped and the path auto-excluded on
|
|
214
|
+
* the next run. Increase for very large mono-repos. Default: 180 (3 min). */
|
|
215
|
+
projectScanTimeout?: number;
|
|
214
216
|
scanners?: ScannersConfig | false;
|
|
215
217
|
thresholds?: {
|
|
216
218
|
failOnError?: {
|
|
@@ -378,32 +380,6 @@ interface ServiceDependenciesResult {
|
|
|
378
380
|
storage: ServiceDependencyItem[];
|
|
379
381
|
search: ServiceDependencyItem[];
|
|
380
382
|
}
|
|
381
|
-
type SecurityScannerStatus = 'up-to-date' | 'review-needed' | 'unknown' | 'unavailable';
|
|
382
|
-
interface SecurityToolAssessment {
|
|
383
|
-
name: 'semgrep' | 'gitleaks' | 'trufflehog';
|
|
384
|
-
category: 'sast' | 'secrets';
|
|
385
|
-
command: string;
|
|
386
|
-
available: boolean;
|
|
387
|
-
version: string | null;
|
|
388
|
-
minRecommendedVersion: string;
|
|
389
|
-
status: SecurityScannerStatus;
|
|
390
|
-
risks: string[];
|
|
391
|
-
}
|
|
392
|
-
interface SecretHeuristicFinding {
|
|
393
|
-
file: string;
|
|
394
|
-
detector: string;
|
|
395
|
-
sample: string;
|
|
396
|
-
}
|
|
397
|
-
interface SecurityScannersResult {
|
|
398
|
-
semgrep: SecurityToolAssessment;
|
|
399
|
-
secretScanners: SecurityToolAssessment[];
|
|
400
|
-
configFiles: {
|
|
401
|
-
semgrep: boolean;
|
|
402
|
-
gitleaks: boolean;
|
|
403
|
-
trufflehog: boolean;
|
|
404
|
-
};
|
|
405
|
-
heuristicFindings: SecretHeuristicFinding[];
|
|
406
|
-
}
|
|
407
383
|
/** Detected project archetype (fingerprint) */
|
|
408
384
|
type ProjectArchetype = 'nextjs' | 'remix' | 'sveltekit' | 'nuxt' | 'nestjs' | 'express' | 'fastify' | 'hono' | 'koa' | 'serverless' | 'library' | 'cli' | 'monorepo' | 'unknown';
|
|
409
385
|
/** Architectural layer classification */
|
|
@@ -611,11 +587,9 @@ interface ExtendedScanResults {
|
|
|
611
587
|
breakingChangeExposure?: BreakingChangeExposureResult;
|
|
612
588
|
fileHotspots?: FileHotspotsResult;
|
|
613
589
|
securityPosture?: SecurityPostureResult;
|
|
614
|
-
securityScanners?: SecurityScannersResult;
|
|
615
590
|
serviceDependencies?: ServiceDependenciesResult;
|
|
616
591
|
architecture?: ArchitectureResult;
|
|
617
592
|
codeQuality?: CodeQualityResult;
|
|
618
|
-
owaspCategoryMapping?: OwaspCategoryMappingResult;
|
|
619
593
|
uiPurpose?: UiPurposeResult;
|
|
620
594
|
runtimeConfiguration?: RuntimeConfigurationResult;
|
|
621
595
|
dataStores?: DataStoresResult;
|
|
@@ -624,25 +598,6 @@ interface ExtendedScanResults {
|
|
|
624
598
|
assetBranding?: AssetBrandingResult;
|
|
625
599
|
ossGovernance?: OssGovernanceResult;
|
|
626
600
|
}
|
|
627
|
-
interface OwaspFinding {
|
|
628
|
-
ruleId: string;
|
|
629
|
-
path: string;
|
|
630
|
-
line: number;
|
|
631
|
-
endLine?: number;
|
|
632
|
-
message: string;
|
|
633
|
-
severity: 'low' | 'medium' | 'high';
|
|
634
|
-
categories: string[];
|
|
635
|
-
cwe: string | null;
|
|
636
|
-
}
|
|
637
|
-
interface OwaspCategoryMappingResult {
|
|
638
|
-
scanner: 'semgrep';
|
|
639
|
-
available: boolean;
|
|
640
|
-
mode: OwaspScannerMode;
|
|
641
|
-
scannedFiles: number;
|
|
642
|
-
findings: OwaspFinding[];
|
|
643
|
-
categoryCounts: Record<string, number>;
|
|
644
|
-
errors: string[];
|
|
645
|
-
}
|
|
646
601
|
|
|
647
602
|
declare function runScan(rootDir: string, opts: ScanOptions): Promise<ScanArtifact>;
|
|
648
603
|
|
package/dist/index.js
CHANGED
package/package.json
CHANGED