@westbayberry/dg 1.0.17 → 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 +299 -184
- 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,13 +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
|
|
117
|
-
|
|
118
|
-
process.stderr.write(
|
|
119
|
-
"Error: Not logged in. Run `dg login` to authenticate.\n"
|
|
120
|
-
);
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
259
|
+
const apiKey = dgrc.apiKey && dgrc.apiKey.length > 0 ? dgrc.apiKey : null;
|
|
260
|
+
const deviceId = getOrCreateDeviceId();
|
|
123
261
|
const modeRaw = values.mode ?? process.env.DG_MODE ?? dgrc.mode ?? "warn";
|
|
124
262
|
if (!["block", "warn", "off"].includes(modeRaw)) {
|
|
125
263
|
process.stderr.write(
|
|
@@ -147,6 +285,7 @@ function parseConfig(argv) {
|
|
|
147
285
|
}
|
|
148
286
|
return {
|
|
149
287
|
apiKey,
|
|
288
|
+
deviceId,
|
|
150
289
|
apiUrl: values["api-url"] ?? process.env.DG_API_URL ?? dgrc.apiUrl ?? "https://api.westbayberry.com",
|
|
151
290
|
mode: modeRaw,
|
|
152
291
|
blockThreshold,
|
|
@@ -165,6 +304,7 @@ var USAGE;
|
|
|
165
304
|
var init_config = __esm({
|
|
166
305
|
"src/config.ts"() {
|
|
167
306
|
"use strict";
|
|
307
|
+
init_auth();
|
|
168
308
|
USAGE = `
|
|
169
309
|
Dependency Guardian \u2014 Supply chain security scanner
|
|
170
310
|
|
|
@@ -231,8 +371,8 @@ var init_config = __esm({
|
|
|
231
371
|
|
|
232
372
|
// src/npm-wrapper.ts
|
|
233
373
|
import { spawn } from "node:child_process";
|
|
234
|
-
import { readFileSync as
|
|
235
|
-
import { join as
|
|
374
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "node:fs";
|
|
375
|
+
import { join as join3 } from "node:path";
|
|
236
376
|
function parseNpmArgs(args) {
|
|
237
377
|
let dgForce = false;
|
|
238
378
|
const filtered = [];
|
|
@@ -378,10 +518,10 @@ function runNpm(args) {
|
|
|
378
518
|
});
|
|
379
519
|
}
|
|
380
520
|
function readBareInstallPackages(cwd2) {
|
|
381
|
-
const pkgPath =
|
|
382
|
-
if (!
|
|
521
|
+
const pkgPath = join3(cwd2, "package.json");
|
|
522
|
+
if (!existsSync3(pkgPath)) return [];
|
|
383
523
|
try {
|
|
384
|
-
const pkg = JSON.parse(
|
|
524
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
385
525
|
const specs = [];
|
|
386
526
|
for (const [name, range] of Object.entries(pkg.dependencies ?? {})) {
|
|
387
527
|
if (typeof range === "string") specs.push(`${name}@${range}`);
|
|
@@ -27448,9 +27588,9 @@ var init_ansi_styles = __esm({
|
|
|
27448
27588
|
|
|
27449
27589
|
// node_modules/wrap-ansi/index.js
|
|
27450
27590
|
function wrapAnsi(string, columns, options) {
|
|
27451
|
-
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");
|
|
27452
27592
|
}
|
|
27453
|
-
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;
|
|
27454
27594
|
var init_wrap_ansi = __esm({
|
|
27455
27595
|
"node_modules/wrap-ansi/index.js"() {
|
|
27456
27596
|
init_string_width();
|
|
@@ -27522,7 +27662,7 @@ var init_wrap_ansi = __esm({
|
|
|
27522
27662
|
}
|
|
27523
27663
|
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
27524
27664
|
};
|
|
27525
|
-
|
|
27665
|
+
exec2 = (string, columns, options = {}) => {
|
|
27526
27666
|
if (options.trim !== false && string.trim() === "") {
|
|
27527
27667
|
return "";
|
|
27528
27668
|
}
|
|
@@ -35955,135 +36095,6 @@ var init_build2 = __esm({
|
|
|
35955
36095
|
}
|
|
35956
36096
|
});
|
|
35957
36097
|
|
|
35958
|
-
// src/auth.ts
|
|
35959
|
-
var auth_exports = {};
|
|
35960
|
-
__export(auth_exports, {
|
|
35961
|
-
clearCredentials: () => clearCredentials,
|
|
35962
|
-
createAuthSession: () => createAuthSession,
|
|
35963
|
-
getStoredApiKey: () => getStoredApiKey,
|
|
35964
|
-
openBrowser: () => openBrowser,
|
|
35965
|
-
pollAuthSession: () => pollAuthSession,
|
|
35966
|
-
saveCredentials: () => saveCredentials
|
|
35967
|
-
});
|
|
35968
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, chmodSync, unlinkSync, existsSync as existsSync4 } from "node:fs";
|
|
35969
|
-
import { join as join4 } from "node:path";
|
|
35970
|
-
import { homedir as homedir3 } from "node:os";
|
|
35971
|
-
import { exec as exec2 } from "node:child_process";
|
|
35972
|
-
async function createAuthSession() {
|
|
35973
|
-
let res;
|
|
35974
|
-
try {
|
|
35975
|
-
res = await globalThis.fetch(`${WEB_BASE}/cli/auth/sessions`, {
|
|
35976
|
-
method: "POST",
|
|
35977
|
-
headers: { "Content-Type": "application/json" }
|
|
35978
|
-
});
|
|
35979
|
-
} catch {
|
|
35980
|
-
throw new Error("Could not connect to westbayberry.com");
|
|
35981
|
-
}
|
|
35982
|
-
if (!res.ok) {
|
|
35983
|
-
const body = await res.text().catch(() => "");
|
|
35984
|
-
throw new Error(
|
|
35985
|
-
`Failed to create auth session (HTTP ${res.status})${body ? `: ${body}` : ""}`
|
|
35986
|
-
);
|
|
35987
|
-
}
|
|
35988
|
-
const json = await res.json();
|
|
35989
|
-
return {
|
|
35990
|
-
sessionId: json.session_id,
|
|
35991
|
-
verifyUrl: json.verify_url,
|
|
35992
|
-
expiresIn: json.expires_in
|
|
35993
|
-
};
|
|
35994
|
-
}
|
|
35995
|
-
async function pollAuthSession(sessionId) {
|
|
35996
|
-
let res;
|
|
35997
|
-
try {
|
|
35998
|
-
res = await globalThis.fetch(
|
|
35999
|
-
`${WEB_BASE}/cli/auth/sessions/${sessionId}/token`
|
|
36000
|
-
);
|
|
36001
|
-
} catch {
|
|
36002
|
-
return { status: "expired" };
|
|
36003
|
-
}
|
|
36004
|
-
if (res.status === 404) {
|
|
36005
|
-
return { status: "expired" };
|
|
36006
|
-
}
|
|
36007
|
-
if (!res.ok) {
|
|
36008
|
-
return { status: "expired" };
|
|
36009
|
-
}
|
|
36010
|
-
const json = await res.json();
|
|
36011
|
-
return {
|
|
36012
|
-
status: json.status,
|
|
36013
|
-
apiKey: json.api_key,
|
|
36014
|
-
email: json.email
|
|
36015
|
-
};
|
|
36016
|
-
}
|
|
36017
|
-
function configPath() {
|
|
36018
|
-
return join4(homedir3(), CONFIG_FILE);
|
|
36019
|
-
}
|
|
36020
|
-
function readConfig() {
|
|
36021
|
-
try {
|
|
36022
|
-
const raw = readFileSync5(configPath(), "utf-8");
|
|
36023
|
-
const parsed = JSON.parse(raw);
|
|
36024
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
36025
|
-
return parsed;
|
|
36026
|
-
}
|
|
36027
|
-
return {};
|
|
36028
|
-
} catch {
|
|
36029
|
-
return {};
|
|
36030
|
-
}
|
|
36031
|
-
}
|
|
36032
|
-
function saveCredentials(apiKey) {
|
|
36033
|
-
const data = readConfig();
|
|
36034
|
-
data.apiKey = apiKey;
|
|
36035
|
-
const p = configPath();
|
|
36036
|
-
writeFileSync2(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
36037
|
-
chmodSync(p, 384);
|
|
36038
|
-
}
|
|
36039
|
-
function clearCredentials() {
|
|
36040
|
-
const p = configPath();
|
|
36041
|
-
if (!existsSync4(p)) return;
|
|
36042
|
-
const data = readConfig();
|
|
36043
|
-
delete data.apiKey;
|
|
36044
|
-
if (Object.keys(data).length === 0) {
|
|
36045
|
-
unlinkSync(p);
|
|
36046
|
-
} else {
|
|
36047
|
-
writeFileSync2(p, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
36048
|
-
chmodSync(p, 384);
|
|
36049
|
-
}
|
|
36050
|
-
}
|
|
36051
|
-
function getStoredApiKey() {
|
|
36052
|
-
const data = readConfig();
|
|
36053
|
-
if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
|
|
36054
|
-
return data.apiKey;
|
|
36055
|
-
}
|
|
36056
|
-
return null;
|
|
36057
|
-
}
|
|
36058
|
-
function openBrowser(url) {
|
|
36059
|
-
if (!/^https?:\/\//i.test(url)) return;
|
|
36060
|
-
const escaped = url.replace(/"/g, '\\"');
|
|
36061
|
-
let cmd;
|
|
36062
|
-
switch (process.platform) {
|
|
36063
|
-
case "darwin":
|
|
36064
|
-
cmd = `open "${escaped}"`;
|
|
36065
|
-
break;
|
|
36066
|
-
case "linux":
|
|
36067
|
-
cmd = `xdg-open "${escaped}"`;
|
|
36068
|
-
break;
|
|
36069
|
-
case "win32":
|
|
36070
|
-
cmd = `start "" "${escaped}"`;
|
|
36071
|
-
break;
|
|
36072
|
-
default:
|
|
36073
|
-
return;
|
|
36074
|
-
}
|
|
36075
|
-
exec2(cmd, () => {
|
|
36076
|
-
});
|
|
36077
|
-
}
|
|
36078
|
-
var WEB_BASE, CONFIG_FILE;
|
|
36079
|
-
var init_auth = __esm({
|
|
36080
|
-
"src/auth.ts"() {
|
|
36081
|
-
"use strict";
|
|
36082
|
-
WEB_BASE = "https://westbayberry.com";
|
|
36083
|
-
CONFIG_FILE = ".dgrc.json";
|
|
36084
|
-
}
|
|
36085
|
-
});
|
|
36086
|
-
|
|
36087
36098
|
// src/ui/hooks/useLogin.ts
|
|
36088
36099
|
function reducer(state, action) {
|
|
36089
36100
|
switch (action.type) {
|
|
@@ -38909,9 +38920,34 @@ var init_LoginApp = __esm({
|
|
|
38909
38920
|
});
|
|
38910
38921
|
|
|
38911
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
|
+
}
|
|
38912
38945
|
async function callAnalyzeAPI(packages, config, onProgress) {
|
|
38913
38946
|
if (packages.length <= BATCH_SIZE) {
|
|
38914
|
-
|
|
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;
|
|
38915
38951
|
}
|
|
38916
38952
|
const batches = [];
|
|
38917
38953
|
for (let i = 0; i < packages.length; i += BATCH_SIZE) {
|
|
@@ -38981,29 +39017,26 @@ async function callAnalyzeBatch(packages, config) {
|
|
|
38981
39017
|
}
|
|
38982
39018
|
};
|
|
38983
39019
|
const controller = new AbortController();
|
|
38984
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
39020
|
+
const timeoutId = setTimeout(() => controller.abort(), 18e4);
|
|
38985
39021
|
let response;
|
|
38986
39022
|
try {
|
|
38987
39023
|
response = await fetch(url, {
|
|
38988
39024
|
method: "POST",
|
|
38989
|
-
headers:
|
|
38990
|
-
"Content-Type": "application/json",
|
|
38991
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
38992
|
-
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
38993
|
-
},
|
|
39025
|
+
headers: buildHeaders(config),
|
|
38994
39026
|
body: JSON.stringify(payload),
|
|
38995
39027
|
signal: controller.signal
|
|
38996
39028
|
});
|
|
38997
39029
|
} catch (error) {
|
|
38998
39030
|
clearTimeout(timeoutId);
|
|
38999
39031
|
if (error instanceof Error && error.name === "AbortError") {
|
|
39000
|
-
throw new APIError("Request timed out after
|
|
39032
|
+
throw new APIError("Request timed out after 180s. Try scanning fewer packages.", 408, "");
|
|
39001
39033
|
}
|
|
39002
39034
|
const cause = error instanceof Error && error.cause;
|
|
39003
39035
|
const detail = cause ? `: ${cause.message || cause}` : "";
|
|
39004
39036
|
throw new Error(`fetch failed${detail}`);
|
|
39005
39037
|
}
|
|
39006
39038
|
clearTimeout(timeoutId);
|
|
39039
|
+
await handleTrialExhausted(response);
|
|
39007
39040
|
if (response.status === 401) {
|
|
39008
39041
|
throw new APIError(
|
|
39009
39042
|
"Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
|
|
@@ -39061,16 +39094,12 @@ async function callPyPIBatch(packages, config) {
|
|
|
39061
39094
|
}
|
|
39062
39095
|
};
|
|
39063
39096
|
const controller = new AbortController();
|
|
39064
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
39097
|
+
const timeoutId = setTimeout(() => controller.abort(), 18e4);
|
|
39065
39098
|
let response;
|
|
39066
39099
|
try {
|
|
39067
39100
|
response = await fetch(url, {
|
|
39068
39101
|
method: "POST",
|
|
39069
|
-
headers:
|
|
39070
|
-
"Content-Type": "application/json",
|
|
39071
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
39072
|
-
"User-Agent": "dependency-guardian-cli/1.0.0"
|
|
39073
|
-
},
|
|
39102
|
+
headers: buildHeaders(config),
|
|
39074
39103
|
body: JSON.stringify(payload),
|
|
39075
39104
|
signal: controller.signal
|
|
39076
39105
|
});
|
|
@@ -39084,6 +39113,7 @@ async function callPyPIBatch(packages, config) {
|
|
|
39084
39113
|
throw new Error(`fetch failed${detail}`);
|
|
39085
39114
|
}
|
|
39086
39115
|
clearTimeout(timeoutId);
|
|
39116
|
+
await handleTrialExhausted(response);
|
|
39087
39117
|
if (response.status === 401) {
|
|
39088
39118
|
throw new APIError(
|
|
39089
39119
|
"Invalid API key. Run `dg logout` then `dg login` to re-authenticate.",
|
|
@@ -39104,7 +39134,7 @@ async function callPyPIBatch(packages, config) {
|
|
|
39104
39134
|
}
|
|
39105
39135
|
return await response.json();
|
|
39106
39136
|
}
|
|
39107
|
-
var APIError, BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS;
|
|
39137
|
+
var APIError, TrialExhaustedError, BATCH_SIZE, MAX_RETRIES, RETRY_DELAY_MS;
|
|
39108
39138
|
var init_api = __esm({
|
|
39109
39139
|
"src/api.ts"() {
|
|
39110
39140
|
"use strict";
|
|
@@ -39116,7 +39146,15 @@ var init_api = __esm({
|
|
|
39116
39146
|
this.name = "APIError";
|
|
39117
39147
|
}
|
|
39118
39148
|
};
|
|
39119
|
-
|
|
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;
|
|
39120
39158
|
MAX_RETRIES = 2;
|
|
39121
39159
|
RETRY_DELAY_MS = 5e3;
|
|
39122
39160
|
}
|
|
@@ -39733,6 +39771,31 @@ __export(static_output_exports, {
|
|
|
39733
39771
|
runStaticLogin: () => runStaticLogin,
|
|
39734
39772
|
runStaticNpm: () => runStaticNpm
|
|
39735
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
|
+
}
|
|
39736
39799
|
function severityColor(sev) {
|
|
39737
39800
|
if (sev >= 5) return (s) => import_chalk4.default.bold.red(s);
|
|
39738
39801
|
if (sev >= 4) return import_chalk4.default.red;
|
|
@@ -39934,16 +39997,23 @@ async function runStatic(config) {
|
|
|
39934
39997
|
dbg(
|
|
39935
39998
|
`packages to scan: ${packages.map((p) => `${p.name}@${p.version}`).slice(0, 10).join(", ")}${packages.length > 10 ? ` (+${packages.length - 10} more)` : ""}`
|
|
39936
39999
|
);
|
|
39937
|
-
|
|
39938
|
-
|
|
39939
|
-
|
|
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}...
|
|
39940
40005
|
`));
|
|
39941
|
-
|
|
39942
|
-
|
|
39943
|
-
|
|
39944
|
-
|
|
40006
|
+
});
|
|
40007
|
+
dbg(
|
|
40008
|
+
`API responded in ${Date.now() - startMs}ms, action=${result.action}, score=${result.score}`
|
|
40009
|
+
);
|
|
40010
|
+
} catch (error) {
|
|
40011
|
+
if (handleTrialExhausted2(error)) return;
|
|
40012
|
+
throw error;
|
|
40013
|
+
}
|
|
39945
40014
|
const output = renderResultStatic(result, config);
|
|
39946
40015
|
process.stdout.write(output + "\n");
|
|
40016
|
+
printTrialBanner(result);
|
|
39947
40017
|
if (result.action === "block" && config.mode === "block") {
|
|
39948
40018
|
process.exit(2);
|
|
39949
40019
|
} else if (result.action === "block" || result.action === "warn") {
|
|
@@ -40038,6 +40108,7 @@ async function scanAndInstallStatic(resolved, parsed, config) {
|
|
|
40038
40108
|
);
|
|
40039
40109
|
}
|
|
40040
40110
|
} catch (error) {
|
|
40111
|
+
if (handleTrialExhausted2(error)) return;
|
|
40041
40112
|
const msg = error instanceof Error ? error.message : String(error);
|
|
40042
40113
|
process.stderr.write(
|
|
40043
40114
|
import_chalk4.default.yellow(
|
|
@@ -40053,15 +40124,17 @@ async function scanAndInstallStatic(resolved, parsed, config) {
|
|
|
40053
40124
|
process.stderr.write(
|
|
40054
40125
|
import_chalk4.default.green(
|
|
40055
40126
|
` ${import_chalk4.default.bold("\u2713")} ${toScan.length} package${toScan.length !== 1 ? "s" : ""} scanned \u2014 all clear
|
|
40056
|
-
|
|
40057
40127
|
`
|
|
40058
40128
|
)
|
|
40059
40129
|
);
|
|
40130
|
+
printTrialBanner(result);
|
|
40131
|
+
process.stderr.write("\n");
|
|
40060
40132
|
const code = await runNpm(parsed.rawArgs);
|
|
40061
40133
|
process.exit(code);
|
|
40062
40134
|
}
|
|
40063
40135
|
const output = renderResultStatic(result, config);
|
|
40064
40136
|
process.stdout.write(output + "\n");
|
|
40137
|
+
printTrialBanner(result);
|
|
40065
40138
|
if (result.action === "warn") {
|
|
40066
40139
|
process.stderr.write(
|
|
40067
40140
|
import_chalk4.default.yellow(" Warnings detected. Proceeding with install.\n\n")
|
|
@@ -40380,6 +40453,8 @@ function reducer2(_state, action) {
|
|
|
40380
40453
|
return { phase: "done", exitCode: action.exitCode };
|
|
40381
40454
|
case "ERROR":
|
|
40382
40455
|
return { phase: "error", message: action.message, proceed: action.proceed };
|
|
40456
|
+
case "TRIAL_EXHAUSTED":
|
|
40457
|
+
return { phase: "trial_exhausted" };
|
|
40383
40458
|
}
|
|
40384
40459
|
}
|
|
40385
40460
|
function useNpmWrapper(npmArgs, config) {
|
|
@@ -40484,6 +40559,10 @@ function useNpmWrapper(npmArgs, config) {
|
|
|
40484
40559
|
return;
|
|
40485
40560
|
}
|
|
40486
40561
|
} catch (error) {
|
|
40562
|
+
if (error instanceof TrialExhaustedError) {
|
|
40563
|
+
dispatch({ type: "TRIAL_EXHAUSTED" });
|
|
40564
|
+
return;
|
|
40565
|
+
}
|
|
40487
40566
|
const message = error instanceof Error ? error.message : String(error);
|
|
40488
40567
|
dispatch({ type: "ERROR", message, proceed: true });
|
|
40489
40568
|
dispatch({ type: "INSTALLING" });
|
|
@@ -40858,6 +40937,11 @@ var init_NpmWrapperApp = __esm({
|
|
|
40858
40937
|
const timer = setTimeout(() => exit(), 0);
|
|
40859
40938
|
return () => clearTimeout(timer);
|
|
40860
40939
|
}
|
|
40940
|
+
if (state.phase === "trial_exhausted") {
|
|
40941
|
+
process.exitCode = 1;
|
|
40942
|
+
const timer = setTimeout(() => exit(), 0);
|
|
40943
|
+
return () => clearTimeout(timer);
|
|
40944
|
+
}
|
|
40861
40945
|
if (state.phase === "passthrough") {
|
|
40862
40946
|
}
|
|
40863
40947
|
}, [state, exit]);
|
|
@@ -40957,6 +41041,15 @@ var init_NpmWrapperApp = __esm({
|
|
|
40957
41041
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorView, { error: new Error(state.message) });
|
|
40958
41042
|
case "passthrough":
|
|
40959
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
|
+
] });
|
|
40960
41053
|
}
|
|
40961
41054
|
};
|
|
40962
41055
|
}
|
|
@@ -40979,6 +41072,8 @@ function reducer3(_state, action) {
|
|
|
40979
41072
|
return { phase: "results", result: action.result, durationMs: action.durationMs, skippedCount: action.skippedCount };
|
|
40980
41073
|
case "ERROR":
|
|
40981
41074
|
return { phase: "error", error: action.error };
|
|
41075
|
+
case "TRIAL_EXHAUSTED":
|
|
41076
|
+
return { phase: "trial_exhausted", scansUsed: action.scansUsed, maxScans: action.maxScans };
|
|
40982
41077
|
}
|
|
40983
41078
|
}
|
|
40984
41079
|
function useScan(config) {
|
|
@@ -41037,6 +41132,10 @@ async function runNpmScan(packages, skippedCount, config, dispatch) {
|
|
|
41037
41132
|
});
|
|
41038
41133
|
dispatch({ type: "SCAN_COMPLETE", result, durationMs: Date.now() - startMs, skippedCount });
|
|
41039
41134
|
} catch (error) {
|
|
41135
|
+
if (error instanceof TrialExhaustedError) {
|
|
41136
|
+
dispatch({ type: "TRIAL_EXHAUSTED", scansUsed: error.scansUsed, maxScans: error.maxScans });
|
|
41137
|
+
return;
|
|
41138
|
+
}
|
|
41040
41139
|
dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
|
|
41041
41140
|
}
|
|
41042
41141
|
}
|
|
@@ -41107,6 +41206,10 @@ async function scanProjects(projects, config, dispatch) {
|
|
|
41107
41206
|
};
|
|
41108
41207
|
dispatch({ type: "SCAN_COMPLETE", result: merged, durationMs: merged.durationMs, skippedCount: 0 });
|
|
41109
41208
|
} catch (error) {
|
|
41209
|
+
if (error instanceof TrialExhaustedError) {
|
|
41210
|
+
dispatch({ type: "TRIAL_EXHAUSTED", scansUsed: error.scansUsed, maxScans: error.maxScans });
|
|
41211
|
+
return;
|
|
41212
|
+
}
|
|
41110
41213
|
dispatch({ type: "ERROR", error: error instanceof Error ? error : new Error(String(error)) });
|
|
41111
41214
|
}
|
|
41112
41215
|
}
|
|
@@ -41799,10 +41902,8 @@ var init_App2 = __esm({
|
|
|
41799
41902
|
(0, import_react32.useEffect)(() => {
|
|
41800
41903
|
if (!process.stdout.isTTY) return;
|
|
41801
41904
|
process.stdout.write("\x1B[?1049h");
|
|
41802
|
-
process.stdout.write("\x1B[?1003h");
|
|
41803
41905
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
41804
41906
|
return () => {
|
|
41805
|
-
process.stdout.write("\x1B[?1003l");
|
|
41806
41907
|
process.stdout.write("\x1B[?1049l");
|
|
41807
41908
|
process.stdout.write("\x1B[?25h");
|
|
41808
41909
|
};
|
|
@@ -41837,6 +41938,11 @@ var init_App2 = __esm({
|
|
|
41837
41938
|
const timer = setTimeout(() => exit(), 0);
|
|
41838
41939
|
return () => clearTimeout(timer);
|
|
41839
41940
|
}
|
|
41941
|
+
if (state.phase === "trial_exhausted") {
|
|
41942
|
+
process.exitCode = 1;
|
|
41943
|
+
const timer = setTimeout(() => exit(), 0);
|
|
41944
|
+
return () => clearTimeout(timer);
|
|
41945
|
+
}
|
|
41840
41946
|
}, [state, config, exit]);
|
|
41841
41947
|
const content = (() => {
|
|
41842
41948
|
switch (state.phase) {
|
|
@@ -41887,6 +41993,15 @@ var init_App2 = __esm({
|
|
|
41887
41993
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: state.message });
|
|
41888
41994
|
case "error":
|
|
41889
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
|
+
] });
|
|
41890
42005
|
}
|
|
41891
42006
|
})();
|
|
41892
42007
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { flexDirection: "column", height: termRows, children: content });
|
|
@@ -41899,16 +42014,16 @@ init_config();
|
|
|
41899
42014
|
init_npm_wrapper();
|
|
41900
42015
|
|
|
41901
42016
|
// src/update-check.ts
|
|
41902
|
-
import { readFileSync as
|
|
41903
|
-
import { homedir as
|
|
41904
|
-
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";
|
|
41905
42020
|
import { spawn as spawn2, execSync } from "node:child_process";
|
|
41906
42021
|
var PKG_NAME = "@westbayberry/dg";
|
|
41907
|
-
var CACHE_FILE =
|
|
42022
|
+
var CACHE_FILE = join4(homedir3(), ".dg-update-check.json");
|
|
41908
42023
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
41909
42024
|
function readCache() {
|
|
41910
42025
|
try {
|
|
41911
|
-
const raw =
|
|
42026
|
+
const raw = readFileSync4(CACHE_FILE, "utf-8");
|
|
41912
42027
|
const data = JSON.parse(raw);
|
|
41913
42028
|
if (typeof data.latest === "string" && typeof data.checkedAt === "number") {
|
|
41914
42029
|
return data;
|
|
@@ -41919,7 +42034,7 @@ function readCache() {
|
|
|
41919
42034
|
}
|
|
41920
42035
|
function writeCache(entry) {
|
|
41921
42036
|
try {
|
|
41922
|
-
|
|
42037
|
+
writeFileSync2(CACHE_FILE, JSON.stringify(entry), "utf-8");
|
|
41923
42038
|
} catch {
|
|
41924
42039
|
}
|
|
41925
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",
|