@westbayberry/dg 1.0.5 → 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 +316 -341
- 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
|
}
|
|
@@ -3118,7 +3306,7 @@ var require_react_development = __commonJS({
|
|
|
3118
3306
|
var dispatcher = resolveDispatcher();
|
|
3119
3307
|
return dispatcher.useRef(initialValue);
|
|
3120
3308
|
}
|
|
3121
|
-
function
|
|
3309
|
+
function useEffect12(create2, deps) {
|
|
3122
3310
|
var dispatcher = resolveDispatcher();
|
|
3123
3311
|
return dispatcher.useEffect(create2, deps);
|
|
3124
3312
|
}
|
|
@@ -3901,15 +4089,15 @@ var require_react_development = __commonJS({
|
|
|
3901
4089
|
exports.useContext = useContext7;
|
|
3902
4090
|
exports.useDebugValue = useDebugValue;
|
|
3903
4091
|
exports.useDeferredValue = useDeferredValue;
|
|
3904
|
-
exports.useEffect =
|
|
4092
|
+
exports.useEffect = useEffect12;
|
|
3905
4093
|
exports.useId = useId;
|
|
3906
4094
|
exports.useImperativeHandle = useImperativeHandle;
|
|
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;
|
|
@@ -4282,47 +4470,6 @@ var init_base = __esm({
|
|
|
4282
4470
|
});
|
|
4283
4471
|
|
|
4284
4472
|
// node_modules/ansi-escapes/index.js
|
|
4285
|
-
var ansi_escapes_exports = {};
|
|
4286
|
-
__export(ansi_escapes_exports, {
|
|
4287
|
-
ConEmu: () => ConEmu,
|
|
4288
|
-
beep: () => beep,
|
|
4289
|
-
beginSynchronizedOutput: () => beginSynchronizedOutput,
|
|
4290
|
-
clearScreen: () => clearScreen,
|
|
4291
|
-
clearTerminal: () => clearTerminal,
|
|
4292
|
-
clearViewport: () => clearViewport,
|
|
4293
|
-
cursorBackward: () => cursorBackward,
|
|
4294
|
-
cursorDown: () => cursorDown,
|
|
4295
|
-
cursorForward: () => cursorForward,
|
|
4296
|
-
cursorGetPosition: () => cursorGetPosition,
|
|
4297
|
-
cursorHide: () => cursorHide,
|
|
4298
|
-
cursorLeft: () => cursorLeft,
|
|
4299
|
-
cursorMove: () => cursorMove,
|
|
4300
|
-
cursorNextLine: () => cursorNextLine,
|
|
4301
|
-
cursorPrevLine: () => cursorPrevLine,
|
|
4302
|
-
cursorRestorePosition: () => cursorRestorePosition,
|
|
4303
|
-
cursorSavePosition: () => cursorSavePosition,
|
|
4304
|
-
cursorShow: () => cursorShow,
|
|
4305
|
-
cursorTo: () => cursorTo,
|
|
4306
|
-
cursorUp: () => cursorUp,
|
|
4307
|
-
default: () => base_exports,
|
|
4308
|
-
endSynchronizedOutput: () => endSynchronizedOutput,
|
|
4309
|
-
enterAlternativeScreen: () => enterAlternativeScreen,
|
|
4310
|
-
eraseDown: () => eraseDown,
|
|
4311
|
-
eraseEndLine: () => eraseEndLine,
|
|
4312
|
-
eraseLine: () => eraseLine,
|
|
4313
|
-
eraseLines: () => eraseLines,
|
|
4314
|
-
eraseScreen: () => eraseScreen,
|
|
4315
|
-
eraseStartLine: () => eraseStartLine,
|
|
4316
|
-
eraseUp: () => eraseUp,
|
|
4317
|
-
exitAlternativeScreen: () => exitAlternativeScreen,
|
|
4318
|
-
iTerm: () => iTerm,
|
|
4319
|
-
image: () => image,
|
|
4320
|
-
link: () => link,
|
|
4321
|
-
scrollDown: () => scrollDown,
|
|
4322
|
-
scrollUp: () => scrollUp,
|
|
4323
|
-
setCwd: () => setCwd,
|
|
4324
|
-
synchronizedOutput: () => synchronizedOutput
|
|
4325
|
-
});
|
|
4326
4473
|
var init_ansi_escapes = __esm({
|
|
4327
4474
|
"node_modules/ansi-escapes/index.js"() {
|
|
4328
4475
|
init_base();
|
|
@@ -35809,9 +35956,6 @@ var init_build2 = __esm({
|
|
|
35809
35956
|
// src/auth.ts
|
|
35810
35957
|
var auth_exports = {};
|
|
35811
35958
|
__export(auth_exports, {
|
|
35812
|
-
MAX_POLL_ATTEMPTS: () => MAX_POLL_ATTEMPTS,
|
|
35813
|
-
POLL_INTERVAL_MS: () => POLL_INTERVAL_MS,
|
|
35814
|
-
WEB_BASE: () => WEB_BASE,
|
|
35815
35959
|
clearCredentials: () => clearCredentials,
|
|
35816
35960
|
createAuthSession: () => createAuthSession,
|
|
35817
35961
|
getStoredApiKey: () => getStoredApiKey,
|
|
@@ -35842,7 +35986,6 @@ async function createAuthSession() {
|
|
|
35842
35986
|
const json = await res.json();
|
|
35843
35987
|
return {
|
|
35844
35988
|
sessionId: json.session_id,
|
|
35845
|
-
userCode: json.user_code,
|
|
35846
35989
|
verifyUrl: json.verify_url,
|
|
35847
35990
|
expiresIn: json.expires_in
|
|
35848
35991
|
};
|
|
@@ -35930,13 +36073,11 @@ function openBrowser(url) {
|
|
|
35930
36073
|
exec2(cmd, () => {
|
|
35931
36074
|
});
|
|
35932
36075
|
}
|
|
35933
|
-
var WEB_BASE,
|
|
36076
|
+
var WEB_BASE, CONFIG_FILE;
|
|
35934
36077
|
var init_auth = __esm({
|
|
35935
36078
|
"src/auth.ts"() {
|
|
35936
36079
|
"use strict";
|
|
35937
36080
|
WEB_BASE = "https://westbayberry.com";
|
|
35938
|
-
POLL_INTERVAL_MS = 2e3;
|
|
35939
|
-
MAX_POLL_ATTEMPTS = 150;
|
|
35940
36081
|
CONFIG_FILE = ".dgrc.json";
|
|
35941
36082
|
}
|
|
35942
36083
|
});
|
|
@@ -35947,7 +36088,7 @@ function reducer(_state, action) {
|
|
|
35947
36088
|
case "ALREADY_LOGGED_IN":
|
|
35948
36089
|
return { phase: "already_logged_in", apiKey: action.apiKey };
|
|
35949
36090
|
case "SESSION_CREATED":
|
|
35950
|
-
return { phase: "waiting",
|
|
36091
|
+
return { phase: "waiting", verifyUrl: action.verifyUrl };
|
|
35951
36092
|
case "AUTH_COMPLETE":
|
|
35952
36093
|
return { phase: "success", email: action.email };
|
|
35953
36094
|
case "AUTH_EXPIRED":
|
|
@@ -35982,13 +36123,12 @@ function useLogin() {
|
|
|
35982
36123
|
}
|
|
35983
36124
|
dispatch({
|
|
35984
36125
|
type: "SESSION_CREATED",
|
|
35985
|
-
userCode: session.userCode,
|
|
35986
36126
|
verifyUrl: session.verifyUrl
|
|
35987
36127
|
});
|
|
35988
36128
|
openBrowser(session.verifyUrl);
|
|
35989
|
-
for (let i = 0; i <
|
|
36129
|
+
for (let i = 0; i < MAX_POLL_ATTEMPTS; i++) {
|
|
35990
36130
|
if (cancelled) return;
|
|
35991
|
-
await new Promise((r) => setTimeout(r,
|
|
36131
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
35992
36132
|
if (cancelled) return;
|
|
35993
36133
|
try {
|
|
35994
36134
|
const result = await pollAuthSession(session.sessionId);
|
|
@@ -36018,14 +36158,14 @@ function useLogin() {
|
|
|
36018
36158
|
}, []);
|
|
36019
36159
|
return state;
|
|
36020
36160
|
}
|
|
36021
|
-
var import_react22,
|
|
36161
|
+
var import_react22, POLL_INTERVAL_MS, MAX_POLL_ATTEMPTS;
|
|
36022
36162
|
var init_useLogin = __esm({
|
|
36023
36163
|
"src/ui/hooks/useLogin.ts"() {
|
|
36024
36164
|
"use strict";
|
|
36025
36165
|
import_react22 = __toESM(require_react());
|
|
36026
36166
|
init_auth();
|
|
36027
|
-
|
|
36028
|
-
|
|
36167
|
+
POLL_INTERVAL_MS = 2e3;
|
|
36168
|
+
MAX_POLL_ATTEMPTS = 150;
|
|
36029
36169
|
}
|
|
36030
36170
|
});
|
|
36031
36171
|
|
|
@@ -38697,13 +38837,6 @@ var init_LoginApp = __esm({
|
|
|
38697
38837
|
] });
|
|
38698
38838
|
case "waiting":
|
|
38699
38839
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, children: [
|
|
38700
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
38701
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: true, children: "Your verification code:" }),
|
|
38702
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
38703
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { bold: true, color: "green", children: [
|
|
38704
|
-
" ",
|
|
38705
|
-
state.userCode
|
|
38706
|
-
] }),
|
|
38707
38840
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: " " }),
|
|
38708
38841
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: "Opening browser to authenticate..." }),
|
|
38709
38842
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "If it didn't open, visit:" }),
|
|
@@ -38843,7 +38976,7 @@ async function callAnalyzeBatch(packages, config) {
|
|
|
38843
38976
|
clearTimeout(timeoutId);
|
|
38844
38977
|
if (response.status === 401) {
|
|
38845
38978
|
throw new APIError(
|
|
38846
|
-
"Invalid API key.
|
|
38979
|
+
"Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
|
|
38847
38980
|
401,
|
|
38848
38981
|
""
|
|
38849
38982
|
);
|
|
@@ -38923,7 +39056,7 @@ async function callPyPIBatch(packages, config) {
|
|
|
38923
39056
|
clearTimeout(timeoutId);
|
|
38924
39057
|
if (response.status === 401) {
|
|
38925
39058
|
throw new APIError(
|
|
38926
|
-
"Invalid API key.
|
|
39059
|
+
"Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
|
|
38927
39060
|
401,
|
|
38928
39061
|
""
|
|
38929
39062
|
);
|
|
@@ -39799,7 +39932,7 @@ async function runStaticLogin() {
|
|
|
39799
39932
|
process.exit(1);
|
|
39800
39933
|
}
|
|
39801
39934
|
process.stderr.write(`
|
|
39802
|
-
|
|
39935
|
+
Opening browser to authenticate...
|
|
39803
39936
|
`);
|
|
39804
39937
|
process.stderr.write(` Visit: ${import_chalk4.default.cyan(session.verifyUrl)}
|
|
39805
39938
|
|
|
@@ -40156,13 +40289,10 @@ function useNpmWrapper(npmArgs, config) {
|
|
|
40156
40289
|
}
|
|
40157
40290
|
} catch (error) {
|
|
40158
40291
|
const message = error instanceof Error ? error.message : String(error);
|
|
40159
|
-
|
|
40160
|
-
dispatch({ type: "
|
|
40161
|
-
|
|
40162
|
-
|
|
40163
|
-
const code = await runNpm(parsedRef.current.rawArgs);
|
|
40164
|
-
dispatch({ type: "DONE", exitCode: code });
|
|
40165
|
-
}
|
|
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 });
|
|
40166
40296
|
}
|
|
40167
40297
|
})();
|
|
40168
40298
|
}, [npmArgs, config]);
|
|
@@ -40468,7 +40598,7 @@ function getHint(error) {
|
|
|
40468
40598
|
if (typeof statusCode !== "number") return null;
|
|
40469
40599
|
switch (statusCode) {
|
|
40470
40600
|
case 401:
|
|
40471
|
-
return "
|
|
40601
|
+
return "Not authenticated. Run `dg login` to sign in.";
|
|
40472
40602
|
case 429:
|
|
40473
40603
|
return "Rate limit exceeded. Upgrade at westbayberry.com/pricing";
|
|
40474
40604
|
case 504:
|
|
@@ -40759,7 +40889,10 @@ var init_discover = __esm({
|
|
|
40759
40889
|
"coverage",
|
|
40760
40890
|
".cache",
|
|
40761
40891
|
".pytest_cache",
|
|
40762
|
-
".mypy_cache"
|
|
40892
|
+
".mypy_cache",
|
|
40893
|
+
"validation-results",
|
|
40894
|
+
"test-fixtures",
|
|
40895
|
+
"fixtures"
|
|
40763
40896
|
]);
|
|
40764
40897
|
NPM_LOCKFILES = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "npm-shrinkwrap.json"];
|
|
40765
40898
|
PYTHON_DEPFILES = ["requirements.txt", "Pipfile.lock", "poetry.lock"];
|
|
@@ -40772,6 +40905,8 @@ function reducer3(_state, action) {
|
|
|
40772
40905
|
switch (action.type) {
|
|
40773
40906
|
case "PROJECTS_FOUND":
|
|
40774
40907
|
return { phase: "selecting", projects: action.projects };
|
|
40908
|
+
case "RESTART_SELECTION":
|
|
40909
|
+
return { phase: "selecting", projects: action.projects };
|
|
40775
40910
|
case "DISCOVERY_COMPLETE":
|
|
40776
40911
|
return { phase: "scanning", done: 0, total: action.packages.length, currentBatch: [] };
|
|
40777
40912
|
case "DISCOVERY_EMPTY":
|
|
@@ -40787,6 +40922,7 @@ function reducer3(_state, action) {
|
|
|
40787
40922
|
function useScan(config) {
|
|
40788
40923
|
const [state, dispatch] = (0, import_react27.useReducer)(reducer3, { phase: "discovering" });
|
|
40789
40924
|
const started = (0, import_react27.useRef)(false);
|
|
40925
|
+
const discoveredProjects = (0, import_react27.useRef)(null);
|
|
40790
40926
|
(0, import_react27.useEffect)(() => {
|
|
40791
40927
|
if (started.current) return;
|
|
40792
40928
|
started.current = true;
|
|
@@ -40802,6 +40938,7 @@ function useScan(config) {
|
|
|
40802
40938
|
runNpmScan(packages, discovery.skipped.length, config, dispatch);
|
|
40803
40939
|
} catch {
|
|
40804
40940
|
const projects = discoverProjects(process.cwd());
|
|
40941
|
+
discoveredProjects.current = projects.length > 1 ? projects : null;
|
|
40805
40942
|
if (projects.length === 0) {
|
|
40806
40943
|
dispatch({ type: "DISCOVERY_EMPTY", message: "No dependency files found." });
|
|
40807
40944
|
return;
|
|
@@ -40818,7 +40955,15 @@ function useScan(config) {
|
|
|
40818
40955
|
dispatch({ type: "DISCOVERY_COMPLETE", packages: [], skippedCount: 0 });
|
|
40819
40956
|
scanProjects(projects, config, dispatch);
|
|
40820
40957
|
}, [config]);
|
|
40821
|
-
|
|
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
|
+
};
|
|
40822
40967
|
}
|
|
40823
40968
|
async function runNpmScan(packages, skippedCount, config, dispatch) {
|
|
40824
40969
|
try {
|
|
@@ -41054,6 +41199,16 @@ function affectsLine(group) {
|
|
|
41054
41199
|
if (names.length <= 5) return names.join(", ");
|
|
41055
41200
|
return names.slice(0, 5).join(", ") + ` + ${names.length - 5} more`;
|
|
41056
41201
|
}
|
|
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
|
+
}
|
|
41057
41212
|
var import_react29, import_chalk10, import_jsx_runtime10, SEVERITY_LABELS3, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, InteractiveResultsView, T, FindingsSummary, FindingsDetail;
|
|
41058
41213
|
var init_InteractiveResultsView = __esm({
|
|
41059
41214
|
async "src/ui/components/InteractiveResultsView.tsx"() {
|
|
@@ -41084,8 +41239,16 @@ var init_InteractiveResultsView = __esm({
|
|
|
41084
41239
|
result,
|
|
41085
41240
|
config,
|
|
41086
41241
|
durationMs,
|
|
41087
|
-
onExit
|
|
41242
|
+
onExit,
|
|
41243
|
+
onBack
|
|
41088
41244
|
}) => {
|
|
41245
|
+
(0, import_react29.useEffect)(() => {
|
|
41246
|
+
if (!process.stdout.isTTY) return;
|
|
41247
|
+
process.stdout.write("\x1B[?1049h");
|
|
41248
|
+
return () => {
|
|
41249
|
+
process.stdout.write("\x1B[?1049l");
|
|
41250
|
+
};
|
|
41251
|
+
}, []);
|
|
41089
41252
|
const flagged = (0, import_react29.useMemo)(
|
|
41090
41253
|
() => result.packages.filter((p) => p.score > 0),
|
|
41091
41254
|
[result.packages]
|
|
@@ -41096,45 +41259,41 @@ var init_InteractiveResultsView = __esm({
|
|
|
41096
41259
|
);
|
|
41097
41260
|
const total = result.packages.length;
|
|
41098
41261
|
const groups = (0, import_react29.useMemo)(() => groupPackages3(flagged), [flagged]);
|
|
41099
|
-
const [
|
|
41100
|
-
|
|
41101
|
-
|
|
41102
|
-
|
|
41103
|
-
|
|
41104
|
-
|
|
41105
|
-
const
|
|
41106
|
-
|
|
41107
|
-
cursorRef.current = cursorIndex;
|
|
41108
|
-
expandLevelRef.current = expandLevel;
|
|
41109
|
-
expandedIdxRef.current = expandedIndex;
|
|
41110
|
-
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;
|
|
41111
41270
|
const { stdout } = use_stdout_default();
|
|
41112
41271
|
const termCols = stdout?.columns ?? process.stdout.columns ?? 80;
|
|
41113
41272
|
const termRows = stdout?.rows ?? process.stdout.rows ?? 24;
|
|
41114
41273
|
const availableRows = Math.max(5, termRows - FIXED_CHROME);
|
|
41115
41274
|
const innerWidth = Math.max(40, termCols - 6);
|
|
41116
41275
|
const getLevel = (idx) => {
|
|
41117
|
-
return expandedIndex === idx ? expandLevel : null;
|
|
41276
|
+
return view.expandedIndex === idx ? view.expandLevel : null;
|
|
41118
41277
|
};
|
|
41119
41278
|
const expandTargetHeight = (0, import_react29.useMemo)(() => {
|
|
41120
|
-
if (expandedIndex === null || expandLevel === null) return 0;
|
|
41121
|
-
const group = groups[expandedIndex];
|
|
41279
|
+
if (view.expandedIndex === null || view.expandLevel === null) return 0;
|
|
41280
|
+
const group = groups[view.expandedIndex];
|
|
41122
41281
|
if (!group) return 0;
|
|
41123
|
-
if (expandLevel === "summary") return findingsSummaryHeight(group);
|
|
41282
|
+
if (view.expandLevel === "summary") return findingsSummaryHeight(group);
|
|
41124
41283
|
return findingsDetailHeight(group, result.safeVersions);
|
|
41125
|
-
}, [expandedIndex, expandLevel, groups, result.safeVersions]);
|
|
41284
|
+
}, [view.expandedIndex, view.expandLevel, groups, result.safeVersions]);
|
|
41126
41285
|
const { visibleLines: animVisibleLines } = useExpandAnimation(
|
|
41127
41286
|
expandTargetHeight,
|
|
41128
|
-
expandedIndex !== null
|
|
41287
|
+
view.expandedIndex !== null
|
|
41129
41288
|
);
|
|
41130
41289
|
const animatedGroupHeight = (group, level, idx) => {
|
|
41131
41290
|
if (level === null) return 1;
|
|
41132
|
-
if (idx === expandedIndex) return 1 + animVisibleLines;
|
|
41291
|
+
if (idx === view.expandedIndex) return 1 + animVisibleLines;
|
|
41133
41292
|
return groupRowHeight(group, level, result.safeVersions);
|
|
41134
41293
|
};
|
|
41135
41294
|
const visibleEnd = (0, import_react29.useMemo)(() => {
|
|
41136
41295
|
let consumed = 0;
|
|
41137
|
-
let end =
|
|
41296
|
+
let end = view.viewport;
|
|
41138
41297
|
while (end < groups.length) {
|
|
41139
41298
|
const level = getLevel(end);
|
|
41140
41299
|
const h = animatedGroupHeight(groups[end], level, end);
|
|
@@ -41142,9 +41301,9 @@ var init_InteractiveResultsView = __esm({
|
|
|
41142
41301
|
consumed += h;
|
|
41143
41302
|
end++;
|
|
41144
41303
|
}
|
|
41145
|
-
if (end ===
|
|
41304
|
+
if (end === view.viewport && groups.length > 0) end = view.viewport + 1;
|
|
41146
41305
|
return end;
|
|
41147
|
-
}, [
|
|
41306
|
+
}, [view.viewport, groups, view.expandedIndex, view.expandLevel, animVisibleLines, availableRows, result.safeVersions]);
|
|
41148
41307
|
const adjustViewport = (cursor, expIdx, expLvl, currentStart) => {
|
|
41149
41308
|
if (cursor < currentStart) return cursor;
|
|
41150
41309
|
const getLvl = (i) => expIdx === i ? expLvl : null;
|
|
@@ -41169,43 +41328,39 @@ var init_InteractiveResultsView = __esm({
|
|
|
41169
41328
|
if (input === "q" || key.return) onExit();
|
|
41170
41329
|
return;
|
|
41171
41330
|
}
|
|
41172
|
-
const cursor =
|
|
41173
|
-
const expLvl = expandLevelRef.current;
|
|
41174
|
-
const expIdx = expandedIdxRef.current;
|
|
41175
|
-
const vpStart = viewportRef.current;
|
|
41331
|
+
const { cursor, expandLevel: expLvl, expandedIndex: expIdx, viewport: vpStart } = viewRef.current;
|
|
41176
41332
|
if (key.upArrow) {
|
|
41177
41333
|
const next = Math.max(0, cursor - 1);
|
|
41178
41334
|
const newVp = adjustViewport(next, expIdx, expLvl, vpStart < next ? vpStart : next);
|
|
41179
|
-
|
|
41180
|
-
setViewportStart(newVp);
|
|
41335
|
+
dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
|
|
41181
41336
|
} else if (key.downArrow) {
|
|
41182
41337
|
const next = Math.min(groups.length - 1, cursor + 1);
|
|
41183
41338
|
const newVp = adjustViewport(next, expIdx, expLvl, vpStart);
|
|
41184
|
-
|
|
41185
|
-
setViewportStart(newVp);
|
|
41339
|
+
dispatchView({ type: "MOVE", cursor: next, viewport: newVp });
|
|
41186
41340
|
} else if (key.return) {
|
|
41187
41341
|
let newExpIdx;
|
|
41188
41342
|
let newExpLvl;
|
|
41189
|
-
if (expIdx !==
|
|
41190
|
-
newExpIdx = cursor;
|
|
41191
|
-
newExpLvl = "summary";
|
|
41192
|
-
} else if (expLvl === "summary") {
|
|
41193
|
-
newExpIdx = cursor;
|
|
41194
|
-
newExpLvl = "detail";
|
|
41195
|
-
} else {
|
|
41343
|
+
if (expIdx === cursor && expLvl !== null) {
|
|
41196
41344
|
newExpIdx = null;
|
|
41197
41345
|
newExpLvl = null;
|
|
41346
|
+
} else {
|
|
41347
|
+
newExpIdx = cursor;
|
|
41348
|
+
newExpLvl = "summary";
|
|
41198
41349
|
}
|
|
41199
|
-
setExpandedIndex(newExpIdx);
|
|
41200
|
-
setExpandLevel(newExpLvl);
|
|
41201
41350
|
const newVp = adjustViewport(cursor, newExpIdx, newExpLvl, vpStart);
|
|
41202
|
-
|
|
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();
|
|
41203
41358
|
} else if (input === "q") {
|
|
41204
41359
|
onExit();
|
|
41205
41360
|
}
|
|
41206
41361
|
});
|
|
41207
|
-
const visibleGroups = groups.slice(
|
|
41208
|
-
const aboveCount =
|
|
41362
|
+
const visibleGroups = groups.slice(view.viewport, visibleEnd);
|
|
41363
|
+
const aboveCount = view.viewport;
|
|
41209
41364
|
const belowCount = groups.length - visibleEnd;
|
|
41210
41365
|
const nameCol = Math.max(20, innerWidth - 22);
|
|
41211
41366
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
@@ -41244,8 +41399,8 @@ var init_InteractiveResultsView = __esm({
|
|
|
41244
41399
|
" more above"
|
|
41245
41400
|
] }),
|
|
41246
41401
|
visibleGroups.map((group, visIdx) => {
|
|
41247
|
-
const globalIdx =
|
|
41248
|
-
const isCursor = globalIdx ===
|
|
41402
|
+
const globalIdx = view.viewport + visIdx;
|
|
41403
|
+
const isCursor = globalIdx === view.cursor;
|
|
41249
41404
|
const level = getLevel(globalIdx);
|
|
41250
41405
|
const rep = group.packages[0];
|
|
41251
41406
|
const { label, color } = actionBadge4(rep.score, config);
|
|
@@ -41264,7 +41419,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41264
41419
|
{
|
|
41265
41420
|
group,
|
|
41266
41421
|
maxWidth: innerWidth - 8,
|
|
41267
|
-
maxLines: globalIdx === expandedIndex ? animVisibleLines : void 0
|
|
41422
|
+
maxLines: globalIdx === view.expandedIndex ? animVisibleLines : void 0
|
|
41268
41423
|
}
|
|
41269
41424
|
),
|
|
41270
41425
|
level === "detail" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -41273,7 +41428,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41273
41428
|
group,
|
|
41274
41429
|
safeVersion: result.safeVersions[rep.name],
|
|
41275
41430
|
maxWidth: innerWidth - 8,
|
|
41276
|
-
maxLines: globalIdx === expandedIndex ? animVisibleLines : void 0
|
|
41431
|
+
maxLines: globalIdx === view.expandedIndex ? animVisibleLines : void 0
|
|
41277
41432
|
}
|
|
41278
41433
|
)
|
|
41279
41434
|
] }, group.key);
|
|
@@ -41320,13 +41475,23 @@ var init_InteractiveResultsView = __esm({
|
|
|
41320
41475
|
" navigate",
|
|
41321
41476
|
" ",
|
|
41322
41477
|
import_chalk10.default.cyan("\u23CE"),
|
|
41323
|
-
"
|
|
41478
|
+
" toggle",
|
|
41479
|
+
" ",
|
|
41480
|
+
import_chalk10.default.cyan("e"),
|
|
41481
|
+
" detail",
|
|
41324
41482
|
" ",
|
|
41483
|
+
onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
41484
|
+
import_chalk10.default.cyan("b"),
|
|
41485
|
+
" back",
|
|
41486
|
+
" "
|
|
41487
|
+
] }),
|
|
41325
41488
|
import_chalk10.default.cyan("q"),
|
|
41326
41489
|
" quit"
|
|
41327
41490
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
41328
41491
|
"Press ",
|
|
41329
41492
|
import_chalk10.default.cyan("q"),
|
|
41493
|
+
" or ",
|
|
41494
|
+
import_chalk10.default.cyan("Enter"),
|
|
41330
41495
|
" to exit"
|
|
41331
41496
|
] })
|
|
41332
41497
|
] })
|
|
@@ -41334,13 +41499,9 @@ var init_InteractiveResultsView = __esm({
|
|
|
41334
41499
|
};
|
|
41335
41500
|
T = {
|
|
41336
41501
|
branch: import_chalk10.default.dim("\u251C\u2500\u2500"),
|
|
41337
|
-
// ├──
|
|
41338
41502
|
last: import_chalk10.default.dim("\u2514\u2500\u2500"),
|
|
41339
|
-
// └──
|
|
41340
41503
|
pipe: import_chalk10.default.dim("\u2502"),
|
|
41341
|
-
// │
|
|
41342
41504
|
blank: " "
|
|
41343
|
-
//
|
|
41344
41505
|
};
|
|
41345
41506
|
FindingsSummary = ({ group, maxWidth, maxLines }) => {
|
|
41346
41507
|
const rep = group.packages[0];
|
|
@@ -41506,12 +41667,7 @@ var init_ProjectSelector = __esm({
|
|
|
41506
41667
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: isCursor, inverse: isCursor, children: line }, i);
|
|
41507
41668
|
}),
|
|
41508
41669
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "" }),
|
|
41509
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.
|
|
41510
|
-
selected.size,
|
|
41511
|
-
" of ",
|
|
41512
|
-
projects.length,
|
|
41513
|
-
" selected"
|
|
41514
|
-
] })
|
|
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` })
|
|
41515
41671
|
] });
|
|
41516
41672
|
};
|
|
41517
41673
|
}
|
|
@@ -41536,7 +41692,7 @@ var init_App2 = __esm({
|
|
|
41536
41692
|
await init_ProjectSelector();
|
|
41537
41693
|
import_jsx_runtime12 = __toESM(require_jsx_runtime());
|
|
41538
41694
|
App2 = ({ config }) => {
|
|
41539
|
-
const { state, scanSelectedProjects } = useScan(config);
|
|
41695
|
+
const { state, scanSelectedProjects, restartSelection } = useScan(config);
|
|
41540
41696
|
const { exit } = use_app_default();
|
|
41541
41697
|
const handleResultsExit = (0, import_react31.useCallback)(() => {
|
|
41542
41698
|
if (state.phase === "results") {
|
|
@@ -41603,7 +41759,8 @@ var init_App2 = __esm({
|
|
|
41603
41759
|
result: state.result,
|
|
41604
41760
|
config,
|
|
41605
41761
|
durationMs: state.durationMs,
|
|
41606
|
-
onExit: handleResultsExit
|
|
41762
|
+
onExit: handleResultsExit,
|
|
41763
|
+
onBack: restartSelection ?? void 0
|
|
41607
41764
|
}
|
|
41608
41765
|
);
|
|
41609
41766
|
case "empty":
|
|
@@ -41615,191 +41772,8 @@ var init_App2 = __esm({
|
|
|
41615
41772
|
}
|
|
41616
41773
|
});
|
|
41617
41774
|
|
|
41618
|
-
// src/config.ts
|
|
41619
|
-
import { parseArgs } from "node:util";
|
|
41620
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
41621
|
-
import { join } from "node:path";
|
|
41622
|
-
import { homedir } from "node:os";
|
|
41623
|
-
function loadDgrc() {
|
|
41624
|
-
const candidates = [
|
|
41625
|
-
join(process.cwd(), ".dgrc.json"),
|
|
41626
|
-
join(homedir(), ".dgrc.json")
|
|
41627
|
-
];
|
|
41628
|
-
for (const filepath of candidates) {
|
|
41629
|
-
if (existsSync(filepath)) {
|
|
41630
|
-
try {
|
|
41631
|
-
return JSON.parse(readFileSync(filepath, "utf-8"));
|
|
41632
|
-
} catch {
|
|
41633
|
-
process.stderr.write(`Warning: Failed to parse ${filepath}, ignoring.
|
|
41634
|
-
`);
|
|
41635
|
-
}
|
|
41636
|
-
}
|
|
41637
|
-
}
|
|
41638
|
-
return {};
|
|
41639
|
-
}
|
|
41640
|
-
var USAGE = `
|
|
41641
|
-
Dependency Guardian \u2014 Supply chain security scanner
|
|
41642
|
-
|
|
41643
|
-
Usage:
|
|
41644
|
-
dependency-guardian scan [options]
|
|
41645
|
-
dg scan [options]
|
|
41646
|
-
dg npm install <pkg> [npm-flags]
|
|
41647
|
-
dg wrap
|
|
41648
|
-
|
|
41649
|
-
Commands:
|
|
41650
|
-
scan Scan dependencies (auto-discovers npm + Python projects)
|
|
41651
|
-
npm Wrap npm commands \u2014 scans packages before installing
|
|
41652
|
-
hook install Install git pre-commit hook to scan lockfile changes
|
|
41653
|
-
hook uninstall Remove the pre-commit hook
|
|
41654
|
-
update Check for and install the latest version
|
|
41655
|
-
login Authenticate with your WestBayBerry account
|
|
41656
|
-
logout Remove saved credentials
|
|
41657
|
-
wrap Show instructions to alias npm to dg
|
|
41658
|
-
|
|
41659
|
-
Options:
|
|
41660
|
-
--api-key <key> API key (or set DG_API_KEY env var)
|
|
41661
|
-
--api-url <url> API base URL (default: https://api.westbayberry.com)
|
|
41662
|
-
--mode <mode> block | warn | off (default: warn)
|
|
41663
|
-
--block-threshold <n> Score threshold for blocking (default: 70)
|
|
41664
|
-
--warn-threshold <n> Score threshold for warnings (default: 60)
|
|
41665
|
-
--max-packages <n> Max packages per scan (default: 200)
|
|
41666
|
-
--allowlist <pkgs> Comma-separated package names to skip
|
|
41667
|
-
--json Output JSON for CI parsing
|
|
41668
|
-
--scan-all Scan all packages, not just changed
|
|
41669
|
-
--base-lockfile <path> Path to base lockfile for explicit diff
|
|
41670
|
-
--workspace <dir> Scan a specific workspace subdirectory
|
|
41671
|
-
--debug Show diagnostic output (discovery, batches, timing)
|
|
41672
|
-
--no-config Skip loading .dgrc.json config file
|
|
41673
|
-
--help Show this help message
|
|
41674
|
-
--version Show version number
|
|
41675
|
-
|
|
41676
|
-
Config File:
|
|
41677
|
-
Place a .dgrc.json in your project root or home directory.
|
|
41678
|
-
Precedence: CLI flags > env vars > .dgrc.json > defaults
|
|
41679
|
-
|
|
41680
|
-
Environment Variables:
|
|
41681
|
-
DG_API_KEY API key
|
|
41682
|
-
DG_API_URL API base URL
|
|
41683
|
-
DG_MODE Mode (block/warn/off)
|
|
41684
|
-
DG_ALLOWLIST Comma-separated allowlist
|
|
41685
|
-
DG_DEBUG Enable debug output (set to 1)
|
|
41686
|
-
DG_WORKSPACE Workspace subdirectory to scan
|
|
41687
|
-
|
|
41688
|
-
Exit Codes:
|
|
41689
|
-
0 pass \u2014 No risks detected
|
|
41690
|
-
1 warn \u2014 Risks detected (advisory)
|
|
41691
|
-
2 block \u2014 High-risk packages detected
|
|
41692
|
-
3 error \u2014 Internal error (API failure, config error)
|
|
41693
|
-
|
|
41694
|
-
Examples:
|
|
41695
|
-
DG_API_KEY=dg_live_xxx dg scan
|
|
41696
|
-
dg scan --api-key dg_live_xxx --json
|
|
41697
|
-
dg scan --scan-all --mode block
|
|
41698
|
-
dg scan --base-lockfile ./main-lockfile.json
|
|
41699
|
-
dg npm install express lodash
|
|
41700
|
-
dg npm install @scope/pkg@^2.0.0
|
|
41701
|
-
dg npm install risky-pkg --dg-force
|
|
41702
|
-
`.trimStart();
|
|
41703
|
-
function getVersion() {
|
|
41704
|
-
try {
|
|
41705
|
-
const pkg = JSON.parse(
|
|
41706
|
-
readFileSync(join(__dirname, "..", "package.json"), "utf-8")
|
|
41707
|
-
);
|
|
41708
|
-
return pkg.version ?? "1.0.0";
|
|
41709
|
-
} catch {
|
|
41710
|
-
return "1.0.0";
|
|
41711
|
-
}
|
|
41712
|
-
}
|
|
41713
|
-
function parseConfig(argv) {
|
|
41714
|
-
const { values, positionals } = parseArgs({
|
|
41715
|
-
args: argv.slice(2),
|
|
41716
|
-
options: {
|
|
41717
|
-
"api-key": { type: "string" },
|
|
41718
|
-
"api-url": { type: "string" },
|
|
41719
|
-
mode: { type: "string" },
|
|
41720
|
-
"block-threshold": { type: "string" },
|
|
41721
|
-
"warn-threshold": { type: "string" },
|
|
41722
|
-
"max-packages": { type: "string" },
|
|
41723
|
-
allowlist: { type: "string" },
|
|
41724
|
-
json: { type: "boolean", default: false },
|
|
41725
|
-
"scan-all": { type: "boolean", default: false },
|
|
41726
|
-
"base-lockfile": { type: "string" },
|
|
41727
|
-
workspace: { type: "string", short: "w" },
|
|
41728
|
-
debug: { type: "boolean", default: false },
|
|
41729
|
-
"no-config": { type: "boolean", default: false },
|
|
41730
|
-
help: { type: "boolean", default: false },
|
|
41731
|
-
version: { type: "boolean", default: false }
|
|
41732
|
-
},
|
|
41733
|
-
allowPositionals: true,
|
|
41734
|
-
strict: false
|
|
41735
|
-
});
|
|
41736
|
-
if (values.help) {
|
|
41737
|
-
process.stdout.write(USAGE);
|
|
41738
|
-
process.exit(0);
|
|
41739
|
-
}
|
|
41740
|
-
if (values.version) {
|
|
41741
|
-
process.stdout.write(`dependency-guardian v${getVersion()}
|
|
41742
|
-
`);
|
|
41743
|
-
process.exit(0);
|
|
41744
|
-
}
|
|
41745
|
-
const command = positionals[0] ?? "scan";
|
|
41746
|
-
const noConfig = values["no-config"];
|
|
41747
|
-
const dgrc = noConfig ? {} : loadDgrc();
|
|
41748
|
-
if (values["api-key"]) {
|
|
41749
|
-
process.stderr.write(
|
|
41750
|
-
"Warning: --api-key is deprecated (visible in process list). Use DG_API_KEY env var instead.\n"
|
|
41751
|
-
);
|
|
41752
|
-
}
|
|
41753
|
-
const apiKey = values["api-key"] ?? process.env.DG_API_KEY ?? dgrc.apiKey ?? "";
|
|
41754
|
-
if (!apiKey) {
|
|
41755
|
-
process.stderr.write(
|
|
41756
|
-
"Error: API key required. Run `dg login` to authenticate,\nor set the DG_API_KEY environment variable.\nGet your key at https://westbayberry.com/dashboard\n"
|
|
41757
|
-
);
|
|
41758
|
-
process.exit(1);
|
|
41759
|
-
}
|
|
41760
|
-
const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
|
|
41761
|
-
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
41762
|
-
process.stderr.write(
|
|
41763
|
-
`Error: Invalid mode "${modeRaw}". Must be block, warn, or off.
|
|
41764
|
-
`
|
|
41765
|
-
);
|
|
41766
|
-
process.exit(1);
|
|
41767
|
-
}
|
|
41768
|
-
const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
|
|
41769
|
-
const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
|
|
41770
|
-
const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
|
|
41771
|
-
const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
|
|
41772
|
-
const debug = values.debug || process.env.DG_DEBUG === "1";
|
|
41773
|
-
if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
|
|
41774
|
-
process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
|
|
41775
|
-
process.exit(1);
|
|
41776
|
-
}
|
|
41777
|
-
if (isNaN(warnThreshold) || warnThreshold < 0 || warnThreshold > 100) {
|
|
41778
|
-
process.stderr.write("Error: --warn-threshold must be a number between 0 and 100\n");
|
|
41779
|
-
process.exit(1);
|
|
41780
|
-
}
|
|
41781
|
-
if (isNaN(maxPackages) || maxPackages < 1 || maxPackages > 1e4) {
|
|
41782
|
-
process.stderr.write("Error: --max-packages must be a number between 1 and 10000\n");
|
|
41783
|
-
process.exit(1);
|
|
41784
|
-
}
|
|
41785
|
-
return {
|
|
41786
|
-
apiKey,
|
|
41787
|
-
apiUrl: values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com",
|
|
41788
|
-
mode: modeRaw,
|
|
41789
|
-
blockThreshold,
|
|
41790
|
-
warnThreshold,
|
|
41791
|
-
maxPackages,
|
|
41792
|
-
allowlist: allowlistRaw ? allowlistRaw.split(",").map((s) => s.trim()).filter(Boolean) : dgrc.allowlist ?? [],
|
|
41793
|
-
json: values.json,
|
|
41794
|
-
scanAll: values["scan-all"],
|
|
41795
|
-
baseLockfile: values["base-lockfile"] ?? null,
|
|
41796
|
-
workspace: values.workspace ?? process.env.DG_WORKSPACE ?? null,
|
|
41797
|
-
command,
|
|
41798
|
-
debug
|
|
41799
|
-
};
|
|
41800
|
-
}
|
|
41801
|
-
|
|
41802
41775
|
// src/bin.ts
|
|
41776
|
+
init_config();
|
|
41803
41777
|
init_npm_wrapper();
|
|
41804
41778
|
|
|
41805
41779
|
// src/update-check.ts
|
|
@@ -41861,8 +41835,7 @@ function spawnBackgroundUpdate(version) {
|
|
|
41861
41835
|
try {
|
|
41862
41836
|
const child = spawn2("npm", ["install", "-g", `${PKG_NAME}@${version}`], {
|
|
41863
41837
|
detached: true,
|
|
41864
|
-
stdio: "ignore"
|
|
41865
|
-
shell: true
|
|
41838
|
+
stdio: "ignore"
|
|
41866
41839
|
});
|
|
41867
41840
|
child.unref();
|
|
41868
41841
|
} catch {
|
|
@@ -41924,10 +41897,20 @@ async function runUpdate(currentVersion) {
|
|
|
41924
41897
|
}
|
|
41925
41898
|
|
|
41926
41899
|
// src/bin.ts
|
|
41927
|
-
var CLI_VERSION =
|
|
41900
|
+
var CLI_VERSION = getVersion();
|
|
41928
41901
|
var isInteractive = process.stdout.isTTY === true && !process.env.CI && !process.env.NO_COLOR;
|
|
41929
41902
|
async function main() {
|
|
41930
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
|
+
}
|
|
41931
41914
|
if (rawCommand === "wrap") {
|
|
41932
41915
|
handleWrapCommand();
|
|
41933
41916
|
return;
|
|
@@ -41958,7 +41941,7 @@ async function main() {
|
|
|
41958
41941
|
const { clearCredentials: clearCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
41959
41942
|
clearCredentials2();
|
|
41960
41943
|
const chalk9 = (await Promise.resolve().then(() => __toESM(require_source()))).default;
|
|
41961
|
-
process.stderr.write(chalk9.green(" Logged out
|
|
41944
|
+
process.stderr.write(chalk9.green(" Logged out.\n"));
|
|
41962
41945
|
return;
|
|
41963
41946
|
}
|
|
41964
41947
|
const config = parseConfig(process.argv);
|
|
@@ -41993,18 +41976,10 @@ async function main() {
|
|
|
41993
41976
|
process.exit(0);
|
|
41994
41977
|
}
|
|
41995
41978
|
const { App: App3 } = await init_App2().then(() => App_exports);
|
|
41996
|
-
const {
|
|
41997
|
-
|
|
41998
|
-
|
|
41999
|
-
|
|
42000
|
-
try {
|
|
42001
|
-
const { waitUntilExit } = render2(
|
|
42002
|
-
React16.createElement(App3, { config })
|
|
42003
|
-
);
|
|
42004
|
-
await waitUntilExit();
|
|
42005
|
-
} finally {
|
|
42006
|
-
process.stdout.write(MOUSE_OFF + exitAlternativeScreen2);
|
|
42007
|
-
}
|
|
41979
|
+
const { waitUntilExit } = render2(
|
|
41980
|
+
React16.createElement(App3, { config })
|
|
41981
|
+
);
|
|
41982
|
+
await waitUntilExit();
|
|
42008
41983
|
}
|
|
42009
41984
|
const updateMsg = await updatePromise;
|
|
42010
41985
|
if (updateMsg) {
|