@westbayberry/dg 1.0.6 → 1.0.7
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 +59 -44
- package/dist/index.mjs +300 -280
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @westbayberry/dg
|
|
2
2
|
|
|
3
|
-
Supply chain security scanner for npm dependencies. Detects malicious packages, typosquatting, dependency confusion, and 20+ attack patterns before they reach production.
|
|
3
|
+
Supply chain security scanner for npm and Python dependencies. Detects malicious packages, typosquatting, dependency confusion, and 20+ attack patterns before they reach production.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,44 +8,42 @@ Supply chain security scanner for npm dependencies. Detects malicious packages,
|
|
|
8
8
|
npm install -g @westbayberry/dg
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Or run without installing:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npx @westbayberry/dg scan
|
|
15
|
-
```
|
|
16
|
-
|
|
17
11
|
## Quick Start
|
|
18
12
|
|
|
19
|
-
1. Get your API key at [westbayberry.com/dashboard](https://westbayberry.com/dashboard)
|
|
20
|
-
2. Run a scan:
|
|
21
|
-
|
|
22
13
|
```bash
|
|
23
|
-
|
|
14
|
+
dg login
|
|
24
15
|
dg scan
|
|
25
16
|
```
|
|
26
17
|
|
|
27
|
-
|
|
18
|
+
The CLI auto-discovers projects in your directory tree — npm lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) and Python dependency files (requirements.txt, Pipfile.lock, poetry.lock). If multiple projects are found, you pick which ones to scan.
|
|
28
19
|
|
|
29
|
-
##
|
|
20
|
+
## Commands
|
|
30
21
|
|
|
31
22
|
```
|
|
32
|
-
dg scan [options]
|
|
23
|
+
dg scan [options] Scan dependencies (auto-discovers npm + Python projects)
|
|
24
|
+
dg npm install <pkg> Scan packages before installing
|
|
25
|
+
dg login Authenticate with your WestBayBerry account
|
|
26
|
+
dg logout Remove saved credentials
|
|
27
|
+
dg hook install Install git pre-commit hook to scan lockfile changes
|
|
28
|
+
dg hook uninstall Remove the pre-commit hook
|
|
29
|
+
dg update Check for and install the latest version
|
|
30
|
+
dg wrap Show instructions to alias npm to dg
|
|
33
31
|
```
|
|
34
32
|
|
|
35
|
-
### Options
|
|
36
|
-
|
|
37
|
-
| Flag |
|
|
38
|
-
|
|
39
|
-
| `--
|
|
40
|
-
| `--
|
|
41
|
-
| `--
|
|
42
|
-
| `--
|
|
43
|
-
| `--
|
|
44
|
-
| `--
|
|
45
|
-
| `--
|
|
46
|
-
| `--
|
|
47
|
-
| `--
|
|
48
|
-
| `--
|
|
33
|
+
### Scan Options
|
|
34
|
+
|
|
35
|
+
| Flag | Default | Description |
|
|
36
|
+
|------|---------|-------------|
|
|
37
|
+
| `--mode <mode>` | `warn` | `block` / `warn` / `off` |
|
|
38
|
+
| `--block-threshold <n>` | `70` | Score threshold for blocking |
|
|
39
|
+
| `--warn-threshold <n>` | `60` | Score threshold for warnings |
|
|
40
|
+
| `--max-packages <n>` | `200` | Max packages per scan |
|
|
41
|
+
| `--allowlist <pkgs>` | | Comma-separated packages to skip |
|
|
42
|
+
| `--json` | | Output JSON for CI parsing |
|
|
43
|
+
| `--scan-all` | | Scan all packages, not just changed |
|
|
44
|
+
| `--base-lockfile <path>` | | Explicit base lockfile for diff |
|
|
45
|
+
| `--workspace <dir>` | | Scan a specific workspace subdirectory |
|
|
46
|
+
| `--debug` | | Show diagnostic output |
|
|
49
47
|
|
|
50
48
|
### Exit Codes
|
|
51
49
|
|
|
@@ -54,37 +52,54 @@ dg scan [options]
|
|
|
54
52
|
| `0` | Pass | Continue |
|
|
55
53
|
| `1` | Warning | Advisory — review recommended |
|
|
56
54
|
| `2` | Block | Fail the pipeline |
|
|
55
|
+
| `3` | Error | Internal error |
|
|
56
|
+
|
|
57
|
+
## CI Setup
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
Authenticate first, then add the scan to your pipeline:
|
|
59
60
|
|
|
60
|
-
### GitHub Actions
|
|
61
|
+
### GitHub Actions
|
|
61
62
|
|
|
62
63
|
```yaml
|
|
63
64
|
- name: Scan dependencies
|
|
64
|
-
run:
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
run: |
|
|
66
|
+
npx @westbayberry/dg login
|
|
67
|
+
npx @westbayberry/dg scan --mode block --json
|
|
67
68
|
```
|
|
68
69
|
|
|
69
|
-
###
|
|
70
|
+
### Any CI
|
|
70
71
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- npx @westbayberry/dg scan --mode block
|
|
75
|
-
variables:
|
|
76
|
-
DG_API_KEY: $DG_API_KEY
|
|
72
|
+
```bash
|
|
73
|
+
npx @westbayberry/dg login
|
|
74
|
+
npx @westbayberry/dg scan --mode block --json
|
|
77
75
|
```
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
## Git Hook
|
|
78
|
+
|
|
79
|
+
Block commits that introduce risky dependencies:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
dg hook install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This installs a pre-commit hook that runs `dg scan --mode block` whenever a lockfile change is staged. Use `dg hook uninstall` to remove it.
|
|
86
|
+
|
|
87
|
+
## npm Wrapper
|
|
88
|
+
|
|
89
|
+
Scan packages before installing:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
dg npm install express lodash
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use `--dg-force` to bypass a block. Or alias npm globally:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
echo 'alias npm="dg npm"' >> ~/.zshrc
|
|
84
99
|
```
|
|
85
100
|
|
|
86
101
|
## Links
|
|
87
102
|
|
|
88
|
-
- [Dashboard
|
|
103
|
+
- [Dashboard](https://westbayberry.com/dashboard)
|
|
89
104
|
- [Documentation](https://westbayberry.com/docs)
|
|
90
105
|
- [Pricing](https://westbayberry.com/pricing)
|
package/dist/index.mjs
CHANGED
|
@@ -39,6 +39,194 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
mod
|
|
40
40
|
));
|
|
41
41
|
|
|
42
|
+
// src/config.ts
|
|
43
|
+
var config_exports = {};
|
|
44
|
+
__export(config_exports, {
|
|
45
|
+
USAGE: () => USAGE,
|
|
46
|
+
getVersion: () => getVersion,
|
|
47
|
+
parseConfig: () => parseConfig
|
|
48
|
+
});
|
|
49
|
+
import { parseArgs } from "node:util";
|
|
50
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
51
|
+
import { join } from "node:path";
|
|
52
|
+
import { homedir } from "node:os";
|
|
53
|
+
function loadDgrc() {
|
|
54
|
+
const candidates = [
|
|
55
|
+
join(process.cwd(), ".dgrc.json"),
|
|
56
|
+
join(homedir(), ".dgrc.json")
|
|
57
|
+
];
|
|
58
|
+
for (const filepath of candidates) {
|
|
59
|
+
if (existsSync(filepath)) {
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(readFileSync(filepath, "utf-8"));
|
|
62
|
+
} catch {
|
|
63
|
+
process.stderr.write(`Warning: Failed to parse ${filepath}, ignoring.
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
function getVersion() {
|
|
71
|
+
try {
|
|
72
|
+
const pkg = JSON.parse(
|
|
73
|
+
readFileSync(join(__dirname, "..", "package.json"), "utf-8")
|
|
74
|
+
);
|
|
75
|
+
return pkg.version ?? "1.0.0";
|
|
76
|
+
} catch {
|
|
77
|
+
return "1.0.0";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function parseConfig(argv) {
|
|
81
|
+
const { values, positionals } = parseArgs({
|
|
82
|
+
args: argv.slice(2),
|
|
83
|
+
options: {
|
|
84
|
+
"api-url": { type: "string" },
|
|
85
|
+
mode: { type: "string" },
|
|
86
|
+
"block-threshold": { type: "string" },
|
|
87
|
+
"warn-threshold": { type: "string" },
|
|
88
|
+
"max-packages": { type: "string" },
|
|
89
|
+
allowlist: { type: "string" },
|
|
90
|
+
json: { type: "boolean", default: false },
|
|
91
|
+
"scan-all": { type: "boolean", default: false },
|
|
92
|
+
"base-lockfile": { type: "string" },
|
|
93
|
+
workspace: { type: "string", short: "w" },
|
|
94
|
+
debug: { type: "boolean", default: false },
|
|
95
|
+
"no-config": { type: "boolean", default: false },
|
|
96
|
+
help: { type: "boolean", default: false },
|
|
97
|
+
version: { type: "boolean", default: false }
|
|
98
|
+
},
|
|
99
|
+
allowPositionals: true,
|
|
100
|
+
strict: false
|
|
101
|
+
});
|
|
102
|
+
if (values.help) {
|
|
103
|
+
process.stdout.write(USAGE);
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
if (values.version) {
|
|
107
|
+
process.stdout.write(`dependency-guardian v${getVersion()}
|
|
108
|
+
`);
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
const command = positionals[0] ?? "scan";
|
|
112
|
+
const noConfig = values["no-config"];
|
|
113
|
+
const dgrc = noConfig ? {} : loadDgrc();
|
|
114
|
+
const apiKey = dgrc.apiKey ?? "";
|
|
115
|
+
if (!apiKey) {
|
|
116
|
+
process.stderr.write(
|
|
117
|
+
"Error: Not logged in. Run `dg login` to authenticate.\n"
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
|
|
122
|
+
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
123
|
+
process.stderr.write(
|
|
124
|
+
`Error: Invalid mode "${modeRaw}". Must be block, warn, or off.
|
|
125
|
+
`
|
|
126
|
+
);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
|
|
130
|
+
const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
|
|
131
|
+
const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
|
|
132
|
+
const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
|
|
133
|
+
const debug = values.debug || process.env.DG_DEBUG === "1";
|
|
134
|
+
if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
|
|
135
|
+
process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
if (isNaN(warnThreshold) || warnThreshold < 0 || warnThreshold > 100) {
|
|
139
|
+
process.stderr.write("Error: --warn-threshold must be a number between 0 and 100\n");
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
if (isNaN(maxPackages) || maxPackages < 1 || maxPackages > 1e4) {
|
|
143
|
+
process.stderr.write("Error: --max-packages must be a number between 1 and 10000\n");
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
apiKey,
|
|
148
|
+
apiUrl: values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com",
|
|
149
|
+
mode: modeRaw,
|
|
150
|
+
blockThreshold,
|
|
151
|
+
warnThreshold,
|
|
152
|
+
maxPackages,
|
|
153
|
+
allowlist: allowlistRaw ? allowlistRaw.split(",").map((s) => s.trim()).filter(Boolean) : dgrc.allowlist ?? [],
|
|
154
|
+
json: values.json,
|
|
155
|
+
scanAll: values["scan-all"],
|
|
156
|
+
baseLockfile: values["base-lockfile"] ?? null,
|
|
157
|
+
workspace: values.workspace ?? process.env.DG_WORKSPACE ?? null,
|
|
158
|
+
command,
|
|
159
|
+
debug
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
var USAGE;
|
|
163
|
+
var init_config = __esm({
|
|
164
|
+
"src/config.ts"() {
|
|
165
|
+
"use strict";
|
|
166
|
+
USAGE = `
|
|
167
|
+
Dependency Guardian \u2014 Supply chain security scanner
|
|
168
|
+
|
|
169
|
+
Usage:
|
|
170
|
+
dependency-guardian scan [options]
|
|
171
|
+
dg scan [options]
|
|
172
|
+
dg npm install <pkg> [npm-flags]
|
|
173
|
+
dg wrap
|
|
174
|
+
|
|
175
|
+
Commands:
|
|
176
|
+
scan Scan dependencies (auto-discovers npm + Python projects)
|
|
177
|
+
npm Wrap npm commands \u2014 scans packages before installing
|
|
178
|
+
hook install Install git pre-commit hook to scan lockfile changes
|
|
179
|
+
hook uninstall Remove the pre-commit hook
|
|
180
|
+
update Check for and install the latest version
|
|
181
|
+
login Authenticate with your WestBayBerry account
|
|
182
|
+
logout Remove saved credentials
|
|
183
|
+
wrap Show instructions to alias npm to dg
|
|
184
|
+
|
|
185
|
+
Options:
|
|
186
|
+
--api-url <url> API base URL (default: https://api.westbayberry.com)
|
|
187
|
+
--mode <mode> block | warn | off (default: warn)
|
|
188
|
+
--block-threshold <n> Score threshold for blocking (default: 70)
|
|
189
|
+
--warn-threshold <n> Score threshold for warnings (default: 60)
|
|
190
|
+
--max-packages <n> Max packages per scan (default: 200)
|
|
191
|
+
--allowlist <pkgs> Comma-separated package names to skip
|
|
192
|
+
--json Output JSON for CI parsing
|
|
193
|
+
--scan-all Scan all packages, not just changed
|
|
194
|
+
--base-lockfile <path> Path to base lockfile for explicit diff
|
|
195
|
+
--workspace <dir> Scan a specific workspace subdirectory
|
|
196
|
+
--debug Show diagnostic output (discovery, batches, timing)
|
|
197
|
+
--no-config Skip loading .dgrc.json config file
|
|
198
|
+
--help Show this help message
|
|
199
|
+
--version Show version number
|
|
200
|
+
|
|
201
|
+
Config File:
|
|
202
|
+
Place a .dgrc.json in your project root or home directory.
|
|
203
|
+
Precedence: CLI flags > env vars > .dgrc.json > defaults
|
|
204
|
+
|
|
205
|
+
Environment Variables:
|
|
206
|
+
DG_API_URL API base URL
|
|
207
|
+
DG_MODE Mode (block/warn/off)
|
|
208
|
+
DG_ALLOWLIST Comma-separated allowlist
|
|
209
|
+
DG_DEBUG Enable debug output (set to 1)
|
|
210
|
+
DG_WORKSPACE Workspace subdirectory to scan
|
|
211
|
+
|
|
212
|
+
Exit Codes:
|
|
213
|
+
0 pass \u2014 No risks detected
|
|
214
|
+
1 warn \u2014 Risks detected (advisory)
|
|
215
|
+
2 block \u2014 High-risk packages detected
|
|
216
|
+
3 error \u2014 Internal error (API failure, config error)
|
|
217
|
+
|
|
218
|
+
Examples:
|
|
219
|
+
dg scan
|
|
220
|
+
dg scan --json
|
|
221
|
+
dg scan --scan-all --mode block
|
|
222
|
+
dg scan --base-lockfile ./main-lockfile.json
|
|
223
|
+
dg npm install express lodash
|
|
224
|
+
dg npm install @scope/pkg@^2.0.0
|
|
225
|
+
dg npm install risky-pkg --dg-force
|
|
226
|
+
`.trimStart();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
42
230
|
// src/npm-wrapper.ts
|
|
43
231
|
import { spawn } from "node:child_process";
|
|
44
232
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
@@ -3106,11 +3294,11 @@ var require_react_development = __commonJS({
|
|
|
3106
3294
|
}
|
|
3107
3295
|
return dispatcher.useContext(Context);
|
|
3108
3296
|
}
|
|
3109
|
-
function
|
|
3297
|
+
function useState5(initialState) {
|
|
3110
3298
|
var dispatcher = resolveDispatcher();
|
|
3111
3299
|
return dispatcher.useState(initialState);
|
|
3112
3300
|
}
|
|
3113
|
-
function
|
|
3301
|
+
function useReducer5(reducer4, initialArg, init) {
|
|
3114
3302
|
var dispatcher = resolveDispatcher();
|
|
3115
3303
|
return dispatcher.useReducer(reducer4, initialArg, init);
|
|
3116
3304
|
}
|
|
@@ -3907,9 +4095,9 @@ var require_react_development = __commonJS({
|
|
|
3907
4095
|
exports.useInsertionEffect = useInsertionEffect;
|
|
3908
4096
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
3909
4097
|
exports.useMemo = useMemo4;
|
|
3910
|
-
exports.useReducer =
|
|
4098
|
+
exports.useReducer = useReducer5;
|
|
3911
4099
|
exports.useRef = useRef6;
|
|
3912
|
-
exports.useState =
|
|
4100
|
+
exports.useState = useState5;
|
|
3913
4101
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
3914
4102
|
exports.useTransition = useTransition;
|
|
3915
4103
|
exports.version = ReactVersion;
|
|
@@ -35768,9 +35956,6 @@ var init_build2 = __esm({
|
|
|
35768
35956
|
// src/auth.ts
|
|
35769
35957
|
var auth_exports = {};
|
|
35770
35958
|
__export(auth_exports, {
|
|
35771
|
-
MAX_POLL_ATTEMPTS: () => MAX_POLL_ATTEMPTS,
|
|
35772
|
-
POLL_INTERVAL_MS: () => POLL_INTERVAL_MS,
|
|
35773
|
-
WEB_BASE: () => WEB_BASE,
|
|
35774
35959
|
clearCredentials: () => clearCredentials,
|
|
35775
35960
|
createAuthSession: () => createAuthSession,
|
|
35776
35961
|
getStoredApiKey: () => getStoredApiKey,
|
|
@@ -35801,7 +35986,6 @@ async function createAuthSession() {
|
|
|
35801
35986
|
const json = await res.json();
|
|
35802
35987
|
return {
|
|
35803
35988
|
sessionId: json.session_id,
|
|
35804
|
-
userCode: json.user_code,
|
|
35805
35989
|
verifyUrl: json.verify_url,
|
|
35806
35990
|
expiresIn: json.expires_in
|
|
35807
35991
|
};
|
|
@@ -35889,13 +36073,11 @@ function openBrowser(url) {
|
|
|
35889
36073
|
exec2(cmd, () => {
|
|
35890
36074
|
});
|
|
35891
36075
|
}
|
|
35892
|
-
var WEB_BASE,
|
|
36076
|
+
var WEB_BASE, CONFIG_FILE;
|
|
35893
36077
|
var init_auth = __esm({
|
|
35894
36078
|
"src/auth.ts"() {
|
|
35895
36079
|
"use strict";
|
|
35896
36080
|
WEB_BASE = "https://westbayberry.com";
|
|
35897
|
-
POLL_INTERVAL_MS = 2e3;
|
|
35898
|
-
MAX_POLL_ATTEMPTS = 150;
|
|
35899
36081
|
CONFIG_FILE = ".dgrc.json";
|
|
35900
36082
|
}
|
|
35901
36083
|
});
|
|
@@ -35906,7 +36088,7 @@ function reducer(_state, action) {
|
|
|
35906
36088
|
case "ALREADY_LOGGED_IN":
|
|
35907
36089
|
return { phase: "already_logged_in", apiKey: action.apiKey };
|
|
35908
36090
|
case "SESSION_CREATED":
|
|
35909
|
-
return { phase: "waiting",
|
|
36091
|
+
return { phase: "waiting", verifyUrl: action.verifyUrl };
|
|
35910
36092
|
case "AUTH_COMPLETE":
|
|
35911
36093
|
return { phase: "success", email: action.email };
|
|
35912
36094
|
case "AUTH_EXPIRED":
|
|
@@ -35941,13 +36123,12 @@ function useLogin() {
|
|
|
35941
36123
|
}
|
|
35942
36124
|
dispatch({
|
|
35943
36125
|
type: "SESSION_CREATED",
|
|
35944
|
-
userCode: session.userCode,
|
|
35945
36126
|
verifyUrl: session.verifyUrl
|
|
35946
36127
|
});
|
|
35947
36128
|
openBrowser(session.verifyUrl);
|
|
35948
|
-
for (let i = 0; i <
|
|
36129
|
+
for (let i = 0; i < MAX_POLL_ATTEMPTS; i++) {
|
|
35949
36130
|
if (cancelled) return;
|
|
35950
|
-
await new Promise((r) => setTimeout(r,
|
|
36131
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
35951
36132
|
if (cancelled) return;
|
|
35952
36133
|
try {
|
|
35953
36134
|
const result = await pollAuthSession(session.sessionId);
|
|
@@ -35977,14 +36158,14 @@ function useLogin() {
|
|
|
35977
36158
|
}, []);
|
|
35978
36159
|
return state;
|
|
35979
36160
|
}
|
|
35980
|
-
var import_react22,
|
|
36161
|
+
var import_react22, POLL_INTERVAL_MS, MAX_POLL_ATTEMPTS;
|
|
35981
36162
|
var init_useLogin = __esm({
|
|
35982
36163
|
"src/ui/hooks/useLogin.ts"() {
|
|
35983
36164
|
"use strict";
|
|
35984
36165
|
import_react22 = __toESM(require_react());
|
|
35985
36166
|
init_auth();
|
|
35986
|
-
|
|
35987
|
-
|
|
36167
|
+
POLL_INTERVAL_MS = 2e3;
|
|
36168
|
+
MAX_POLL_ATTEMPTS = 150;
|
|
35988
36169
|
}
|
|
35989
36170
|
});
|
|
35990
36171
|
|
|
@@ -38656,13 +38837,6 @@ var init_LoginApp = __esm({
|
|
|
38656
38837
|
] });
|
|
38657
38838
|
case "waiting":
|
|
38658
38839
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
|
|
38659
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
38660
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: true, children: "Your verification code:" }),
|
|
38661
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
38662
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { bold: true, color: "green", children: [
|
|
38663
|
-
" ",
|
|
38664
|
-
state.userCode
|
|
38665
|
-
] }),
|
|
38666
38840
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
38667
38841
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: "Opening browser to authenticate..." }),
|
|
38668
38842
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "If it didn't open, visit:" }),
|
|
@@ -39758,7 +39932,7 @@ async function runStaticLogin() {
|
|
|
39758
39932
|
process.exit(1);
|
|
39759
39933
|
}
|
|
39760
39934
|
process.stderr.write(`
|
|
39761
|
-
|
|
39935
|
+
Opening browser to authenticate...
|
|
39762
39936
|
`);
|
|
39763
39937
|
process.stderr.write(` Visit: ${import_chalk4.default.cyan(session.verifyUrl)}
|
|
39764
39938
|
|
|
@@ -40115,13 +40289,10 @@ function useNpmWrapper(npmArgs, config) {
|
|
|
40115
40289
|
}
|
|
40116
40290
|
} catch (error) {
|
|
40117
40291
|
const message = error instanceof Error ? error.message : String(error);
|
|
40118
|
-
|
|
40119
|
-
dispatch({ type: "
|
|
40120
|
-
|
|
40121
|
-
|
|
40122
|
-
const code = await runNpm(parsedRef.current.rawArgs);
|
|
40123
|
-
dispatch({ type: "DONE", exitCode: code });
|
|
40124
|
-
}
|
|
40292
|
+
dispatch({ type: "ERROR", message, proceed: true });
|
|
40293
|
+
dispatch({ type: "INSTALLING" });
|
|
40294
|
+
const code = await runNpm(parsedRef.current.rawArgs);
|
|
40295
|
+
dispatch({ type: "DONE", exitCode: code });
|
|
40125
40296
|
}
|
|
40126
40297
|
})();
|
|
40127
40298
|
}, [npmArgs, config]);
|
|
@@ -40427,7 +40598,7 @@ function getHint(error) {
|
|
|
40427
40598
|
if (typeof statusCode !== "number") return null;
|
|
40428
40599
|
switch (statusCode) {
|
|
40429
40600
|
case 401:
|
|
40430
|
-
return "
|
|
40601
|
+
return "Not authenticated. Run `dg login` to sign in.";
|
|
40431
40602
|
case 429:
|
|
40432
40603
|
return "Rate limit exceeded. Upgrade at westbayberry.com/pricing";
|
|
40433
40604
|
case 504:
|
|
@@ -40734,6 +40905,8 @@ function reducer3(_state, action) {
|
|
|
40734
40905
|
switch (action.type) {
|
|
40735
40906
|
case "PROJECTS_FOUND":
|
|
40736
40907
|
return { phase: "selecting", projects: action.projects };
|
|
40908
|
+
case "RESTART_SELECTION":
|
|
40909
|
+
return { phase: "selecting", projects: action.projects };
|
|
40737
40910
|
case "DISCOVERY_COMPLETE":
|
|
40738
40911
|
return { phase: "scanning", done: 0, total: action.packages.length, currentBatch: [] };
|
|
40739
40912
|
case "DISCOVERY_EMPTY":
|
|
@@ -40749,6 +40922,7 @@ function reducer3(_state, action) {
|
|
|
40749
40922
|
function useScan(config) {
|
|
40750
40923
|
const [state, dispatch] = (0, import_react27.useReducer)(reducer3, { phase: "discovering" });
|
|
40751
40924
|
const started = (0, import_react27.useRef)(false);
|
|
40925
|
+
const discoveredProjects = (0, import_react27.useRef)(null);
|
|
40752
40926
|
(0, import_react27.useEffect)(() => {
|
|
40753
40927
|
if (started.current) return;
|
|
40754
40928
|
started.current = true;
|
|
@@ -40764,6 +40938,7 @@ function useScan(config) {
|
|
|
40764
40938
|
runNpmScan(packages, discovery.skipped.length, config, dispatch);
|
|
40765
40939
|
} catch {
|
|
40766
40940
|
const projects = discoverProjects(process.cwd());
|
|
40941
|
+
discoveredProjects.current = projects.length > 1 ? projects : null;
|
|
40767
40942
|
if (projects.length === 0) {
|
|
40768
40943
|
dispatch({ type: "DISCOVERY_EMPTY", message: "No dependency files found." });
|
|
40769
40944
|
return;
|
|
@@ -40780,7 +40955,15 @@ function useScan(config) {
|
|
|
40780
40955
|
dispatch({ type: "DISCOVERY_COMPLETE", packages: [], skippedCount: 0 });
|
|
40781
40956
|
scanProjects(projects, config, dispatch);
|
|
40782
40957
|
}, [config]);
|
|
40783
|
-
|
|
40958
|
+
const restartSelection = (0, import_react27.useCallback)(() => {
|
|
40959
|
+
if (!discoveredProjects.current) return;
|
|
40960
|
+
dispatch({ type: "RESTART_SELECTION", projects: discoveredProjects.current });
|
|
40961
|
+
}, []);
|
|
40962
|
+
return {
|
|
40963
|
+
state,
|
|
40964
|
+
scanSelectedProjects,
|
|
40965
|
+
restartSelection: discoveredProjects.current ? restartSelection : null
|
|
40966
|
+
};
|
|
40784
40967
|
}
|
|
40785
40968
|
async function runNpmScan(packages, skippedCount, config, dispatch) {
|
|
40786
40969
|
try {
|
|
@@ -41016,7 +41199,17 @@ function affectsLine(group) {
|
|
|
41016
41199
|
if (names.length <= 5) return names.join(", ");
|
|
41017
41200
|
return names.slice(0, 5).join(", ") + ` + ${names.length - 5} more`;
|
|
41018
41201
|
}
|
|
41019
|
-
|
|
41202
|
+
function viewReducer(_state, action) {
|
|
41203
|
+
switch (action.type) {
|
|
41204
|
+
case "MOVE":
|
|
41205
|
+
return { ..._state, cursor: action.cursor, viewport: action.viewport };
|
|
41206
|
+
case "EXPAND":
|
|
41207
|
+
return { ..._state, expandedIndex: action.expandedIndex, expandLevel: action.expandLevel, viewport: action.viewport };
|
|
41208
|
+
case "MOVE_EXPAND":
|
|
41209
|
+
return { cursor: action.cursor, expandedIndex: action.expandedIndex, expandLevel: action.expandLevel, viewport: action.viewport };
|
|
41210
|
+
}
|
|
41211
|
+
}
|
|
41212
|
+
var import_react29, import_chalk10, import_jsx_runtime10, SEVERITY_LABELS3, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, InteractiveResultsView, T, FindingsSummary, FindingsDetail;
|
|
41020
41213
|
var init_InteractiveResultsView = __esm({
|
|
41021
41214
|
async "src/ui/components/InteractiveResultsView.tsx"() {
|
|
41022
41215
|
"use strict";
|
|
@@ -41042,19 +41235,18 @@ var init_InteractiveResultsView = __esm({
|
|
|
41042
41235
|
};
|
|
41043
41236
|
EVIDENCE_LIMIT2 = 2;
|
|
41044
41237
|
FIXED_CHROME = 16;
|
|
41045
|
-
MOUSE_ON = "\x1B[?1000h\x1B[?1003h\x1B[?1006h";
|
|
41046
|
-
MOUSE_OFF = "\x1B[?1006l\x1B[?1003l\x1B[?1000l";
|
|
41047
41238
|
InteractiveResultsView = ({
|
|
41048
41239
|
result,
|
|
41049
41240
|
config,
|
|
41050
41241
|
durationMs,
|
|
41051
|
-
onExit
|
|
41242
|
+
onExit,
|
|
41243
|
+
onBack
|
|
41052
41244
|
}) => {
|
|
41053
41245
|
(0, import_react29.useEffect)(() => {
|
|
41054
41246
|
if (!process.stdout.isTTY) return;
|
|
41055
|
-
process.stdout.write("\x1B[?1049h"
|
|
41247
|
+
process.stdout.write("\x1B[?1049h");
|
|
41056
41248
|
return () => {
|
|
41057
|
-
process.stdout.write(
|
|
41249
|
+
process.stdout.write("\x1B[?1049l");
|
|
41058
41250
|
};
|
|
41059
41251
|
}, []);
|
|
41060
41252
|
const flagged = (0, import_react29.useMemo)(
|
|
@@ -41067,45 +41259,41 @@ var init_InteractiveResultsView = __esm({
|
|
|
41067
41259
|
);
|
|
41068
41260
|
const total = result.packages.length;
|
|
41069
41261
|
const groups = (0, import_react29.useMemo)(() => groupPackages3(flagged), [flagged]);
|
|
41070
|
-
const [
|
|
41071
|
-
|
|
41072
|
-
|
|
41073
|
-
|
|
41074
|
-
|
|
41075
|
-
|
|
41076
|
-
const
|
|
41077
|
-
|
|
41078
|
-
cursorRef.current = cursorIndex;
|
|
41079
|
-
expandLevelRef.current = expandLevel;
|
|
41080
|
-
expandedIdxRef.current = expandedIndex;
|
|
41081
|
-
viewportRef.current = viewportStart;
|
|
41262
|
+
const [view, dispatchView] = (0, import_react29.useReducer)(viewReducer, {
|
|
41263
|
+
cursor: 0,
|
|
41264
|
+
expandLevel: null,
|
|
41265
|
+
expandedIndex: null,
|
|
41266
|
+
viewport: 0
|
|
41267
|
+
});
|
|
41268
|
+
const viewRef = (0, import_react29.useRef)(view);
|
|
41269
|
+
viewRef.current = view;
|
|
41082
41270
|
const { stdout } = use_stdout_default();
|
|
41083
41271
|
const termCols = stdout?.columns ?? process.stdout.columns ?? 80;
|
|
41084
41272
|
const termRows = stdout?.rows ?? process.stdout.rows ?? 24;
|
|
41085
41273
|
const availableRows = Math.max(5, termRows - FIXED_CHROME);
|
|
41086
41274
|
const innerWidth = Math.max(40, termCols - 6);
|
|
41087
41275
|
const getLevel = (idx) => {
|
|
41088
|
-
return expandedIndex === idx ? expandLevel : null;
|
|
41276
|
+
return view.expandedIndex === idx ? view.expandLevel : null;
|
|
41089
41277
|
};
|
|
41090
41278
|
const expandTargetHeight = (0, import_react29.useMemo)(() => {
|
|
41091
|
-
if (expandedIndex === null || expandLevel === null) return 0;
|
|
41092
|
-
const group = groups[expandedIndex];
|
|
41279
|
+
if (view.expandedIndex === null || view.expandLevel === null) return 0;
|
|
41280
|
+
const group = groups[view.expandedIndex];
|
|
41093
41281
|
if (!group) return 0;
|
|
41094
|
-
if (expandLevel === "summary") return findingsSummaryHeight(group);
|
|
41282
|
+
if (view.expandLevel === "summary") return findingsSummaryHeight(group);
|
|
41095
41283
|
return findingsDetailHeight(group, result.safeVersions);
|
|
41096
|
-
}, [expandedIndex, expandLevel, groups, result.safeVersions]);
|
|
41284
|
+
}, [view.expandedIndex, view.expandLevel, groups, result.safeVersions]);
|
|
41097
41285
|
const { visibleLines: animVisibleLines } = useExpandAnimation(
|
|
41098
41286
|
expandTargetHeight,
|
|
41099
|
-
expandedIndex !== null
|
|
41287
|
+
view.expandedIndex !== null
|
|
41100
41288
|
);
|
|
41101
41289
|
const animatedGroupHeight = (group, level, idx) => {
|
|
41102
41290
|
if (level === null) return 1;
|
|
41103
|
-
if (idx === expandedIndex) return 1 + animVisibleLines;
|
|
41291
|
+
if (idx === view.expandedIndex) return 1 + animVisibleLines;
|
|
41104
41292
|
return groupRowHeight(group, level, result.safeVersions);
|
|
41105
41293
|
};
|
|
41106
41294
|
const visibleEnd = (0, import_react29.useMemo)(() => {
|
|
41107
41295
|
let consumed = 0;
|
|
41108
|
-
let end =
|
|
41296
|
+
let end = view.viewport;
|
|
41109
41297
|
while (end < groups.length) {
|
|
41110
41298
|
const level = getLevel(end);
|
|
41111
41299
|
const h = animatedGroupHeight(groups[end], level, end);
|
|
@@ -41113,9 +41301,9 @@ var init_InteractiveResultsView = __esm({
|
|
|
41113
41301
|
consumed += h;
|
|
41114
41302
|
end++;
|
|
41115
41303
|
}
|
|
41116
|
-
if (end ===
|
|
41304
|
+
if (end === view.viewport && groups.length > 0) end = view.viewport + 1;
|
|
41117
41305
|
return end;
|
|
41118
|
-
}, [
|
|
41306
|
+
}, [view.viewport, groups, view.expandedIndex, view.expandLevel, animVisibleLines, availableRows, result.safeVersions]);
|
|
41119
41307
|
const adjustViewport = (cursor, expIdx, expLvl, currentStart) => {
|
|
41120
41308
|
if (cursor < currentStart) return cursor;
|
|
41121
41309
|
const getLvl = (i) => expIdx === i ? expLvl : null;
|
|
@@ -41140,43 +41328,39 @@ var init_InteractiveResultsView = __esm({
|
|
|
41140
41328
|
if (input === "q" || key.return) onExit();
|
|
41141
41329
|
return;
|
|
41142
41330
|
}
|
|
41143
|
-
const cursor =
|
|
41144
|
-
const expLvl = expandLevelRef.current;
|
|
41145
|
-
const expIdx = expandedIdxRef.current;
|
|
41146
|
-
const vpStart = viewportRef.current;
|
|
41331
|
+
const { cursor, expandLevel: expLvl, expandedIndex: expIdx, viewport: vpStart } = viewRef.current;
|
|
41147
41332
|
if (key.upArrow) {
|
|
41148
41333
|
const next = Math.max(0, cursor - 1);
|
|
41149
41334
|
const newVp = adjustViewport(next, expIdx, expLvl, vpStart < next ? vpStart : next);
|
|
41150
|
-
|
|
41151
|
-
setViewportStart(newVp);
|
|
41335
|
+
dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
|
|
41152
41336
|
} else if (key.downArrow) {
|
|
41153
41337
|
const next = Math.min(groups.length - 1, cursor + 1);
|
|
41154
41338
|
const newVp = adjustViewport(next, expIdx, expLvl, vpStart);
|
|
41155
|
-
|
|
41156
|
-
setViewportStart(newVp);
|
|
41339
|
+
dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
|
|
41157
41340
|
} else if (key.return) {
|
|
41158
41341
|
let newExpIdx;
|
|
41159
41342
|
let newExpLvl;
|
|
41160
|
-
if (expIdx !==
|
|
41161
|
-
newExpIdx = cursor;
|
|
41162
|
-
newExpLvl = "summary";
|
|
41163
|
-
} else if (expLvl === "summary") {
|
|
41164
|
-
newExpIdx = cursor;
|
|
41165
|
-
newExpLvl = "detail";
|
|
41166
|
-
} else {
|
|
41343
|
+
if (expIdx === cursor && expLvl !== null) {
|
|
41167
41344
|
newExpIdx = null;
|
|
41168
41345
|
newExpLvl = null;
|
|
41346
|
+
} else {
|
|
41347
|
+
newExpIdx = cursor;
|
|
41348
|
+
newExpLvl = "summary";
|
|
41169
41349
|
}
|
|
41170
|
-
setExpandedIndex(newExpIdx);
|
|
41171
|
-
setExpandLevel(newExpLvl);
|
|
41172
41350
|
const newVp = adjustViewport(cursor, newExpIdx, newExpLvl, vpStart);
|
|
41173
|
-
|
|
41351
|
+
dispatchView({ type: "EXPAND", expandedIndex: newExpIdx, expandLevel: newExpLvl, viewport: newVp });
|
|
41352
|
+
} else if (input === "e") {
|
|
41353
|
+
if (expIdx === cursor && expLvl === "detail") return;
|
|
41354
|
+
const newVp = adjustViewport(cursor, cursor, "detail", vpStart);
|
|
41355
|
+
dispatchView({ type: "EXPAND", expandedIndex: cursor, expandLevel: "detail", viewport: newVp });
|
|
41356
|
+
} else if (input === "b" && onBack) {
|
|
41357
|
+
onBack();
|
|
41174
41358
|
} else if (input === "q") {
|
|
41175
41359
|
onExit();
|
|
41176
41360
|
}
|
|
41177
41361
|
});
|
|
41178
|
-
const visibleGroups = groups.slice(
|
|
41179
|
-
const aboveCount =
|
|
41362
|
+
const visibleGroups = groups.slice(view.viewport, visibleEnd);
|
|
41363
|
+
const aboveCount = view.viewport;
|
|
41180
41364
|
const belowCount = groups.length - visibleEnd;
|
|
41181
41365
|
const nameCol = Math.max(20, innerWidth - 22);
|
|
41182
41366
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
@@ -41215,8 +41399,8 @@ var init_InteractiveResultsView = __esm({
|
|
|
41215
41399
|
" more above"
|
|
41216
41400
|
] }),
|
|
41217
41401
|
visibleGroups.map((group, visIdx) => {
|
|
41218
|
-
const globalIdx =
|
|
41219
|
-
const isCursor = globalIdx ===
|
|
41402
|
+
const globalIdx = view.viewport + visIdx;
|
|
41403
|
+
const isCursor = globalIdx === view.cursor;
|
|
41220
41404
|
const level = getLevel(globalIdx);
|
|
41221
41405
|
const rep = group.packages[0];
|
|
41222
41406
|
const { label, color } = actionBadge4(rep.score, config);
|
|
@@ -41235,7 +41419,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41235
41419
|
{
|
|
41236
41420
|
group,
|
|
41237
41421
|
maxWidth: innerWidth - 8,
|
|
41238
|
-
maxLines: globalIdx === expandedIndex ? animVisibleLines : void 0
|
|
41422
|
+
maxLines: globalIdx === view.expandedIndex ? animVisibleLines : void 0
|
|
41239
41423
|
}
|
|
41240
41424
|
),
|
|
41241
41425
|
level === "detail" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -41244,7 +41428,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41244
41428
|
group,
|
|
41245
41429
|
safeVersion: result.safeVersions[rep.name],
|
|
41246
41430
|
maxWidth: innerWidth - 8,
|
|
41247
|
-
maxLines: globalIdx === expandedIndex ? animVisibleLines : void 0
|
|
41431
|
+
maxLines: globalIdx === view.expandedIndex ? animVisibleLines : void 0
|
|
41248
41432
|
}
|
|
41249
41433
|
)
|
|
41250
41434
|
] }, group.key);
|
|
@@ -41291,13 +41475,23 @@ var init_InteractiveResultsView = __esm({
|
|
|
41291
41475
|
" navigate",
|
|
41292
41476
|
" ",
|
|
41293
41477
|
import_chalk10.default.cyan("\u23CE"),
|
|
41294
|
-
"
|
|
41478
|
+
" toggle",
|
|
41295
41479
|
" ",
|
|
41480
|
+
import_chalk10.default.cyan("e"),
|
|
41481
|
+
" detail",
|
|
41482
|
+
" ",
|
|
41483
|
+
onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
41484
|
+
import_chalk10.default.cyan("b"),
|
|
41485
|
+
" back",
|
|
41486
|
+
" "
|
|
41487
|
+
] }),
|
|
41296
41488
|
import_chalk10.default.cyan("q"),
|
|
41297
41489
|
" quit"
|
|
41298
41490
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
41299
41491
|
"Press ",
|
|
41300
41492
|
import_chalk10.default.cyan("q"),
|
|
41493
|
+
" or ",
|
|
41494
|
+
import_chalk10.default.cyan("Enter"),
|
|
41301
41495
|
" to exit"
|
|
41302
41496
|
] })
|
|
41303
41497
|
] })
|
|
@@ -41305,13 +41499,9 @@ var init_InteractiveResultsView = __esm({
|
|
|
41305
41499
|
};
|
|
41306
41500
|
T = {
|
|
41307
41501
|
branch: import_chalk10.default.dim("\u251C\u2500\u2500"),
|
|
41308
|
-
// ├──
|
|
41309
41502
|
last: import_chalk10.default.dim("\u2514\u2500\u2500"),
|
|
41310
|
-
// └──
|
|
41311
41503
|
pipe: import_chalk10.default.dim("\u2502"),
|
|
41312
|
-
// │
|
|
41313
41504
|
blank: " "
|
|
41314
|
-
//
|
|
41315
41505
|
};
|
|
41316
41506
|
FindingsSummary = ({ group, maxWidth, maxLines }) => {
|
|
41317
41507
|
const rep = group.packages[0];
|
|
@@ -41477,12 +41667,7 @@ var init_ProjectSelector = __esm({
|
|
|
41477
41667
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: isCursor, inverse: isCursor, children: line }, i);
|
|
41478
41668
|
}),
|
|
41479
41669
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
|
|
41480
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.
|
|
41481
|
-
selected.size,
|
|
41482
|
-
" of ",
|
|
41483
|
-
projects.length,
|
|
41484
|
-
" selected"
|
|
41485
|
-
] })
|
|
41670
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: selected.size === 0 ? "Select at least 1 project to scan" : `${selected.size} of ${projects.length} selected` })
|
|
41486
41671
|
] });
|
|
41487
41672
|
};
|
|
41488
41673
|
}
|
|
@@ -41507,7 +41692,7 @@ var init_App2 = __esm({
|
|
|
41507
41692
|
await init_ProjectSelector();
|
|
41508
41693
|
import_jsx_runtime12 = __toESM(require_jsx_runtime());
|
|
41509
41694
|
App2 = ({ config }) => {
|
|
41510
|
-
const { state, scanSelectedProjects } = useScan(config);
|
|
41695
|
+
const { state, scanSelectedProjects, restartSelection } = useScan(config);
|
|
41511
41696
|
const { exit } = use_app_default();
|
|
41512
41697
|
const handleResultsExit = (0, import_react31.useCallback)(() => {
|
|
41513
41698
|
if (state.phase === "results") {
|
|
@@ -41574,7 +41759,8 @@ var init_App2 = __esm({
|
|
|
41574
41759
|
result: state.result,
|
|
41575
41760
|
config,
|
|
41576
41761
|
durationMs: state.durationMs,
|
|
41577
|
-
onExit: handleResultsExit
|
|
41762
|
+
onExit: handleResultsExit,
|
|
41763
|
+
onBack: restartSelection ?? void 0
|
|
41578
41764
|
}
|
|
41579
41765
|
);
|
|
41580
41766
|
case "empty":
|
|
@@ -41586,183 +41772,8 @@ var init_App2 = __esm({
|
|
|
41586
41772
|
}
|
|
41587
41773
|
});
|
|
41588
41774
|
|
|
41589
|
-
// src/config.ts
|
|
41590
|
-
import { parseArgs } from "node:util";
|
|
41591
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
41592
|
-
import { join } from "node:path";
|
|
41593
|
-
import { homedir } from "node:os";
|
|
41594
|
-
function loadDgrc() {
|
|
41595
|
-
const candidates = [
|
|
41596
|
-
join(process.cwd(), ".dgrc.json"),
|
|
41597
|
-
join(homedir(), ".dgrc.json")
|
|
41598
|
-
];
|
|
41599
|
-
for (const filepath of candidates) {
|
|
41600
|
-
if (existsSync(filepath)) {
|
|
41601
|
-
try {
|
|
41602
|
-
return JSON.parse(readFileSync(filepath, "utf-8"));
|
|
41603
|
-
} catch {
|
|
41604
|
-
process.stderr.write(`Warning: Failed to parse ${filepath}, ignoring.
|
|
41605
|
-
`);
|
|
41606
|
-
}
|
|
41607
|
-
}
|
|
41608
|
-
}
|
|
41609
|
-
return {};
|
|
41610
|
-
}
|
|
41611
|
-
var USAGE = `
|
|
41612
|
-
Dependency Guardian \u2014 Supply chain security scanner
|
|
41613
|
-
|
|
41614
|
-
Usage:
|
|
41615
|
-
dependency-guardian scan [options]
|
|
41616
|
-
dg scan [options]
|
|
41617
|
-
dg npm install <pkg> [npm-flags]
|
|
41618
|
-
dg wrap
|
|
41619
|
-
|
|
41620
|
-
Commands:
|
|
41621
|
-
scan Scan dependencies (auto-discovers npm + Python projects)
|
|
41622
|
-
npm Wrap npm commands \u2014 scans packages before installing
|
|
41623
|
-
hook install Install git pre-commit hook to scan lockfile changes
|
|
41624
|
-
hook uninstall Remove the pre-commit hook
|
|
41625
|
-
update Check for and install the latest version
|
|
41626
|
-
login Authenticate with your WestBayBerry account
|
|
41627
|
-
logout Remove saved credentials
|
|
41628
|
-
wrap Show instructions to alias npm to dg
|
|
41629
|
-
|
|
41630
|
-
Options:
|
|
41631
|
-
--api-url <url> API base URL (default: https://api.westbayberry.com)
|
|
41632
|
-
--mode <mode> block | warn | off (default: warn)
|
|
41633
|
-
--block-threshold <n> Score threshold for blocking (default: 70)
|
|
41634
|
-
--warn-threshold <n> Score threshold for warnings (default: 60)
|
|
41635
|
-
--max-packages <n> Max packages per scan (default: 200)
|
|
41636
|
-
--allowlist <pkgs> Comma-separated package names to skip
|
|
41637
|
-
--json Output JSON for CI parsing
|
|
41638
|
-
--scan-all Scan all packages, not just changed
|
|
41639
|
-
--base-lockfile <path> Path to base lockfile for explicit diff
|
|
41640
|
-
--workspace <dir> Scan a specific workspace subdirectory
|
|
41641
|
-
--debug Show diagnostic output (discovery, batches, timing)
|
|
41642
|
-
--no-config Skip loading .dgrc.json config file
|
|
41643
|
-
--help Show this help message
|
|
41644
|
-
--version Show version number
|
|
41645
|
-
|
|
41646
|
-
Config File:
|
|
41647
|
-
Place a .dgrc.json in your project root or home directory.
|
|
41648
|
-
Precedence: CLI flags > env vars > .dgrc.json > defaults
|
|
41649
|
-
|
|
41650
|
-
Environment Variables:
|
|
41651
|
-
DG_API_URL API base URL
|
|
41652
|
-
DG_MODE Mode (block/warn/off)
|
|
41653
|
-
DG_ALLOWLIST Comma-separated allowlist
|
|
41654
|
-
DG_DEBUG Enable debug output (set to 1)
|
|
41655
|
-
DG_WORKSPACE Workspace subdirectory to scan
|
|
41656
|
-
|
|
41657
|
-
Exit Codes:
|
|
41658
|
-
0 pass \u2014 No risks detected
|
|
41659
|
-
1 warn \u2014 Risks detected (advisory)
|
|
41660
|
-
2 block \u2014 High-risk packages detected
|
|
41661
|
-
3 error \u2014 Internal error (API failure, config error)
|
|
41662
|
-
|
|
41663
|
-
Examples:
|
|
41664
|
-
dg scan
|
|
41665
|
-
dg scan --json
|
|
41666
|
-
dg scan --scan-all --mode block
|
|
41667
|
-
dg scan --base-lockfile ./main-lockfile.json
|
|
41668
|
-
dg npm install express lodash
|
|
41669
|
-
dg npm install @scope/pkg@^2.0.0
|
|
41670
|
-
dg npm install risky-pkg --dg-force
|
|
41671
|
-
`.trimStart();
|
|
41672
|
-
function getVersion() {
|
|
41673
|
-
try {
|
|
41674
|
-
const pkg = JSON.parse(
|
|
41675
|
-
readFileSync(join(__dirname, "..", "package.json"), "utf-8")
|
|
41676
|
-
);
|
|
41677
|
-
return pkg.version ?? "1.0.0";
|
|
41678
|
-
} catch {
|
|
41679
|
-
return "1.0.0";
|
|
41680
|
-
}
|
|
41681
|
-
}
|
|
41682
|
-
function parseConfig(argv) {
|
|
41683
|
-
const { values, positionals } = parseArgs({
|
|
41684
|
-
args: argv.slice(2),
|
|
41685
|
-
options: {
|
|
41686
|
-
"api-url": { type: "string" },
|
|
41687
|
-
mode: { type: "string" },
|
|
41688
|
-
"block-threshold": { type: "string" },
|
|
41689
|
-
"warn-threshold": { type: "string" },
|
|
41690
|
-
"max-packages": { type: "string" },
|
|
41691
|
-
allowlist: { type: "string" },
|
|
41692
|
-
json: { type: "boolean", default: false },
|
|
41693
|
-
"scan-all": { type: "boolean", default: false },
|
|
41694
|
-
"base-lockfile": { type: "string" },
|
|
41695
|
-
workspace: { type: "string", short: "w" },
|
|
41696
|
-
debug: { type: "boolean", default: false },
|
|
41697
|
-
"no-config": { type: "boolean", default: false },
|
|
41698
|
-
help: { type: "boolean", default: false },
|
|
41699
|
-
version: { type: "boolean", default: false }
|
|
41700
|
-
},
|
|
41701
|
-
allowPositionals: true,
|
|
41702
|
-
strict: false
|
|
41703
|
-
});
|
|
41704
|
-
if (values.help) {
|
|
41705
|
-
process.stdout.write(USAGE);
|
|
41706
|
-
process.exit(0);
|
|
41707
|
-
}
|
|
41708
|
-
if (values.version) {
|
|
41709
|
-
process.stdout.write(`dependency-guardian v${getVersion()}
|
|
41710
|
-
`);
|
|
41711
|
-
process.exit(0);
|
|
41712
|
-
}
|
|
41713
|
-
const command = positionals[0] ?? "scan";
|
|
41714
|
-
const noConfig = values["no-config"];
|
|
41715
|
-
const dgrc = noConfig ? {} : loadDgrc();
|
|
41716
|
-
const apiKey = dgrc.apiKey ?? "";
|
|
41717
|
-
if (!apiKey) {
|
|
41718
|
-
process.stderr.write(
|
|
41719
|
-
"Error: Not logged in. Run `dg login` to authenticate.\n"
|
|
41720
|
-
);
|
|
41721
|
-
process.exit(1);
|
|
41722
|
-
}
|
|
41723
|
-
const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
|
|
41724
|
-
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
41725
|
-
process.stderr.write(
|
|
41726
|
-
`Error: Invalid mode "${modeRaw}". Must be block, warn, or off.
|
|
41727
|
-
`
|
|
41728
|
-
);
|
|
41729
|
-
process.exit(1);
|
|
41730
|
-
}
|
|
41731
|
-
const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
|
|
41732
|
-
const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
|
|
41733
|
-
const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
|
|
41734
|
-
const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
|
|
41735
|
-
const debug = values.debug || process.env.DG_DEBUG === "1";
|
|
41736
|
-
if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
|
|
41737
|
-
process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
|
|
41738
|
-
process.exit(1);
|
|
41739
|
-
}
|
|
41740
|
-
if (isNaN(warnThreshold) || warnThreshold < 0 || warnThreshold > 100) {
|
|
41741
|
-
process.stderr.write("Error: --warn-threshold must be a number between 0 and 100\n");
|
|
41742
|
-
process.exit(1);
|
|
41743
|
-
}
|
|
41744
|
-
if (isNaN(maxPackages) || maxPackages < 1 || maxPackages > 1e4) {
|
|
41745
|
-
process.stderr.write("Error: --max-packages must be a number between 1 and 10000\n");
|
|
41746
|
-
process.exit(1);
|
|
41747
|
-
}
|
|
41748
|
-
return {
|
|
41749
|
-
apiKey,
|
|
41750
|
-
apiUrl: values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com",
|
|
41751
|
-
mode: modeRaw,
|
|
41752
|
-
blockThreshold,
|
|
41753
|
-
warnThreshold,
|
|
41754
|
-
maxPackages,
|
|
41755
|
-
allowlist: allowlistRaw ? allowlistRaw.split(",").map((s) => s.trim()).filter(Boolean) : dgrc.allowlist ?? [],
|
|
41756
|
-
json: values.json,
|
|
41757
|
-
scanAll: values["scan-all"],
|
|
41758
|
-
baseLockfile: values["base-lockfile"] ?? null,
|
|
41759
|
-
workspace: values.workspace ?? process.env.DG_WORKSPACE ?? null,
|
|
41760
|
-
command,
|
|
41761
|
-
debug
|
|
41762
|
-
};
|
|
41763
|
-
}
|
|
41764
|
-
|
|
41765
41775
|
// src/bin.ts
|
|
41776
|
+
init_config();
|
|
41766
41777
|
init_npm_wrapper();
|
|
41767
41778
|
|
|
41768
41779
|
// src/update-check.ts
|
|
@@ -41824,8 +41835,7 @@ function spawnBackgroundUpdate(version) {
|
|
|
41824
41835
|
try {
|
|
41825
41836
|
const child = spawn2("npm", ["install", "-g", `${PKG_NAME}@${version}`], {
|
|
41826
41837
|
detached: true,
|
|
41827
|
-
stdio: "ignore"
|
|
41828
|
-
shell: true
|
|
41838
|
+
stdio: "ignore"
|
|
41829
41839
|
});
|
|
41830
41840
|
child.unref();
|
|
41831
41841
|
} catch {
|
|
@@ -41887,10 +41897,20 @@ async function runUpdate(currentVersion) {
|
|
|
41887
41897
|
}
|
|
41888
41898
|
|
|
41889
41899
|
// src/bin.ts
|
|
41890
|
-
var CLI_VERSION =
|
|
41900
|
+
var CLI_VERSION = getVersion();
|
|
41891
41901
|
var isInteractive = process.stdout.isTTY === true && !process.env.CI && !process.env.NO_COLOR;
|
|
41892
41902
|
async function main() {
|
|
41893
41903
|
const rawCommand = process.argv[2];
|
|
41904
|
+
if (!rawCommand || rawCommand === "--help" || rawCommand === "-h") {
|
|
41905
|
+
const { USAGE: USAGE3 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
41906
|
+
process.stdout.write(USAGE3);
|
|
41907
|
+
return;
|
|
41908
|
+
}
|
|
41909
|
+
if (rawCommand === "--version" || rawCommand === "-v") {
|
|
41910
|
+
process.stdout.write(`dependency-guardian v${CLI_VERSION}
|
|
41911
|
+
`);
|
|
41912
|
+
return;
|
|
41913
|
+
}
|
|
41894
41914
|
if (rawCommand === "wrap") {
|
|
41895
41915
|
handleWrapCommand();
|
|
41896
41916
|
return;
|
|
@@ -41921,7 +41941,7 @@ async function main() {
|
|
|
41921
41941
|
const { clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
41922
41942
|
clearCredentials2();
|
|
41923
41943
|
const chalk9 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
|
|
41924
|
-
process.stderr.write(chalk9.green(" Logged out
|
|
41944
|
+
process.stderr.write(chalk9.green(" Logged out.\n"));
|
|
41925
41945
|
return;
|
|
41926
41946
|
}
|
|
41927
41947
|
const config = parseConfig(process.argv);
|