@westbayberry/dg 1.0.2 → 1.0.3
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 +1 -1
- package/dist/index.js +365 -11
- package/dist/index.mjs +40477 -0
- package/package.json +13 -5
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -180,15 +180,37 @@ exports.getVersion = getVersion;
|
|
|
180
180
|
const node_util_1 = __nccwpck_require__(975);
|
|
181
181
|
const node_fs_1 = __nccwpck_require__(24);
|
|
182
182
|
const node_path_1 = __nccwpck_require__(760);
|
|
183
|
+
const node_os_1 = __nccwpck_require__(161);
|
|
184
|
+
function loadDgrc() {
|
|
185
|
+
const candidates = [
|
|
186
|
+
(0, node_path_1.join)(process.cwd(), ".dgrc.json"),
|
|
187
|
+
(0, node_path_1.join)((0, node_os_1.homedir)(), ".dgrc.json"),
|
|
188
|
+
];
|
|
189
|
+
for (const filepath of candidates) {
|
|
190
|
+
if ((0, node_fs_1.existsSync)(filepath)) {
|
|
191
|
+
try {
|
|
192
|
+
return JSON.parse((0, node_fs_1.readFileSync)(filepath, "utf-8"));
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
process.stderr.write(`Warning: Failed to parse ${filepath}, ignoring.\n`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
183
201
|
const USAGE = `
|
|
184
202
|
Dependency Guardian — Supply chain security scanner
|
|
185
203
|
|
|
186
204
|
Usage:
|
|
187
205
|
dependency-guardian scan [options]
|
|
188
206
|
dg scan [options]
|
|
207
|
+
dg npm install <pkg> [npm-flags]
|
|
208
|
+
dg wrap
|
|
189
209
|
|
|
190
210
|
Commands:
|
|
191
211
|
scan Scan dependencies for security risks (default)
|
|
212
|
+
npm Wrap npm commands — scans packages before installing
|
|
213
|
+
wrap Show instructions to alias npm to dg
|
|
192
214
|
|
|
193
215
|
Options:
|
|
194
216
|
--api-key <key> API key (or set DG_API_KEY env var)
|
|
@@ -201,25 +223,36 @@ const USAGE = `
|
|
|
201
223
|
--json Output JSON for CI parsing
|
|
202
224
|
--scan-all Scan all packages, not just changed
|
|
203
225
|
--base-lockfile <path> Path to base lockfile for explicit diff
|
|
226
|
+
--debug Show diagnostic output (discovery, batches, timing)
|
|
227
|
+
--no-config Skip loading .dgrc.json config file
|
|
204
228
|
--help Show this help message
|
|
205
229
|
--version Show version number
|
|
206
230
|
|
|
231
|
+
Config File:
|
|
232
|
+
Place a .dgrc.json in your project root or home directory.
|
|
233
|
+
Precedence: CLI flags > env vars > .dgrc.json > defaults
|
|
234
|
+
|
|
207
235
|
Environment Variables:
|
|
208
236
|
DG_API_KEY API key
|
|
209
237
|
DG_API_URL API base URL
|
|
210
238
|
DG_MODE Mode (block/warn/off)
|
|
211
239
|
DG_ALLOWLIST Comma-separated allowlist
|
|
240
|
+
DG_DEBUG Enable debug output (set to 1)
|
|
212
241
|
|
|
213
242
|
Exit Codes:
|
|
214
243
|
0 pass — No risks detected
|
|
215
244
|
1 warn — Risks detected (advisory)
|
|
216
245
|
2 block — High-risk packages detected
|
|
246
|
+
3 error — Internal error (API failure, config error)
|
|
217
247
|
|
|
218
248
|
Examples:
|
|
219
249
|
DG_API_KEY=dg_live_xxx dg scan
|
|
220
250
|
dg scan --api-key dg_live_xxx --json
|
|
221
251
|
dg scan --scan-all --mode block
|
|
222
252
|
dg scan --base-lockfile ./main-lockfile.json
|
|
253
|
+
dg npm install express lodash
|
|
254
|
+
dg npm install @scope/pkg@^2.0.0
|
|
255
|
+
dg npm install risky-pkg --dg-force
|
|
223
256
|
`.trimStart();
|
|
224
257
|
exports.USAGE = USAGE;
|
|
225
258
|
function getVersion() {
|
|
@@ -245,6 +278,8 @@ function parseConfig(argv) {
|
|
|
245
278
|
json: { type: "boolean", default: false },
|
|
246
279
|
"scan-all": { type: "boolean", default: false },
|
|
247
280
|
"base-lockfile": { type: "string" },
|
|
281
|
+
debug: { type: "boolean", default: false },
|
|
282
|
+
"no-config": { type: "boolean", default: false },
|
|
248
283
|
help: { type: "boolean", default: false },
|
|
249
284
|
version: { type: "boolean", default: false },
|
|
250
285
|
},
|
|
@@ -260,11 +295,14 @@ function parseConfig(argv) {
|
|
|
260
295
|
process.exit(0);
|
|
261
296
|
}
|
|
262
297
|
const command = positionals[0] ?? "scan";
|
|
298
|
+
const noConfig = values["no-config"];
|
|
299
|
+
const dgrc = noConfig ? {} : loadDgrc();
|
|
263
300
|
if (values["api-key"]) {
|
|
264
301
|
process.stderr.write("Warning: --api-key is deprecated (visible in process list). Use DG_API_KEY env var instead.\n");
|
|
265
302
|
}
|
|
266
303
|
const apiKey = values["api-key"] ??
|
|
267
304
|
process.env.DG_API_KEY ??
|
|
305
|
+
dgrc.apiKey ??
|
|
268
306
|
"";
|
|
269
307
|
if (!apiKey) {
|
|
270
308
|
process.stderr.write("Error: API key required. Set DG_API_KEY environment variable.\n" +
|
|
@@ -274,6 +312,7 @@ function parseConfig(argv) {
|
|
|
274
312
|
}
|
|
275
313
|
const modeRaw = values.mode ??
|
|
276
314
|
process.env.DG_MODE ??
|
|
315
|
+
dgrc.mode ??
|
|
277
316
|
"warn";
|
|
278
317
|
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
279
318
|
process.stderr.write(`Error: Invalid mode "${modeRaw}". Must be block, warn, or off.\n`);
|
|
@@ -282,23 +321,40 @@ function parseConfig(argv) {
|
|
|
282
321
|
const allowlistRaw = values.allowlist ??
|
|
283
322
|
process.env.DG_ALLOWLIST ??
|
|
284
323
|
"";
|
|
324
|
+
const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
|
|
325
|
+
const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
|
|
326
|
+
const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
|
|
327
|
+
const debug = values.debug || process.env.DG_DEBUG === "1";
|
|
328
|
+
if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
|
|
329
|
+
process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
if (isNaN(warnThreshold) || warnThreshold < 0 || warnThreshold > 100) {
|
|
333
|
+
process.stderr.write("Error: --warn-threshold must be a number between 0 and 100\n");
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
if (isNaN(maxPackages) || maxPackages < 1 || maxPackages > 10000) {
|
|
337
|
+
process.stderr.write("Error: --max-packages must be a number between 1 and 10000\n");
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
285
340
|
return {
|
|
286
341
|
apiKey,
|
|
287
342
|
apiUrl: values["api-url"] ??
|
|
288
343
|
process.env.DG_API_URL ??
|
|
344
|
+
dgrc.apiUrl ??
|
|
289
345
|
"https://api.westbayberry.com",
|
|
290
346
|
mode: modeRaw,
|
|
291
|
-
blockThreshold
|
|
292
|
-
warnThreshold
|
|
293
|
-
maxPackages
|
|
347
|
+
blockThreshold,
|
|
348
|
+
warnThreshold,
|
|
349
|
+
maxPackages,
|
|
294
350
|
allowlist: allowlistRaw
|
|
295
|
-
.split(",")
|
|
296
|
-
.
|
|
297
|
-
.filter(Boolean),
|
|
351
|
+
? allowlistRaw.split(",").map((s) => s.trim()).filter(Boolean)
|
|
352
|
+
: (dgrc.allowlist ?? []),
|
|
298
353
|
json: values.json,
|
|
299
354
|
scanAll: values["scan-all"],
|
|
300
355
|
baseLockfile: values["base-lockfile"] ?? null,
|
|
301
356
|
command,
|
|
357
|
+
debug,
|
|
302
358
|
};
|
|
303
359
|
}
|
|
304
360
|
|
|
@@ -476,6 +532,273 @@ function toPackageInput(change) {
|
|
|
476
532
|
}
|
|
477
533
|
|
|
478
534
|
|
|
535
|
+
/***/ }),
|
|
536
|
+
|
|
537
|
+
/***/ 124:
|
|
538
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
542
|
+
exports.parseNpmArgs = parseNpmArgs;
|
|
543
|
+
exports.parsePackageSpec = parsePackageSpec;
|
|
544
|
+
exports.resolveVersion = resolveVersion;
|
|
545
|
+
exports.resolvePackages = resolvePackages;
|
|
546
|
+
exports.runNpm = runNpm;
|
|
547
|
+
exports.handleNpmCommand = handleNpmCommand;
|
|
548
|
+
exports.handleWrapCommand = handleWrapCommand;
|
|
549
|
+
const node_child_process_1 = __nccwpck_require__(421);
|
|
550
|
+
const color_1 = __nccwpck_require__(988);
|
|
551
|
+
const api_1 = __nccwpck_require__(879);
|
|
552
|
+
const output_1 = __nccwpck_require__(202);
|
|
553
|
+
/** npm commands that install packages and should trigger a scan */
|
|
554
|
+
const INSTALL_COMMANDS = new Set(["install", "i", "add", "update", "up"]);
|
|
555
|
+
/**
|
|
556
|
+
* Parse the argv after `dg npm ...` to extract the npm command and package specifiers.
|
|
557
|
+
*/
|
|
558
|
+
function parseNpmArgs(args) {
|
|
559
|
+
let dgForce = false;
|
|
560
|
+
const filtered = [];
|
|
561
|
+
// Extract dg-specific flags before passing to npm
|
|
562
|
+
for (const arg of args) {
|
|
563
|
+
if (arg === "--dg-force") {
|
|
564
|
+
dgForce = true;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
filtered.push(arg);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const command = filtered[0] ?? "";
|
|
571
|
+
const shouldScan = INSTALL_COMMANDS.has(command);
|
|
572
|
+
// Extract package specifiers: anything that's not a flag and not the command itself
|
|
573
|
+
const packages = [];
|
|
574
|
+
if (shouldScan) {
|
|
575
|
+
for (let i = 1; i < filtered.length; i++) {
|
|
576
|
+
const arg = filtered[i];
|
|
577
|
+
// Skip flags and their values
|
|
578
|
+
if (arg.startsWith("-")) {
|
|
579
|
+
// Flags that take a value: skip next arg too
|
|
580
|
+
if (flagTakesValue(arg)) {
|
|
581
|
+
i++;
|
|
582
|
+
}
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
packages.push(arg);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return {
|
|
589
|
+
command,
|
|
590
|
+
packages,
|
|
591
|
+
rawArgs: filtered,
|
|
592
|
+
dgForce,
|
|
593
|
+
shouldScan,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
/** npm flags that consume the next argument as a value */
|
|
597
|
+
function flagTakesValue(flag) {
|
|
598
|
+
const valueFlagPrefixes = [
|
|
599
|
+
"--save-prefix",
|
|
600
|
+
"--tag",
|
|
601
|
+
"--registry",
|
|
602
|
+
"--cache",
|
|
603
|
+
"--prefix",
|
|
604
|
+
"--fund",
|
|
605
|
+
"--omit",
|
|
606
|
+
"--install-strategy",
|
|
607
|
+
"--workspace",
|
|
608
|
+
];
|
|
609
|
+
// Short flags that take values
|
|
610
|
+
if (flag === "-w")
|
|
611
|
+
return true;
|
|
612
|
+
for (const prefix of valueFlagPrefixes) {
|
|
613
|
+
if (flag === prefix)
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
// --flag=value style doesn't consume next arg
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Parse a package specifier like "express", "@scope/pkg@^2.0.0", "pkg@latest"
|
|
621
|
+
* into { name, versionSpec }.
|
|
622
|
+
*/
|
|
623
|
+
function parsePackageSpec(spec) {
|
|
624
|
+
// Scoped: @scope/pkg@version
|
|
625
|
+
if (spec.startsWith("@")) {
|
|
626
|
+
const slashIdx = spec.indexOf("/");
|
|
627
|
+
if (slashIdx === -1) {
|
|
628
|
+
return { name: spec, versionSpec: null };
|
|
629
|
+
}
|
|
630
|
+
const afterSlash = spec.slice(slashIdx + 1);
|
|
631
|
+
const atIdx = afterSlash.indexOf("@");
|
|
632
|
+
if (atIdx === -1) {
|
|
633
|
+
return { name: spec, versionSpec: null };
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
name: spec.slice(0, slashIdx + 1 + atIdx),
|
|
637
|
+
versionSpec: afterSlash.slice(atIdx + 1),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
// Unscoped: pkg@version
|
|
641
|
+
const atIdx = spec.indexOf("@");
|
|
642
|
+
if (atIdx === -1 || atIdx === 0) {
|
|
643
|
+
return { name: spec, versionSpec: null };
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
name: spec.slice(0, atIdx),
|
|
647
|
+
versionSpec: spec.slice(atIdx + 1),
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Resolve what version npm would install for a given package specifier.
|
|
652
|
+
* Uses `npm view <spec> version` to get the resolved version.
|
|
653
|
+
*/
|
|
654
|
+
function resolveVersion(spec) {
|
|
655
|
+
try {
|
|
656
|
+
const version = (0, node_child_process_1.execSync)(`npm view "${spec}" version`, {
|
|
657
|
+
encoding: "utf-8",
|
|
658
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
659
|
+
timeout: 15000,
|
|
660
|
+
}).trim();
|
|
661
|
+
return version || null;
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Build PackageInput[] from package specifiers by resolving versions.
|
|
669
|
+
*/
|
|
670
|
+
function resolvePackages(specs) {
|
|
671
|
+
const resolved = [];
|
|
672
|
+
const failed = [];
|
|
673
|
+
for (const spec of specs) {
|
|
674
|
+
const { name, versionSpec } = parsePackageSpec(spec);
|
|
675
|
+
const querySpec = versionSpec ? `${name}@${versionSpec}` : name;
|
|
676
|
+
const version = resolveVersion(querySpec);
|
|
677
|
+
if (version) {
|
|
678
|
+
resolved.push({
|
|
679
|
+
name,
|
|
680
|
+
version,
|
|
681
|
+
previousVersion: null,
|
|
682
|
+
isNew: true,
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
failed.push(spec);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { resolved, failed };
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Run the actual npm command, inheriting stdio.
|
|
693
|
+
* Returns the npm exit code.
|
|
694
|
+
*/
|
|
695
|
+
function runNpm(args) {
|
|
696
|
+
return new Promise((resolve) => {
|
|
697
|
+
const child = (0, node_child_process_1.spawn)("npm", args, {
|
|
698
|
+
stdio: "inherit",
|
|
699
|
+
shell: false,
|
|
700
|
+
});
|
|
701
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
702
|
+
child.on("error", () => resolve(1));
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Main npm wrapper entry point.
|
|
707
|
+
*/
|
|
708
|
+
async function handleNpmCommand(npmArgs, config) {
|
|
709
|
+
const parsed = parseNpmArgs(npmArgs);
|
|
710
|
+
// Non-install commands: pass through directly
|
|
711
|
+
if (!parsed.shouldScan) {
|
|
712
|
+
const code = await runNpm(parsed.rawArgs);
|
|
713
|
+
process.exit(code);
|
|
714
|
+
}
|
|
715
|
+
// No packages specified (e.g. bare `npm install` from package.json)
|
|
716
|
+
if (parsed.packages.length === 0) {
|
|
717
|
+
process.stderr.write((0, color_1.dim)(" Dependency Guardian: no new packages specified, passing through to npm.\n"));
|
|
718
|
+
const code = await runNpm(parsed.rawArgs);
|
|
719
|
+
process.exit(code);
|
|
720
|
+
}
|
|
721
|
+
// Resolve versions
|
|
722
|
+
process.stderr.write((0, color_1.dim)(` Resolving ${parsed.packages.length} package${parsed.packages.length !== 1 ? "s" : ""}...\n`));
|
|
723
|
+
const { resolved, failed } = resolvePackages(parsed.packages);
|
|
724
|
+
if (failed.length > 0) {
|
|
725
|
+
process.stderr.write((0, color_1.yellow)(` Warning: Could not resolve versions for: ${failed.join(", ")}\n`));
|
|
726
|
+
}
|
|
727
|
+
if (resolved.length === 0) {
|
|
728
|
+
process.stderr.write((0, color_1.dim)(" No packages to scan. Passing through to npm.\n"));
|
|
729
|
+
const code = await runNpm(parsed.rawArgs);
|
|
730
|
+
process.exit(code);
|
|
731
|
+
}
|
|
732
|
+
// Filter allowlist
|
|
733
|
+
const toScan = resolved.filter((p) => !config.allowlist.includes(p.name));
|
|
734
|
+
if (toScan.length === 0) {
|
|
735
|
+
process.stderr.write((0, color_1.dim)(" All packages are allowlisted. Passing through to npm.\n"));
|
|
736
|
+
const code = await runNpm(parsed.rawArgs);
|
|
737
|
+
process.exit(code);
|
|
738
|
+
}
|
|
739
|
+
// Scan
|
|
740
|
+
process.stderr.write((0, color_1.dim)(` Scanning ${toScan.length} package${toScan.length !== 1 ? "s" : ""}...\n`));
|
|
741
|
+
let result;
|
|
742
|
+
try {
|
|
743
|
+
const startMs = Date.now();
|
|
744
|
+
result = await (0, api_1.callAnalyzeAPI)(toScan, config);
|
|
745
|
+
const elapsed = Date.now() - startMs;
|
|
746
|
+
if (config.debug) {
|
|
747
|
+
process.stderr.write((0, color_1.dim)(` [debug] API responded in ${elapsed}ms, action=${result.action}, score=${result.score}\n`));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
catch (error) {
|
|
751
|
+
// API unavailable — warn and proceed
|
|
752
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
753
|
+
process.stderr.write((0, color_1.yellow)(` Warning: Scan failed (${msg}). Proceeding with install.\n`));
|
|
754
|
+
const code = await runNpm(parsed.rawArgs);
|
|
755
|
+
process.exit(code);
|
|
756
|
+
return; // unreachable, but helps TypeScript
|
|
757
|
+
}
|
|
758
|
+
// Handle result
|
|
759
|
+
if (result.action === "pass") {
|
|
760
|
+
process.stderr.write((0, color_1.green)(` ${(0, color_1.bold)("\u2713")} ${toScan.length} package${toScan.length !== 1 ? "s" : ""} scanned \u2014 all clear\n\n`));
|
|
761
|
+
const code = await runNpm(parsed.rawArgs);
|
|
762
|
+
process.exit(code);
|
|
763
|
+
}
|
|
764
|
+
// Render findings
|
|
765
|
+
const output = (0, output_1.renderResult)(result, config);
|
|
766
|
+
process.stdout.write(output + "\n");
|
|
767
|
+
if (result.action === "warn") {
|
|
768
|
+
process.stderr.write((0, color_1.yellow)(" Warnings detected. Proceeding with install.\n\n"));
|
|
769
|
+
const code = await runNpm(parsed.rawArgs);
|
|
770
|
+
process.exit(code);
|
|
771
|
+
}
|
|
772
|
+
// Block
|
|
773
|
+
if (result.action === "block") {
|
|
774
|
+
if (parsed.dgForce) {
|
|
775
|
+
process.stderr.write((0, color_1.yellow)((0, color_1.bold)(" --dg-force: Bypassing block. Install at your own risk.\n\n")));
|
|
776
|
+
const code = await runNpm(parsed.rawArgs);
|
|
777
|
+
process.exit(code);
|
|
778
|
+
}
|
|
779
|
+
process.stderr.write((0, color_1.red)((0, color_1.bold)(" BLOCKED: ")) +
|
|
780
|
+
(0, color_1.red)("High-risk packages detected. Install aborted.\n"));
|
|
781
|
+
process.stderr.write((0, color_1.dim)(" Use --dg-force to bypass this check.\n\n"));
|
|
782
|
+
process.exit(2);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const WRAP_USAGE = `
|
|
786
|
+
Set up dg as your npm wrapper:
|
|
787
|
+
|
|
788
|
+
Option 1 — Shell alias (recommended):
|
|
789
|
+
Add to your ~/.zshrc or ~/.bashrc:
|
|
790
|
+
alias npm='dg npm'
|
|
791
|
+
|
|
792
|
+
Option 2 — Per-project .npmrc:
|
|
793
|
+
Not yet supported.
|
|
794
|
+
|
|
795
|
+
Once set up, every \`npm install\` will be scanned automatically.
|
|
796
|
+
`.trimStart();
|
|
797
|
+
function handleWrapCommand() {
|
|
798
|
+
process.stdout.write(WRAP_USAGE);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
|
|
479
802
|
/***/ }),
|
|
480
803
|
|
|
481
804
|
/***/ 202:
|
|
@@ -538,9 +861,10 @@ function renderResult(result, config) {
|
|
|
538
861
|
lines.push("");
|
|
539
862
|
return lines.join("\n");
|
|
540
863
|
}
|
|
541
|
-
// Package table
|
|
542
|
-
const
|
|
543
|
-
const
|
|
864
|
+
// Package table — adapt to terminal width
|
|
865
|
+
const termWidth = process.stdout.columns || 80;
|
|
866
|
+
const COL_NAME = Math.max(16, Math.min(40, Math.floor(termWidth * 0.3)));
|
|
867
|
+
const COL_VER = Math.max(14, Math.min(30, Math.floor(termWidth * 0.25)));
|
|
544
868
|
const COL_SCORE = 7;
|
|
545
869
|
lines.push(` ${(0, color_1.dim)(pad("Package", COL_NAME))}${(0, color_1.dim)(pad("Version", COL_VER))}${(0, color_1.dim)(pad("Score", COL_SCORE))}${(0, color_1.dim)("Action")}`);
|
|
546
870
|
lines.push(` ${(0, color_1.dim)(pad("\u2500".repeat(COL_NAME - 2), COL_NAME))}${(0, color_1.dim)(pad("\u2500".repeat(COL_VER - 2), COL_VER))}${(0, color_1.dim)(pad("\u2500".repeat(COL_SCORE - 2), COL_SCORE))}${(0, color_1.dim)("\u2500".repeat(6))}`);
|
|
@@ -721,7 +1045,7 @@ function parseLockfile(content) {
|
|
|
721
1045
|
if (path === "")
|
|
722
1046
|
continue;
|
|
723
1047
|
const name = extractPackageName(path);
|
|
724
|
-
if (name) {
|
|
1048
|
+
if (name && !packages.has(name)) {
|
|
725
1049
|
const e = entry;
|
|
726
1050
|
packages.set(name, {
|
|
727
1051
|
version: e.version ?? "",
|
|
@@ -778,6 +1102,13 @@ module.exports = require("node:fs");
|
|
|
778
1102
|
|
|
779
1103
|
/***/ }),
|
|
780
1104
|
|
|
1105
|
+
/***/ 161:
|
|
1106
|
+
/***/ ((module) => {
|
|
1107
|
+
|
|
1108
|
+
module.exports = require("node:os");
|
|
1109
|
+
|
|
1110
|
+
/***/ }),
|
|
1111
|
+
|
|
781
1112
|
/***/ 760:
|
|
782
1113
|
/***/ ((module) => {
|
|
783
1114
|
|
|
@@ -841,15 +1172,35 @@ const lockfile_1 = __nccwpck_require__(746);
|
|
|
841
1172
|
const api_1 = __nccwpck_require__(879);
|
|
842
1173
|
const output_1 = __nccwpck_require__(202);
|
|
843
1174
|
const color_1 = __nccwpck_require__(988);
|
|
1175
|
+
const npm_wrapper_1 = __nccwpck_require__(124);
|
|
844
1176
|
async function main() {
|
|
1177
|
+
// Check for `dg npm ...` or `dg wrap` before full config parsing
|
|
1178
|
+
const rawCommand = process.argv[2];
|
|
1179
|
+
if (rawCommand === "npm") {
|
|
1180
|
+
// Parse config but don't require lockfile discovery
|
|
1181
|
+
const config = (0, config_1.parseConfig)(process.argv);
|
|
1182
|
+
await (0, npm_wrapper_1.handleNpmCommand)(process.argv.slice(3), config);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (rawCommand === "wrap") {
|
|
1186
|
+
(0, npm_wrapper_1.handleWrapCommand)();
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
845
1189
|
const config = (0, config_1.parseConfig)(process.argv);
|
|
1190
|
+
const dbg = (msg) => {
|
|
1191
|
+
if (config.debug)
|
|
1192
|
+
process.stderr.write((0, color_1.dim)(` [debug] ${msg}\n`));
|
|
1193
|
+
};
|
|
846
1194
|
if (config.mode === "off") {
|
|
847
1195
|
process.stderr.write((0, color_1.dim)(" Dependency Guardian: mode is off — skipping.\n"));
|
|
848
1196
|
process.exit(0);
|
|
849
1197
|
}
|
|
1198
|
+
dbg(`mode=${config.mode} block=${config.blockThreshold} warn=${config.warnThreshold} max=${config.maxPackages}`);
|
|
1199
|
+
dbg(`api=${config.apiUrl}`);
|
|
850
1200
|
// Discover packages
|
|
851
1201
|
process.stderr.write((0, color_1.dim)(" Discovering package changes...\n"));
|
|
852
1202
|
const discovery = (0, lockfile_1.discoverChanges)(process.cwd(), config);
|
|
1203
|
+
dbg(`discovery method: ${discovery.method}`);
|
|
853
1204
|
if (discovery.packages.length === 0) {
|
|
854
1205
|
process.stderr.write((0, color_1.dim)(" No package changes detected.\n"));
|
|
855
1206
|
process.exit(0);
|
|
@@ -864,10 +1215,13 @@ async function main() {
|
|
|
864
1215
|
if (discovery.skipped.length > 0) {
|
|
865
1216
|
process.stderr.write((0, color_1.yellow)(` Warning: ${discovery.skipped.length} package(s) skipped (max-packages=${config.maxPackages})\n`));
|
|
866
1217
|
}
|
|
1218
|
+
dbg(`packages to scan: ${packages.map(p => `${p.name}@${p.version}`).slice(0, 10).join(", ")}${packages.length > 10 ? ` (+${packages.length - 10} more)` : ""}`);
|
|
867
1219
|
// Call Detection API
|
|
1220
|
+
const startMs = Date.now();
|
|
868
1221
|
const result = await (0, api_1.callAnalyzeAPI)(packages, config, (done, total) => {
|
|
869
1222
|
process.stderr.write((0, color_1.dim)(` Analyzed ${done}/${total}...\n`));
|
|
870
1223
|
});
|
|
1224
|
+
dbg(`API responded in ${Date.now() - startMs}ms, action=${result.action}, score=${result.score}`);
|
|
871
1225
|
// Render output
|
|
872
1226
|
const output = (0, output_1.renderResult)(result, config);
|
|
873
1227
|
process.stdout.write(output + "\n");
|
|
@@ -882,7 +1236,7 @@ async function main() {
|
|
|
882
1236
|
}
|
|
883
1237
|
main().catch((err) => {
|
|
884
1238
|
process.stderr.write(`\n ${(0, color_1.bold)((0, color_1.red)("Error:"))} ${err.message}\n\n`);
|
|
885
|
-
process.exit(1)
|
|
1239
|
+
process.exit(3); // Exit 3 = internal error (distinct from exit 1 = warnings)
|
|
886
1240
|
});
|
|
887
1241
|
|
|
888
1242
|
})();
|