@westbayberry/dg 1.0.13 → 1.0.16
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 +88 -17
- package/dist/index.mjs +405 -278
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @westbayberry/dg
|
|
2
2
|
|
|
3
|
-
Supply chain security scanner for npm and Python dependencies.
|
|
3
|
+
Supply chain security scanner for npm and Python dependencies. Scans lockfile changes against 26+ detectors to catch malicious packages, typosquatting, dependency confusion, credential theft, and obfuscated code before they reach production.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,6 +8,8 @@ Supply chain security scanner for npm and Python dependencies. Detects malicious
|
|
|
8
8
|
npm install -g @westbayberry/dg
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
Requires Node.js 18+.
|
|
12
|
+
|
|
11
13
|
## Quick Start
|
|
12
14
|
|
|
13
15
|
```bash
|
|
@@ -15,48 +17,93 @@ dg login
|
|
|
15
17
|
dg scan
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
The CLI
|
|
20
|
+
The CLI walks your directory tree and finds 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, an interactive selector lets you pick which ones to scan.
|
|
21
|
+
|
|
22
|
+
Only changed packages are scanned by default — `dg` diffs your lockfile against the git merge-base with `main` to find what's new or updated.
|
|
19
23
|
|
|
20
24
|
## Commands
|
|
21
25
|
|
|
22
26
|
```
|
|
23
|
-
dg scan [options] Scan dependencies
|
|
24
|
-
dg npm install <pkg> Scan packages before installing
|
|
27
|
+
dg scan [options] Scan dependencies for supply chain threats
|
|
28
|
+
dg npm install <pkg> Scan packages before installing them
|
|
25
29
|
dg login Authenticate with your WestBayBerry account
|
|
26
30
|
dg logout Remove saved credentials
|
|
27
|
-
dg hook install Install git pre-commit hook
|
|
31
|
+
dg hook install Install git pre-commit hook
|
|
28
32
|
dg hook uninstall Remove the pre-commit hook
|
|
29
33
|
dg update Check for and install the latest version
|
|
30
34
|
dg wrap Show instructions to alias npm to dg
|
|
31
35
|
```
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
## What It Detects
|
|
38
|
+
|
|
39
|
+
Each package is analyzed by 26+ detectors covering:
|
|
40
|
+
|
|
41
|
+
| Category | Examples |
|
|
42
|
+
|----------|----------|
|
|
43
|
+
| **Code execution** | `child_process` spawning, `eval`/`Function` calls, shell command injection |
|
|
44
|
+
| **Network exfiltration** | HTTP/WebSocket/DNS/gRPC calls, URL obfuscation, data exfil patterns |
|
|
45
|
+
| **Credential theft** | Reading SSH keys, browser tokens, cloud credentials, `.npmrc`/`.pypirc` |
|
|
46
|
+
| **Install scripts** | Suspicious `preinstall`/`postinstall` hooks, download-and-execute chains |
|
|
47
|
+
| **Obfuscation** | Hex/unicode encoding, string reconstruction, phantom eval, minified payloads |
|
|
48
|
+
| **Supply chain** | Typosquatting, dependency confusion, version squatting, borrowed repo URLs |
|
|
49
|
+
| **Persistence** | Writing to shell configs, cron jobs, systemd units, SSH `authorized_keys` |
|
|
50
|
+
| **Behavioral** | Time-gated payloads, purpose mismatch, runtime evasion, binary addons |
|
|
51
|
+
| **Reputation** | Missing/fake GitHub repos, ghost packages, low download counts |
|
|
52
|
+
|
|
53
|
+
Findings include severity (1–5), confidence (0–1), and code evidence with file paths and line numbers.
|
|
54
|
+
|
|
55
|
+
## Scan Options
|
|
34
56
|
|
|
35
57
|
| Flag | Default | Description |
|
|
36
58
|
|------|---------|-------------|
|
|
37
59
|
| `--mode <mode>` | `warn` | `block` / `warn` / `off` |
|
|
38
|
-
| `--block-threshold <n>` | `70` | Score threshold for blocking |
|
|
39
|
-
| `--warn-threshold <n>` | `60` | Score threshold for warnings |
|
|
60
|
+
| `--block-threshold <n>` | `70` | Score threshold for blocking (0–100) |
|
|
61
|
+
| `--warn-threshold <n>` | `60` | Score threshold for warnings (0–100) |
|
|
40
62
|
| `--max-packages <n>` | `200` | Max packages per scan |
|
|
41
63
|
| `--allowlist <pkgs>` | | Comma-separated packages to skip |
|
|
42
|
-
| `--json` | | Output JSON for CI parsing |
|
|
64
|
+
| `--json` | | Output raw JSON (for CI parsing) |
|
|
43
65
|
| `--scan-all` | | Scan all packages, not just changed |
|
|
44
66
|
| `--base-lockfile <path>` | | Explicit base lockfile for diff |
|
|
45
67
|
| `--workspace <dir>` | | Scan a specific workspace subdirectory |
|
|
46
|
-
| `--debug` | | Show
|
|
68
|
+
| `--debug` | | Show discovery, batching, and timing info |
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
## Exit Codes
|
|
49
71
|
|
|
50
72
|
| Code | Meaning | CI Action |
|
|
51
73
|
|------|---------|-----------|
|
|
52
74
|
| `0` | Pass | Continue |
|
|
53
75
|
| `1` | Warning | Advisory — review recommended |
|
|
54
76
|
| `2` | Block | Fail the pipeline |
|
|
55
|
-
| `3` | Error | Internal error |
|
|
77
|
+
| `3` | Error | Internal error (auth, network, etc.) |
|
|
56
78
|
|
|
57
|
-
##
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
Settings can come from CLI flags, environment variables, or a `.dgrc.json` config file (searched in the current directory, then `~/`). CLI flags take highest precedence.
|
|
82
|
+
|
|
83
|
+
### `.dgrc.json`
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"apiKey": "dg_...",
|
|
88
|
+
"mode": "block",
|
|
89
|
+
"blockThreshold": 70,
|
|
90
|
+
"warnThreshold": 60,
|
|
91
|
+
"maxPackages": 200,
|
|
92
|
+
"allowlist": ["known-safe-pkg"]
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Environment Variables
|
|
97
|
+
|
|
98
|
+
| Variable | Description |
|
|
99
|
+
|----------|-------------|
|
|
100
|
+
| `DG_API_URL` | API base URL |
|
|
101
|
+
| `DG_MODE` | `block` / `warn` / `off` |
|
|
102
|
+
| `DG_ALLOWLIST` | Comma-separated allowlist |
|
|
103
|
+
| `DG_DEBUG` | Set to `1` for diagnostic output |
|
|
104
|
+
| `DG_WORKSPACE` | Workspace subdirectory |
|
|
58
105
|
|
|
59
|
-
|
|
106
|
+
## CI Setup
|
|
60
107
|
|
|
61
108
|
### GitHub Actions
|
|
62
109
|
|
|
@@ -74,6 +121,18 @@ npx @westbayberry/dg login
|
|
|
74
121
|
npx @westbayberry/dg scan --mode block --json
|
|
75
122
|
```
|
|
76
123
|
|
|
124
|
+
The `--json` flag outputs machine-readable results. Exit code `2` signals a blocked scan — wire it into your pipeline to fail the build.
|
|
125
|
+
|
|
126
|
+
### Monorepo / Workspace
|
|
127
|
+
|
|
128
|
+
Scan a specific workspace:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
dg scan --workspace packages/api
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or let `dg` discover all projects and pick interactively.
|
|
135
|
+
|
|
77
136
|
## Git Hook
|
|
78
137
|
|
|
79
138
|
Block commits that introduce risky dependencies:
|
|
@@ -82,22 +141,34 @@ Block commits that introduce risky dependencies:
|
|
|
82
141
|
dg hook install
|
|
83
142
|
```
|
|
84
143
|
|
|
85
|
-
This
|
|
144
|
+
This adds a pre-commit hook that runs `dg scan --mode block` whenever a lockfile is staged. If any package scores above the block threshold, the commit is rejected. Remove it with `dg hook uninstall`.
|
|
86
145
|
|
|
87
146
|
## npm Wrapper
|
|
88
147
|
|
|
89
|
-
Scan packages before
|
|
148
|
+
Scan packages before they're installed:
|
|
90
149
|
|
|
91
150
|
```bash
|
|
92
151
|
dg npm install express lodash
|
|
93
152
|
```
|
|
94
153
|
|
|
95
|
-
|
|
154
|
+
Packages are resolved and scanned through the API. If a package is blocked, you'll get a confirmation prompt — press `y` to install anyway, or use `--dg-force` to skip the prompt.
|
|
155
|
+
|
|
156
|
+
To make this the default for all `npm install` commands:
|
|
96
157
|
|
|
97
158
|
```bash
|
|
98
159
|
echo 'alias npm="dg npm"' >> ~/.zshrc
|
|
99
160
|
```
|
|
100
161
|
|
|
162
|
+
## Python Support
|
|
163
|
+
|
|
164
|
+
Python projects are detected alongside npm. The scanner reads:
|
|
165
|
+
|
|
166
|
+
- `requirements.txt` — `name==version` pins
|
|
167
|
+
- `Pipfile.lock` — default and develop sections
|
|
168
|
+
- `poetry.lock` — `[[package]]` entries
|
|
169
|
+
|
|
170
|
+
Python packages are analyzed through the same detection engine against the PyPI registry.
|
|
171
|
+
|
|
101
172
|
## Links
|
|
102
173
|
|
|
103
174
|
- [Dashboard](https://westbayberry.com/dashboard)
|
package/dist/index.mjs
CHANGED
|
@@ -114,12 +114,6 @@ function parseConfig(argv) {
|
|
|
114
114
|
const noConfig = values["no-config"];
|
|
115
115
|
const dgrc = noConfig ? {} : loadDgrc();
|
|
116
116
|
const apiKey = dgrc.apiKey ?? "";
|
|
117
|
-
if (!apiKey) {
|
|
118
|
-
process.stderr.write(
|
|
119
|
-
"Error: Not logged in. Run `dg login` to authenticate.\n"
|
|
120
|
-
);
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
117
|
const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
|
|
124
118
|
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
125
119
|
process.stderr.write(
|
|
@@ -131,7 +125,10 @@ function parseConfig(argv) {
|
|
|
131
125
|
const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
|
|
132
126
|
const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
|
|
133
127
|
const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
|
|
134
|
-
|
|
128
|
+
let maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
|
|
129
|
+
if (!apiKey) {
|
|
130
|
+
maxPackages = Math.min(maxPackages, 50);
|
|
131
|
+
}
|
|
135
132
|
const debug = values.debug || process.env.DG_DEBUG === "1";
|
|
136
133
|
if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
|
|
137
134
|
process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
|
|
@@ -3296,7 +3293,7 @@ var require_react_development = __commonJS({
|
|
|
3296
3293
|
}
|
|
3297
3294
|
return dispatcher.useContext(Context);
|
|
3298
3295
|
}
|
|
3299
|
-
function
|
|
3296
|
+
function useState7(initialState) {
|
|
3300
3297
|
var dispatcher = resolveDispatcher();
|
|
3301
3298
|
return dispatcher.useState(initialState);
|
|
3302
3299
|
}
|
|
@@ -3304,7 +3301,7 @@ var require_react_development = __commonJS({
|
|
|
3304
3301
|
var dispatcher = resolveDispatcher();
|
|
3305
3302
|
return dispatcher.useReducer(reducer4, initialArg, init);
|
|
3306
3303
|
}
|
|
3307
|
-
function
|
|
3304
|
+
function useRef7(initialValue) {
|
|
3308
3305
|
var dispatcher = resolveDispatcher();
|
|
3309
3306
|
return dispatcher.useRef(initialValue);
|
|
3310
3307
|
}
|
|
@@ -4098,8 +4095,8 @@ var require_react_development = __commonJS({
|
|
|
4098
4095
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
4099
4096
|
exports.useMemo = useMemo4;
|
|
4100
4097
|
exports.useReducer = useReducer5;
|
|
4101
|
-
exports.useRef =
|
|
4102
|
-
exports.useState =
|
|
4098
|
+
exports.useRef = useRef7;
|
|
4099
|
+
exports.useState = useState7;
|
|
4103
4100
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
4104
4101
|
exports.useTransition = useTransition;
|
|
4105
4102
|
exports.version = ReactVersion;
|
|
@@ -38917,15 +38914,35 @@ async function callAnalyzeAPI(packages, config, onProgress) {
|
|
|
38917
38914
|
for (let i = 0; i < packages.length; i += BATCH_SIZE) {
|
|
38918
38915
|
batches.push(packages.slice(i, i + BATCH_SIZE));
|
|
38919
38916
|
}
|
|
38920
|
-
const
|
|
38917
|
+
const MAX_CONCURRENT_BATCHES = 2;
|
|
38918
|
+
const results = new Array(batches.length);
|
|
38921
38919
|
let completed = 0;
|
|
38922
|
-
|
|
38923
|
-
|
|
38924
|
-
|
|
38920
|
+
let nextIdx = 0;
|
|
38921
|
+
async function runBatch(idx) {
|
|
38922
|
+
const result = await callBatchWithRetry(batches[idx], config);
|
|
38923
|
+
results[idx] = result;
|
|
38924
|
+
completed += batches[idx].length;
|
|
38925
38925
|
if (onProgress) {
|
|
38926
|
-
onProgress(completed, packages.length,
|
|
38926
|
+
onProgress(completed, packages.length, batches[idx].map((p) => p.name));
|
|
38927
|
+
}
|
|
38928
|
+
}
|
|
38929
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
38930
|
+
while (nextIdx < batches.length && inFlight.size < MAX_CONCURRENT_BATCHES) {
|
|
38931
|
+
const idx = nextIdx++;
|
|
38932
|
+
const p = runBatch(idx).then(() => {
|
|
38933
|
+
inFlight.delete(p);
|
|
38934
|
+
});
|
|
38935
|
+
inFlight.add(p);
|
|
38936
|
+
}
|
|
38937
|
+
while (inFlight.size > 0) {
|
|
38938
|
+
await Promise.race(inFlight);
|
|
38939
|
+
while (nextIdx < batches.length && inFlight.size < MAX_CONCURRENT_BATCHES) {
|
|
38940
|
+
const idx = nextIdx++;
|
|
38941
|
+
const p = runBatch(idx).then(() => {
|
|
38942
|
+
inFlight.delete(p);
|
|
38943
|
+
});
|
|
38944
|
+
inFlight.add(p);
|
|
38927
38945
|
}
|
|
38928
|
-
results.push(result);
|
|
38929
38946
|
}
|
|
38930
38947
|
return mergeResponses(results, config);
|
|
38931
38948
|
}
|
|
@@ -38984,13 +39001,16 @@ async function callAnalyzeBatch(packages, config) {
|
|
|
38984
39001
|
const timeoutId = setTimeout(() => controller.abort(), 12e4);
|
|
38985
39002
|
let response;
|
|
38986
39003
|
try {
|
|
39004
|
+
const headers = {
|
|
39005
|
+
"Content-Type": "application/json",
|
|
39006
|
+
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
39007
|
+
};
|
|
39008
|
+
if (config.apiKey) {
|
|
39009
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
39010
|
+
}
|
|
38987
39011
|
response = await fetch(url, {
|
|
38988
39012
|
method: "POST",
|
|
38989
|
-
headers
|
|
38990
|
-
"Content-Type": "application/json",
|
|
38991
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
38992
|
-
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
38993
|
-
},
|
|
39013
|
+
headers,
|
|
38994
39014
|
body: JSON.stringify(payload),
|
|
38995
39015
|
signal: controller.signal
|
|
38996
39016
|
});
|
|
@@ -39012,6 +39032,14 @@ async function callAnalyzeBatch(packages, config) {
|
|
|
39012
39032
|
);
|
|
39013
39033
|
}
|
|
39014
39034
|
if (response.status === 429) {
|
|
39035
|
+
const body = await response.json().catch(() => ({}));
|
|
39036
|
+
if (body.anonymous) {
|
|
39037
|
+
throw new APIError(
|
|
39038
|
+
"Anonymous scan limit reached. Run `dg login` for 200 free scans/month.",
|
|
39039
|
+
429,
|
|
39040
|
+
""
|
|
39041
|
+
);
|
|
39042
|
+
}
|
|
39015
39043
|
throw new APIError(
|
|
39016
39044
|
"Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing",
|
|
39017
39045
|
429,
|
|
@@ -39064,13 +39092,16 @@ async function callPyPIBatch(packages, config) {
|
|
|
39064
39092
|
const timeoutId = setTimeout(() => controller.abort(), 12e4);
|
|
39065
39093
|
let response;
|
|
39066
39094
|
try {
|
|
39095
|
+
const headers = {
|
|
39096
|
+
"Content-Type": "application/json",
|
|
39097
|
+
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
39098
|
+
};
|
|
39099
|
+
if (config.apiKey) {
|
|
39100
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
39101
|
+
}
|
|
39067
39102
|
response = await fetch(url, {
|
|
39068
39103
|
method: "POST",
|
|
39069
|
-
headers
|
|
39070
|
-
"Content-Type": "application/json",
|
|
39071
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
39072
|
-
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
39073
|
-
},
|
|
39104
|
+
headers,
|
|
39074
39105
|
body: JSON.stringify(payload),
|
|
39075
39106
|
signal: controller.signal
|
|
39076
39107
|
});
|
|
@@ -39092,6 +39123,14 @@ async function callPyPIBatch(packages, config) {
|
|
|
39092
39123
|
);
|
|
39093
39124
|
}
|
|
39094
39125
|
if (response.status === 429) {
|
|
39126
|
+
const body = await response.json().catch(() => ({}));
|
|
39127
|
+
if (body.anonymous) {
|
|
39128
|
+
throw new APIError(
|
|
39129
|
+
"Anonymous scan limit reached. Run `dg login` for 200 free scans/month.",
|
|
39130
|
+
429,
|
|
39131
|
+
""
|
|
39132
|
+
);
|
|
39133
|
+
}
|
|
39095
39134
|
throw new APIError(
|
|
39096
39135
|
"Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing",
|
|
39097
39136
|
429,
|
|
@@ -39116,7 +39155,7 @@ var init_api = __esm({
|
|
|
39116
39155
|
this.name = "APIError";
|
|
39117
39156
|
}
|
|
39118
39157
|
};
|
|
39119
|
-
BATCH_SIZE =
|
|
39158
|
+
BATCH_SIZE = 200;
|
|
39120
39159
|
MAX_RETRIES = 2;
|
|
39121
39160
|
RETRY_DELAY_MS = 5e3;
|
|
39122
39161
|
}
|
|
@@ -39587,6 +39626,144 @@ var init_lockfile = __esm({
|
|
|
39587
39626
|
}
|
|
39588
39627
|
});
|
|
39589
39628
|
|
|
39629
|
+
// src/discover.ts
|
|
39630
|
+
var discover_exports = {};
|
|
39631
|
+
__export(discover_exports, {
|
|
39632
|
+
discoverProjects: () => discoverProjects
|
|
39633
|
+
});
|
|
39634
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync, statSync } from "node:fs";
|
|
39635
|
+
import { join as join6, relative, basename } from "node:path";
|
|
39636
|
+
function discoverProjects(root) {
|
|
39637
|
+
const projects = [];
|
|
39638
|
+
walk(root, root, 0, projects);
|
|
39639
|
+
return projects.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
39640
|
+
}
|
|
39641
|
+
function walk(dir, root, depth, out) {
|
|
39642
|
+
if (depth > MAX_DEPTH) return;
|
|
39643
|
+
for (const lockfile of NPM_LOCKFILES) {
|
|
39644
|
+
const lockPath = join6(dir, lockfile);
|
|
39645
|
+
if (existsSync6(lockPath)) {
|
|
39646
|
+
const count = countNpmPackages(lockPath);
|
|
39647
|
+
if (count > 0) {
|
|
39648
|
+
out.push({
|
|
39649
|
+
path: dir,
|
|
39650
|
+
relativePath: relative(root, dir) || ".",
|
|
39651
|
+
ecosystem: "npm",
|
|
39652
|
+
depFile: lockfile,
|
|
39653
|
+
packageCount: count
|
|
39654
|
+
});
|
|
39655
|
+
}
|
|
39656
|
+
break;
|
|
39657
|
+
}
|
|
39658
|
+
}
|
|
39659
|
+
for (const depFile of PYTHON_DEPFILES) {
|
|
39660
|
+
const depPath = join6(dir, depFile);
|
|
39661
|
+
if (existsSync6(depPath)) {
|
|
39662
|
+
const count = countPythonPackages(depPath, depFile);
|
|
39663
|
+
if (count > 0) {
|
|
39664
|
+
out.push({
|
|
39665
|
+
path: dir,
|
|
39666
|
+
relativePath: relative(root, dir) || ".",
|
|
39667
|
+
ecosystem: "pypi",
|
|
39668
|
+
depFile,
|
|
39669
|
+
packageCount: count
|
|
39670
|
+
});
|
|
39671
|
+
}
|
|
39672
|
+
break;
|
|
39673
|
+
}
|
|
39674
|
+
}
|
|
39675
|
+
let entries;
|
|
39676
|
+
try {
|
|
39677
|
+
entries = readdirSync(dir);
|
|
39678
|
+
} catch {
|
|
39679
|
+
return;
|
|
39680
|
+
}
|
|
39681
|
+
for (const entry of entries) {
|
|
39682
|
+
if (SKIP_DIRS.has(entry) || entry.startsWith(".")) continue;
|
|
39683
|
+
const full = join6(dir, entry);
|
|
39684
|
+
try {
|
|
39685
|
+
if (statSync(full).isDirectory()) {
|
|
39686
|
+
walk(full, root, depth + 1, out);
|
|
39687
|
+
}
|
|
39688
|
+
} catch {
|
|
39689
|
+
}
|
|
39690
|
+
}
|
|
39691
|
+
}
|
|
39692
|
+
function countNpmPackages(lockPath) {
|
|
39693
|
+
try {
|
|
39694
|
+
const name = basename(lockPath);
|
|
39695
|
+
const content = readFileSync7(lockPath, "utf-8");
|
|
39696
|
+
if (name === "yarn.lock") {
|
|
39697
|
+
return (content.match(/^\S.*:$/gm) || []).length;
|
|
39698
|
+
}
|
|
39699
|
+
if (name === "pnpm-lock.yaml") {
|
|
39700
|
+
return (content.match(/^\s{2}'?[/@]/gm) || []).length || estimateLines(content, 200);
|
|
39701
|
+
}
|
|
39702
|
+
const parsed = JSON.parse(content);
|
|
39703
|
+
if (parsed.packages) {
|
|
39704
|
+
return Object.keys(parsed.packages).filter((k) => k !== "").length;
|
|
39705
|
+
}
|
|
39706
|
+
if (parsed.dependencies) {
|
|
39707
|
+
return Object.keys(parsed.dependencies).length;
|
|
39708
|
+
}
|
|
39709
|
+
return 0;
|
|
39710
|
+
} catch {
|
|
39711
|
+
return 0;
|
|
39712
|
+
}
|
|
39713
|
+
}
|
|
39714
|
+
function countPythonPackages(depPath, depFile) {
|
|
39715
|
+
try {
|
|
39716
|
+
const content = readFileSync7(depPath, "utf-8");
|
|
39717
|
+
if (depFile === "Pipfile.lock") {
|
|
39718
|
+
const parsed = JSON.parse(content);
|
|
39719
|
+
const defaultCount = Object.keys(parsed.default || {}).length;
|
|
39720
|
+
const devCount = Object.keys(parsed.develop || {}).length;
|
|
39721
|
+
return defaultCount + devCount;
|
|
39722
|
+
}
|
|
39723
|
+
if (depFile === "poetry.lock") {
|
|
39724
|
+
return (content.match(/^\[\[package\]\]/gm) || []).length;
|
|
39725
|
+
}
|
|
39726
|
+
return content.split("\n").filter(
|
|
39727
|
+
(line) => line.trim() && !line.trim().startsWith("#") && !line.trim().startsWith("-")
|
|
39728
|
+
).length;
|
|
39729
|
+
} catch {
|
|
39730
|
+
return 0;
|
|
39731
|
+
}
|
|
39732
|
+
}
|
|
39733
|
+
function estimateLines(content, fallback) {
|
|
39734
|
+
const lines = content.split("\n").length;
|
|
39735
|
+
return lines > 20 ? Math.floor(lines / 4) : fallback;
|
|
39736
|
+
}
|
|
39737
|
+
var SKIP_DIRS, NPM_LOCKFILES, PYTHON_DEPFILES, MAX_DEPTH;
|
|
39738
|
+
var init_discover = __esm({
|
|
39739
|
+
"src/discover.ts"() {
|
|
39740
|
+
"use strict";
|
|
39741
|
+
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
39742
|
+
"node_modules",
|
|
39743
|
+
".venv",
|
|
39744
|
+
"venv",
|
|
39745
|
+
"__pycache__",
|
|
39746
|
+
".git",
|
|
39747
|
+
".tox",
|
|
39748
|
+
".eggs",
|
|
39749
|
+
"dist",
|
|
39750
|
+
"build",
|
|
39751
|
+
".next",
|
|
39752
|
+
".nuxt",
|
|
39753
|
+
"coverage",
|
|
39754
|
+
".cache",
|
|
39755
|
+
".pytest_cache",
|
|
39756
|
+
".mypy_cache",
|
|
39757
|
+
"validation-results",
|
|
39758
|
+
"test-fixtures",
|
|
39759
|
+
"fixtures"
|
|
39760
|
+
]);
|
|
39761
|
+
NPM_LOCKFILES = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "npm-shrinkwrap.json"];
|
|
39762
|
+
PYTHON_DEPFILES = ["requirements.txt", "Pipfile.lock", "poetry.lock"];
|
|
39763
|
+
MAX_DEPTH = 8;
|
|
39764
|
+
}
|
|
39765
|
+
});
|
|
39766
|
+
|
|
39590
39767
|
// src/static-output.ts
|
|
39591
39768
|
var static_output_exports = {};
|
|
39592
39769
|
__export(static_output_exports, {
|
|
@@ -39617,7 +39794,7 @@ function truncate(s, max) {
|
|
|
39617
39794
|
function groupPackages(packages) {
|
|
39618
39795
|
const map = /* @__PURE__ */ new Map();
|
|
39619
39796
|
for (const pkg of packages) {
|
|
39620
|
-
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.
|
|
39797
|
+
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.category}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
|
|
39621
39798
|
const group = map.get(fingerprint) ?? [];
|
|
39622
39799
|
group.push(pkg);
|
|
39623
39800
|
map.set(fingerprint, group);
|
|
@@ -39699,7 +39876,7 @@ function renderResultStatic(result, config) {
|
|
|
39699
39876
|
const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
|
|
39700
39877
|
const colorFn = severityColor(finding.severity);
|
|
39701
39878
|
lines.push(
|
|
39702
|
-
` ${colorFn(pad(sevLabel, 10))}${finding.
|
|
39879
|
+
` ${colorFn(pad(sevLabel, 10))}${finding.category} \u2014 ${finding.title}`
|
|
39703
39880
|
);
|
|
39704
39881
|
const evidenceLimit = 3;
|
|
39705
39882
|
for (let i = 0; i < Math.min(finding.evidence.length, evidenceLimit); i++) {
|
|
@@ -39750,7 +39927,24 @@ async function runStatic(config) {
|
|
|
39750
39927
|
const discovery = discoverChanges(process.cwd(), config);
|
|
39751
39928
|
dbg(`discovery method: ${discovery.method}`);
|
|
39752
39929
|
if (discovery.packages.length === 0) {
|
|
39753
|
-
|
|
39930
|
+
const { discoverProjects: discoverProjects2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
|
|
39931
|
+
const subProjects = discoverProjects2(process.cwd());
|
|
39932
|
+
if (subProjects.length > 0) {
|
|
39933
|
+
process.stderr.write(import_chalk4.default.yellow(" No packages found in current directory, but found projects in subdirectories:\n\n"));
|
|
39934
|
+
for (const proj of subProjects) {
|
|
39935
|
+
const pkgLabel = proj.packageCount === 1 ? "package" : "packages";
|
|
39936
|
+
process.stderr.write(
|
|
39937
|
+
import_chalk4.default.white(` ${proj.ecosystem} `) + import_chalk4.default.cyan(proj.relativePath) + import_chalk4.default.dim(` (${proj.packageCount} ${pkgLabel})
|
|
39938
|
+
`)
|
|
39939
|
+
);
|
|
39940
|
+
}
|
|
39941
|
+
process.stderr.write(import_chalk4.default.dim("\n Use --workspace to scan a specific project:\n"));
|
|
39942
|
+
process.stderr.write(import_chalk4.default.dim(` dg scan --workspace ${subProjects[0].relativePath}${config.scanAll ? " --scan-all" : ""}
|
|
39943
|
+
|
|
39944
|
+
`));
|
|
39945
|
+
} else {
|
|
39946
|
+
process.stderr.write(import_chalk4.default.dim(" No package changes detected.\n"));
|
|
39947
|
+
}
|
|
39754
39948
|
process.exit(0);
|
|
39755
39949
|
}
|
|
39756
39950
|
const packages = discovery.packages.filter(
|
|
@@ -39789,6 +39983,11 @@ async function runStatic(config) {
|
|
|
39789
39983
|
);
|
|
39790
39984
|
const output = renderResultStatic(result, config);
|
|
39791
39985
|
process.stdout.write(output + "\n");
|
|
39986
|
+
if (!config.apiKey && !config.json) {
|
|
39987
|
+
process.stderr.write(
|
|
39988
|
+
"\n" + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n") + import_chalk4.default.cyan(" Want this on every PR? ") + "Run " + import_chalk4.default.white("dg login") + import_chalk4.default.dim(" \u2192 free tier, 200 scans/month\n") + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n")
|
|
39989
|
+
);
|
|
39990
|
+
}
|
|
39792
39991
|
if (result.action === "block" && config.mode === "block") {
|
|
39793
39992
|
process.exit(2);
|
|
39794
39993
|
} else if (result.action === "block" || result.action === "warn") {
|
|
@@ -39907,6 +40106,11 @@ async function scanAndInstallStatic(resolved, parsed, config) {
|
|
|
39907
40106
|
}
|
|
39908
40107
|
const output = renderResultStatic(result, config);
|
|
39909
40108
|
process.stdout.write(output + "\n");
|
|
40109
|
+
if (!config.apiKey) {
|
|
40110
|
+
process.stderr.write(
|
|
40111
|
+
"\n" + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n") + import_chalk4.default.cyan(" Want this on every PR? ") + "Run " + import_chalk4.default.white("dg login") + import_chalk4.default.dim(" \u2192 free tier, 200 scans/month\n") + import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n")
|
|
40112
|
+
);
|
|
40113
|
+
}
|
|
39910
40114
|
if (result.action === "warn") {
|
|
39911
40115
|
process.stderr.write(
|
|
39912
40116
|
import_chalk4.default.yellow(" Warnings detected. Proceeding with install.\n\n")
|
|
@@ -40037,14 +40241,14 @@ __export(hook_exports, {
|
|
|
40037
40241
|
});
|
|
40038
40242
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
40039
40243
|
import {
|
|
40040
|
-
existsSync as
|
|
40041
|
-
readFileSync as
|
|
40244
|
+
existsSync as existsSync7,
|
|
40245
|
+
readFileSync as readFileSync8,
|
|
40042
40246
|
writeFileSync as writeFileSync3,
|
|
40043
40247
|
mkdirSync,
|
|
40044
40248
|
chmodSync as chmodSync2,
|
|
40045
40249
|
unlinkSync as unlinkSync2
|
|
40046
40250
|
} from "node:fs";
|
|
40047
|
-
import { join as
|
|
40251
|
+
import { join as join7 } from "node:path";
|
|
40048
40252
|
function findGitDir() {
|
|
40049
40253
|
try {
|
|
40050
40254
|
return execFileSync2("git", ["rev-parse", "--git-dir"], {
|
|
@@ -40057,11 +40261,11 @@ function findGitDir() {
|
|
|
40057
40261
|
}
|
|
40058
40262
|
function installHook() {
|
|
40059
40263
|
const gitDir = findGitDir();
|
|
40060
|
-
const hooksDir =
|
|
40061
|
-
const hookPath =
|
|
40264
|
+
const hooksDir = join7(gitDir, "hooks");
|
|
40265
|
+
const hookPath = join7(hooksDir, "pre-commit");
|
|
40062
40266
|
mkdirSync(hooksDir, { recursive: true });
|
|
40063
|
-
if (
|
|
40064
|
-
const existing =
|
|
40267
|
+
if (existsSync7(hookPath)) {
|
|
40268
|
+
const existing = readFileSync8(hookPath, "utf-8");
|
|
40065
40269
|
if (existing.includes(HOOK_MARKER)) {
|
|
40066
40270
|
process.stderr.write(" Hook already installed.\n");
|
|
40067
40271
|
return;
|
|
@@ -40081,12 +40285,12 @@ function installHook() {
|
|
|
40081
40285
|
}
|
|
40082
40286
|
function uninstallHook() {
|
|
40083
40287
|
const gitDir = findGitDir();
|
|
40084
|
-
const hookPath =
|
|
40085
|
-
if (!
|
|
40288
|
+
const hookPath = join7(gitDir, "hooks", "pre-commit");
|
|
40289
|
+
if (!existsSync7(hookPath)) {
|
|
40086
40290
|
process.stderr.write(" No hook to remove.\n");
|
|
40087
40291
|
return;
|
|
40088
40292
|
}
|
|
40089
|
-
const content =
|
|
40293
|
+
const content = readFileSync8(hookPath, "utf-8");
|
|
40090
40294
|
if (!content.includes(HOOK_MARKER)) {
|
|
40091
40295
|
process.stderr.write(
|
|
40092
40296
|
" No Dependency Guardian hook found in pre-commit.\n"
|
|
@@ -40447,7 +40651,7 @@ var init_DurationLine = __esm({
|
|
|
40447
40651
|
function groupPackages2(packages) {
|
|
40448
40652
|
const map = /* @__PURE__ */ new Map();
|
|
40449
40653
|
for (const pkg of packages) {
|
|
40450
|
-
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.
|
|
40654
|
+
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.category}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
|
|
40451
40655
|
const group = map.get(fingerprint) ?? [];
|
|
40452
40656
|
group.push(pkg);
|
|
40453
40657
|
map.set(fingerprint, group);
|
|
@@ -40567,7 +40771,7 @@ var init_ResultsView = __esm({
|
|
|
40567
40771
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
|
|
40568
40772
|
" ",
|
|
40569
40773
|
colorFn(pad2(sevLabel, 10)),
|
|
40570
|
-
finding.
|
|
40774
|
+
finding.category,
|
|
40571
40775
|
" ",
|
|
40572
40776
|
"\u2014",
|
|
40573
40777
|
" ",
|
|
@@ -40582,7 +40786,7 @@ var init_ResultsView = __esm({
|
|
|
40582
40786
|
import_chalk6.default.dim(`... and ${overflow} more`)
|
|
40583
40787
|
] }),
|
|
40584
40788
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " })
|
|
40585
|
-
] }, `${finding.
|
|
40789
|
+
] }, `${finding.category}-${idx}`);
|
|
40586
40790
|
}),
|
|
40587
40791
|
safeVersion && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
|
|
40588
40792
|
" ",
|
|
@@ -40807,140 +41011,6 @@ var init_NpmWrapperApp = __esm({
|
|
|
40807
41011
|
}
|
|
40808
41012
|
});
|
|
40809
41013
|
|
|
40810
|
-
// src/discover.ts
|
|
40811
|
-
import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync, statSync } from "node:fs";
|
|
40812
|
-
import { join as join7, relative, basename } from "node:path";
|
|
40813
|
-
function discoverProjects(root) {
|
|
40814
|
-
const projects = [];
|
|
40815
|
-
walk(root, root, 0, projects);
|
|
40816
|
-
return projects.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
40817
|
-
}
|
|
40818
|
-
function walk(dir, root, depth, out) {
|
|
40819
|
-
if (depth > MAX_DEPTH) return;
|
|
40820
|
-
for (const lockfile of NPM_LOCKFILES) {
|
|
40821
|
-
const lockPath = join7(dir, lockfile);
|
|
40822
|
-
if (existsSync7(lockPath)) {
|
|
40823
|
-
const count = countNpmPackages(lockPath);
|
|
40824
|
-
if (count > 0) {
|
|
40825
|
-
out.push({
|
|
40826
|
-
path: dir,
|
|
40827
|
-
relativePath: relative(root, dir) || ".",
|
|
40828
|
-
ecosystem: "npm",
|
|
40829
|
-
depFile: lockfile,
|
|
40830
|
-
packageCount: count
|
|
40831
|
-
});
|
|
40832
|
-
}
|
|
40833
|
-
break;
|
|
40834
|
-
}
|
|
40835
|
-
}
|
|
40836
|
-
for (const depFile of PYTHON_DEPFILES) {
|
|
40837
|
-
const depPath = join7(dir, depFile);
|
|
40838
|
-
if (existsSync7(depPath)) {
|
|
40839
|
-
const count = countPythonPackages(depPath, depFile);
|
|
40840
|
-
if (count > 0) {
|
|
40841
|
-
out.push({
|
|
40842
|
-
path: dir,
|
|
40843
|
-
relativePath: relative(root, dir) || ".",
|
|
40844
|
-
ecosystem: "pypi",
|
|
40845
|
-
depFile,
|
|
40846
|
-
packageCount: count
|
|
40847
|
-
});
|
|
40848
|
-
}
|
|
40849
|
-
break;
|
|
40850
|
-
}
|
|
40851
|
-
}
|
|
40852
|
-
let entries;
|
|
40853
|
-
try {
|
|
40854
|
-
entries = readdirSync(dir);
|
|
40855
|
-
} catch {
|
|
40856
|
-
return;
|
|
40857
|
-
}
|
|
40858
|
-
for (const entry of entries) {
|
|
40859
|
-
if (SKIP_DIRS.has(entry) || entry.startsWith(".")) continue;
|
|
40860
|
-
const full = join7(dir, entry);
|
|
40861
|
-
try {
|
|
40862
|
-
if (statSync(full).isDirectory()) {
|
|
40863
|
-
walk(full, root, depth + 1, out);
|
|
40864
|
-
}
|
|
40865
|
-
} catch {
|
|
40866
|
-
}
|
|
40867
|
-
}
|
|
40868
|
-
}
|
|
40869
|
-
function countNpmPackages(lockPath) {
|
|
40870
|
-
try {
|
|
40871
|
-
const name = basename(lockPath);
|
|
40872
|
-
const content = readFileSync8(lockPath, "utf-8");
|
|
40873
|
-
if (name === "yarn.lock") {
|
|
40874
|
-
return (content.match(/^\S.*:$/gm) || []).length;
|
|
40875
|
-
}
|
|
40876
|
-
if (name === "pnpm-lock.yaml") {
|
|
40877
|
-
return (content.match(/^\s{2}'?[/@]/gm) || []).length || estimateLines(content, 200);
|
|
40878
|
-
}
|
|
40879
|
-
const parsed = JSON.parse(content);
|
|
40880
|
-
if (parsed.packages) {
|
|
40881
|
-
return Object.keys(parsed.packages).filter((k) => k !== "").length;
|
|
40882
|
-
}
|
|
40883
|
-
if (parsed.dependencies) {
|
|
40884
|
-
return Object.keys(parsed.dependencies).length;
|
|
40885
|
-
}
|
|
40886
|
-
return 0;
|
|
40887
|
-
} catch {
|
|
40888
|
-
return 0;
|
|
40889
|
-
}
|
|
40890
|
-
}
|
|
40891
|
-
function countPythonPackages(depPath, depFile) {
|
|
40892
|
-
try {
|
|
40893
|
-
const content = readFileSync8(depPath, "utf-8");
|
|
40894
|
-
if (depFile === "Pipfile.lock") {
|
|
40895
|
-
const parsed = JSON.parse(content);
|
|
40896
|
-
const defaultCount = Object.keys(parsed.default || {}).length;
|
|
40897
|
-
const devCount = Object.keys(parsed.develop || {}).length;
|
|
40898
|
-
return defaultCount + devCount;
|
|
40899
|
-
}
|
|
40900
|
-
if (depFile === "poetry.lock") {
|
|
40901
|
-
return (content.match(/^\[\[package\]\]/gm) || []).length;
|
|
40902
|
-
}
|
|
40903
|
-
return content.split("\n").filter(
|
|
40904
|
-
(line) => line.trim() && !line.trim().startsWith("#") && !line.trim().startsWith("-")
|
|
40905
|
-
).length;
|
|
40906
|
-
} catch {
|
|
40907
|
-
return 0;
|
|
40908
|
-
}
|
|
40909
|
-
}
|
|
40910
|
-
function estimateLines(content, fallback) {
|
|
40911
|
-
const lines = content.split("\n").length;
|
|
40912
|
-
return lines > 20 ? Math.floor(lines / 4) : fallback;
|
|
40913
|
-
}
|
|
40914
|
-
var SKIP_DIRS, NPM_LOCKFILES, PYTHON_DEPFILES, MAX_DEPTH;
|
|
40915
|
-
var init_discover = __esm({
|
|
40916
|
-
"src/discover.ts"() {
|
|
40917
|
-
"use strict";
|
|
40918
|
-
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
40919
|
-
"node_modules",
|
|
40920
|
-
".venv",
|
|
40921
|
-
"venv",
|
|
40922
|
-
"__pycache__",
|
|
40923
|
-
".git",
|
|
40924
|
-
".tox",
|
|
40925
|
-
".eggs",
|
|
40926
|
-
"dist",
|
|
40927
|
-
"build",
|
|
40928
|
-
".next",
|
|
40929
|
-
".nuxt",
|
|
40930
|
-
"coverage",
|
|
40931
|
-
".cache",
|
|
40932
|
-
".pytest_cache",
|
|
40933
|
-
".mypy_cache",
|
|
40934
|
-
"validation-results",
|
|
40935
|
-
"test-fixtures",
|
|
40936
|
-
"fixtures"
|
|
40937
|
-
]);
|
|
40938
|
-
NPM_LOCKFILES = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "npm-shrinkwrap.json"];
|
|
40939
|
-
PYTHON_DEPFILES = ["requirements.txt", "Pipfile.lock", "poetry.lock"];
|
|
40940
|
-
MAX_DEPTH = 8;
|
|
40941
|
-
}
|
|
40942
|
-
});
|
|
40943
|
-
|
|
40944
41014
|
// src/ui/hooks/useScan.ts
|
|
40945
41015
|
function reducer3(_state, action) {
|
|
40946
41016
|
switch (action.type) {
|
|
@@ -40969,6 +41039,10 @@ function useScan(config) {
|
|
|
40969
41039
|
started.current = true;
|
|
40970
41040
|
const projects = discoverProjects(process.cwd());
|
|
40971
41041
|
if (projects.length > 1) setMultiProjects(projects);
|
|
41042
|
+
if (projects.length > 1) {
|
|
41043
|
+
dispatch({ type: "PROJECTS_FOUND", projects });
|
|
41044
|
+
return;
|
|
41045
|
+
}
|
|
40972
41046
|
try {
|
|
40973
41047
|
const discovery = discoverChanges(process.cwd(), config);
|
|
40974
41048
|
const packages = discovery.packages.filter((p) => !config.allowlist.includes(p.name));
|
|
@@ -40984,12 +41058,8 @@ function useScan(config) {
|
|
|
40984
41058
|
dispatch({ type: "DISCOVERY_EMPTY", message: "No dependency files found." });
|
|
40985
41059
|
return;
|
|
40986
41060
|
}
|
|
40987
|
-
|
|
40988
|
-
|
|
40989
|
-
scanProjects(projects, config, dispatch);
|
|
40990
|
-
return;
|
|
40991
|
-
}
|
|
40992
|
-
dispatch({ type: "PROJECTS_FOUND", projects });
|
|
41061
|
+
dispatch({ type: "DISCOVERY_COMPLETE", packages: [], skippedCount: 0 });
|
|
41062
|
+
scanProjects(projects, config, dispatch);
|
|
40993
41063
|
}
|
|
40994
41064
|
}, [config]);
|
|
40995
41065
|
const scanSelectedProjects = (0, import_react27.useCallback)((projects) => {
|
|
@@ -41180,11 +41250,45 @@ var init_useExpandAnimation = __esm({
|
|
|
41180
41250
|
}
|
|
41181
41251
|
});
|
|
41182
41252
|
|
|
41253
|
+
// src/ui/hooks/useTerminalSize.ts
|
|
41254
|
+
function useTerminalSize() {
|
|
41255
|
+
const { stdout } = use_stdout_default();
|
|
41256
|
+
const [size, setSize] = (0, import_react29.useState)({
|
|
41257
|
+
rows: stdout?.rows ?? process.stdout.rows ?? 24,
|
|
41258
|
+
cols: stdout?.columns ?? process.stdout.columns ?? 80
|
|
41259
|
+
});
|
|
41260
|
+
(0, import_react29.useEffect)(() => {
|
|
41261
|
+
const handle = () => {
|
|
41262
|
+
const rows = process.stdout.rows ?? 24;
|
|
41263
|
+
const cols = process.stdout.columns ?? 80;
|
|
41264
|
+
if (process.stdout.isTTY) {
|
|
41265
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
41266
|
+
}
|
|
41267
|
+
setSize({ rows, cols });
|
|
41268
|
+
};
|
|
41269
|
+
process.stdout.setMaxListeners(process.stdout.getMaxListeners() + 1);
|
|
41270
|
+
process.stdout.on("resize", handle);
|
|
41271
|
+
return () => {
|
|
41272
|
+
process.stdout.off("resize", handle);
|
|
41273
|
+
process.stdout.setMaxListeners(Math.max(0, process.stdout.getMaxListeners() - 1));
|
|
41274
|
+
};
|
|
41275
|
+
}, []);
|
|
41276
|
+
return size;
|
|
41277
|
+
}
|
|
41278
|
+
var import_react29;
|
|
41279
|
+
var init_useTerminalSize = __esm({
|
|
41280
|
+
async "src/ui/hooks/useTerminalSize.ts"() {
|
|
41281
|
+
"use strict";
|
|
41282
|
+
import_react29 = __toESM(require_react());
|
|
41283
|
+
await init_build2();
|
|
41284
|
+
}
|
|
41285
|
+
});
|
|
41286
|
+
|
|
41183
41287
|
// src/ui/components/InteractiveResultsView.tsx
|
|
41184
41288
|
function groupPackages3(packages) {
|
|
41185
41289
|
const map = /* @__PURE__ */ new Map();
|
|
41186
41290
|
for (const pkg of packages) {
|
|
41187
|
-
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.
|
|
41291
|
+
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.category}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
|
|
41188
41292
|
const group = map.get(fingerprint) ?? [];
|
|
41189
41293
|
group.push(pkg);
|
|
41190
41294
|
map.set(fingerprint, group);
|
|
@@ -41250,15 +41354,16 @@ function viewReducer(_state, action) {
|
|
|
41250
41354
|
return { cursor: action.cursor, expandedIndex: action.expandedIndex, expandLevel: action.expandLevel, viewport: action.viewport };
|
|
41251
41355
|
}
|
|
41252
41356
|
}
|
|
41253
|
-
var
|
|
41357
|
+
var import_react30, import_chalk10, import_jsx_runtime10, SEVERITY_LABELS3, SEVERITY_COLORS, EVIDENCE_LIMIT2, FIXED_CHROME, InteractiveResultsView, T, FindingsSummary, FindingsDetail;
|
|
41254
41358
|
var init_InteractiveResultsView = __esm({
|
|
41255
41359
|
async "src/ui/components/InteractiveResultsView.tsx"() {
|
|
41256
41360
|
"use strict";
|
|
41257
|
-
|
|
41361
|
+
import_react30 = __toESM(require_react());
|
|
41258
41362
|
await init_build2();
|
|
41259
41363
|
import_chalk10 = __toESM(require_source());
|
|
41260
41364
|
await init_ScoreHeader();
|
|
41261
41365
|
init_useExpandAnimation();
|
|
41366
|
+
await init_useTerminalSize();
|
|
41262
41367
|
import_jsx_runtime10 = __toESM(require_jsx_runtime());
|
|
41263
41368
|
SEVERITY_LABELS3 = {
|
|
41264
41369
|
5: "CRIT",
|
|
@@ -41283,40 +41388,32 @@ var init_InteractiveResultsView = __esm({
|
|
|
41283
41388
|
onExit,
|
|
41284
41389
|
onBack
|
|
41285
41390
|
}) => {
|
|
41286
|
-
(0,
|
|
41287
|
-
if (!process.stdout.isTTY) return;
|
|
41288
|
-
process.stdout.write("\x1B[?1049h");
|
|
41289
|
-
return () => {
|
|
41290
|
-
process.stdout.write("\x1B[?1049l");
|
|
41291
|
-
};
|
|
41292
|
-
}, []);
|
|
41293
|
-
const flagged = (0, import_react29.useMemo)(
|
|
41391
|
+
const flagged = (0, import_react30.useMemo)(
|
|
41294
41392
|
() => result.packages.filter((p) => p.score > 0),
|
|
41295
41393
|
[result.packages]
|
|
41296
41394
|
);
|
|
41297
|
-
const clean = (0,
|
|
41395
|
+
const clean = (0, import_react30.useMemo)(
|
|
41298
41396
|
() => result.packages.filter((p) => p.score === 0),
|
|
41299
41397
|
[result.packages]
|
|
41300
41398
|
);
|
|
41301
41399
|
const total = result.packages.length;
|
|
41302
|
-
const groups = (0,
|
|
41303
|
-
const [view, dispatchView] = (0,
|
|
41400
|
+
const groups = (0, import_react30.useMemo)(() => groupPackages3(flagged), [flagged]);
|
|
41401
|
+
const [view, dispatchView] = (0, import_react30.useReducer)(viewReducer, {
|
|
41304
41402
|
cursor: 0,
|
|
41305
41403
|
expandLevel: null,
|
|
41306
41404
|
expandedIndex: null,
|
|
41307
41405
|
viewport: 0
|
|
41308
41406
|
});
|
|
41309
|
-
const viewRef = (0,
|
|
41407
|
+
const viewRef = (0, import_react30.useRef)(view);
|
|
41310
41408
|
viewRef.current = view;
|
|
41311
|
-
const {
|
|
41312
|
-
const
|
|
41313
|
-
const
|
|
41314
|
-
const availableRows = Math.max(5, termRows - FIXED_CHROME);
|
|
41409
|
+
const { rows: termRows, cols: termCols } = useTerminalSize();
|
|
41410
|
+
const chromeHeight = FIXED_CHROME + (config.apiKey ? 0 : 2);
|
|
41411
|
+
const availableRows = Math.max(5, termRows - chromeHeight);
|
|
41315
41412
|
const innerWidth = Math.max(40, termCols - 6);
|
|
41316
41413
|
const getLevel = (idx) => {
|
|
41317
41414
|
return view.expandedIndex === idx ? view.expandLevel : null;
|
|
41318
41415
|
};
|
|
41319
|
-
const expandTargetHeight = (0,
|
|
41416
|
+
const expandTargetHeight = (0, import_react30.useMemo)(() => {
|
|
41320
41417
|
if (view.expandedIndex === null || view.expandLevel === null) return 0;
|
|
41321
41418
|
const group = groups[view.expandedIndex];
|
|
41322
41419
|
if (!group) return 0;
|
|
@@ -41332,7 +41429,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41332
41429
|
if (idx === view.expandedIndex) return 1 + animVisibleLines;
|
|
41333
41430
|
return groupRowHeight(group, level, result.safeVersions);
|
|
41334
41431
|
};
|
|
41335
|
-
const visibleEnd = (0,
|
|
41432
|
+
const visibleEnd = (0, import_react30.useMemo)(() => {
|
|
41336
41433
|
let consumed = 0;
|
|
41337
41434
|
let end = view.viewport;
|
|
41338
41435
|
while (end < groups.length) {
|
|
@@ -41364,6 +41461,13 @@ var init_InteractiveResultsView = __esm({
|
|
|
41364
41461
|
}
|
|
41365
41462
|
return newStart;
|
|
41366
41463
|
};
|
|
41464
|
+
(0, import_react30.useEffect)(() => {
|
|
41465
|
+
if (groups.length === 0) return;
|
|
41466
|
+
const { cursor, expandedIndex, expandLevel, viewport } = viewRef.current;
|
|
41467
|
+
const clamped = Math.min(viewport, Math.max(0, groups.length - 1));
|
|
41468
|
+
const newVp = adjustViewport(cursor, expandedIndex, expandLevel, clamped);
|
|
41469
|
+
dispatchView({ type: "MOVE", cursor, viewport: newVp });
|
|
41470
|
+
}, [availableRows]);
|
|
41367
41471
|
use_input_default((input, key) => {
|
|
41368
41472
|
if (groups.length === 0) {
|
|
41369
41473
|
if (input === "q" || key.return) onExit();
|
|
@@ -41391,13 +41495,15 @@ var init_InteractiveResultsView = __esm({
|
|
|
41391
41495
|
const newVp = adjustViewport(cursor, newExpIdx, newExpLvl, vpStart);
|
|
41392
41496
|
dispatchView({ type: "EXPAND", expandedIndex: newExpIdx, expandLevel: newExpLvl, viewport: newVp });
|
|
41393
41497
|
} else if (input === "e") {
|
|
41394
|
-
if (expIdx === cursor && expLvl === "detail")
|
|
41395
|
-
|
|
41396
|
-
|
|
41397
|
-
|
|
41398
|
-
if (expIdx !== null && expLvl !== null) {
|
|
41498
|
+
if (expIdx === cursor && expLvl === "detail") {
|
|
41499
|
+
const newVp = adjustViewport(cursor, cursor, "summary", vpStart);
|
|
41500
|
+
dispatchView({ type: "EXPAND", expandedIndex: cursor, expandLevel: "summary", viewport: newVp });
|
|
41501
|
+
} else if (expIdx === cursor && expLvl === "summary") {
|
|
41399
41502
|
const newVp = adjustViewport(cursor, null, null, vpStart);
|
|
41400
41503
|
dispatchView({ type: "EXPAND", expandedIndex: null, expandLevel: null, viewport: newVp });
|
|
41504
|
+
} else {
|
|
41505
|
+
const newVp = adjustViewport(cursor, cursor, "detail", vpStart);
|
|
41506
|
+
dispatchView({ type: "EXPAND", expandedIndex: cursor, expandLevel: "detail", viewport: newVp });
|
|
41401
41507
|
}
|
|
41402
41508
|
} else if (input === "b" && onBack) {
|
|
41403
41509
|
onBack();
|
|
@@ -41514,6 +41620,12 @@ var init_InteractiveResultsView = __esm({
|
|
|
41514
41620
|
]
|
|
41515
41621
|
}
|
|
41516
41622
|
),
|
|
41623
|
+
!config.apiKey && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { paddingLeft: 1, paddingRight: 1, width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { children: [
|
|
41624
|
+
import_chalk10.default.cyan("Want this on every PR?"),
|
|
41625
|
+
" Run ",
|
|
41626
|
+
import_chalk10.default.white("dg login"),
|
|
41627
|
+
import_chalk10.default.dim(" \u2192 free tier, 200 scans/month")
|
|
41628
|
+
] }) }),
|
|
41517
41629
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
|
|
41518
41630
|
" ",
|
|
41519
41631
|
groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
@@ -41524,10 +41636,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41524
41636
|
" toggle",
|
|
41525
41637
|
" ",
|
|
41526
41638
|
import_chalk10.default.cyan("e"),
|
|
41527
|
-
" expand",
|
|
41528
|
-
" ",
|
|
41529
|
-
import_chalk10.default.cyan("c"),
|
|
41530
|
-
" collapse",
|
|
41639
|
+
" expand/collapse",
|
|
41531
41640
|
" ",
|
|
41532
41641
|
onBack && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
41533
41642
|
import_chalk10.default.cyan("b"),
|
|
@@ -41569,8 +41678,8 @@ var init_InteractiveResultsView = __esm({
|
|
|
41569
41678
|
" ",
|
|
41570
41679
|
sevColor(pad3(sevLabel, 4)),
|
|
41571
41680
|
" ",
|
|
41572
|
-
import_chalk10.default.dim(f.
|
|
41573
|
-
] }, `${f.
|
|
41681
|
+
import_chalk10.default.dim(f.category)
|
|
41682
|
+
] }, `${f.category}-${idx}`)
|
|
41574
41683
|
);
|
|
41575
41684
|
}
|
|
41576
41685
|
if (hasAffects) {
|
|
@@ -41608,15 +41717,15 @@ var init_InteractiveResultsView = __esm({
|
|
|
41608
41717
|
" ",
|
|
41609
41718
|
sevColor(pad3(sevLabel, 4)),
|
|
41610
41719
|
" ",
|
|
41611
|
-
import_chalk10.default.bold(finding.
|
|
41612
|
-
] }, `${finding.
|
|
41720
|
+
import_chalk10.default.bold(finding.category)
|
|
41721
|
+
] }, `${finding.category}-${idx}-badge`)
|
|
41613
41722
|
);
|
|
41614
41723
|
allLines.push(
|
|
41615
41724
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
|
|
41616
41725
|
continuation,
|
|
41617
41726
|
" ",
|
|
41618
41727
|
finding.title
|
|
41619
|
-
] }, `${finding.
|
|
41728
|
+
] }, `${finding.category}-${idx}-title`)
|
|
41620
41729
|
);
|
|
41621
41730
|
for (let i = 0; i < evidenceSlice.length; i++) {
|
|
41622
41731
|
allLines.push(
|
|
@@ -41626,7 +41735,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41626
41735
|
import_chalk10.default.dim("\u203A"),
|
|
41627
41736
|
" ",
|
|
41628
41737
|
truncate3(evidenceSlice[i], evidenceWidth)
|
|
41629
|
-
] }, `${finding.
|
|
41738
|
+
] }, `${finding.category}-${idx}-ev-${i}`)
|
|
41630
41739
|
);
|
|
41631
41740
|
}
|
|
41632
41741
|
if (overflow > 0) {
|
|
@@ -41635,7 +41744,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41635
41744
|
continuation,
|
|
41636
41745
|
" ",
|
|
41637
41746
|
import_chalk10.default.dim(`+${overflow} more`)
|
|
41638
|
-
] }, `${finding.
|
|
41747
|
+
] }, `${finding.category}-${idx}-overflow`)
|
|
41639
41748
|
);
|
|
41640
41749
|
}
|
|
41641
41750
|
}
|
|
@@ -41664,19 +41773,16 @@ var init_InteractiveResultsView = __esm({
|
|
|
41664
41773
|
});
|
|
41665
41774
|
|
|
41666
41775
|
// src/ui/components/ProjectSelector.tsx
|
|
41667
|
-
var
|
|
41776
|
+
var import_react31, import_jsx_runtime11, ProjectSelector;
|
|
41668
41777
|
var init_ProjectSelector = __esm({
|
|
41669
41778
|
async "src/ui/components/ProjectSelector.tsx"() {
|
|
41670
41779
|
"use strict";
|
|
41671
|
-
|
|
41780
|
+
import_react31 = __toESM(require_react());
|
|
41672
41781
|
await init_build2();
|
|
41673
41782
|
import_jsx_runtime11 = __toESM(require_jsx_runtime());
|
|
41674
41783
|
ProjectSelector = ({ projects, onConfirm, onCancel }) => {
|
|
41675
|
-
const [cursor, setCursor] = (0,
|
|
41676
|
-
(0,
|
|
41677
|
-
process.stdout.write("\x1B[2J\x1B[H");
|
|
41678
|
-
}, []);
|
|
41679
|
-
const [selected, setSelected] = (0, import_react30.useState)(() => new Set(projects.map((_, i) => i)));
|
|
41784
|
+
const [cursor, setCursor] = (0, import_react31.useState)(0);
|
|
41785
|
+
const [selected, setSelected] = (0, import_react31.useState)(() => new Set(projects.map((_, i) => i)));
|
|
41680
41786
|
use_input_default((input, key) => {
|
|
41681
41787
|
if (key.upArrow) {
|
|
41682
41788
|
setCursor((c) => Math.max(0, c - 1));
|
|
@@ -41730,11 +41836,11 @@ var App_exports = {};
|
|
|
41730
41836
|
__export(App_exports, {
|
|
41731
41837
|
App: () => App2
|
|
41732
41838
|
});
|
|
41733
|
-
var
|
|
41839
|
+
var import_react32, import_jsx_runtime12, App2;
|
|
41734
41840
|
var init_App2 = __esm({
|
|
41735
41841
|
async "src/ui/App.tsx"() {
|
|
41736
41842
|
"use strict";
|
|
41737
|
-
|
|
41843
|
+
import_react32 = __toESM(require_react());
|
|
41738
41844
|
await init_build2();
|
|
41739
41845
|
init_useScan();
|
|
41740
41846
|
await init_Spinner();
|
|
@@ -41742,11 +41848,29 @@ var init_App2 = __esm({
|
|
|
41742
41848
|
await init_InteractiveResultsView();
|
|
41743
41849
|
await init_ErrorView();
|
|
41744
41850
|
await init_ProjectSelector();
|
|
41851
|
+
await init_useTerminalSize();
|
|
41745
41852
|
import_jsx_runtime12 = __toESM(require_jsx_runtime());
|
|
41746
41853
|
App2 = ({ config }) => {
|
|
41747
41854
|
const { state, scanSelectedProjects, restartSelection } = useScan(config);
|
|
41748
41855
|
const { exit } = use_app_default();
|
|
41749
|
-
const
|
|
41856
|
+
const { rows: termRows } = useTerminalSize();
|
|
41857
|
+
const prevPhaseRef = (0, import_react32.useRef)(state.phase);
|
|
41858
|
+
(0, import_react32.useEffect)(() => {
|
|
41859
|
+
if (!process.stdout.isTTY) return;
|
|
41860
|
+
process.stdout.write("\x1B[?1049h");
|
|
41861
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
41862
|
+
return () => {
|
|
41863
|
+
process.stdout.write("\x1B[?1049l");
|
|
41864
|
+
process.stdout.write("\x1B[?25h");
|
|
41865
|
+
};
|
|
41866
|
+
}, []);
|
|
41867
|
+
(0, import_react32.useEffect)(() => {
|
|
41868
|
+
if (prevPhaseRef.current !== state.phase && process.stdout.isTTY) {
|
|
41869
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
41870
|
+
}
|
|
41871
|
+
prevPhaseRef.current = state.phase;
|
|
41872
|
+
}, [state.phase]);
|
|
41873
|
+
const handleResultsExit = (0, import_react32.useCallback)(() => {
|
|
41750
41874
|
if (state.phase === "results") {
|
|
41751
41875
|
const { result } = state;
|
|
41752
41876
|
if (result.action === "block" && config.mode === "block") {
|
|
@@ -41759,7 +41883,7 @@ var init_App2 = __esm({
|
|
|
41759
41883
|
}
|
|
41760
41884
|
exit();
|
|
41761
41885
|
}, [state, config, exit]);
|
|
41762
|
-
(0,
|
|
41886
|
+
(0, import_react32.useEffect)(() => {
|
|
41763
41887
|
if (state.phase === "empty") {
|
|
41764
41888
|
process.exitCode = 0;
|
|
41765
41889
|
const timer = setTimeout(() => exit(), 0);
|
|
@@ -41771,55 +41895,58 @@ var init_App2 = __esm({
|
|
|
41771
41895
|
return () => clearTimeout(timer);
|
|
41772
41896
|
}
|
|
41773
41897
|
}, [state, config, exit]);
|
|
41774
|
-
|
|
41775
|
-
|
|
41776
|
-
|
|
41777
|
-
|
|
41778
|
-
|
|
41779
|
-
|
|
41780
|
-
|
|
41781
|
-
|
|
41782
|
-
|
|
41783
|
-
|
|
41784
|
-
|
|
41785
|
-
|
|
41898
|
+
const content = (() => {
|
|
41899
|
+
switch (state.phase) {
|
|
41900
|
+
case "discovering":
|
|
41901
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner2, { label: "Searching for dependencies..." });
|
|
41902
|
+
case "selecting":
|
|
41903
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
41904
|
+
ProjectSelector,
|
|
41905
|
+
{
|
|
41906
|
+
projects: state.projects,
|
|
41907
|
+
onConfirm: scanSelectedProjects,
|
|
41908
|
+
onCancel: () => {
|
|
41909
|
+
process.exitCode = 0;
|
|
41910
|
+
exit();
|
|
41911
|
+
}
|
|
41786
41912
|
}
|
|
41787
|
-
|
|
41788
|
-
|
|
41789
|
-
|
|
41790
|
-
|
|
41791
|
-
|
|
41792
|
-
|
|
41913
|
+
);
|
|
41914
|
+
case "scanning":
|
|
41915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
41916
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
41917
|
+
ProgressBar,
|
|
41918
|
+
{
|
|
41919
|
+
value: state.done,
|
|
41920
|
+
total: state.total,
|
|
41921
|
+
label: state.currentBatch.length > 0 ? state.currentBatch[state.currentBatch.length - 1] : void 0
|
|
41922
|
+
}
|
|
41923
|
+
),
|
|
41924
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { dimColor: true, children: [
|
|
41925
|
+
"Scanning ",
|
|
41926
|
+
state.done,
|
|
41927
|
+
"/",
|
|
41928
|
+
state.total,
|
|
41929
|
+
" packages..."
|
|
41930
|
+
] })
|
|
41931
|
+
] });
|
|
41932
|
+
case "results":
|
|
41933
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
41934
|
+
InteractiveResultsView,
|
|
41793
41935
|
{
|
|
41794
|
-
|
|
41795
|
-
|
|
41796
|
-
|
|
41936
|
+
result: state.result,
|
|
41937
|
+
config,
|
|
41938
|
+
durationMs: state.durationMs,
|
|
41939
|
+
onExit: handleResultsExit,
|
|
41940
|
+
onBack: restartSelection ?? void 0
|
|
41797
41941
|
}
|
|
41798
|
-
)
|
|
41799
|
-
|
|
41800
|
-
|
|
41801
|
-
|
|
41802
|
-
|
|
41803
|
-
|
|
41804
|
-
|
|
41805
|
-
|
|
41806
|
-
] });
|
|
41807
|
-
case "results":
|
|
41808
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
41809
|
-
InteractiveResultsView,
|
|
41810
|
-
{
|
|
41811
|
-
result: state.result,
|
|
41812
|
-
config,
|
|
41813
|
-
durationMs: state.durationMs,
|
|
41814
|
-
onExit: handleResultsExit,
|
|
41815
|
-
onBack: restartSelection ?? void 0
|
|
41816
|
-
}
|
|
41817
|
-
);
|
|
41818
|
-
case "empty":
|
|
41819
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: state.message });
|
|
41820
|
-
case "error":
|
|
41821
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorView, { error: state.error });
|
|
41822
|
-
}
|
|
41942
|
+
);
|
|
41943
|
+
case "empty":
|
|
41944
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: state.message });
|
|
41945
|
+
case "error":
|
|
41946
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorView, { error: state.error });
|
|
41947
|
+
}
|
|
41948
|
+
})();
|
|
41949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", height: termRows, children: content });
|
|
41823
41950
|
};
|
|
41824
41951
|
}
|
|
41825
41952
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@westbayberry/dg",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Supply chain security scanner
|
|
3
|
+
"version": "1.0.16",
|
|
4
|
+
"description": "Supply chain security scanner for npm and Python dependencies — detects malicious packages, typosquatting, dependency confusion, and 26+ attack patterns",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dependency-guardian": "dist/index.mjs",
|
|
7
7
|
"dg": "dist/index.mjs"
|
|
@@ -12,13 +12,21 @@
|
|
|
12
12
|
"license": "UNLICENSED",
|
|
13
13
|
"author": "WestBayBerry",
|
|
14
14
|
"homepage": "https://westbayberry.com",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/WestBayBerry/dependency-guardian-action.git"
|
|
18
|
+
},
|
|
15
19
|
"keywords": [
|
|
16
20
|
"security",
|
|
17
21
|
"npm",
|
|
22
|
+
"pypi",
|
|
18
23
|
"supply-chain",
|
|
19
24
|
"scanner",
|
|
20
25
|
"cli",
|
|
21
|
-
"dependencies"
|
|
26
|
+
"dependencies",
|
|
27
|
+
"malware",
|
|
28
|
+
"typosquatting",
|
|
29
|
+
"dependency-confusion"
|
|
22
30
|
],
|
|
23
31
|
"publishConfig": {
|
|
24
32
|
"access": "public"
|