@westbayberry/dg 1.0.16 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +320 -262
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -39,6 +39,149 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
mod
|
|
40
40
|
));
|
|
41
41
|
|
|
42
|
+
// src/auth.ts
|
|
43
|
+
var auth_exports = {};
|
|
44
|
+
__export(auth_exports, {
|
|
45
|
+
clearCredentials: () => clearCredentials,
|
|
46
|
+
createAuthSession: () => createAuthSession,
|
|
47
|
+
getOrCreateDeviceId: () => getOrCreateDeviceId,
|
|
48
|
+
getStoredApiKey: () => getStoredApiKey,
|
|
49
|
+
openBrowser: () => openBrowser,
|
|
50
|
+
pollAuthSession: () => pollAuthSession,
|
|
51
|
+
saveCredentials: () => saveCredentials
|
|
52
|
+
});
|
|
53
|
+
import { readFileSync, writeFileSync, chmodSync, unlinkSync, existsSync } from "node:fs";
|
|
54
|
+
import { join } from "node:path";
|
|
55
|
+
import { homedir } from "node:os";
|
|
56
|
+
import { exec } from "node:child_process";
|
|
57
|
+
import { randomUUID } from "node:crypto";
|
|
58
|
+
async function createAuthSession() {
|
|
59
|
+
let res;
|
|
60
|
+
try {
|
|
61
|
+
res = await globalThis.fetch(`${WEB_BASE}/cli/auth/sessions`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" }
|
|
64
|
+
});
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Error("Could not connect to westbayberry.com");
|
|
67
|
+
}
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
const body = await res.text().catch(() => "");
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Failed to create auth session (HTTP ${res.status})${body ? `: ${body}` : ""}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const json = await res.json();
|
|
75
|
+
return {
|
|
76
|
+
sessionId: json.session_id,
|
|
77
|
+
verifyUrl: json.verify_url,
|
|
78
|
+
expiresIn: json.expires_in
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function pollAuthSession(sessionId) {
|
|
82
|
+
let res;
|
|
83
|
+
try {
|
|
84
|
+
res = await globalThis.fetch(
|
|
85
|
+
`${WEB_BASE}/cli/auth/sessions/${sessionId}/token`
|
|
86
|
+
);
|
|
87
|
+
} catch {
|
|
88
|
+
return { status: "expired" };
|
|
89
|
+
}
|
|
90
|
+
if (res.status === 404) {
|
|
91
|
+
return { status: "expired" };
|
|
92
|
+
}
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
return { status: "expired" };
|
|
95
|
+
}
|
|
96
|
+
const json = await res.json();
|
|
97
|
+
return {
|
|
98
|
+
status: json.status,
|
|
99
|
+
apiKey: json.api_key,
|
|
100
|
+
email: json.email
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function configPath() {
|
|
104
|
+
return join(homedir(), CONFIG_FILE);
|
|
105
|
+
}
|
|
106
|
+
function readConfig() {
|
|
107
|
+
try {
|
|
108
|
+
const raw = readFileSync(configPath(), "utf-8");
|
|
109
|
+
const parsed = JSON.parse(raw);
|
|
110
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
111
|
+
return parsed;
|
|
112
|
+
}
|
|
113
|
+
return {};
|
|
114
|
+
} catch {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function saveCredentials(apiKey) {
|
|
119
|
+
const data = readConfig();
|
|
120
|
+
data.apiKey = apiKey;
|
|
121
|
+
const p = configPath();
|
|
122
|
+
writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
123
|
+
chmodSync(p, 384);
|
|
124
|
+
}
|
|
125
|
+
function clearCredentials() {
|
|
126
|
+
const p = configPath();
|
|
127
|
+
if (!existsSync(p)) return;
|
|
128
|
+
const data = readConfig();
|
|
129
|
+
delete data.apiKey;
|
|
130
|
+
if (Object.keys(data).length === 0) {
|
|
131
|
+
unlinkSync(p);
|
|
132
|
+
} else {
|
|
133
|
+
writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
134
|
+
chmodSync(p, 384);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function getStoredApiKey() {
|
|
138
|
+
const data = readConfig();
|
|
139
|
+
if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
|
|
140
|
+
return data.apiKey;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
function getOrCreateDeviceId() {
|
|
145
|
+
const data = readConfig();
|
|
146
|
+
if (typeof data.deviceId === "string" && data.deviceId.length > 0) {
|
|
147
|
+
return data.deviceId;
|
|
148
|
+
}
|
|
149
|
+
const deviceId = randomUUID();
|
|
150
|
+
data.deviceId = deviceId;
|
|
151
|
+
const p = configPath();
|
|
152
|
+
writeFileSync(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
153
|
+
chmodSync(p, 384);
|
|
154
|
+
return deviceId;
|
|
155
|
+
}
|
|
156
|
+
function openBrowser(url) {
|
|
157
|
+
if (!/^https?:\/\//i.test(url)) return;
|
|
158
|
+
const escaped = url.replace(/"/g, '\\"');
|
|
159
|
+
let cmd;
|
|
160
|
+
switch (process.platform) {
|
|
161
|
+
case "darwin":
|
|
162
|
+
cmd = `open "${escaped}"`;
|
|
163
|
+
break;
|
|
164
|
+
case "linux":
|
|
165
|
+
cmd = `xdg-open "${escaped}"`;
|
|
166
|
+
break;
|
|
167
|
+
case "win32":
|
|
168
|
+
cmd = `start "" "${escaped}"`;
|
|
169
|
+
break;
|
|
170
|
+
default:
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
exec(cmd, () => {
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
var WEB_BASE, CONFIG_FILE;
|
|
177
|
+
var init_auth = __esm({
|
|
178
|
+
"src/auth.ts"() {
|
|
179
|
+
"use strict";
|
|
180
|
+
WEB_BASE = "https://westbayberry.com";
|
|
181
|
+
CONFIG_FILE = ".dgrc.json";
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
42
185
|
// src/config.ts
|
|
43
186
|
var config_exports = {};
|
|
44
187
|
__export(config_exports, {
|
|
@@ -47,19 +190,19 @@ __export(config_exports, {
|
|
|
47
190
|
parseConfig: () => parseConfig
|
|
48
191
|
});
|
|
49
192
|
import { parseArgs } from "node:util";
|
|
50
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
51
|
-
import { join, dirname } from "node:path";
|
|
193
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
194
|
+
import { join as join2, dirname } from "node:path";
|
|
52
195
|
import { fileURLToPath } from "node:url";
|
|
53
|
-
import { homedir } from "node:os";
|
|
196
|
+
import { homedir as homedir2 } from "node:os";
|
|
54
197
|
function loadDgrc() {
|
|
55
198
|
const candidates = [
|
|
56
|
-
|
|
57
|
-
|
|
199
|
+
join2(process.cwd(), ".dgrc.json"),
|
|
200
|
+
join2(homedir2(), ".dgrc.json")
|
|
58
201
|
];
|
|
59
202
|
for (const filepath of candidates) {
|
|
60
|
-
if (
|
|
203
|
+
if (existsSync2(filepath)) {
|
|
61
204
|
try {
|
|
62
|
-
return JSON.parse(
|
|
205
|
+
return JSON.parse(readFileSync2(filepath, "utf-8"));
|
|
63
206
|
} catch {
|
|
64
207
|
process.stderr.write(`Warning: Failed to parse ${filepath}, ignoring.
|
|
65
208
|
`);
|
|
@@ -72,7 +215,7 @@ function getVersion() {
|
|
|
72
215
|
try {
|
|
73
216
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
74
217
|
const pkg = JSON.parse(
|
|
75
|
-
|
|
218
|
+
readFileSync2(join2(thisDir, "..", "package.json"), "utf-8")
|
|
76
219
|
);
|
|
77
220
|
return pkg.version ?? "1.0.0";
|
|
78
221
|
} catch {
|
|
@@ -113,7 +256,8 @@ function parseConfig(argv) {
|
|
|
113
256
|
const command = positionals[0] ?? "scan";
|
|
114
257
|
const noConfig = values["no-config"];
|
|
115
258
|
const dgrc = noConfig ? {} : loadDgrc();
|
|
116
|
-
const apiKey = dgrc.apiKey
|
|
259
|
+
const apiKey = dgrc.apiKey && dgrc.apiKey.length > 0 ? dgrc.apiKey : null;
|
|
260
|
+
const deviceId = getOrCreateDeviceId();
|
|
117
261
|
const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
|
|
118
262
|
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
119
263
|
process.stderr.write(
|
|
@@ -125,10 +269,7 @@ function parseConfig(argv) {
|
|
|
125
269
|
const allowlistRaw = values.allowlist ?? process.env.DG_ALLOWLIST ?? "";
|
|
126
270
|
const blockThreshold = Number(values["block-threshold"] ?? dgrc.blockThreshold ?? "70");
|
|
127
271
|
const warnThreshold = Number(values["warn-threshold"] ?? dgrc.warnThreshold ?? "60");
|
|
128
|
-
|
|
129
|
-
if (!apiKey) {
|
|
130
|
-
maxPackages = Math.min(maxPackages, 50);
|
|
131
|
-
}
|
|
272
|
+
const maxPackages = Number(values["max-packages"] ?? dgrc.maxPackages ?? "200");
|
|
132
273
|
const debug = values.debug || process.env.DG_DEBUG === "1";
|
|
133
274
|
if (isNaN(blockThreshold) || blockThreshold < 0 || blockThreshold > 100) {
|
|
134
275
|
process.stderr.write("Error: --block-threshold must be a number between 0 and 100\n");
|
|
@@ -144,6 +285,7 @@ function parseConfig(argv) {
|
|
|
144
285
|
}
|
|
145
286
|
return {
|
|
146
287
|
apiKey,
|
|
288
|
+
deviceId,
|
|
147
289
|
apiUrl: values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com",
|
|
148
290
|
mode: modeRaw,
|
|
149
291
|
blockThreshold,
|
|
@@ -162,6 +304,7 @@ var USAGE;
|
|
|
162
304
|
var init_config = __esm({
|
|
163
305
|
"src/config.ts"() {
|
|
164
306
|
"use strict";
|
|
307
|
+
init_auth();
|
|
165
308
|
USAGE = `
|
|
166
309
|
Dependency Guardian \u2014 Supply chain security scanner
|
|
167
310
|
|
|
@@ -228,8 +371,8 @@ var init_config = __esm({
|
|
|
228
371
|
|
|
229
372
|
// src/npm-wrapper.ts
|
|
230
373
|
import { spawn } from "node:child_process";
|
|
231
|
-
import { readFileSync as
|
|
232
|
-
import { join as
|
|
374
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
|
|
375
|
+
import { join as join3 } from "node:path";
|
|
233
376
|
function parseNpmArgs(args) {
|
|
234
377
|
let dgForce = false;
|
|
235
378
|
const filtered = [];
|
|
@@ -375,10 +518,10 @@ function runNpm(args) {
|
|
|
375
518
|
});
|
|
376
519
|
}
|
|
377
520
|
function readBareInstallPackages(cwd2) {
|
|
378
|
-
const pkgPath =
|
|
379
|
-
if (!
|
|
521
|
+
const pkgPath = join3(cwd2, "package.json");
|
|
522
|
+
if (!existsSync3(pkgPath)) return [];
|
|
380
523
|
try {
|
|
381
|
-
const pkg = JSON.parse(
|
|
524
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
382
525
|
const specs = [];
|
|
383
526
|
for (const [name, range] of Object.entries(pkg.dependencies ?? {})) {
|
|
384
527
|
if (typeof range === "string") specs.push(`${name}@${range}`);
|
|
@@ -27445,9 +27588,9 @@ var init_ansi_styles = __esm({
|
|
|
27445
27588
|
|
|
27446
27589
|
// node_modules/wrap-ansi/index.js
|
|
27447
27590
|
function wrapAnsi(string, columns, options) {
|
|
27448
|
-
return String(string).normalize().replaceAll("\r\n", "\n").split("\n").map((line) =>
|
|
27591
|
+
return String(string).normalize().replaceAll("\r\n", "\n").split("\n").map((line) => exec2(line, columns, options)).join("\n");
|
|
27449
27592
|
}
|
|
27450
|
-
var ESCAPES, END_CODE, ANSI_ESCAPE_BELL, ANSI_CSI, ANSI_OSC, ANSI_SGR_TERMINATOR, ANSI_ESCAPE_LINK, wrapAnsiCode, wrapAnsiHyperlink, wordLengths, wrapWord, stringVisibleTrimSpacesRight,
|
|
27593
|
+
var ESCAPES, END_CODE, ANSI_ESCAPE_BELL, ANSI_CSI, ANSI_OSC, ANSI_SGR_TERMINATOR, ANSI_ESCAPE_LINK, wrapAnsiCode, wrapAnsiHyperlink, wordLengths, wrapWord, stringVisibleTrimSpacesRight, exec2;
|
|
27451
27594
|
var init_wrap_ansi = __esm({
|
|
27452
27595
|
"node_modules/wrap-ansi/index.js"() {
|
|
27453
27596
|
init_string_width();
|
|
@@ -27519,7 +27662,7 @@ var init_wrap_ansi = __esm({
|
|
|
27519
27662
|
}
|
|
27520
27663
|
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
27521
27664
|
};
|
|
27522
|
-
|
|
27665
|
+
exec2 = (string, columns, options = {}) => {
|
|
27523
27666
|
if (options.trim !== false && string.trim() === "") {
|
|
27524
27667
|
return "";
|
|
27525
27668
|
}
|
|
@@ -35952,135 +36095,6 @@ var init_build2 = __esm({
|
|
|
35952
36095
|
}
|
|
35953
36096
|
});
|
|
35954
36097
|
|
|
35955
|
-
// src/auth.ts
|
|
35956
|
-
var auth_exports = {};
|
|
35957
|
-
__export(auth_exports, {
|
|
35958
|
-
clearCredentials: () => clearCredentials,
|
|
35959
|
-
createAuthSession: () => createAuthSession,
|
|
35960
|
-
getStoredApiKey: () => getStoredApiKey,
|
|
35961
|
-
openBrowser: () => openBrowser,
|
|
35962
|
-
pollAuthSession: () => pollAuthSession,
|
|
35963
|
-
saveCredentials: () => saveCredentials
|
|
35964
|
-
});
|
|
35965
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, chmodSync, unlinkSync, existsSync as existsSync4 } from "node:fs";
|
|
35966
|
-
import { join as join4 } from "node:path";
|
|
35967
|
-
import { homedir as homedir3 } from "node:os";
|
|
35968
|
-
import { exec as exec2 } from "node:child_process";
|
|
35969
|
-
async function createAuthSession() {
|
|
35970
|
-
let res;
|
|
35971
|
-
try {
|
|
35972
|
-
res = await globalThis.fetch(`${WEB_BASE}/cli/auth/sessions`, {
|
|
35973
|
-
method: "POST",
|
|
35974
|
-
headers: { "Content-Type": "application/json" }
|
|
35975
|
-
});
|
|
35976
|
-
} catch {
|
|
35977
|
-
throw new Error("Could not connect to westbayberry.com");
|
|
35978
|
-
}
|
|
35979
|
-
if (!res.ok) {
|
|
35980
|
-
const body = await res.text().catch(() => "");
|
|
35981
|
-
throw new Error(
|
|
35982
|
-
`Failed to create auth session (HTTP ${res.status})${body ? `: ${body}` : ""}`
|
|
35983
|
-
);
|
|
35984
|
-
}
|
|
35985
|
-
const json = await res.json();
|
|
35986
|
-
return {
|
|
35987
|
-
sessionId: json.session_id,
|
|
35988
|
-
verifyUrl: json.verify_url,
|
|
35989
|
-
expiresIn: json.expires_in
|
|
35990
|
-
};
|
|
35991
|
-
}
|
|
35992
|
-
async function pollAuthSession(sessionId) {
|
|
35993
|
-
let res;
|
|
35994
|
-
try {
|
|
35995
|
-
res = await globalThis.fetch(
|
|
35996
|
-
`${WEB_BASE}/cli/auth/sessions/${sessionId}/token`
|
|
35997
|
-
);
|
|
35998
|
-
} catch {
|
|
35999
|
-
return { status: "expired" };
|
|
36000
|
-
}
|
|
36001
|
-
if (res.status === 404) {
|
|
36002
|
-
return { status: "expired" };
|
|
36003
|
-
}
|
|
36004
|
-
if (!res.ok) {
|
|
36005
|
-
return { status: "expired" };
|
|
36006
|
-
}
|
|
36007
|
-
const json = await res.json();
|
|
36008
|
-
return {
|
|
36009
|
-
status: json.status,
|
|
36010
|
-
apiKey: json.api_key,
|
|
36011
|
-
email: json.email
|
|
36012
|
-
};
|
|
36013
|
-
}
|
|
36014
|
-
function configPath() {
|
|
36015
|
-
return join4(homedir3(), CONFIG_FILE);
|
|
36016
|
-
}
|
|
36017
|
-
function readConfig() {
|
|
36018
|
-
try {
|
|
36019
|
-
const raw = readFileSync5(configPath(), "utf-8");
|
|
36020
|
-
const parsed = JSON.parse(raw);
|
|
36021
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
36022
|
-
return parsed;
|
|
36023
|
-
}
|
|
36024
|
-
return {};
|
|
36025
|
-
} catch {
|
|
36026
|
-
return {};
|
|
36027
|
-
}
|
|
36028
|
-
}
|
|
36029
|
-
function saveCredentials(apiKey) {
|
|
36030
|
-
const data = readConfig();
|
|
36031
|
-
data.apiKey = apiKey;
|
|
36032
|
-
const p = configPath();
|
|
36033
|
-
writeFileSync2(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
36034
|
-
chmodSync(p, 384);
|
|
36035
|
-
}
|
|
36036
|
-
function clearCredentials() {
|
|
36037
|
-
const p = configPath();
|
|
36038
|
-
if (!existsSync4(p)) return;
|
|
36039
|
-
const data = readConfig();
|
|
36040
|
-
delete data.apiKey;
|
|
36041
|
-
if (Object.keys(data).length === 0) {
|
|
36042
|
-
unlinkSync(p);
|
|
36043
|
-
} else {
|
|
36044
|
-
writeFileSync2(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
36045
|
-
chmodSync(p, 384);
|
|
36046
|
-
}
|
|
36047
|
-
}
|
|
36048
|
-
function getStoredApiKey() {
|
|
36049
|
-
const data = readConfig();
|
|
36050
|
-
if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
|
|
36051
|
-
return data.apiKey;
|
|
36052
|
-
}
|
|
36053
|
-
return null;
|
|
36054
|
-
}
|
|
36055
|
-
function openBrowser(url) {
|
|
36056
|
-
if (!/^https?:\/\//i.test(url)) return;
|
|
36057
|
-
const escaped = url.replace(/"/g, '\\"');
|
|
36058
|
-
let cmd;
|
|
36059
|
-
switch (process.platform) {
|
|
36060
|
-
case "darwin":
|
|
36061
|
-
cmd = `open "${escaped}"`;
|
|
36062
|
-
break;
|
|
36063
|
-
case "linux":
|
|
36064
|
-
cmd = `xdg-open "${escaped}"`;
|
|
36065
|
-
break;
|
|
36066
|
-
case "win32":
|
|
36067
|
-
cmd = `start "" "${escaped}"`;
|
|
36068
|
-
break;
|
|
36069
|
-
default:
|
|
36070
|
-
return;
|
|
36071
|
-
}
|
|
36072
|
-
exec2(cmd, () => {
|
|
36073
|
-
});
|
|
36074
|
-
}
|
|
36075
|
-
var WEB_BASE, CONFIG_FILE;
|
|
36076
|
-
var init_auth = __esm({
|
|
36077
|
-
"src/auth.ts"() {
|
|
36078
|
-
"use strict";
|
|
36079
|
-
WEB_BASE = "https://westbayberry.com";
|
|
36080
|
-
CONFIG_FILE = ".dgrc.json";
|
|
36081
|
-
}
|
|
36082
|
-
});
|
|
36083
|
-
|
|
36084
36098
|
// src/ui/hooks/useLogin.ts
|
|
36085
36099
|
function reducer(state, action) {
|
|
36086
36100
|
switch (action.type) {
|
|
@@ -38906,43 +38920,48 @@ var init_LoginApp = __esm({
|
|
|
38906
38920
|
});
|
|
38907
38921
|
|
|
38908
38922
|
// src/api.ts
|
|
38923
|
+
function buildHeaders(config) {
|
|
38924
|
+
const headers = {
|
|
38925
|
+
"Content-Type": "application/json",
|
|
38926
|
+
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
38927
|
+
};
|
|
38928
|
+
if (config.apiKey) {
|
|
38929
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
38930
|
+
} else {
|
|
38931
|
+
headers["X-Device-Id"] = config.deviceId;
|
|
38932
|
+
}
|
|
38933
|
+
return headers;
|
|
38934
|
+
}
|
|
38935
|
+
async function handleTrialExhausted(response) {
|
|
38936
|
+
if (response.status === 403) {
|
|
38937
|
+
const body = await response.json().catch(() => ({}));
|
|
38938
|
+
if (body && body.trialExhausted) {
|
|
38939
|
+
const b = body;
|
|
38940
|
+
throw new TrialExhaustedError(b.scansUsed, b.maxScans);
|
|
38941
|
+
}
|
|
38942
|
+
throw new APIError("Forbidden", 403, JSON.stringify(body));
|
|
38943
|
+
}
|
|
38944
|
+
}
|
|
38909
38945
|
async function callAnalyzeAPI(packages, config, onProgress) {
|
|
38910
38946
|
if (packages.length <= BATCH_SIZE) {
|
|
38911
|
-
|
|
38947
|
+
if (onProgress) onProgress(0, packages.length, packages.map((p) => p.name));
|
|
38948
|
+
const result = await callBatchWithRetry(packages, config);
|
|
38949
|
+
if (onProgress) onProgress(packages.length, packages.length, packages.map((p) => p.name));
|
|
38950
|
+
return result;
|
|
38912
38951
|
}
|
|
38913
38952
|
const batches = [];
|
|
38914
38953
|
for (let i = 0; i < packages.length; i += BATCH_SIZE) {
|
|
38915
38954
|
batches.push(packages.slice(i, i + BATCH_SIZE));
|
|
38916
38955
|
}
|
|
38917
|
-
const
|
|
38918
|
-
const results = new Array(batches.length);
|
|
38956
|
+
const results = [];
|
|
38919
38957
|
let completed = 0;
|
|
38920
|
-
|
|
38921
|
-
|
|
38922
|
-
|
|
38923
|
-
results[idx] = result;
|
|
38924
|
-
completed += batches[idx].length;
|
|
38958
|
+
for (const batch of batches) {
|
|
38959
|
+
const result = await callBatchWithRetry(batch, config);
|
|
38960
|
+
completed += batch.length;
|
|
38925
38961
|
if (onProgress) {
|
|
38926
|
-
onProgress(completed, packages.length,
|
|
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);
|
|
38962
|
+
onProgress(completed, packages.length, batch.map((p) => p.name));
|
|
38945
38963
|
}
|
|
38964
|
+
results.push(result);
|
|
38946
38965
|
}
|
|
38947
38966
|
return mergeResponses(results, config);
|
|
38948
38967
|
}
|
|
@@ -38998,32 +39017,26 @@ async function callAnalyzeBatch(packages, config) {
|
|
|
38998
39017
|
}
|
|
38999
39018
|
};
|
|
39000
39019
|
const controller = new AbortController();
|
|
39001
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
39020
|
+
const timeoutId = setTimeout(() => controller.abort(), 18e4);
|
|
39002
39021
|
let response;
|
|
39003
39022
|
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
|
-
}
|
|
39011
39023
|
response = await fetch(url, {
|
|
39012
39024
|
method: "POST",
|
|
39013
|
-
headers,
|
|
39025
|
+
headers: buildHeaders(config),
|
|
39014
39026
|
body: JSON.stringify(payload),
|
|
39015
39027
|
signal: controller.signal
|
|
39016
39028
|
});
|
|
39017
39029
|
} catch (error) {
|
|
39018
39030
|
clearTimeout(timeoutId);
|
|
39019
39031
|
if (error instanceof Error && error.name === "AbortError") {
|
|
39020
|
-
throw new APIError("Request timed out after
|
|
39032
|
+
throw new APIError("Request timed out after 180s. Try scanning fewer packages.", 408, "");
|
|
39021
39033
|
}
|
|
39022
39034
|
const cause = error instanceof Error && error.cause;
|
|
39023
39035
|
const detail = cause ? `: ${cause.message || cause}` : "";
|
|
39024
39036
|
throw new Error(`fetch failed${detail}`);
|
|
39025
39037
|
}
|
|
39026
39038
|
clearTimeout(timeoutId);
|
|
39039
|
+
await handleTrialExhausted(response);
|
|
39027
39040
|
if (response.status === 401) {
|
|
39028
39041
|
throw new APIError(
|
|
39029
39042
|
"Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
|
|
@@ -39032,14 +39045,6 @@ async function callAnalyzeBatch(packages, config) {
|
|
|
39032
39045
|
);
|
|
39033
39046
|
}
|
|
39034
39047
|
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
|
-
}
|
|
39043
39048
|
throw new APIError(
|
|
39044
39049
|
"Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing",
|
|
39045
39050
|
429,
|
|
@@ -39089,19 +39094,12 @@ async function callPyPIBatch(packages, config) {
|
|
|
39089
39094
|
}
|
|
39090
39095
|
};
|
|
39091
39096
|
const controller = new AbortController();
|
|
39092
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
39097
|
+
const timeoutId = setTimeout(() => controller.abort(), 18e4);
|
|
39093
39098
|
let response;
|
|
39094
39099
|
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
|
-
}
|
|
39102
39100
|
response = await fetch(url, {
|
|
39103
39101
|
method: "POST",
|
|
39104
|
-
headers,
|
|
39102
|
+
headers: buildHeaders(config),
|
|
39105
39103
|
body: JSON.stringify(payload),
|
|
39106
39104
|
signal: controller.signal
|
|
39107
39105
|
});
|
|
@@ -39115,6 +39113,7 @@ async function callPyPIBatch(packages, config) {
|
|
|
39115
39113
|
throw new Error(`fetch failed${detail}`);
|
|
39116
39114
|
}
|
|
39117
39115
|
clearTimeout(timeoutId);
|
|
39116
|
+
await handleTrialExhausted(response);
|
|
39118
39117
|
if (response.status === 401) {
|
|
39119
39118
|
throw new APIError(
|
|
39120
39119
|
"Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
|
|
@@ -39123,14 +39122,6 @@ async function callPyPIBatch(packages, config) {
|
|
|
39123
39122
|
);
|
|
39124
39123
|
}
|
|
39125
39124
|
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
|
-
}
|
|
39134
39125
|
throw new APIError(
|
|
39135
39126
|
"Rate limit exceeded. Upgrade your plan at https://westbayberry.com/pricing",
|
|
39136
39127
|
429,
|
|
@@ -39143,7 +39134,7 @@ async function callPyPIBatch(packages, config) {
|
|
|
39143
39134
|
}
|
|
39144
39135
|
return await response.json();
|
|
39145
39136
|
}
|
|
39146
|
-
var APIError, BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS;
|
|
39137
|
+
var APIError, TrialExhaustedError, BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS;
|
|
39147
39138
|
var init_api = __esm({
|
|
39148
39139
|
"src/api.ts"() {
|
|
39149
39140
|
"use strict";
|
|
@@ -39155,7 +39146,15 @@ var init_api = __esm({
|
|
|
39155
39146
|
this.name = "APIError";
|
|
39156
39147
|
}
|
|
39157
39148
|
};
|
|
39158
|
-
|
|
39149
|
+
TrialExhaustedError = class extends Error {
|
|
39150
|
+
constructor(scansUsed, maxScans) {
|
|
39151
|
+
super("Free trial scans used up. Run `dg login` to create a free account and continue scanning.");
|
|
39152
|
+
this.scansUsed = scansUsed;
|
|
39153
|
+
this.maxScans = maxScans;
|
|
39154
|
+
this.name = "TrialExhaustedError";
|
|
39155
|
+
}
|
|
39156
|
+
};
|
|
39157
|
+
BATCH_SIZE = 75;
|
|
39159
39158
|
MAX_RETRIES = 2;
|
|
39160
39159
|
RETRY_DELAY_MS = 5e3;
|
|
39161
39160
|
}
|
|
@@ -39772,6 +39771,31 @@ __export(static_output_exports, {
|
|
|
39772
39771
|
runStaticLogin: () => runStaticLogin,
|
|
39773
39772
|
runStaticNpm: () => runStaticNpm
|
|
39774
39773
|
});
|
|
39774
|
+
function printTrialBanner(result) {
|
|
39775
|
+
if (result.trialScansRemaining === void 0) return;
|
|
39776
|
+
const remaining = result.trialScansRemaining;
|
|
39777
|
+
if (remaining > 0) {
|
|
39778
|
+
process.stderr.write(
|
|
39779
|
+
import_chalk4.default.dim(` ${remaining} free scan${remaining !== 1 ? "s" : ""} remaining. `) + import_chalk4.default.dim(`Run \`dg login\` for unlimited scans.
|
|
39780
|
+
`)
|
|
39781
|
+
);
|
|
39782
|
+
} else {
|
|
39783
|
+
process.stderr.write(
|
|
39784
|
+
import_chalk4.default.yellow(` No free scans remaining. `) + import_chalk4.default.yellow(`Run \`dg login\` to continue scanning.
|
|
39785
|
+
`)
|
|
39786
|
+
);
|
|
39787
|
+
}
|
|
39788
|
+
}
|
|
39789
|
+
function handleTrialExhausted2(error) {
|
|
39790
|
+
if (error instanceof TrialExhaustedError) {
|
|
39791
|
+
process.stderr.write(
|
|
39792
|
+
import_chalk4.default.yellow("\n Free trial scans used up.\n") + import_chalk4.default.white(" Run `dg login` to create a free account and continue scanning.\n\n")
|
|
39793
|
+
);
|
|
39794
|
+
process.exit(1);
|
|
39795
|
+
return true;
|
|
39796
|
+
}
|
|
39797
|
+
return false;
|
|
39798
|
+
}
|
|
39775
39799
|
function severityColor(sev) {
|
|
39776
39800
|
if (sev >= 5) return (s) => import_chalk4.default.bold.red(s);
|
|
39777
39801
|
if (sev >= 4) return import_chalk4.default.red;
|
|
@@ -39794,7 +39818,7 @@ function truncate(s, max) {
|
|
|
39794
39818
|
function groupPackages(packages) {
|
|
39795
39819
|
const map = /* @__PURE__ */ new Map();
|
|
39796
39820
|
for (const pkg of packages) {
|
|
39797
|
-
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.
|
|
39821
|
+
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.id}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
|
|
39798
39822
|
const group = map.get(fingerprint) ?? [];
|
|
39799
39823
|
group.push(pkg);
|
|
39800
39824
|
map.set(fingerprint, group);
|
|
@@ -39876,7 +39900,7 @@ function renderResultStatic(result, config) {
|
|
|
39876
39900
|
const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
|
|
39877
39901
|
const colorFn = severityColor(finding.severity);
|
|
39878
39902
|
lines.push(
|
|
39879
|
-
` ${colorFn(pad(sevLabel, 10))}${finding.
|
|
39903
|
+
` ${colorFn(pad(sevLabel, 10))}${finding.id} \u2014 ${finding.title}`
|
|
39880
39904
|
);
|
|
39881
39905
|
const evidenceLimit = 3;
|
|
39882
39906
|
for (let i = 0; i < Math.min(finding.evidence.length, evidenceLimit); i++) {
|
|
@@ -39973,21 +39997,23 @@ async function runStatic(config) {
|
|
|
39973
39997
|
dbg(
|
|
39974
39998
|
`packages to scan: ${packages.map((p) => `${p.name}@${p.version}`).slice(0, 10).join(", ")}${packages.length > 10 ? ` (+${packages.length - 10} more)` : ""}`
|
|
39975
39999
|
);
|
|
39976
|
-
|
|
39977
|
-
|
|
39978
|
-
|
|
40000
|
+
let result;
|
|
40001
|
+
try {
|
|
40002
|
+
const startMs = Date.now();
|
|
40003
|
+
result = await callAnalyzeAPI(packages, config, (done, total) => {
|
|
40004
|
+
process.stderr.write(import_chalk4.default.dim(` Analyzed ${done}/${total}...
|
|
39979
40005
|
`));
|
|
39980
|
-
|
|
39981
|
-
|
|
39982
|
-
|
|
39983
|
-
);
|
|
39984
|
-
const output = renderResultStatic(result, config);
|
|
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")
|
|
40006
|
+
});
|
|
40007
|
+
dbg(
|
|
40008
|
+
`API responded in ${Date.now() - startMs}ms, action=${result.action}, score=${result.score}`
|
|
39989
40009
|
);
|
|
40010
|
+
} catch (error) {
|
|
40011
|
+
if (handleTrialExhausted2(error)) return;
|
|
40012
|
+
throw error;
|
|
39990
40013
|
}
|
|
40014
|
+
const output = renderResultStatic(result, config);
|
|
40015
|
+
process.stdout.write(output + "\n");
|
|
40016
|
+
printTrialBanner(result);
|
|
39991
40017
|
if (result.action === "block" && config.mode === "block") {
|
|
39992
40018
|
process.exit(2);
|
|
39993
40019
|
} else if (result.action === "block" || result.action === "warn") {
|
|
@@ -40082,6 +40108,7 @@ async function scanAndInstallStatic(resolved, parsed, config) {
|
|
|
40082
40108
|
);
|
|
40083
40109
|
}
|
|
40084
40110
|
} catch (error) {
|
|
40111
|
+
if (handleTrialExhausted2(error)) return;
|
|
40085
40112
|
const msg = error instanceof Error ? error.message : String(error);
|
|
40086
40113
|
process.stderr.write(
|
|
40087
40114
|
import_chalk4.default.yellow(
|
|
@@ -40097,20 +40124,17 @@ async function scanAndInstallStatic(resolved, parsed, config) {
|
|
|
40097
40124
|
process.stderr.write(
|
|
40098
40125
|
import_chalk4.default.green(
|
|
40099
40126
|
` ${import_chalk4.default.bold("\u2713")} ${toScan.length} package${toScan.length !== 1 ? "s" : ""} scanned \u2014 all clear
|
|
40100
|
-
|
|
40101
40127
|
`
|
|
40102
40128
|
)
|
|
40103
40129
|
);
|
|
40130
|
+
printTrialBanner(result);
|
|
40131
|
+
process.stderr.write("\n");
|
|
40104
40132
|
const code = await runNpm(parsed.rawArgs);
|
|
40105
40133
|
process.exit(code);
|
|
40106
40134
|
}
|
|
40107
40135
|
const output = renderResultStatic(result, config);
|
|
40108
40136
|
process.stdout.write(output + "\n");
|
|
40109
|
-
|
|
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
|
-
}
|
|
40137
|
+
printTrialBanner(result);
|
|
40114
40138
|
if (result.action === "warn") {
|
|
40115
40139
|
process.stderr.write(
|
|
40116
40140
|
import_chalk4.default.yellow(" Warnings detected. Proceeding with install.\n\n")
|
|
@@ -40429,6 +40453,8 @@ function reducer2(_state, action) {
|
|
|
40429
40453
|
return { phase: "done", exitCode: action.exitCode };
|
|
40430
40454
|
case "ERROR":
|
|
40431
40455
|
return { phase: "error", message: action.message, proceed: action.proceed };
|
|
40456
|
+
case "TRIAL_EXHAUSTED":
|
|
40457
|
+
return { phase: "trial_exhausted" };
|
|
40432
40458
|
}
|
|
40433
40459
|
}
|
|
40434
40460
|
function useNpmWrapper(npmArgs, config) {
|
|
@@ -40533,6 +40559,10 @@ function useNpmWrapper(npmArgs, config) {
|
|
|
40533
40559
|
return;
|
|
40534
40560
|
}
|
|
40535
40561
|
} catch (error) {
|
|
40562
|
+
if (error instanceof TrialExhaustedError) {
|
|
40563
|
+
dispatch({ type: "TRIAL_EXHAUSTED" });
|
|
40564
|
+
return;
|
|
40565
|
+
}
|
|
40536
40566
|
const message = error instanceof Error ? error.message : String(error);
|
|
40537
40567
|
dispatch({ type: "ERROR", message, proceed: true });
|
|
40538
40568
|
dispatch({ type: "INSTALLING" });
|
|
@@ -40651,7 +40681,7 @@ var init_DurationLine = __esm({
|
|
|
40651
40681
|
function groupPackages2(packages) {
|
|
40652
40682
|
const map = /* @__PURE__ */ new Map();
|
|
40653
40683
|
for (const pkg of packages) {
|
|
40654
|
-
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.
|
|
40684
|
+
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.id}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
|
|
40655
40685
|
const group = map.get(fingerprint) ?? [];
|
|
40656
40686
|
group.push(pkg);
|
|
40657
40687
|
map.set(fingerprint, group);
|
|
@@ -40771,7 +40801,7 @@ var init_ResultsView = __esm({
|
|
|
40771
40801
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
|
|
40772
40802
|
" ",
|
|
40773
40803
|
colorFn(pad2(sevLabel, 10)),
|
|
40774
|
-
finding.
|
|
40804
|
+
finding.id,
|
|
40775
40805
|
" ",
|
|
40776
40806
|
"\u2014",
|
|
40777
40807
|
" ",
|
|
@@ -40786,7 +40816,7 @@ var init_ResultsView = __esm({
|
|
|
40786
40816
|
import_chalk6.default.dim(`... and ${overflow} more`)
|
|
40787
40817
|
] }),
|
|
40788
40818
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " })
|
|
40789
|
-
] }, `${finding.
|
|
40819
|
+
] }, `${finding.id}-${idx}`);
|
|
40790
40820
|
}),
|
|
40791
40821
|
safeVersion && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { children: [
|
|
40792
40822
|
" ",
|
|
@@ -40907,6 +40937,11 @@ var init_NpmWrapperApp = __esm({
|
|
|
40907
40937
|
const timer = setTimeout(() => exit(), 0);
|
|
40908
40938
|
return () => clearTimeout(timer);
|
|
40909
40939
|
}
|
|
40940
|
+
if (state.phase === "trial_exhausted") {
|
|
40941
|
+
process.exitCode = 1;
|
|
40942
|
+
const timer = setTimeout(() => exit(), 0);
|
|
40943
|
+
return () => clearTimeout(timer);
|
|
40944
|
+
}
|
|
40910
40945
|
if (state.phase === "passthrough") {
|
|
40911
40946
|
}
|
|
40912
40947
|
}, [state, exit]);
|
|
@@ -41006,6 +41041,15 @@ var init_NpmWrapperApp = __esm({
|
|
|
41006
41041
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorView, { error: new Error(state.message) });
|
|
41007
41042
|
case "passthrough":
|
|
41008
41043
|
return null;
|
|
41044
|
+
case "trial_exhausted":
|
|
41045
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
|
|
41046
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
|
|
41047
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { children: [
|
|
41048
|
+
"Run ",
|
|
41049
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
|
|
41050
|
+
" to create a free account and continue scanning."
|
|
41051
|
+
] })
|
|
41052
|
+
] });
|
|
41009
41053
|
}
|
|
41010
41054
|
};
|
|
41011
41055
|
}
|
|
@@ -41028,6 +41072,8 @@ function reducer3(_state, action) {
|
|
|
41028
41072
|
return { phase: "results", result: action.result, durationMs: action.durationMs, skippedCount: action.skippedCount };
|
|
41029
41073
|
case "ERROR":
|
|
41030
41074
|
return { phase: "error", error: action.error };
|
|
41075
|
+
case "TRIAL_EXHAUSTED":
|
|
41076
|
+
return { phase: "trial_exhausted", scansUsed: action.scansUsed, maxScans: action.maxScans };
|
|
41031
41077
|
}
|
|
41032
41078
|
}
|
|
41033
41079
|
function useScan(config) {
|
|
@@ -41086,6 +41132,10 @@ async function runNpmScan(packages, skippedCount, config, dispatch) {
|
|
|
41086
41132
|
});
|
|
41087
41133
|
dispatch({ type: "SCAN_COMPLETE", result, durationMs: Date.now() - startMs, skippedCount });
|
|
41088
41134
|
} catch (error) {
|
|
41135
|
+
if (error instanceof TrialExhaustedError) {
|
|
41136
|
+
dispatch({ type: "TRIAL_EXHAUSTED", scansUsed: error.scansUsed, maxScans: error.maxScans });
|
|
41137
|
+
return;
|
|
41138
|
+
}
|
|
41089
41139
|
dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
|
|
41090
41140
|
}
|
|
41091
41141
|
}
|
|
@@ -41156,6 +41206,10 @@ async function scanProjects(projects, config, dispatch) {
|
|
|
41156
41206
|
};
|
|
41157
41207
|
dispatch({ type: "SCAN_COMPLETE", result: merged, durationMs: merged.durationMs, skippedCount: 0 });
|
|
41158
41208
|
} catch (error) {
|
|
41209
|
+
if (error instanceof TrialExhaustedError) {
|
|
41210
|
+
dispatch({ type: "TRIAL_EXHAUSTED", scansUsed: error.scansUsed, maxScans: error.maxScans });
|
|
41211
|
+
return;
|
|
41212
|
+
}
|
|
41159
41213
|
dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
|
|
41160
41214
|
}
|
|
41161
41215
|
}
|
|
@@ -41288,7 +41342,7 @@ var init_useTerminalSize = __esm({
|
|
|
41288
41342
|
function groupPackages3(packages) {
|
|
41289
41343
|
const map = /* @__PURE__ */ new Map();
|
|
41290
41344
|
for (const pkg of packages) {
|
|
41291
|
-
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.
|
|
41345
|
+
const fingerprint = pkg.findings.length === 0 ? `__clean_${pkg.score}` : pkg.findings.map((f) => `${f.id}:${f.severity}`).sort().join("|") + `|score:${pkg.score}`;
|
|
41292
41346
|
const group = map.get(fingerprint) ?? [];
|
|
41293
41347
|
group.push(pkg);
|
|
41294
41348
|
map.set(fingerprint, group);
|
|
@@ -41407,8 +41461,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41407
41461
|
const viewRef = (0, import_react30.useRef)(view);
|
|
41408
41462
|
viewRef.current = view;
|
|
41409
41463
|
const { rows: termRows, cols: termCols } = useTerminalSize();
|
|
41410
|
-
const
|
|
41411
|
-
const availableRows = Math.max(5, termRows - chromeHeight);
|
|
41464
|
+
const availableRows = Math.max(5, termRows - FIXED_CHROME);
|
|
41412
41465
|
const innerWidth = Math.max(40, termCols - 6);
|
|
41413
41466
|
const getLevel = (idx) => {
|
|
41414
41467
|
return view.expandedIndex === idx ? view.expandLevel : null;
|
|
@@ -41496,9 +41549,6 @@ var init_InteractiveResultsView = __esm({
|
|
|
41496
41549
|
dispatchView({ type: "EXPAND", expandedIndex: newExpIdx, expandLevel: newExpLvl, viewport: newVp });
|
|
41497
41550
|
} else if (input === "e") {
|
|
41498
41551
|
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") {
|
|
41502
41552
|
const newVp = adjustViewport(cursor, null, null, vpStart);
|
|
41503
41553
|
dispatchView({ type: "EXPAND", expandedIndex: null, expandLevel: null, viewport: newVp });
|
|
41504
41554
|
} else {
|
|
@@ -41620,12 +41670,6 @@ var init_InteractiveResultsView = __esm({
|
|
|
41620
41670
|
]
|
|
41621
41671
|
}
|
|
41622
41672
|
),
|
|
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
|
-
] }) }),
|
|
41629
41673
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
|
|
41630
41674
|
" ",
|
|
41631
41675
|
groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
@@ -41678,8 +41722,8 @@ var init_InteractiveResultsView = __esm({
|
|
|
41678
41722
|
" ",
|
|
41679
41723
|
sevColor(pad3(sevLabel, 4)),
|
|
41680
41724
|
" ",
|
|
41681
|
-
import_chalk10.default.dim(f.
|
|
41682
|
-
] }, `${f.
|
|
41725
|
+
import_chalk10.default.dim(f.id)
|
|
41726
|
+
] }, `${f.id}-${idx}`)
|
|
41683
41727
|
);
|
|
41684
41728
|
}
|
|
41685
41729
|
if (hasAffects) {
|
|
@@ -41717,15 +41761,15 @@ var init_InteractiveResultsView = __esm({
|
|
|
41717
41761
|
" ",
|
|
41718
41762
|
sevColor(pad3(sevLabel, 4)),
|
|
41719
41763
|
" ",
|
|
41720
|
-
import_chalk10.default.bold(finding.
|
|
41721
|
-
] }, `${finding.
|
|
41764
|
+
import_chalk10.default.bold(finding.id)
|
|
41765
|
+
] }, `${finding.id}-${idx}-badge`)
|
|
41722
41766
|
);
|
|
41723
41767
|
allLines.push(
|
|
41724
41768
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text, { dimColor: true, children: [
|
|
41725
41769
|
continuation,
|
|
41726
41770
|
" ",
|
|
41727
41771
|
finding.title
|
|
41728
|
-
] }, `${finding.
|
|
41772
|
+
] }, `${finding.id}-${idx}-title`)
|
|
41729
41773
|
);
|
|
41730
41774
|
for (let i = 0; i < evidenceSlice.length; i++) {
|
|
41731
41775
|
allLines.push(
|
|
@@ -41735,7 +41779,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41735
41779
|
import_chalk10.default.dim("\u203A"),
|
|
41736
41780
|
" ",
|
|
41737
41781
|
truncate3(evidenceSlice[i], evidenceWidth)
|
|
41738
|
-
] }, `${finding.
|
|
41782
|
+
] }, `${finding.id}-${idx}-ev-${i}`)
|
|
41739
41783
|
);
|
|
41740
41784
|
}
|
|
41741
41785
|
if (overflow > 0) {
|
|
@@ -41744,7 +41788,7 @@ var init_InteractiveResultsView = __esm({
|
|
|
41744
41788
|
continuation,
|
|
41745
41789
|
" ",
|
|
41746
41790
|
import_chalk10.default.dim(`+${overflow} more`)
|
|
41747
|
-
] }, `${finding.
|
|
41791
|
+
] }, `${finding.id}-${idx}-overflow`)
|
|
41748
41792
|
);
|
|
41749
41793
|
}
|
|
41750
41794
|
}
|
|
@@ -41894,6 +41938,11 @@ var init_App2 = __esm({
|
|
|
41894
41938
|
const timer = setTimeout(() => exit(), 0);
|
|
41895
41939
|
return () => clearTimeout(timer);
|
|
41896
41940
|
}
|
|
41941
|
+
if (state.phase === "trial_exhausted") {
|
|
41942
|
+
process.exitCode = 1;
|
|
41943
|
+
const timer = setTimeout(() => exit(), 0);
|
|
41944
|
+
return () => clearTimeout(timer);
|
|
41945
|
+
}
|
|
41897
41946
|
}, [state, config, exit]);
|
|
41898
41947
|
const content = (() => {
|
|
41899
41948
|
switch (state.phase) {
|
|
@@ -41944,6 +41993,15 @@ var init_App2 = __esm({
|
|
|
41944
41993
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: state.message });
|
|
41945
41994
|
case "error":
|
|
41946
41995
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorView, { error: state.error });
|
|
41996
|
+
case "trial_exhausted":
|
|
41997
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
|
|
41998
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "yellow", bold: true, children: "Free trial scans used up." }),
|
|
41999
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { children: [
|
|
42000
|
+
"Run ",
|
|
42001
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "cyan", bold: true, children: "dg login" }),
|
|
42002
|
+
" to create a free account and continue scanning."
|
|
42003
|
+
] })
|
|
42004
|
+
] });
|
|
41947
42005
|
}
|
|
41948
42006
|
})();
|
|
41949
42007
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", height: termRows, children: content });
|
|
@@ -41956,16 +42014,16 @@ init_config();
|
|
|
41956
42014
|
init_npm_wrapper();
|
|
41957
42015
|
|
|
41958
42016
|
// src/update-check.ts
|
|
41959
|
-
import { readFileSync as
|
|
41960
|
-
import { homedir as
|
|
41961
|
-
import { join as
|
|
42017
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
42018
|
+
import { homedir as homedir3 } from "node:os";
|
|
42019
|
+
import { join as join4 } from "node:path";
|
|
41962
42020
|
import { spawn as spawn2, execSync } from "node:child_process";
|
|
41963
42021
|
var PKG_NAME = "@westbayberry/dg";
|
|
41964
|
-
var CACHE_FILE =
|
|
42022
|
+
var CACHE_FILE = join4(homedir3(), ".dg-update-check.json");
|
|
41965
42023
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
41966
42024
|
function readCache() {
|
|
41967
42025
|
try {
|
|
41968
|
-
const raw =
|
|
42026
|
+
const raw = readFileSync4(CACHE_FILE, "utf-8");
|
|
41969
42027
|
const data = JSON.parse(raw);
|
|
41970
42028
|
if (typeof data.latest === "string" && typeof data.checkedAt === "number") {
|
|
41971
42029
|
return data;
|
|
@@ -41976,7 +42034,7 @@ function readCache() {
|
|
|
41976
42034
|
}
|
|
41977
42035
|
function writeCache(entry) {
|
|
41978
42036
|
try {
|
|
41979
|
-
|
|
42037
|
+
writeFileSync2(CACHE_FILE, JSON.stringify(entry), "utf-8");
|
|
41980
42038
|
} catch {
|
|
41981
42039
|
}
|
|
41982
42040
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@westbayberry/dg",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
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",
|