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