@westbayberry/dg 1.0.2 → 1.0.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/dist/index.js DELETED
@@ -1,892 +0,0 @@
1
- #!/usr/bin/env node
2
- /******/ (() => { // webpackBootstrap
3
- /******/ "use strict";
4
- /******/ var __webpack_modules__ = ({
5
-
6
- /***/ 879:
7
- /***/ ((__unused_webpack_module, exports) => {
8
-
9
-
10
- Object.defineProperty(exports, "__esModule", ({ value: true }));
11
- exports.APIError = void 0;
12
- exports.callAnalyzeAPI = callAnalyzeAPI;
13
- class APIError extends Error {
14
- constructor(message, statusCode, body) {
15
- super(message);
16
- this.statusCode = statusCode;
17
- this.body = body;
18
- this.name = "APIError";
19
- }
20
- }
21
- exports.APIError = APIError;
22
- const BATCH_SIZE = 15;
23
- const MAX_RETRIES = 2;
24
- const RETRY_DELAY_MS = 5000;
25
- async function callAnalyzeAPI(packages, config, onProgress) {
26
- if (packages.length <= BATCH_SIZE) {
27
- return callAnalyzeBatch(packages, config);
28
- }
29
- const batches = [];
30
- for (let i = 0; i < packages.length; i += BATCH_SIZE) {
31
- batches.push(packages.slice(i, i + BATCH_SIZE));
32
- }
33
- // Process batches sequentially — parallel batches overload the server on cold scans
34
- const results = [];
35
- let completed = 0;
36
- for (const batch of batches) {
37
- const result = await callBatchWithRetry(batch, config);
38
- completed += batch.length;
39
- if (onProgress) {
40
- onProgress(completed, packages.length);
41
- }
42
- results.push(result);
43
- }
44
- return mergeResponses(results, config);
45
- }
46
- /**
47
- * Retry a batch on 504 (gateway timeout). The server continues processing
48
- * during a timeout, so retries find previously-analyzed packages cached
49
- * in Redis — fewer cold packages means faster response.
50
- */
51
- async function callBatchWithRetry(packages, config) {
52
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
53
- try {
54
- return await callAnalyzeBatch(packages, config);
55
- }
56
- catch (error) {
57
- const is504 = error instanceof APIError && error.statusCode === 504;
58
- const isTimeout = error instanceof APIError && error.statusCode === 408;
59
- if ((is504 || isTimeout) && attempt < MAX_RETRIES) {
60
- // Server is still working — wait, then retry.
61
- // Packages that finished before the timeout are now cached.
62
- const delay = RETRY_DELAY_MS * (attempt + 1);
63
- process.stderr.write(` Batch timed out, retrying in ${delay / 1000}s (attempt ${attempt + 2}/${MAX_RETRIES + 1})...\n`);
64
- await new Promise((r) => setTimeout(r, delay));
65
- continue;
66
- }
67
- throw error;
68
- }
69
- }
70
- // Unreachable, but TypeScript needs it
71
- throw new Error("Exhausted retries");
72
- }
73
- function mergeResponses(results, config) {
74
- const allPackages = results.flatMap((r) => r.packages);
75
- const maxScore = Math.max(0, ...allPackages.map((p) => p.score));
76
- const action = maxScore >= config.blockThreshold
77
- ? "block"
78
- : maxScore >= config.warnThreshold
79
- ? "warn"
80
- : "pass";
81
- const safeVersions = {};
82
- for (const r of results) {
83
- Object.assign(safeVersions, r.safeVersions);
84
- }
85
- return {
86
- score: maxScore,
87
- action: action,
88
- packages: allPackages,
89
- safeVersions,
90
- durationMs: Math.max(0, ...results.map((r) => r.durationMs)),
91
- };
92
- }
93
- async function callAnalyzeBatch(packages, config) {
94
- const url = `${config.apiUrl}/v1/analyze`;
95
- const payload = {
96
- packages: packages.map((p) => ({
97
- name: p.name,
98
- version: p.version,
99
- previousVersion: p.previousVersion,
100
- isNew: p.isNew,
101
- })),
102
- config: {
103
- blockThreshold: config.blockThreshold,
104
- warnThreshold: config.warnThreshold,
105
- },
106
- };
107
- const controller = new AbortController();
108
- const timeoutId = setTimeout(() => controller.abort(), 120000);
109
- let response;
110
- try {
111
- response = await fetch(url, {
112
- method: "POST",
113
- headers: {
114
- "Content-Type": "application/json",
115
- Authorization: `Bearer ${config.apiKey}`,
116
- "User-Agent": "dependency-guardian-cli/1.0.0",
117
- },
118
- body: JSON.stringify(payload),
119
- signal: controller.signal,
120
- });
121
- }
122
- catch (error) {
123
- clearTimeout(timeoutId);
124
- if (error instanceof Error && error.name === "AbortError") {
125
- throw new APIError("Request timed out after 120s. Try scanning fewer packages.", 408, "");
126
- }
127
- // Node.js fetch wraps the real error in .cause
128
- const cause = error instanceof Error && error.cause;
129
- const detail = cause ? `: ${cause.message || cause}` : "";
130
- throw new Error(`fetch failed${detail}`);
131
- }
132
- clearTimeout(timeoutId);
133
- if (response.status === 401) {
134
- throw new APIError("Invalid API key. Check your --api-key or DG_API_KEY value.\n" +
135
- "Get your key at https://westbayberry.com/dashboard", 401, "");
136
- }
137
- if (response.status === 429) {
138
- throw new APIError("Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing", 429, "");
139
- }
140
- if (!response.ok) {
141
- const body = await response.text();
142
- throw new APIError(`API returned ${response.status}: ${body}`, response.status, body);
143
- }
144
- return (await response.json());
145
- }
146
-
147
-
148
- /***/ }),
149
-
150
- /***/ 988:
151
- /***/ ((__unused_webpack_module, exports) => {
152
-
153
-
154
- /**
155
- * Zero-dependency ANSI color helpers.
156
- * Disabled when stdout is not a TTY or NO_COLOR is set.
157
- */
158
- Object.defineProperty(exports, "__esModule", ({ value: true }));
159
- exports.cyan = exports.yellow = exports.green = exports.red = exports.dim = exports.bold = void 0;
160
- const enabled = process.stdout.isTTY === true && !process.env.NO_COLOR;
161
- const wrap = (code, reset) => enabled ? (s) => `\x1b[${code}m${s}\x1b[${reset}m` : (s) => s;
162
- exports.bold = wrap("1", "22");
163
- exports.dim = wrap("2", "22");
164
- exports.red = wrap("31", "39");
165
- exports.green = wrap("32", "39");
166
- exports.yellow = wrap("33", "39");
167
- exports.cyan = wrap("36", "39");
168
-
169
-
170
- /***/ }),
171
-
172
- /***/ 973:
173
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
174
-
175
-
176
- Object.defineProperty(exports, "__esModule", ({ value: true }));
177
- exports.USAGE = void 0;
178
- exports.parseConfig = parseConfig;
179
- exports.getVersion = getVersion;
180
- const node_util_1 = __nccwpck_require__(975);
181
- const node_fs_1 = __nccwpck_require__(24);
182
- const node_path_1 = __nccwpck_require__(760);
183
- const USAGE = `
184
- Dependency Guardian — Supply chain security scanner
185
-
186
- Usage:
187
- dependency-guardian scan [options]
188
- dg scan [options]
189
-
190
- Commands:
191
- scan Scan dependencies for security risks (default)
192
-
193
- Options:
194
- --api-key <key> API key (or set DG_API_KEY env var)
195
- --api-url <url> API base URL (default: https://api.westbayberry.com)
196
- --mode <mode> block | warn | off (default: warn)
197
- --block-threshold <n> Score threshold for blocking (default: 70)
198
- --warn-threshold <n> Score threshold for warnings (default: 60)
199
- --max-packages <n> Max packages per scan (default: 200)
200
- --allowlist <pkgs> Comma-separated package names to skip
201
- --json Output JSON for CI parsing
202
- --scan-all Scan all packages, not just changed
203
- --base-lockfile <path> Path to base lockfile for explicit diff
204
- --help Show this help message
205
- --version Show version number
206
-
207
- Environment Variables:
208
- DG_API_KEY API key
209
- DG_API_URL API base URL
210
- DG_MODE Mode (block/warn/off)
211
- DG_ALLOWLIST Comma-separated allowlist
212
-
213
- Exit Codes:
214
- 0 pass — No risks detected
215
- 1 warn — Risks detected (advisory)
216
- 2 block — High-risk packages detected
217
-
218
- Examples:
219
- DG_API_KEY=dg_live_xxx dg scan
220
- dg scan --api-key dg_live_xxx --json
221
- dg scan --scan-all --mode block
222
- dg scan --base-lockfile ./main-lockfile.json
223
- `.trimStart();
224
- exports.USAGE = USAGE;
225
- function getVersion() {
226
- try {
227
- const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8"));
228
- return pkg.version ?? "1.0.0";
229
- }
230
- catch {
231
- return "1.0.0";
232
- }
233
- }
234
- function parseConfig(argv) {
235
- const { values, positionals } = (0, node_util_1.parseArgs)({
236
- args: argv.slice(2),
237
- options: {
238
- "api-key": { type: "string" },
239
- "api-url": { type: "string" },
240
- mode: { type: "string" },
241
- "block-threshold": { type: "string" },
242
- "warn-threshold": { type: "string" },
243
- "max-packages": { type: "string" },
244
- allowlist: { type: "string" },
245
- json: { type: "boolean", default: false },
246
- "scan-all": { type: "boolean", default: false },
247
- "base-lockfile": { type: "string" },
248
- help: { type: "boolean", default: false },
249
- version: { type: "boolean", default: false },
250
- },
251
- allowPositionals: true,
252
- strict: false,
253
- });
254
- if (values.help) {
255
- process.stdout.write(USAGE);
256
- process.exit(0);
257
- }
258
- if (values.version) {
259
- process.stdout.write(`dependency-guardian v${getVersion()}\n`);
260
- process.exit(0);
261
- }
262
- const command = positionals[0] ?? "scan";
263
- if (values["api-key"]) {
264
- process.stderr.write("Warning: --api-key is deprecated (visible in process list). Use DG_API_KEY env var instead.\n");
265
- }
266
- const apiKey = values["api-key"] ??
267
- process.env.DG_API_KEY ??
268
- "";
269
- if (!apiKey) {
270
- process.stderr.write("Error: API key required. Set DG_API_KEY environment variable.\n" +
271
- "Do NOT pass keys via --api-key (visible in process list).\n" +
272
- "Get your key at https://westbayberry.com/dashboard\n");
273
- process.exit(1);
274
- }
275
- const modeRaw = values.mode ??
276
- process.env.DG_MODE ??
277
- "warn";
278
- if (!["block", "warn", "off"].includes(modeRaw)) {
279
- process.stderr.write(`Error: Invalid mode "${modeRaw}". Must be block, warn, or off.\n`);
280
- process.exit(1);
281
- }
282
- const allowlistRaw = values.allowlist ??
283
- process.env.DG_ALLOWLIST ??
284
- "";
285
- return {
286
- apiKey,
287
- apiUrl: values["api-url"] ??
288
- process.env.DG_API_URL ??
289
- "https://api.westbayberry.com",
290
- mode: modeRaw,
291
- blockThreshold: Number(values["block-threshold"] ?? "70"),
292
- warnThreshold: Number(values["warn-threshold"] ?? "60"),
293
- maxPackages: Number(values["max-packages"] ?? "200"),
294
- allowlist: allowlistRaw
295
- .split(",")
296
- .map((s) => s.trim())
297
- .filter(Boolean),
298
- json: values.json,
299
- scanAll: values["scan-all"],
300
- baseLockfile: values["base-lockfile"] ?? null,
301
- command,
302
- };
303
- }
304
-
305
-
306
- /***/ }),
307
-
308
- /***/ 746:
309
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
310
-
311
-
312
- Object.defineProperty(exports, "__esModule", ({ value: true }));
313
- exports.discoverChanges = discoverChanges;
314
- const node_child_process_1 = __nccwpck_require__(421);
315
- const node_fs_1 = __nccwpck_require__(24);
316
- const node_path_1 = __nccwpck_require__(760);
317
- const parse_package_lock_1 = __nccwpck_require__(88);
318
- const diff_1 = __nccwpck_require__(229);
319
- const parse_package_json_1 = __nccwpck_require__(417);
320
- /**
321
- * Discover changed (or all) packages by reading the local lockfile
322
- * and comparing against a base.
323
- */
324
- function discoverChanges(cwd, config) {
325
- const lockfilePath = findLockfile(cwd);
326
- if (!lockfilePath) {
327
- throw new Error("No package-lock.json found. Run from your project root or use --base-lockfile.");
328
- }
329
- const headContent = (0, node_fs_1.readFileSync)(lockfilePath, "utf-8");
330
- const headParsed = (0, parse_package_lock_1.parseLockfile)(headContent);
331
- const directDeps = getDirectDeps(cwd);
332
- // 1. --scan-all: treat everything as new
333
- if (config.scanAll) {
334
- const packages = [];
335
- for (const [name, entry] of headParsed.packages) {
336
- if (packages.length >= config.maxPackages)
337
- break;
338
- packages.push({
339
- name,
340
- version: entry.version,
341
- previousVersion: null,
342
- isNew: true,
343
- });
344
- }
345
- return { packages, method: "scan-all", skipped: [] };
346
- }
347
- // 2. --base-lockfile: explicit diff
348
- if (config.baseLockfile) {
349
- if (!(0, node_fs_1.existsSync)(config.baseLockfile)) {
350
- throw new Error(`Base lockfile not found: ${config.baseLockfile}`);
351
- }
352
- const baseContent = (0, node_fs_1.readFileSync)(config.baseLockfile, "utf-8");
353
- const baseParsed = (0, parse_package_lock_1.parseLockfile)(baseContent);
354
- const diff = (0, diff_1.diffLockfiles)(baseParsed, headParsed, config.maxPackages, directDeps);
355
- return {
356
- packages: diff.changes.map(toPackageInput),
357
- method: "base-lockfile",
358
- skipped: diff.skipped,
359
- };
360
- }
361
- // 3. Git auto-diff
362
- const baseContent = getGitBaseLockfile(cwd);
363
- if (baseContent !== null) {
364
- const baseParsed = (0, parse_package_lock_1.parseLockfile)(baseContent);
365
- const diff = (0, diff_1.diffLockfiles)(baseParsed, headParsed, config.maxPackages, directDeps);
366
- return {
367
- packages: diff.changes.map(toPackageInput),
368
- method: "git-diff",
369
- skipped: diff.skipped,
370
- };
371
- }
372
- // 4. Fallback: try package.json diff, resolve ranges via lockfile
373
- const pkgJsonPath = (0, node_path_1.join)(cwd, "package.json");
374
- if ((0, node_fs_1.existsSync)(pkgJsonPath)) {
375
- const headPkgJson = (0, node_fs_1.readFileSync)(pkgJsonPath, "utf-8");
376
- const basePkgJson = getGitBaseFile(cwd, "package.json");
377
- if (basePkgJson !== null) {
378
- const diff = (0, parse_package_json_1.diffPackageJsons)(basePkgJson, headPkgJson, config.maxPackages);
379
- // Resolve semver ranges (e.g. "^3.0.1") to exact versions from the lockfile
380
- const resolved = diff.changes.map((change) => {
381
- const lockEntry = headParsed.packages.get(change.name);
382
- return {
383
- ...change,
384
- newVersion: lockEntry?.version ?? change.newVersion,
385
- };
386
- });
387
- return {
388
- packages: resolved.map(toPackageInput),
389
- method: "fallback",
390
- skipped: [],
391
- };
392
- }
393
- }
394
- // Ultimate fallback: scan everything
395
- const packages = [];
396
- for (const [name, entry] of headParsed.packages) {
397
- if (packages.length >= config.maxPackages)
398
- break;
399
- packages.push({
400
- name,
401
- version: entry.version,
402
- previousVersion: null,
403
- isNew: true,
404
- });
405
- }
406
- return { packages, method: "fallback", skipped: [] };
407
- }
408
- function findLockfile(cwd) {
409
- const candidates = ["package-lock.json", "npm-shrinkwrap.json"];
410
- for (const name of candidates) {
411
- const p = (0, node_path_1.join)(cwd, name);
412
- if ((0, node_fs_1.existsSync)(p))
413
- return p;
414
- }
415
- return null;
416
- }
417
- function getDirectDeps(cwd) {
418
- try {
419
- const content = (0, node_fs_1.readFileSync)((0, node_path_1.join)(cwd, "package.json"), "utf-8");
420
- const pkg = JSON.parse(content);
421
- return new Set([
422
- ...Object.keys(pkg.dependencies ?? {}),
423
- ...Object.keys(pkg.devDependencies ?? {}),
424
- ]);
425
- }
426
- catch {
427
- return new Set();
428
- }
429
- }
430
- function getGitBaseLockfile(cwd) {
431
- try {
432
- const mergeBase = (0, node_child_process_1.execSync)("git merge-base HEAD main", {
433
- cwd,
434
- encoding: "utf-8",
435
- stdio: ["pipe", "pipe", "pipe"],
436
- }).trim();
437
- if (!mergeBase)
438
- return null;
439
- return (0, node_child_process_1.execSync)(`git show ${mergeBase}:package-lock.json`, {
440
- cwd,
441
- encoding: "utf-8",
442
- stdio: ["pipe", "pipe", "pipe"],
443
- });
444
- }
445
- catch {
446
- // Not a git repo, no main branch, or file doesn't exist at base
447
- return null;
448
- }
449
- }
450
- function getGitBaseFile(cwd, filename) {
451
- try {
452
- const mergeBase = (0, node_child_process_1.execSync)("git merge-base HEAD main", {
453
- cwd,
454
- encoding: "utf-8",
455
- stdio: ["pipe", "pipe", "pipe"],
456
- }).trim();
457
- if (!mergeBase)
458
- return null;
459
- return (0, node_child_process_1.execSync)(`git show ${mergeBase}:${filename}`, {
460
- cwd,
461
- encoding: "utf-8",
462
- stdio: ["pipe", "pipe", "pipe"],
463
- });
464
- }
465
- catch {
466
- return null;
467
- }
468
- }
469
- function toPackageInput(change) {
470
- return {
471
- name: change.name,
472
- version: change.newVersion,
473
- previousVersion: change.oldVersion,
474
- isNew: change.oldVersion === null,
475
- };
476
- }
477
-
478
-
479
- /***/ }),
480
-
481
- /***/ 202:
482
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
483
-
484
-
485
- Object.defineProperty(exports, "__esModule", ({ value: true }));
486
- exports.renderResult = renderResult;
487
- const color_1 = __nccwpck_require__(988);
488
- const SEVERITY_LABELS = {
489
- 5: "CRITICAL",
490
- 4: "HIGH",
491
- 3: "MEDIUM",
492
- 2: "LOW",
493
- 1: "INFO",
494
- };
495
- function severityColor(sev) {
496
- if (sev >= 5)
497
- return (s) => (0, color_1.bold)((0, color_1.red)(s));
498
- if (sev >= 4)
499
- return color_1.red;
500
- if (sev >= 3)
501
- return color_1.yellow;
502
- if (sev >= 2)
503
- return color_1.cyan;
504
- return color_1.dim;
505
- }
506
- function actionColor(action) {
507
- if (action === "block")
508
- return color_1.red;
509
- if (action === "warn")
510
- return color_1.yellow;
511
- return color_1.green;
512
- }
513
- function pad(s, len) {
514
- return s + " ".repeat(Math.max(0, len - s.length));
515
- }
516
- function rpad(s, len) {
517
- return " ".repeat(Math.max(0, len - s.length)) + s;
518
- }
519
- function renderResult(result, config) {
520
- if (config.json) {
521
- return JSON.stringify(result, null, 2);
522
- }
523
- const lines = [];
524
- const actionStr = result.action.toUpperCase();
525
- const colorAction = actionColor(result.action);
526
- // Header
527
- lines.push("");
528
- lines.push(` ${(0, color_1.bold)("Dependency Guardian")}`);
529
- lines.push(` Score: ${(0, color_1.bold)(String(result.score))}${" ".repeat(Math.max(1, 50 - String(result.score).length))}${colorAction(actionStr)}`);
530
- lines.push("");
531
- // Summary
532
- const flagged = result.packages.filter((p) => p.score > 0).length;
533
- const total = result.packages.length;
534
- lines.push(` ${total} package${total !== 1 ? "s" : ""} scanned, ${flagged} flagged`);
535
- lines.push("");
536
- if (total === 0) {
537
- lines.push(" No packages to scan.");
538
- lines.push("");
539
- return lines.join("\n");
540
- }
541
- // Package table
542
- const COL_NAME = 22;
543
- const COL_VER = 22;
544
- const COL_SCORE = 7;
545
- 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
- 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))}`);
547
- // Sort: flagged first, then by score desc
548
- const sorted = [...result.packages].sort((a, b) => b.score - a.score);
549
- for (const pkg of sorted) {
550
- const versionStr = pkg.version;
551
- const safe = result.safeVersions[pkg.name];
552
- const verDisplay = safe ? `${(0, color_1.dim)(safe + " \u2192 ")}${versionStr}` : versionStr;
553
- const pkgAction = pkg.score >= config.blockThreshold
554
- ? "BLOCK"
555
- : pkg.score >= config.warnThreshold
556
- ? "WARN"
557
- : "pass";
558
- const pkgColor = actionColor(pkg.score >= config.blockThreshold
559
- ? "block"
560
- : pkg.score >= config.warnThreshold
561
- ? "warn"
562
- : "pass");
563
- lines.push(` ${pad(truncate(pkg.name, COL_NAME - 2), COL_NAME)}${pad(verDisplay, COL_VER)}${rpad(String(pkg.score), COL_SCORE - 2)} ${pkgColor(pkgAction)}`);
564
- }
565
- lines.push("");
566
- // Detailed findings for flagged packages
567
- const flaggedPkgs = sorted.filter((p) => p.score > 0);
568
- for (const pkg of flaggedPkgs) {
569
- lines.push(renderPackageDetail(pkg, result.safeVersions[pkg.name]));
570
- }
571
- // Duration
572
- if (result.durationMs) {
573
- lines.push(` ${(0, color_1.dim)(`Completed in ${(result.durationMs / 1000).toFixed(1)}s`)}`);
574
- lines.push("");
575
- }
576
- return lines.join("\n");
577
- }
578
- function renderPackageDetail(pkg, safeVersion) {
579
- const lines = [];
580
- const header = `${pkg.name}@${pkg.version} (score: ${pkg.score})`;
581
- const rule = "\u2500".repeat(Math.max(0, 50 - header.length));
582
- lines.push(` ${(0, color_1.dim)("\u2500\u2500")} ${(0, color_1.bold)(header)} ${(0, color_1.dim)(rule)}`);
583
- lines.push("");
584
- for (const finding of pkg.findings) {
585
- // Skip low-severity informational findings in detail view
586
- if (finding.severity <= 1 && !finding.critical)
587
- continue;
588
- const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
589
- const colorFn = severityColor(finding.severity);
590
- lines.push(` ${colorFn(pad(sevLabel, 10))}${finding.id} \u2014 ${finding.title} (sev ${finding.severity}, conf ${finding.confidence.toFixed(2)})`);
591
- // Show up to 3 evidence lines
592
- const evidenceLimit = 3;
593
- for (let i = 0; i < Math.min(finding.evidence.length, evidenceLimit); i++) {
594
- lines.push(` ${" ".repeat(10)}${(0, color_1.dim)(truncate(finding.evidence[i], 80))}`);
595
- }
596
- if (finding.evidence.length > evidenceLimit) {
597
- lines.push(` ${" ".repeat(10)}${(0, color_1.dim)(`... and ${finding.evidence.length - evidenceLimit} more`)}`);
598
- }
599
- lines.push("");
600
- }
601
- if (safeVersion) {
602
- lines.push(` ${(0, color_1.green)(`Safe version: ${pkg.name}@${safeVersion}`)}`);
603
- lines.push("");
604
- }
605
- return lines.join("\n");
606
- }
607
- function truncate(s, max) {
608
- if (s.length <= max)
609
- return s;
610
- return s.slice(0, max - 1) + "\u2026";
611
- }
612
-
613
-
614
- /***/ }),
615
-
616
- /***/ 229:
617
- /***/ ((__unused_webpack_module, exports) => {
618
-
619
-
620
- Object.defineProperty(exports, "__esModule", ({ value: true }));
621
- exports.diffLockfiles = diffLockfiles;
622
- function diffLockfiles(base, head, maxPackages, directDeps) {
623
- const allChanges = [];
624
- for (const [name, headEntry] of head.packages) {
625
- const baseEntry = base?.packages.get(name);
626
- if (!baseEntry) {
627
- allChanges.push({
628
- name,
629
- oldVersion: null,
630
- newVersion: headEntry.version,
631
- isDirect: directDeps?.has(name) ?? false,
632
- isDev: headEntry.dev ?? false,
633
- });
634
- }
635
- else if (baseEntry.version !== headEntry.version) {
636
- allChanges.push({
637
- name,
638
- oldVersion: baseEntry.version,
639
- newVersion: headEntry.version,
640
- isDirect: directDeps?.has(name) ?? false,
641
- isDev: headEntry.dev ?? false,
642
- });
643
- }
644
- }
645
- allChanges.sort((a, b) => {
646
- if (a.isDirect !== b.isDirect)
647
- return a.isDirect ? -1 : 1;
648
- return a.name.localeCompare(b.name);
649
- });
650
- if (allChanges.length <= maxPackages) {
651
- return { changes: allChanges, skipped: [] };
652
- }
653
- const changes = allChanges.slice(0, maxPackages);
654
- const skipped = allChanges.slice(maxPackages).map((c) => c.name);
655
- return { changes, skipped };
656
- }
657
-
658
-
659
- /***/ }),
660
-
661
- /***/ 417:
662
- /***/ ((__unused_webpack_module, exports) => {
663
-
664
-
665
- Object.defineProperty(exports, "__esModule", ({ value: true }));
666
- exports.diffPackageJsons = diffPackageJsons;
667
- function diffPackageJsons(baseContent, headContent, maxPackages) {
668
- const head = JSON.parse(headContent);
669
- const headDeps = {
670
- ...head.dependencies,
671
- ...head.devDependencies,
672
- };
673
- let baseDeps = {};
674
- if (baseContent) {
675
- const base = JSON.parse(baseContent);
676
- baseDeps = { ...base.dependencies, ...base.devDependencies };
677
- }
678
- const changes = [];
679
- for (const [name, version] of Object.entries(headDeps)) {
680
- if (changes.length >= maxPackages)
681
- break;
682
- const baseVersion = baseDeps[name];
683
- const isDev = !!(head.devDependencies && head.devDependencies[name]);
684
- if (!baseVersion) {
685
- changes.push({
686
- name,
687
- oldVersion: null,
688
- newVersion: version,
689
- isDirect: true,
690
- isDev,
691
- });
692
- }
693
- else if (baseVersion !== version) {
694
- changes.push({
695
- name,
696
- oldVersion: baseVersion,
697
- newVersion: version,
698
- isDirect: true,
699
- isDev,
700
- });
701
- }
702
- }
703
- return { changes };
704
- }
705
-
706
-
707
- /***/ }),
708
-
709
- /***/ 88:
710
- /***/ ((__unused_webpack_module, exports) => {
711
-
712
-
713
- Object.defineProperty(exports, "__esModule", ({ value: true }));
714
- exports.parseLockfile = parseLockfile;
715
- function parseLockfile(content) {
716
- const json = JSON.parse(content);
717
- const lockfileVersion = json.lockfileVersion ?? 1;
718
- const packages = new Map();
719
- if (lockfileVersion >= 2 && json.packages) {
720
- for (const [path, entry] of Object.entries(json.packages)) {
721
- if (path === "")
722
- continue;
723
- const name = extractPackageName(path);
724
- if (name) {
725
- const e = entry;
726
- packages.set(name, {
727
- version: e.version ?? "",
728
- resolved: e.resolved,
729
- integrity: e.integrity,
730
- dev: e.dev,
731
- });
732
- }
733
- }
734
- }
735
- else if (json.dependencies) {
736
- parseLegacyDeps(json.dependencies, packages);
737
- }
738
- return { lockfileVersion, packages };
739
- }
740
- function extractPackageName(nodePath) {
741
- const prefix = "node_modules/";
742
- const lastIdx = nodePath.lastIndexOf(prefix);
743
- if (lastIdx === -1)
744
- return null;
745
- const name = nodePath.slice(lastIdx + prefix.length);
746
- return name || null;
747
- }
748
- function parseLegacyDeps(deps, packages, parentPrefix = "") {
749
- for (const [name, value] of Object.entries(deps)) {
750
- const fullName = parentPrefix ? `${parentPrefix}/${name}` : name;
751
- const entry = value;
752
- packages.set(fullName, {
753
- version: entry.version ?? "",
754
- resolved: entry.resolved,
755
- integrity: entry.integrity,
756
- dev: entry.dev,
757
- });
758
- if (entry.dependencies) {
759
- parseLegacyDeps(entry.dependencies, packages);
760
- }
761
- }
762
- }
763
-
764
-
765
- /***/ }),
766
-
767
- /***/ 421:
768
- /***/ ((module) => {
769
-
770
- module.exports = require("node:child_process");
771
-
772
- /***/ }),
773
-
774
- /***/ 24:
775
- /***/ ((module) => {
776
-
777
- module.exports = require("node:fs");
778
-
779
- /***/ }),
780
-
781
- /***/ 760:
782
- /***/ ((module) => {
783
-
784
- module.exports = require("node:path");
785
-
786
- /***/ }),
787
-
788
- /***/ 975:
789
- /***/ ((module) => {
790
-
791
- module.exports = require("node:util");
792
-
793
- /***/ })
794
-
795
- /******/ });
796
- /************************************************************************/
797
- /******/ // The module cache
798
- /******/ var __webpack_module_cache__ = {};
799
- /******/
800
- /******/ // The require function
801
- /******/ function __nccwpck_require__(moduleId) {
802
- /******/ // Check if module is in cache
803
- /******/ var cachedModule = __webpack_module_cache__[moduleId];
804
- /******/ if (cachedModule !== undefined) {
805
- /******/ return cachedModule.exports;
806
- /******/ }
807
- /******/ // Create a new module (and put it into the cache)
808
- /******/ var module = __webpack_module_cache__[moduleId] = {
809
- /******/ // no module.id needed
810
- /******/ // no module.loaded needed
811
- /******/ exports: {}
812
- /******/ };
813
- /******/
814
- /******/ // Execute the module function
815
- /******/ var threw = true;
816
- /******/ try {
817
- /******/ __webpack_modules__[moduleId](module, module.exports, __nccwpck_require__);
818
- /******/ threw = false;
819
- /******/ } finally {
820
- /******/ if(threw) delete __webpack_module_cache__[moduleId];
821
- /******/ }
822
- /******/
823
- /******/ // Return the exports of the module
824
- /******/ return module.exports;
825
- /******/ }
826
- /******/
827
- /************************************************************************/
828
- /******/ /* webpack/runtime/compat */
829
- /******/
830
- /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
831
- /******/
832
- /************************************************************************/
833
- var __webpack_exports__ = {};
834
- // This entry need to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
835
- (() => {
836
- var exports = __webpack_exports__;
837
-
838
- Object.defineProperty(exports, "__esModule", ({ value: true }));
839
- const config_1 = __nccwpck_require__(973);
840
- const lockfile_1 = __nccwpck_require__(746);
841
- const api_1 = __nccwpck_require__(879);
842
- const output_1 = __nccwpck_require__(202);
843
- const color_1 = __nccwpck_require__(988);
844
- async function main() {
845
- const config = (0, config_1.parseConfig)(process.argv);
846
- if (config.mode === "off") {
847
- process.stderr.write((0, color_1.dim)(" Dependency Guardian: mode is off — skipping.\n"));
848
- process.exit(0);
849
- }
850
- // Discover packages
851
- process.stderr.write((0, color_1.dim)(" Discovering package changes...\n"));
852
- const discovery = (0, lockfile_1.discoverChanges)(process.cwd(), config);
853
- if (discovery.packages.length === 0) {
854
- process.stderr.write((0, color_1.dim)(" No package changes detected.\n"));
855
- process.exit(0);
856
- }
857
- // Filter allowlist
858
- const packages = discovery.packages.filter((p) => !config.allowlist.includes(p.name));
859
- if (packages.length === 0) {
860
- process.stderr.write((0, color_1.dim)(" All changed packages are allowlisted.\n"));
861
- process.exit(0);
862
- }
863
- process.stderr.write((0, color_1.dim)(` Scanning ${packages.length} package${packages.length !== 1 ? "s" : ""} (${discovery.method})...\n`));
864
- if (discovery.skipped.length > 0) {
865
- process.stderr.write((0, color_1.yellow)(` Warning: ${discovery.skipped.length} package(s) skipped (max-packages=${config.maxPackages})\n`));
866
- }
867
- // Call Detection API
868
- const result = await (0, api_1.callAnalyzeAPI)(packages, config, (done, total) => {
869
- process.stderr.write((0, color_1.dim)(` Analyzed ${done}/${total}...\n`));
870
- });
871
- // Render output
872
- const output = (0, output_1.renderResult)(result, config);
873
- process.stdout.write(output + "\n");
874
- // Exit code
875
- if (result.action === "block" && config.mode === "block") {
876
- process.exit(2);
877
- }
878
- else if (result.action === "block" || result.action === "warn") {
879
- process.exit(1);
880
- }
881
- process.exit(0);
882
- }
883
- main().catch((err) => {
884
- process.stderr.write(`\n ${(0, color_1.bold)((0, color_1.red)("Error:"))} ${err.message}\n\n`);
885
- process.exit(1);
886
- });
887
-
888
- })();
889
-
890
- module.exports = __webpack_exports__;
891
- /******/ })()
892
- ;