easy-dep-graph 1.1.3 → 1.2.1
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/LICENSE +21 -21
- package/README.md +162 -130
- package/bin/index.js +2180 -455
- package/package.json +74 -67
package/bin/index.js
CHANGED
|
@@ -1,36 +1,1343 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
|
-
import shell from
|
|
3
|
-
import mustache from
|
|
4
|
-
import fastify from
|
|
5
|
-
import open from
|
|
6
|
-
import fs from
|
|
7
|
-
import path from
|
|
8
|
-
import semver from
|
|
2
|
+
import shell from 'shelljs';
|
|
3
|
+
import mustache from 'mustache';
|
|
4
|
+
import fastify from 'fastify';
|
|
5
|
+
import open from 'open';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import semver from 'semver';
|
|
9
9
|
let flatDeps;
|
|
10
|
+
// ─── Known Malicious / Compromised Packages Database ────────────────
|
|
11
|
+
const knownMaliciousPackages = [
|
|
12
|
+
// === Compromised legitimate packages (specific bad versions) ===
|
|
13
|
+
{
|
|
14
|
+
name: 'axios',
|
|
15
|
+
badVersions: ['1.14.1', '0.30.4'],
|
|
16
|
+
severity: 'critical',
|
|
17
|
+
description: 'Compromised via maintainer account hijack (Mar 2026). Injected RAT malware via plain-crypto-js dependency.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'ua-parser-js',
|
|
21
|
+
badVersions: ['0.7.29', '0.8.0', '1.0.0'],
|
|
22
|
+
severity: 'critical',
|
|
23
|
+
description: 'Compromised via account hijack (Oct 2021). Installed crypto miners and Danabot trojan.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'coa',
|
|
27
|
+
badVersions: ['2.0.3', '2.0.4', '2.1.1', '2.1.3', '3.1.3'],
|
|
28
|
+
severity: 'critical',
|
|
29
|
+
description: 'Compromised via account hijack (Nov 2021). Malicious preinstall script downloaded Danabot trojan.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'rc',
|
|
33
|
+
badVersions: ['1.2.9', '1.3.9', '2.3.9'],
|
|
34
|
+
severity: 'critical',
|
|
35
|
+
description: 'Compromised via account hijack (Nov 2021). Same Danabot trojan payload as coa.',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'event-stream',
|
|
39
|
+
badVersions: ['3.3.6'],
|
|
40
|
+
severity: 'critical',
|
|
41
|
+
description: 'Compromised via social-engineered maintainer transfer (Nov 2018). Stole Bitcoin from Copay wallets via flatmap-stream.',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'colors',
|
|
45
|
+
badVersions: ['1.4.1', '1.4.44-liberty-2'],
|
|
46
|
+
severity: 'high',
|
|
47
|
+
description: 'Sabotaged by maintainer (Jan 2022). Infinite loop printing zalgo text causing DoS. Use 1.4.0 instead.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'faker',
|
|
51
|
+
badVersions: ['6.6.6'],
|
|
52
|
+
severity: 'high',
|
|
53
|
+
description: 'Sabotaged by maintainer (Jan 2022). Package contents wiped. Use @faker-js/faker instead.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'node-ipc',
|
|
57
|
+
badVersions: ['10.1.1', '10.1.2', '9.2.2', '11.0.0', '11.1.0'],
|
|
58
|
+
severity: 'critical',
|
|
59
|
+
description: 'Protestware (Mar 2022). Geo-targeted file destruction for Russian/Belarusian IPs. CVE-2022-23812.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'eslint-scope',
|
|
63
|
+
badVersions: ['3.7.2'],
|
|
64
|
+
severity: 'critical',
|
|
65
|
+
description: 'Compromised (Jul 2018). Stole npm tokens from developer machines.',
|
|
66
|
+
},
|
|
67
|
+
// === Always-malicious packages (any version) ===
|
|
68
|
+
{
|
|
69
|
+
name: 'flatmap-stream',
|
|
70
|
+
badVersions: '*',
|
|
71
|
+
severity: 'critical',
|
|
72
|
+
description: 'Malicious package used in event-stream supply chain attack. Stole cryptocurrency credentials.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'plain-crypto-js',
|
|
76
|
+
badVersions: '*',
|
|
77
|
+
severity: 'critical',
|
|
78
|
+
description: 'Malicious dependency injected into compromised axios versions. Delivers cross-platform RAT.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'peacenotwar',
|
|
82
|
+
badVersions: '*',
|
|
83
|
+
severity: 'high',
|
|
84
|
+
description: 'Protestware dropped by compromised node-ipc. Writes files to user desktop.',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'getcookies',
|
|
88
|
+
badVersions: '*',
|
|
89
|
+
severity: 'critical',
|
|
90
|
+
description: 'Backdoor hidden in express middleware (May 2018). Remote code execution.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'crossenv',
|
|
94
|
+
badVersions: '*',
|
|
95
|
+
severity: 'critical',
|
|
96
|
+
description: 'Typosquat of cross-env. Steals environment variables and npm tokens.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'event-strem',
|
|
100
|
+
badVersions: '*',
|
|
101
|
+
severity: 'critical',
|
|
102
|
+
description: 'Typosquat of event-stream. Credential theft.',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'lodahs',
|
|
106
|
+
badVersions: '*',
|
|
107
|
+
severity: 'critical',
|
|
108
|
+
description: 'Typosquat of lodash. Malware dropper.',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'babelcli',
|
|
112
|
+
badVersions: '*',
|
|
113
|
+
severity: 'critical',
|
|
114
|
+
description: 'Typosquat of babel-cli. Steals environment variables.',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'd3.js',
|
|
118
|
+
badVersions: '*',
|
|
119
|
+
severity: 'critical',
|
|
120
|
+
description: 'Typosquat of d3. Credential theft.',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'ffmpegs',
|
|
124
|
+
badVersions: '*',
|
|
125
|
+
severity: 'critical',
|
|
126
|
+
description: 'Typosquat of ffmpeg. Crypto miner.',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'gruntcli',
|
|
130
|
+
badVersions: '*',
|
|
131
|
+
severity: 'critical',
|
|
132
|
+
description: 'Typosquat of grunt-cli. Steals environment variables.',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'http-proxy.js',
|
|
136
|
+
badVersions: '*',
|
|
137
|
+
severity: 'critical',
|
|
138
|
+
description: 'Typosquat of http-proxy. Credential theft.',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'mongose',
|
|
142
|
+
badVersions: '*',
|
|
143
|
+
severity: 'critical',
|
|
144
|
+
description: 'Typosquat of mongoose. Steals environment variables.',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'node-fabric',
|
|
148
|
+
badVersions: '*',
|
|
149
|
+
severity: 'critical',
|
|
150
|
+
description: 'Typosquat of fabric. Opens remote shell.',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'node-opencv',
|
|
154
|
+
badVersions: '*',
|
|
155
|
+
severity: 'critical',
|
|
156
|
+
description: 'Typosquat of opencv. Credential theft.',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'node-opensl',
|
|
160
|
+
badVersions: '*',
|
|
161
|
+
severity: 'critical',
|
|
162
|
+
description: 'Typosquat of openssl. Credential theft.',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'node-openssl',
|
|
166
|
+
badVersions: '*',
|
|
167
|
+
severity: 'critical',
|
|
168
|
+
description: 'Typosquat of openssl. Credential theft.',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'nodecaffe',
|
|
172
|
+
badVersions: '*',
|
|
173
|
+
severity: 'critical',
|
|
174
|
+
description: 'Typosquat of node-caffe. Credential theft.',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'nodefabric',
|
|
178
|
+
badVersions: '*',
|
|
179
|
+
severity: 'critical',
|
|
180
|
+
description: 'Typosquat of node-fabric. Credential theft.',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'nodemailer-js',
|
|
184
|
+
badVersions: '*',
|
|
185
|
+
severity: 'critical',
|
|
186
|
+
description: 'Typosquat of nodemailer. Credential theft.',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'noderequest',
|
|
190
|
+
badVersions: '*',
|
|
191
|
+
severity: 'critical',
|
|
192
|
+
description: 'Typosquat of request. Credential theft.',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'nodesass',
|
|
196
|
+
badVersions: '*',
|
|
197
|
+
severity: 'critical',
|
|
198
|
+
description: 'Typosquat of node-sass. Credential theft.',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'nodesqlite',
|
|
202
|
+
badVersions: '*',
|
|
203
|
+
severity: 'critical',
|
|
204
|
+
description: 'Typosquat of sqlite. Credential theft.',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'shadowsock',
|
|
208
|
+
badVersions: '*',
|
|
209
|
+
severity: 'critical',
|
|
210
|
+
description: 'Typosquat of shadowsocks. Credential theft.',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'sqlite.js',
|
|
214
|
+
badVersions: '*',
|
|
215
|
+
severity: 'critical',
|
|
216
|
+
description: 'Typosquat of sqlite3. Credential theft.',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'proxy.js',
|
|
220
|
+
badVersions: '*',
|
|
221
|
+
severity: 'critical',
|
|
222
|
+
description: 'Typosquat of proxy. Credential theft.',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'discorsd.js',
|
|
226
|
+
badVersions: '*',
|
|
227
|
+
severity: 'critical',
|
|
228
|
+
description: 'Typosquat of discord.js. Token stealer.',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'colored',
|
|
232
|
+
badVersions: '*',
|
|
233
|
+
severity: 'critical',
|
|
234
|
+
description: 'Typosquat of colors. Credential theft.',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'mariadb',
|
|
238
|
+
badVersions: '*',
|
|
239
|
+
severity: 'critical',
|
|
240
|
+
description: 'Typosquat of mariasql. Credential theft.',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'electron-native-notify',
|
|
244
|
+
badVersions: '*',
|
|
245
|
+
severity: 'critical',
|
|
246
|
+
description: 'Malicious package (Jun 2019). Stole cryptocurrency credentials.',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'rest-client',
|
|
250
|
+
badVersions: '*',
|
|
251
|
+
severity: 'critical',
|
|
252
|
+
description: 'Malicious package (Aug 2019). Injected code to steal credentials.',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'bootstrap-sass',
|
|
256
|
+
badVersions: '*',
|
|
257
|
+
severity: 'high',
|
|
258
|
+
description: 'Typosquat with crypto miner.',
|
|
259
|
+
},
|
|
260
|
+
// === TanStack ecosystem compromise (May 2026) ===
|
|
261
|
+
// Malware in 42 @tanstack/* packages exfiltrates cloud credentials, GitHub tokens, and SSH keys
|
|
262
|
+
// CVE-2026-45321: https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx
|
|
263
|
+
{
|
|
264
|
+
name: '@tanstack/arktype-adapter',
|
|
265
|
+
badVersions: ['1.166.12', '1.166.15'],
|
|
266
|
+
severity: 'critical',
|
|
267
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: '@tanstack/eslint-plugin-router',
|
|
271
|
+
badVersions: ['1.161.9', '1.161.12'],
|
|
272
|
+
severity: 'critical',
|
|
273
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: '@tanstack/eslint-plugin-start',
|
|
277
|
+
badVersions: ['0.0.4', '0.0.7'],
|
|
278
|
+
severity: 'critical',
|
|
279
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: '@tanstack/history',
|
|
283
|
+
badVersions: ['1.161.9', '1.161.12'],
|
|
284
|
+
severity: 'critical',
|
|
285
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: '@tanstack/nitro-v2-vite-plugin',
|
|
289
|
+
badVersions: ['1.154.12', '1.154.15'],
|
|
290
|
+
severity: 'critical',
|
|
291
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: '@tanstack/react-router',
|
|
295
|
+
badVersions: ['1.169.5', '1.169.8'],
|
|
296
|
+
severity: 'critical',
|
|
297
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: '@tanstack/react-router-devtools',
|
|
301
|
+
badVersions: ['1.166.16', '1.166.19'],
|
|
302
|
+
severity: 'critical',
|
|
303
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: '@tanstack/react-router-ssr-query',
|
|
307
|
+
badVersions: ['1.166.15', '1.166.18'],
|
|
308
|
+
severity: 'critical',
|
|
309
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: '@tanstack/react-start',
|
|
313
|
+
badVersions: ['1.167.68', '1.167.71'],
|
|
314
|
+
severity: 'critical',
|
|
315
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: '@tanstack/react-start-client',
|
|
319
|
+
badVersions: ['1.166.51', '1.166.54'],
|
|
320
|
+
severity: 'critical',
|
|
321
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: '@tanstack/react-start-rsc',
|
|
325
|
+
badVersions: ['0.0.47', '0.0.50'],
|
|
326
|
+
severity: 'critical',
|
|
327
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: '@tanstack/react-start-server',
|
|
331
|
+
badVersions: ['1.166.55', '1.166.58'],
|
|
332
|
+
severity: 'critical',
|
|
333
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: '@tanstack/router-cli',
|
|
337
|
+
badVersions: ['1.166.46', '1.166.49'],
|
|
338
|
+
severity: 'critical',
|
|
339
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: '@tanstack/router-core',
|
|
343
|
+
badVersions: ['1.169.5', '1.169.8'],
|
|
344
|
+
severity: 'critical',
|
|
345
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: '@tanstack/router-devtools',
|
|
349
|
+
badVersions: ['1.166.16', '1.166.19'],
|
|
350
|
+
severity: 'critical',
|
|
351
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: '@tanstack/router-devtools-core',
|
|
355
|
+
badVersions: ['1.167.6', '1.167.9'],
|
|
356
|
+
severity: 'critical',
|
|
357
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: '@tanstack/router-generator',
|
|
361
|
+
badVersions: ['1.166.45', '1.166.48'],
|
|
362
|
+
severity: 'critical',
|
|
363
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: '@tanstack/router-plugin',
|
|
367
|
+
badVersions: ['1.167.38', '1.167.41'],
|
|
368
|
+
severity: 'critical',
|
|
369
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: '@tanstack/router-ssr-query-core',
|
|
373
|
+
badVersions: ['1.168.3', '1.168.6'],
|
|
374
|
+
severity: 'critical',
|
|
375
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: '@tanstack/router-utils',
|
|
379
|
+
badVersions: ['1.161.11', '1.161.14'],
|
|
380
|
+
severity: 'critical',
|
|
381
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: '@tanstack/router-vite-plugin',
|
|
385
|
+
badVersions: ['1.166.53', '1.166.56'],
|
|
386
|
+
severity: 'critical',
|
|
387
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: '@tanstack/solid-router',
|
|
391
|
+
badVersions: ['1.169.5', '1.169.8'],
|
|
392
|
+
severity: 'critical',
|
|
393
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: '@tanstack/solid-router-devtools',
|
|
397
|
+
badVersions: ['1.166.16', '1.166.19'],
|
|
398
|
+
severity: 'critical',
|
|
399
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: '@tanstack/solid-router-ssr-query',
|
|
403
|
+
badVersions: ['1.166.15', '1.166.18'],
|
|
404
|
+
severity: 'critical',
|
|
405
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: '@tanstack/solid-start',
|
|
409
|
+
badVersions: ['1.167.65', '1.167.68'],
|
|
410
|
+
severity: 'critical',
|
|
411
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: '@tanstack/solid-start-client',
|
|
415
|
+
badVersions: ['1.166.50', '1.166.53'],
|
|
416
|
+
severity: 'critical',
|
|
417
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: '@tanstack/solid-start-server',
|
|
421
|
+
badVersions: ['1.166.54', '1.166.57'],
|
|
422
|
+
severity: 'critical',
|
|
423
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: '@tanstack/start-client-core',
|
|
427
|
+
badVersions: ['1.168.5', '1.168.8'],
|
|
428
|
+
severity: 'critical',
|
|
429
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: '@tanstack/start-fn-stubs',
|
|
433
|
+
badVersions: ['1.161.9', '1.161.12'],
|
|
434
|
+
severity: 'critical',
|
|
435
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
name: '@tanstack/start-plugin-core',
|
|
439
|
+
badVersions: ['1.169.23', '1.169.26'],
|
|
440
|
+
severity: 'critical',
|
|
441
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: '@tanstack/start-server-core',
|
|
445
|
+
badVersions: ['1.167.33', '1.167.36'],
|
|
446
|
+
severity: 'critical',
|
|
447
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: '@tanstack/start-static-server-functions',
|
|
451
|
+
badVersions: ['1.166.44', '1.166.47'],
|
|
452
|
+
severity: 'critical',
|
|
453
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: '@tanstack/start-storage-context',
|
|
457
|
+
badVersions: ['1.166.38', '1.166.41'],
|
|
458
|
+
severity: 'critical',
|
|
459
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
name: '@tanstack/valibot-adapter',
|
|
463
|
+
badVersions: ['1.166.12', '1.166.15'],
|
|
464
|
+
severity: 'critical',
|
|
465
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
name: '@tanstack/virtual-file-routes',
|
|
469
|
+
badVersions: ['1.161.10', '1.161.13'],
|
|
470
|
+
severity: 'critical',
|
|
471
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: '@tanstack/vue-router',
|
|
475
|
+
badVersions: ['1.169.5', '1.169.8'],
|
|
476
|
+
severity: 'critical',
|
|
477
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: '@tanstack/vue-router-devtools',
|
|
481
|
+
badVersions: ['1.166.16', '1.166.19'],
|
|
482
|
+
severity: 'critical',
|
|
483
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: '@tanstack/vue-router-ssr-query',
|
|
487
|
+
badVersions: ['1.166.15', '1.166.18'],
|
|
488
|
+
severity: 'critical',
|
|
489
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: '@tanstack/vue-start',
|
|
493
|
+
badVersions: ['1.167.61', '1.167.64'],
|
|
494
|
+
severity: 'critical',
|
|
495
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
name: '@tanstack/vue-start-client',
|
|
499
|
+
badVersions: ['1.166.46', '1.166.49'],
|
|
500
|
+
severity: 'critical',
|
|
501
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: '@tanstack/vue-start-server',
|
|
505
|
+
badVersions: ['1.166.50', '1.166.53'],
|
|
506
|
+
severity: 'critical',
|
|
507
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: '@tanstack/zod-adapter',
|
|
511
|
+
badVersions: ['1.166.12', '1.166.15'],
|
|
512
|
+
severity: 'critical',
|
|
513
|
+
description: 'Compromised (May 2026). Malware exfiltrates AWS/GCP credentials, GitHub tokens, SSH keys, and npm tokens. CVE-2026-45321.',
|
|
514
|
+
},
|
|
515
|
+
// === Mistral AI compromise (May 2026) ===
|
|
516
|
+
// Part of "Mini Shai-Hulud is back" worm by TeamPCP threat actor (GHSA-3q49-cfcf-g5fm)
|
|
517
|
+
{
|
|
518
|
+
name: '@mistralai/mistralai',
|
|
519
|
+
badVersions: ['2.2.2', '2.2.3', '2.2.4'],
|
|
520
|
+
severity: 'critical',
|
|
521
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm. Steals credentials and propagates to accessed packages. Full system compromise. GHSA-3q49-cfcf-g5fm.',
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: '@mistralai/mistralai-azure',
|
|
525
|
+
badVersions: ['1.7.1', '1.7.2', '1.7.3'],
|
|
526
|
+
severity: 'critical',
|
|
527
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm. Malware exfiltrates credentials and propagates to accessed packages.',
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: '@mistralai/mistralai-gcp',
|
|
531
|
+
badVersions: ['1.7.1', '1.7.2', '1.7.3'],
|
|
532
|
+
severity: 'critical',
|
|
533
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm. Malware exfiltrates credentials and propagates to accessed packages.',
|
|
534
|
+
},
|
|
535
|
+
// === UIPath supply chain compromise (May 2026) ===
|
|
536
|
+
{
|
|
537
|
+
name: '@uipath/packager-tool-functions',
|
|
538
|
+
badVersions: ['0.1.1'],
|
|
539
|
+
severity: 'critical',
|
|
540
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
name: '@uipath/docsai-tool',
|
|
544
|
+
badVersions: ['1.0.1'],
|
|
545
|
+
severity: 'critical',
|
|
546
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: '@uipath/context-grounding-tool',
|
|
550
|
+
badVersions: ['0.1.1'],
|
|
551
|
+
severity: 'critical',
|
|
552
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: '@uipath/apollo-core',
|
|
556
|
+
badVersions: ['5.9.2'],
|
|
557
|
+
severity: 'critical',
|
|
558
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
name: '@uipath/flow-tool',
|
|
562
|
+
badVersions: ['1.0.2'],
|
|
563
|
+
severity: 'critical',
|
|
564
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
name: '@uipath/maestro-tool',
|
|
568
|
+
badVersions: ['1.0.1'],
|
|
569
|
+
severity: 'critical',
|
|
570
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
name: '@uipath/robot',
|
|
574
|
+
badVersions: ['1.3.4'],
|
|
575
|
+
severity: 'critical',
|
|
576
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: '@uipath/integrationservice-tool',
|
|
580
|
+
badVersions: ['1.0.2'],
|
|
581
|
+
severity: 'critical',
|
|
582
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
name: '@uipath/agent-tool',
|
|
586
|
+
badVersions: ['1.0.1'],
|
|
587
|
+
severity: 'critical',
|
|
588
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: '@uipath/access-policy-sdk',
|
|
592
|
+
badVersions: ['0.3.1'],
|
|
593
|
+
severity: 'critical',
|
|
594
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
name: '@uipath/rpa-tool',
|
|
598
|
+
badVersions: ['0.9.5'],
|
|
599
|
+
severity: 'critical',
|
|
600
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
name: '@uipath/apollo-wind',
|
|
604
|
+
badVersions: ['2.16.2'],
|
|
605
|
+
severity: 'critical',
|
|
606
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: '@uipath/widget.sdk',
|
|
610
|
+
badVersions: ['1.2.3'],
|
|
611
|
+
severity: 'critical',
|
|
612
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
name: '@uipath/common',
|
|
616
|
+
badVersions: ['1.0.1'],
|
|
617
|
+
severity: 'critical',
|
|
618
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
name: '@uipath/functions-tool',
|
|
622
|
+
badVersions: ['1.0.1'],
|
|
623
|
+
severity: 'critical',
|
|
624
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
name: '@uipath/cli',
|
|
628
|
+
badVersions: ['1.0.1'],
|
|
629
|
+
severity: 'critical',
|
|
630
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
name: '@uipath/test-manager-tool',
|
|
634
|
+
badVersions: ['1.0.2'],
|
|
635
|
+
severity: 'critical',
|
|
636
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
name: '@uipath/packager-tool-apiworkflow',
|
|
640
|
+
badVersions: ['0.0.19'],
|
|
641
|
+
severity: 'critical',
|
|
642
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
name: '@uipath/insights-sdk',
|
|
646
|
+
badVersions: ['1.0.1'],
|
|
647
|
+
severity: 'critical',
|
|
648
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
name: '@uipath/rpa-legacy-tool',
|
|
652
|
+
badVersions: ['1.0.1'],
|
|
653
|
+
severity: 'critical',
|
|
654
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
name: '@uipath/solution-packager',
|
|
658
|
+
badVersions: ['0.0.35'],
|
|
659
|
+
severity: 'critical',
|
|
660
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
name: '@uipath/api-workflow-tool',
|
|
664
|
+
badVersions: ['1.0.1'],
|
|
665
|
+
severity: 'critical',
|
|
666
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
name: '@uipath/resources-tool',
|
|
670
|
+
badVersions: ['0.1.11'],
|
|
671
|
+
severity: 'critical',
|
|
672
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
name: '@uipath/ui-widgets-multi-file-upload',
|
|
676
|
+
badVersions: ['1.0.1'],
|
|
677
|
+
severity: 'critical',
|
|
678
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
name: '@uipath/codedagents-tool',
|
|
682
|
+
badVersions: ['0.1.12'],
|
|
683
|
+
severity: 'critical',
|
|
684
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
name: '@uipath/admin-tool',
|
|
688
|
+
badVersions: ['0.1.1'],
|
|
689
|
+
severity: 'critical',
|
|
690
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
name: '@uipath/tool-workflowcompiler',
|
|
694
|
+
badVersions: ['0.0.12'],
|
|
695
|
+
severity: 'critical',
|
|
696
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
name: '@uipath/telemetry',
|
|
700
|
+
badVersions: ['0.0.7'],
|
|
701
|
+
severity: 'critical',
|
|
702
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: '@uipath/resourcecatalog-tool',
|
|
706
|
+
badVersions: ['0.1.1'],
|
|
707
|
+
severity: 'critical',
|
|
708
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
name: '@uipath/aops-policy-tool',
|
|
712
|
+
badVersions: ['0.3.1'],
|
|
713
|
+
severity: 'critical',
|
|
714
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
name: '@uipath/identity-tool',
|
|
718
|
+
badVersions: ['0.1.1'],
|
|
719
|
+
severity: 'critical',
|
|
720
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: '@uipath/packager-tool-bpmn',
|
|
724
|
+
badVersions: ['0.0.9'],
|
|
725
|
+
severity: 'critical',
|
|
726
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
name: '@uipath/case-tool',
|
|
730
|
+
badVersions: ['1.0.1'],
|
|
731
|
+
severity: 'critical',
|
|
732
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
name: '@uipath/ap-chat',
|
|
736
|
+
badVersions: ['1.5.7'],
|
|
737
|
+
severity: 'critical',
|
|
738
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
name: '@uipath/solutionpackager-sdk',
|
|
742
|
+
badVersions: ['1.0.11'],
|
|
743
|
+
severity: 'critical',
|
|
744
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: '@uipath/agent-sdk',
|
|
748
|
+
badVersions: ['1.0.2'],
|
|
749
|
+
severity: 'critical',
|
|
750
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
name: '@uipath/vss',
|
|
754
|
+
badVersions: ['0.1.6'],
|
|
755
|
+
severity: 'critical',
|
|
756
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
name: '@uipath/solution-tool',
|
|
760
|
+
badVersions: ['1.0.1'],
|
|
761
|
+
severity: 'critical',
|
|
762
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
name: '@uipath/maestro-sdk',
|
|
766
|
+
badVersions: ['1.0.1'],
|
|
767
|
+
severity: 'critical',
|
|
768
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
name: '@uipath/packager-tool-workflowcompiler',
|
|
772
|
+
badVersions: ['0.0.16'],
|
|
773
|
+
severity: 'critical',
|
|
774
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: '@uipath/data-fabric-tool',
|
|
778
|
+
badVersions: ['1.0.2'],
|
|
779
|
+
severity: 'critical',
|
|
780
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: '@uipath/project-packager',
|
|
784
|
+
badVersions: ['1.1.16'],
|
|
785
|
+
severity: 'critical',
|
|
786
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
name: '@uipath/orchestrator-tool',
|
|
790
|
+
badVersions: ['1.0.1'],
|
|
791
|
+
severity: 'critical',
|
|
792
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
name: '@uipath/packager-tool-connector',
|
|
796
|
+
badVersions: ['0.0.19'],
|
|
797
|
+
severity: 'critical',
|
|
798
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
name: '@uipath/tasks-tool',
|
|
802
|
+
badVersions: ['1.0.1'],
|
|
803
|
+
severity: 'critical',
|
|
804
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
name: '@uipath/packager-tool-flow',
|
|
808
|
+
badVersions: ['0.0.19'],
|
|
809
|
+
severity: 'critical',
|
|
810
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
name: '@uipath/integrationservice-sdk',
|
|
814
|
+
badVersions: ['1.0.2'],
|
|
815
|
+
severity: 'critical',
|
|
816
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
name: '@uipath/solutionpackager-tool-core',
|
|
820
|
+
badVersions: ['0.0.34'],
|
|
821
|
+
severity: 'critical',
|
|
822
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
name: '@uipath/vertical-solutions-tool',
|
|
826
|
+
badVersions: ['1.0.1'],
|
|
827
|
+
severity: 'critical',
|
|
828
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
name: '@uipath/insights-tool',
|
|
832
|
+
badVersions: ['1.0.1'],
|
|
833
|
+
severity: 'critical',
|
|
834
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
name: '@uipath/auth',
|
|
838
|
+
badVersions: ['1.0.1'],
|
|
839
|
+
severity: 'critical',
|
|
840
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
name: '@uipath/llmgw-tool',
|
|
844
|
+
badVersions: ['1.0.1'],
|
|
845
|
+
severity: 'critical',
|
|
846
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
name: '@uipath/packager-tool-workflowcompiler-browser',
|
|
850
|
+
badVersions: ['0.0.34'],
|
|
851
|
+
severity: 'critical',
|
|
852
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
name: '@uipath/platform-tool',
|
|
856
|
+
badVersions: ['1.0.1'],
|
|
857
|
+
severity: 'critical',
|
|
858
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
name: '@uipath/codedagent-tool',
|
|
862
|
+
badVersions: ['1.0.1'],
|
|
863
|
+
severity: 'critical',
|
|
864
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
name: '@uipath/codedapp-tool',
|
|
868
|
+
badVersions: ['1.0.1'],
|
|
869
|
+
severity: 'critical',
|
|
870
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
name: '@uipath/resource-tool',
|
|
874
|
+
badVersions: ['1.0.1'],
|
|
875
|
+
severity: 'critical',
|
|
876
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
name: '@uipath/gov-tool',
|
|
880
|
+
badVersions: ['0.3.1'],
|
|
881
|
+
severity: 'critical',
|
|
882
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
name: '@uipath/access-policy-tool',
|
|
886
|
+
badVersions: ['0.3.1'],
|
|
887
|
+
severity: 'critical',
|
|
888
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
name: '@uipath/packager-tool-case',
|
|
892
|
+
badVersions: ['0.0.9'],
|
|
893
|
+
severity: 'critical',
|
|
894
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
name: '@uipath/packager-tool-webapp',
|
|
898
|
+
badVersions: ['1.0.6'],
|
|
899
|
+
severity: 'critical',
|
|
900
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
name: '@uipath/traces-tool',
|
|
904
|
+
badVersions: ['1.0.1'],
|
|
905
|
+
severity: 'critical',
|
|
906
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
name: '@uipath/filesystem',
|
|
910
|
+
badVersions: ['1.0.1'],
|
|
911
|
+
severity: 'critical',
|
|
912
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
name: '@uipath/uipath-python-bridge',
|
|
916
|
+
badVersions: ['1.0.1'],
|
|
917
|
+
severity: 'critical',
|
|
918
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
name: '@uipath/apollo-react',
|
|
922
|
+
badVersions: ['4.24.5'],
|
|
923
|
+
severity: 'critical',
|
|
924
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
name: '@uipath/agent.sdk',
|
|
928
|
+
badVersions: ['0.0.18'],
|
|
929
|
+
severity: 'critical',
|
|
930
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
931
|
+
},
|
|
932
|
+
// === ML Toolkit compromise (May 2026) ===
|
|
933
|
+
{
|
|
934
|
+
name: '@ml-toolkit-ts/xgboost',
|
|
935
|
+
badVersions: ['1.0.3', '1.0.4'],
|
|
936
|
+
severity: 'critical',
|
|
937
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
name: 'ml-toolkit-ts',
|
|
941
|
+
badVersions: ['1.0.4', '1.0.5'],
|
|
942
|
+
severity: 'critical',
|
|
943
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
name: '@ml-toolkit-ts/preprocessing',
|
|
947
|
+
badVersions: ['1.0.2', '1.0.3'],
|
|
948
|
+
severity: 'critical',
|
|
949
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
950
|
+
},
|
|
951
|
+
// === Other ecosystem compromises (May 2026) ===
|
|
952
|
+
{
|
|
953
|
+
name: '@supersurkhet/cli',
|
|
954
|
+
badVersions: ['0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7'],
|
|
955
|
+
severity: 'critical',
|
|
956
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
name: '@supersurkhet/sdk',
|
|
960
|
+
badVersions: ['0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7'],
|
|
961
|
+
severity: 'critical',
|
|
962
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
name: '@taskflow-corp/cli',
|
|
966
|
+
badVersions: ['0.1.24', '0.1.25', '0.1.26', '0.1.27', '0.1.28', '0.1.29'],
|
|
967
|
+
severity: 'critical',
|
|
968
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
name: 'safe-action',
|
|
972
|
+
badVersions: ['0.8.3', '0.8.4'],
|
|
973
|
+
severity: 'critical',
|
|
974
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
name: '@draftlab/auth',
|
|
978
|
+
badVersions: ['0.24.1', '0.24.2'],
|
|
979
|
+
severity: 'critical',
|
|
980
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
name: '@draftlab/auth-router',
|
|
984
|
+
badVersions: ['0.5.1', '0.5.2'],
|
|
985
|
+
severity: 'critical',
|
|
986
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
name: '@draftlab/db',
|
|
990
|
+
badVersions: ['0.16.1', '0.16.2'],
|
|
991
|
+
severity: 'critical',
|
|
992
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
name: '@tolka/cli',
|
|
996
|
+
badVersions: ['1.0.2', '1.0.3', '1.0.4', '1.0.5', '1.0.6'],
|
|
997
|
+
severity: 'critical',
|
|
998
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
name: '@dirigible-ai/sdk',
|
|
1002
|
+
badVersions: ['0.6.2', '0.6.3'],
|
|
1003
|
+
severity: 'critical',
|
|
1004
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
name: '@beproduct/nestjs-auth',
|
|
1008
|
+
badVersions: [
|
|
1009
|
+
'0.1.2',
|
|
1010
|
+
'0.1.3',
|
|
1011
|
+
'0.1.4',
|
|
1012
|
+
'0.1.5',
|
|
1013
|
+
'0.1.6',
|
|
1014
|
+
'0.1.7',
|
|
1015
|
+
'0.1.8',
|
|
1016
|
+
'0.1.9',
|
|
1017
|
+
'0.1.10',
|
|
1018
|
+
'0.1.11',
|
|
1019
|
+
'0.1.12',
|
|
1020
|
+
'0.1.13',
|
|
1021
|
+
'0.1.14',
|
|
1022
|
+
'0.1.15',
|
|
1023
|
+
'0.1.16',
|
|
1024
|
+
'0.1.17',
|
|
1025
|
+
'0.1.18',
|
|
1026
|
+
'0.1.19',
|
|
1027
|
+
],
|
|
1028
|
+
severity: 'critical',
|
|
1029
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
name: 'cmux-agent-mcp',
|
|
1033
|
+
badVersions: ['0.1.3', '0.1.4', '0.1.5', '0.1.6', '0.1.7', '0.1.8'],
|
|
1034
|
+
severity: 'critical',
|
|
1035
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: 'git-branch-selector',
|
|
1039
|
+
badVersions: ['1.3.3', '1.3.4', '1.3.5', '1.3.6', '1.3.7'],
|
|
1040
|
+
severity: 'critical',
|
|
1041
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
name: 'agentwork-cli',
|
|
1045
|
+
badVersions: ['0.1.4', '0.1.5'],
|
|
1046
|
+
severity: 'critical',
|
|
1047
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: '@draftauth/core',
|
|
1051
|
+
badVersions: ['0.13.1', '0.13.2'],
|
|
1052
|
+
severity: 'critical',
|
|
1053
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
name: '@draftauth/client',
|
|
1057
|
+
badVersions: ['0.2.1', '0.2.2'],
|
|
1058
|
+
severity: 'critical',
|
|
1059
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
name: 'git-git-git',
|
|
1063
|
+
badVersions: ['1.0.8', '1.0.9', '1.0.10', '1.0.11', '1.0.12'],
|
|
1064
|
+
severity: 'critical',
|
|
1065
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
name: 'nextmove-mcp',
|
|
1069
|
+
badVersions: ['0.1.3', '0.1.4', '0.1.5', '0.1.6', '0.1.7'],
|
|
1070
|
+
severity: 'critical',
|
|
1071
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
name: 'cross-stitch',
|
|
1075
|
+
badVersions: ['1.1.3', '1.1.4', '1.1.5', '1.1.6', '1.1.7'],
|
|
1076
|
+
severity: 'critical',
|
|
1077
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1078
|
+
},
|
|
1079
|
+
// === Squawk ecosystem compromise (May 2026) ===
|
|
1080
|
+
{
|
|
1081
|
+
name: '@squawk/fix-data',
|
|
1082
|
+
badVersions: ['0.6.4', '0.6.5', '0.6.6', '0.6.7', '0.6.8'],
|
|
1083
|
+
severity: 'critical',
|
|
1084
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
name: '@squawk/weather',
|
|
1088
|
+
badVersions: ['0.5.6', '0.5.7', '0.5.8', '0.5.9', '0.5.10'],
|
|
1089
|
+
severity: 'critical',
|
|
1090
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
name: '@squawk/icao-registry-data',
|
|
1094
|
+
badVersions: ['0.8.4', '0.8.5', '0.8.6', '0.8.7', '0.8.8'],
|
|
1095
|
+
severity: 'critical',
|
|
1096
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
name: '@squawk/airport-data',
|
|
1100
|
+
badVersions: ['0.7.4', '0.7.5', '0.7.6', '0.7.7', '0.7.8'],
|
|
1101
|
+
severity: 'critical',
|
|
1102
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
name: '@squawk/flightplan',
|
|
1106
|
+
badVersions: ['0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6'],
|
|
1107
|
+
severity: 'critical',
|
|
1108
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
name: '@squawk/units',
|
|
1112
|
+
badVersions: ['0.4.3', '0.4.4', '0.4.5', '0.4.6', '0.4.7'],
|
|
1113
|
+
severity: 'critical',
|
|
1114
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
name: '@squawk/flight-math',
|
|
1118
|
+
badVersions: ['0.5.4', '0.5.5', '0.5.6', '0.5.7', '0.5.8'],
|
|
1119
|
+
severity: 'critical',
|
|
1120
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
name: '@squawk/fixes',
|
|
1124
|
+
badVersions: ['0.3.2', '0.3.3', '0.3.4', '0.3.5', '0.3.6'],
|
|
1125
|
+
severity: 'critical',
|
|
1126
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
name: '@squawk/airspace-data',
|
|
1130
|
+
badVersions: ['0.5.3', '0.5.4', '0.5.5', '0.5.6', '0.5.7'],
|
|
1131
|
+
severity: 'critical',
|
|
1132
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
name: '@squawk/procedure-data',
|
|
1136
|
+
badVersions: ['0.7.3', '0.7.4', '0.7.5', '0.7.6', '0.7.7'],
|
|
1137
|
+
severity: 'critical',
|
|
1138
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
name: '@squawk/navaids',
|
|
1142
|
+
badVersions: ['0.4.2', '0.4.3', '0.4.4', '0.4.5', '0.4.6'],
|
|
1143
|
+
severity: 'critical',
|
|
1144
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
name: '@squawk/notams',
|
|
1148
|
+
badVersions: ['0.3.6', '0.3.7', '0.3.8', '0.3.9', '0.3.10'],
|
|
1149
|
+
severity: 'critical',
|
|
1150
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
name: '@squawk/airways',
|
|
1154
|
+
badVersions: ['0.4.2', '0.4.3', '0.4.4', '0.4.5', '0.4.6'],
|
|
1155
|
+
severity: 'critical',
|
|
1156
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
name: '@squawk/airports',
|
|
1160
|
+
badVersions: ['0.6.2', '0.6.3', '0.6.4', '0.6.5', '0.6.6'],
|
|
1161
|
+
severity: 'critical',
|
|
1162
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
name: '@squawk/icao-registry',
|
|
1166
|
+
badVersions: ['0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6'],
|
|
1167
|
+
severity: 'critical',
|
|
1168
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
name: '@squawk/airspace',
|
|
1172
|
+
badVersions: ['0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5'],
|
|
1173
|
+
severity: 'critical',
|
|
1174
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
name: '@squawk/geo',
|
|
1178
|
+
badVersions: ['0.4.4', '0.4.5', '0.4.6', '0.4.7', '0.4.8'],
|
|
1179
|
+
severity: 'critical',
|
|
1180
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
name: '@squawk/navaid-data',
|
|
1184
|
+
badVersions: ['0.6.4', '0.6.5', '0.6.6', '0.6.7', '0.6.8'],
|
|
1185
|
+
severity: 'critical',
|
|
1186
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
name: '@squawk/airway-data',
|
|
1190
|
+
badVersions: ['0.5.4', '0.5.5', '0.5.6', '0.5.7', '0.5.8'],
|
|
1191
|
+
severity: 'critical',
|
|
1192
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
name: '@squawk/mcp',
|
|
1196
|
+
badVersions: ['0.9.1', '0.9.2', '0.9.3', '0.9.4', '0.9.5'],
|
|
1197
|
+
severity: 'critical',
|
|
1198
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
name: '@squawk/procedures',
|
|
1202
|
+
badVersions: ['0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6'],
|
|
1203
|
+
severity: 'critical',
|
|
1204
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
name: '@squawk/types',
|
|
1208
|
+
badVersions: ['0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5'],
|
|
1209
|
+
severity: 'critical',
|
|
1210
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1211
|
+
},
|
|
1212
|
+
// === Additional compromise (May 2026) ===
|
|
1213
|
+
{
|
|
1214
|
+
name: 'ts-dna',
|
|
1215
|
+
badVersions: ['3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5'],
|
|
1216
|
+
severity: 'critical',
|
|
1217
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1218
|
+
},
|
|
1219
|
+
// === TallyUI ecosystem compromise (May 2026) ===
|
|
1220
|
+
{
|
|
1221
|
+
name: '@tallyui/pos',
|
|
1222
|
+
badVersions: ['0.1.1', '0.1.2', '0.1.3'],
|
|
1223
|
+
severity: 'critical',
|
|
1224
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
name: '@tallyui/connector-vendure',
|
|
1228
|
+
badVersions: ['1.0.1', '1.0.2', '1.0.3'],
|
|
1229
|
+
severity: 'critical',
|
|
1230
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
name: '@tallyui/connector-shopify',
|
|
1234
|
+
badVersions: ['1.0.1', '1.0.2', '1.0.3'],
|
|
1235
|
+
severity: 'critical',
|
|
1236
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
name: '@tallyui/components',
|
|
1240
|
+
badVersions: ['1.0.1', '1.0.2', '1.0.3'],
|
|
1241
|
+
severity: 'critical',
|
|
1242
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
name: '@tallyui/theme',
|
|
1246
|
+
badVersions: ['0.2.1', '0.2.2', '0.2.3'],
|
|
1247
|
+
severity: 'critical',
|
|
1248
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
name: 'wot-api',
|
|
1252
|
+
badVersions: ['0.8.1', '0.8.2', '0.8.3', '0.8.4'],
|
|
1253
|
+
severity: 'critical',
|
|
1254
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
name: '@tallyui/storage-sqlite',
|
|
1258
|
+
badVersions: ['0.2.1', '0.2.2', '0.2.3'],
|
|
1259
|
+
severity: 'critical',
|
|
1260
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
name: '@tallyui/connector-woocommerce',
|
|
1264
|
+
badVersions: ['1.0.1', '1.0.2', '1.0.3'],
|
|
1265
|
+
severity: 'critical',
|
|
1266
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1267
|
+
},
|
|
1268
|
+
{
|
|
1269
|
+
name: '@tallyui/database',
|
|
1270
|
+
badVersions: ['1.0.1', '1.0.2', '1.0.3'],
|
|
1271
|
+
severity: 'critical',
|
|
1272
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
name: '@tallyui/connector-medusa',
|
|
1276
|
+
badVersions: ['1.0.1', '1.0.2', '1.0.3'],
|
|
1277
|
+
severity: 'critical',
|
|
1278
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
name: '@tallyui/core',
|
|
1282
|
+
badVersions: ['0.2.1', '0.2.2', '0.2.3'],
|
|
1283
|
+
severity: 'critical',
|
|
1284
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1285
|
+
},
|
|
1286
|
+
// === Mesa/MesaDev compromise (May 2026) ===
|
|
1287
|
+
{
|
|
1288
|
+
name: '@mesadev/saguaro',
|
|
1289
|
+
badVersions: ['0.4.22'],
|
|
1290
|
+
severity: 'critical',
|
|
1291
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
name: '@mesadev/sdk',
|
|
1295
|
+
badVersions: ['0.28.3'],
|
|
1296
|
+
severity: 'critical',
|
|
1297
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
name: '@mesadev/rest',
|
|
1301
|
+
badVersions: ['0.28.3'],
|
|
1302
|
+
severity: 'critical',
|
|
1303
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1304
|
+
},
|
|
1305
|
+
// === OpenSearch compromise (May 2026) ===
|
|
1306
|
+
{
|
|
1307
|
+
name: '@opensearch-project/opensearch',
|
|
1308
|
+
badVersions: ['3.5.3', '3.6.2', '3.7.0', '3.8.0'],
|
|
1309
|
+
severity: 'critical',
|
|
1310
|
+
description: 'Compromised (May 2026). Part of "Mini Shai-Hulud is back" worm.',
|
|
1311
|
+
},
|
|
1312
|
+
];
|
|
10
1313
|
void (async function main() {
|
|
11
1314
|
// Check if peer dependencies mode
|
|
12
|
-
const peerDependenciesMode = process.argv.findIndex((a) => a.toLowerCase() ===
|
|
13
|
-
-1;
|
|
1315
|
+
const peerDependenciesMode = process.argv.findIndex((a) => a.toLowerCase() === '--peer-dependencies') !== -1;
|
|
14
1316
|
// List of packages user wants to see the dependencies
|
|
15
|
-
const packagesArgIndex = process.argv.findIndex((a) => a.toLowerCase() ===
|
|
1317
|
+
const packagesArgIndex = process.argv.findIndex((a) => a.toLowerCase() === '--packages');
|
|
16
1318
|
const packagesFilter = packagesArgIndex !== -1 ? process.argv[packagesArgIndex + 1] : undefined;
|
|
17
1319
|
// Only get the dependents of a specific package
|
|
18
|
-
const packageArgIndex = process.argv.findIndex((a) => a.toLowerCase() ===
|
|
1320
|
+
const packageArgIndex = process.argv.findIndex((a) => a.toLowerCase() === '--package-dependents');
|
|
19
1321
|
const packageName = packageArgIndex !== -1 ? process.argv[packageArgIndex + 1] : undefined;
|
|
20
1322
|
// Get port number
|
|
21
|
-
const portArgIndex = process.argv.findIndex((a) => a.toLowerCase() ===
|
|
1323
|
+
const portArgIndex = process.argv.findIndex((a) => a.toLowerCase() === '--port');
|
|
22
1324
|
const port = portArgIndex !== -1 ? +process.argv[portArgIndex + 1] : 8080;
|
|
23
1325
|
// Get if should not open browser
|
|
24
|
-
const shouldOpenBrowser = process.argv.findIndex((a) => a.toLowerCase() ===
|
|
1326
|
+
const shouldOpenBrowser = process.argv.findIndex((a) => a.toLowerCase() === '--no-open') === -1;
|
|
25
1327
|
// Get if should not apply force layout
|
|
26
|
-
const shouldApplyForceLayout = process.argv.findIndex((a) => a.toLowerCase() ===
|
|
27
|
-
|
|
1328
|
+
const shouldApplyForceLayout = process.argv.findIndex((a) => a.toLowerCase() === '--no-force-layout') === -1;
|
|
1329
|
+
// Check if security scan mode
|
|
1330
|
+
const securityScanMode = process.argv.findIndex((a) => a.toLowerCase() === '--security-scan') !== -1;
|
|
1331
|
+
if (securityScanMode) {
|
|
1332
|
+
await runSecurityScanMode(port, shouldOpenBrowser);
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
28
1335
|
if (peerDependenciesMode) {
|
|
29
1336
|
await runPeerDependenciesMode(port, shouldOpenBrowser);
|
|
30
1337
|
return;
|
|
31
1338
|
}
|
|
32
1339
|
// Run npm list command
|
|
33
|
-
const result = shell.exec(`npm list --json ${packageName ??
|
|
1340
|
+
const result = shell.exec(`npm list --json ${packageName ?? '--all'}`, {
|
|
34
1341
|
windowsHide: true,
|
|
35
1342
|
silent: true,
|
|
36
1343
|
});
|
|
@@ -39,7 +1346,7 @@ void (async function main() {
|
|
|
39
1346
|
console.log(`Generating dependency graph for: "${packageInfo.name}"...`);
|
|
40
1347
|
// Filter dependencies if needed
|
|
41
1348
|
if (packagesFilter != null) {
|
|
42
|
-
const packagesFilterList = packagesFilter.split(
|
|
1349
|
+
const packagesFilterList = packagesFilter.split(',').map((d) => d.trim());
|
|
43
1350
|
for (const key of Object.keys(packageInfo.dependencies)) {
|
|
44
1351
|
if (!packagesFilterList.includes(key)) {
|
|
45
1352
|
delete packageInfo.dependencies[key];
|
|
@@ -75,7 +1382,7 @@ void (async function main() {
|
|
|
75
1382
|
x,
|
|
76
1383
|
y,
|
|
77
1384
|
size: 10,
|
|
78
|
-
color: dep[1].isRoot ?
|
|
1385
|
+
color: dep[1].isRoot ? '#22c55e' : '#3b82f6',
|
|
79
1386
|
};
|
|
80
1387
|
})),
|
|
81
1388
|
edges: JSON.stringify(Object.entries(flatDeps)
|
|
@@ -91,7 +1398,7 @@ void (async function main() {
|
|
|
91
1398
|
const app = fastify({
|
|
92
1399
|
logger: false,
|
|
93
1400
|
});
|
|
94
|
-
app.get(
|
|
1401
|
+
app.get('/', (_req, resp) => resp.type('text/html').send(html));
|
|
95
1402
|
// Run the server
|
|
96
1403
|
app.listen({ port }, (err, address) => {
|
|
97
1404
|
if (err)
|
|
@@ -123,10 +1430,10 @@ function flatDepsRecursive(deps, parentDepName) {
|
|
|
123
1430
|
}
|
|
124
1431
|
}
|
|
125
1432
|
async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
126
|
-
console.log(
|
|
1433
|
+
console.log('Analyzing peer dependencies...');
|
|
127
1434
|
// Get the current project's package.json
|
|
128
|
-
const packageJsonPath = path.join(process.cwd(),
|
|
129
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath,
|
|
1435
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
1436
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
130
1437
|
const installedPackages = {
|
|
131
1438
|
...packageJson.dependencies,
|
|
132
1439
|
...packageJson.devDependencies,
|
|
@@ -138,8 +1445,8 @@ async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
|
138
1445
|
const resolvedVersion = resolveVersion(info.versions);
|
|
139
1446
|
const isInPackageJson = installedPackages[name] !== undefined;
|
|
140
1447
|
// Check if installed in node_modules (even if not in package.json)
|
|
141
|
-
const nodeModulesPath = path.join(process.cwd(),
|
|
142
|
-
const pkgPath = path.join(nodeModulesPath, name,
|
|
1448
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules');
|
|
1449
|
+
const pkgPath = path.join(nodeModulesPath, name, 'package.json');
|
|
143
1450
|
const existsInNodeModules = fs.existsSync(pkgPath);
|
|
144
1451
|
const isInstalledByDependency = existsInNodeModules && !isInPackageJson;
|
|
145
1452
|
return {
|
|
@@ -163,8 +1470,8 @@ async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
|
163
1470
|
const app = fastify({
|
|
164
1471
|
logger: false,
|
|
165
1472
|
});
|
|
166
|
-
app.get(
|
|
167
|
-
app.post(
|
|
1473
|
+
app.get('/', (_req, resp) => resp.type('text/html').send(html));
|
|
1474
|
+
app.post('/install', async (req, resp) => {
|
|
168
1475
|
const { package: pkg, version } = req.body;
|
|
169
1476
|
console.log(`Installing ${pkg}@${version}...`);
|
|
170
1477
|
const installResult = shell.exec(`npm install ${pkg}@${version}`, {
|
|
@@ -182,7 +1489,7 @@ async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
|
182
1489
|
console.error(`Failed to install ${pkg}@${version}`);
|
|
183
1490
|
return {
|
|
184
1491
|
success: false,
|
|
185
|
-
message: installResult.stderr ||
|
|
1492
|
+
message: installResult.stderr || 'Installation failed',
|
|
186
1493
|
};
|
|
187
1494
|
}
|
|
188
1495
|
});
|
|
@@ -197,7 +1504,7 @@ async function runPeerDependenciesMode(port, shouldOpenBrowser) {
|
|
|
197
1504
|
}
|
|
198
1505
|
function collectPeerDependenciesRecursively(initialPackages) {
|
|
199
1506
|
const peerDeps = {};
|
|
200
|
-
const nodeModulesPath = path.join(process.cwd(),
|
|
1507
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules');
|
|
201
1508
|
const visited = new Set();
|
|
202
1509
|
const queue = Object.keys(initialPackages);
|
|
203
1510
|
while (queue.length > 0) {
|
|
@@ -208,11 +1515,11 @@ function collectPeerDependenciesRecursively(initialPackages) {
|
|
|
208
1515
|
}
|
|
209
1516
|
visited.add(packageName);
|
|
210
1517
|
try {
|
|
211
|
-
const pkgJsonPath = path.join(nodeModulesPath, packageName,
|
|
1518
|
+
const pkgJsonPath = path.join(nodeModulesPath, packageName, 'package.json');
|
|
212
1519
|
if (!fs.existsSync(pkgJsonPath)) {
|
|
213
1520
|
continue;
|
|
214
1521
|
}
|
|
215
|
-
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath,
|
|
1522
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
216
1523
|
// Collect peer dependencies
|
|
217
1524
|
if (pkgJson.peerDependencies) {
|
|
218
1525
|
for (const [peerDepName, peerDepVersion] of Object.entries(pkgJson.peerDependencies)) {
|
|
@@ -254,7 +1561,7 @@ function collectPeerDependenciesRecursively(initialPackages) {
|
|
|
254
1561
|
}
|
|
255
1562
|
function resolveVersion(versions) {
|
|
256
1563
|
if (versions.length === 0) {
|
|
257
|
-
return { version:
|
|
1564
|
+
return { version: '*', isConflict: false };
|
|
258
1565
|
}
|
|
259
1566
|
if (versions.length === 1) {
|
|
260
1567
|
return { version: versions[0], isConflict: false };
|
|
@@ -286,445 +1593,863 @@ function resolveVersion(versions) {
|
|
|
286
1593
|
}
|
|
287
1594
|
else {
|
|
288
1595
|
// Ranges don't intersect - conflict detected
|
|
289
|
-
return { version: uniqueVersions.join(
|
|
1596
|
+
return { version: uniqueVersions.join(' | '), isConflict: true };
|
|
290
1597
|
}
|
|
291
1598
|
}
|
|
292
1599
|
return { version: intersection, isConflict: false };
|
|
293
1600
|
}
|
|
294
1601
|
catch (error) {
|
|
295
1602
|
// If semver parsing fails, fall back to showing all versions
|
|
296
|
-
return { version: uniqueVersions.join(
|
|
1603
|
+
return { version: uniqueVersions.join(' | '), isConflict: true };
|
|
297
1604
|
}
|
|
298
1605
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
.container {
|
|
319
|
-
max-width: 1200px;
|
|
320
|
-
margin: 0 auto;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
h1 {
|
|
324
|
-
color: white;
|
|
325
|
-
margin-bottom: 2rem;
|
|
326
|
-
font-size: 2.5rem;
|
|
327
|
-
text-align: center;
|
|
328
|
-
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.peer-deps-list {
|
|
332
|
-
background: white;
|
|
333
|
-
border-radius: 12px;
|
|
334
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
|
335
|
-
overflow: hidden;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
.peer-dep-item {
|
|
339
|
-
padding: 1.5rem;
|
|
340
|
-
border-bottom: 1px solid #e5e7eb;
|
|
341
|
-
transition: background-color 0.2s;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.peer-dep-item:last-child {
|
|
345
|
-
border-bottom: none;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
.peer-dep-item:hover {
|
|
349
|
-
background-color: #f9fafb;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
.peer-dep-header {
|
|
353
|
-
display: flex;
|
|
354
|
-
justify-content: space-between;
|
|
355
|
-
align-items: center;
|
|
356
|
-
margin-bottom: 0.75rem;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.peer-dep-name {
|
|
360
|
-
font-size: 1.25rem;
|
|
361
|
-
font-weight: 600;
|
|
362
|
-
color: #1f2937;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.peer-dep-version {
|
|
366
|
-
font-family: 'Courier New', monospace;
|
|
367
|
-
padding: 0.25rem 0.75rem;
|
|
368
|
-
background: #f3f4f6;
|
|
369
|
-
border-radius: 6px;
|
|
370
|
-
font-size: 0.875rem;
|
|
371
|
-
color: #4b5563;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
.peer-dep-version.conflict {
|
|
375
|
-
background: #fee2e2;
|
|
376
|
-
color: #dc2626;
|
|
377
|
-
font-weight: 600;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
.peer-dep-info {
|
|
381
|
-
display: flex;
|
|
382
|
-
justify-content: space-between;
|
|
383
|
-
align-items: center;
|
|
384
|
-
flex-wrap: wrap;
|
|
385
|
-
gap: 1rem;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
.required-by {
|
|
389
|
-
flex: 1;
|
|
390
|
-
min-width: 200px;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.required-by-label {
|
|
394
|
-
font-size: 0.75rem;
|
|
395
|
-
color: #6b7280;
|
|
396
|
-
text-transform: uppercase;
|
|
397
|
-
letter-spacing: 0.05em;
|
|
398
|
-
margin-bottom: 0.25rem;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
.required-by-list {
|
|
402
|
-
display: flex;
|
|
403
|
-
flex-wrap: wrap;
|
|
404
|
-
gap: 0.5rem;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.required-by-tag {
|
|
408
|
-
display: inline-block;
|
|
409
|
-
padding: 0.25rem 0.5rem;
|
|
410
|
-
background: #dbeafe;
|
|
411
|
-
color: #1e40af;
|
|
412
|
-
border-radius: 4px;
|
|
413
|
-
font-size: 0.75rem;
|
|
414
|
-
font-weight: 500;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
.install-btn {
|
|
418
|
-
padding: 0.5rem 1.5rem;
|
|
419
|
-
background: #3b82f6;
|
|
420
|
-
color: white;
|
|
421
|
-
border: none;
|
|
422
|
-
border-radius: 6px;
|
|
423
|
-
font-size: 0.875rem;
|
|
424
|
-
font-weight: 500;
|
|
425
|
-
cursor: pointer;
|
|
426
|
-
transition: all 0.2s;
|
|
427
|
-
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
.install-btn:hover {
|
|
431
|
-
background: #2563eb;
|
|
432
|
-
transform: translateY(-1px);
|
|
433
|
-
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
.install-btn:active {
|
|
437
|
-
transform: translateY(0);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
.install-btn:disabled {
|
|
441
|
-
background: #9ca3af;
|
|
442
|
-
cursor: not-allowed;
|
|
443
|
-
transform: none;
|
|
444
|
-
box-shadow: none;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
.installed-badge {
|
|
448
|
-
display: flex;
|
|
449
|
-
align-items: center;
|
|
450
|
-
gap: 0.5rem;
|
|
451
|
-
padding: 0.5rem 1rem;
|
|
452
|
-
background: #d1fae5;
|
|
453
|
-
color: #065f46;
|
|
454
|
-
border-radius: 6px;
|
|
455
|
-
font-size: 0.875rem;
|
|
456
|
-
font-weight: 600;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
.installed-by-dep-badge {
|
|
460
|
-
display: flex;
|
|
461
|
-
align-items: center;
|
|
462
|
-
gap: 0.5rem;
|
|
463
|
-
padding: 0.5rem 1rem;
|
|
464
|
-
background: #e0e7ff;
|
|
465
|
-
color: #3730a3;
|
|
466
|
-
border-radius: 6px;
|
|
467
|
-
font-size: 0.875rem;
|
|
468
|
-
font-weight: 600;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
.checkmark {
|
|
472
|
-
color: #10b981;
|
|
473
|
-
font-size: 1.25rem;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
.message {
|
|
477
|
-
margin-top: 0.5rem;
|
|
478
|
-
padding: 0.5rem;
|
|
479
|
-
border-radius: 4px;
|
|
480
|
-
font-size: 0.875rem;
|
|
481
|
-
display: none;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
.message.success {
|
|
485
|
-
background: #d1fae5;
|
|
486
|
-
color: #065f46;
|
|
487
|
-
display: block;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.message.error {
|
|
491
|
-
background: #fee2e2;
|
|
492
|
-
color: #dc2626;
|
|
493
|
-
display: block;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
.no-deps {
|
|
497
|
-
padding: 3rem;
|
|
498
|
-
text-align: center;
|
|
499
|
-
color: #6b7280;
|
|
500
|
-
font-size: 1.125rem;
|
|
501
|
-
}
|
|
502
|
-
</style>
|
|
503
|
-
</head>
|
|
504
|
-
<body>
|
|
505
|
-
<div class="container">
|
|
506
|
-
<h1>Peer Dependencies for {{projectName}}</h1>
|
|
507
|
-
<div class="peer-deps-list">
|
|
508
|
-
{{#peerDeps}}
|
|
509
|
-
<div class="peer-dep-item">
|
|
510
|
-
<div class="peer-dep-header">
|
|
511
|
-
<span class="peer-dep-name">{{name}}</span>
|
|
512
|
-
<span class="peer-dep-version {{#isConflict}}conflict{{/isConflict}}">
|
|
513
|
-
{{#isConflict}}Conflict: {{/isConflict}}{{version}}
|
|
514
|
-
</span>
|
|
515
|
-
</div>
|
|
516
|
-
<div class="peer-dep-info">
|
|
517
|
-
<div class="required-by">
|
|
518
|
-
<div class="required-by-label">Required by:</div>
|
|
519
|
-
<div class="required-by-list">
|
|
520
|
-
{{#requiredBy}}
|
|
521
|
-
<span class="required-by-tag">{{.}}</span>
|
|
522
|
-
{{/requiredBy}}
|
|
523
|
-
</div>
|
|
524
|
-
</div>
|
|
525
|
-
<div class="action-container">
|
|
526
|
-
{{#isInstalled}}
|
|
527
|
-
<div class="installed-badge">
|
|
528
|
-
Installed
|
|
529
|
-
</div>
|
|
530
|
-
{{/isInstalled}}
|
|
531
|
-
{{#isInstalledByDependency}}
|
|
532
|
-
<div class="installed-by-dep-badge">
|
|
533
|
-
Installed by dependency
|
|
534
|
-
</div>
|
|
535
|
-
{{/isInstalledByDependency}}
|
|
536
|
-
{{^isInstalled}}
|
|
537
|
-
{{^isInstalledByDependency}}
|
|
538
|
-
<button class="install-btn" onclick="installPackage('{{name}}', '{{version}}', this)">
|
|
539
|
-
npm install
|
|
540
|
-
</button>
|
|
541
|
-
<div class="message" id="msg-{{name}}"></div>
|
|
542
|
-
{{/isInstalledByDependency}}
|
|
543
|
-
{{/isInstalled}}
|
|
544
|
-
</div>
|
|
545
|
-
</div>
|
|
546
|
-
</div>
|
|
547
|
-
{{/peerDeps}}
|
|
548
|
-
{{^peerDeps}}
|
|
549
|
-
<div class="no-deps">
|
|
550
|
-
No peer dependencies found!
|
|
551
|
-
</div>
|
|
552
|
-
{{/peerDeps}}
|
|
553
|
-
</div>
|
|
554
|
-
</div>
|
|
555
|
-
|
|
556
|
-
<script>
|
|
557
|
-
async function installPackage(packageName, version, button) {
|
|
558
|
-
const messageEl = document.getElementById('msg-' + packageName);
|
|
559
|
-
|
|
560
|
-
button.disabled = true;
|
|
561
|
-
button.textContent = 'Installing...';
|
|
562
|
-
messageEl.className = 'message';
|
|
563
|
-
messageEl.textContent = '';
|
|
564
|
-
|
|
565
|
-
try {
|
|
566
|
-
const response = await fetch('/install', {
|
|
567
|
-
method: 'POST',
|
|
568
|
-
headers: {
|
|
569
|
-
'Content-Type': 'application/json',
|
|
570
|
-
},
|
|
571
|
-
body: JSON.stringify({
|
|
572
|
-
package: packageName,
|
|
573
|
-
version: version
|
|
574
|
-
})
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
const result = await response.json();
|
|
578
|
-
|
|
579
|
-
if (result.success) {
|
|
580
|
-
messageEl.className = 'message success';
|
|
581
|
-
messageEl.textContent = result.message;
|
|
582
|
-
button.style.display = 'none';
|
|
583
|
-
|
|
584
|
-
// Optionally reload after a delay
|
|
585
|
-
setTimeout(() => {
|
|
586
|
-
location.reload();
|
|
587
|
-
}, 2000);
|
|
588
|
-
} else {
|
|
589
|
-
messageEl.className = 'message error';
|
|
590
|
-
messageEl.textContent = result.message;
|
|
591
|
-
button.disabled = false;
|
|
592
|
-
button.textContent = 'npm install';
|
|
593
|
-
}
|
|
594
|
-
} catch (error) {
|
|
595
|
-
messageEl.className = 'message error';
|
|
596
|
-
messageEl.textContent = 'Failed to install package: ' + error.message;
|
|
597
|
-
button.disabled = false;
|
|
598
|
-
button.textContent = 'npm install';
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
</script>
|
|
602
|
-
</body>
|
|
603
|
-
</html>
|
|
604
|
-
`;
|
|
605
|
-
}
|
|
606
|
-
function getTemplate() {
|
|
607
|
-
return `
|
|
608
|
-
<html>
|
|
609
|
-
<head>
|
|
610
|
-
<title>{{name}}'s Dependency Graph</title>
|
|
611
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/2.4.0/sigma.min.js"></script>
|
|
612
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphology/0.25.4/graphology.umd.min.js"></script>
|
|
613
|
-
<script type="text/javascript">
|
|
614
|
-
function applyForceLayout(graph, iterations) {
|
|
615
|
-
var nodes = graph.nodes();
|
|
616
|
-
var edges = graph.edges();
|
|
617
|
-
|
|
618
|
-
// Physics constants - lower repulsion for better spacing
|
|
619
|
-
var repulsionStrength = 50;
|
|
620
|
-
var attractionStrength = 0.01;
|
|
621
|
-
var damping = 0.5;
|
|
622
|
-
|
|
623
|
-
for (var iter = 0; iter < iterations; iter++) {
|
|
624
|
-
var forces = {};
|
|
625
|
-
|
|
626
|
-
// Initialize forces
|
|
627
|
-
nodes.forEach(function(nodeId) {
|
|
628
|
-
forces[nodeId] = { x: 0, y: 0 };
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
// Repulsive forces between all nodes
|
|
632
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
633
|
-
for (var j = i + 1; j < nodes.length; j++) {
|
|
634
|
-
var node1 = nodes[i];
|
|
635
|
-
var node2 = nodes[j];
|
|
636
|
-
var attrs1 = graph.getNodeAttributes(node1);
|
|
637
|
-
var attrs2 = graph.getNodeAttributes(node2);
|
|
638
|
-
|
|
639
|
-
var dx = attrs2.x - attrs1.x;
|
|
640
|
-
var dy = attrs2.y - attrs1.y;
|
|
641
|
-
var distance = Math.sqrt(dx * dx + dy * dy) || 0.1;
|
|
642
|
-
var force = repulsionStrength / (distance * distance);
|
|
643
|
-
|
|
644
|
-
var fx = (dx / distance) * force;
|
|
645
|
-
var fy = (dy / distance) * force;
|
|
646
|
-
|
|
647
|
-
forces[node1].x -= fx;
|
|
648
|
-
forces[node1].y -= fy;
|
|
649
|
-
forces[node2].x += fx;
|
|
650
|
-
forces[node2].y += fy;
|
|
1606
|
+
// ─── Security Scan Logic ────────────────────────────────────────────
|
|
1607
|
+
async function runSecurityScanMode(port, shouldOpenBrowser) {
|
|
1608
|
+
console.log('Running security scan...\n');
|
|
1609
|
+
const findings = [];
|
|
1610
|
+
const nodeModulesPath = path.join(process.cwd(), 'node_modules');
|
|
1611
|
+
// Phase 1: Scan node_modules against known malicious packages
|
|
1612
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
1613
|
+
const entries = fs.readdirSync(nodeModulesPath);
|
|
1614
|
+
for (const entry of entries) {
|
|
1615
|
+
// Handle scoped packages (@scope/name)
|
|
1616
|
+
if (entry.startsWith('@')) {
|
|
1617
|
+
const scopePath = path.join(nodeModulesPath, entry);
|
|
1618
|
+
try {
|
|
1619
|
+
const scopedEntries = fs.readdirSync(scopePath);
|
|
1620
|
+
for (const scopedEntry of scopedEntries) {
|
|
1621
|
+
const fullName = `${entry}/${scopedEntry}`;
|
|
1622
|
+
checkPackageAgainstDatabase(nodeModulesPath, fullName, findings);
|
|
651
1623
|
}
|
|
652
1624
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
var source = edge.source || edge[0];
|
|
658
|
-
var target = edge.target || edge[1];
|
|
659
|
-
var attrs1 = graph.getNodeAttributes(source);
|
|
660
|
-
var attrs2 = graph.getNodeAttributes(target);
|
|
661
|
-
|
|
662
|
-
var dx = attrs2.x - attrs1.x;
|
|
663
|
-
var dy = attrs2.y - attrs1.y;
|
|
664
|
-
var distance = Math.sqrt(dx * dx + dy * dy) || 0.1;
|
|
665
|
-
|
|
666
|
-
var fx = dx * attractionStrength;
|
|
667
|
-
var fy = dy * attractionStrength;
|
|
668
|
-
|
|
669
|
-
forces[source].x += fx;
|
|
670
|
-
forces[source].y += fy;
|
|
671
|
-
forces[target].x -= fx;
|
|
672
|
-
forces[target].y -= fy;
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
// Apply forces with damping
|
|
676
|
-
nodes.forEach(function(nodeId) {
|
|
677
|
-
var attrs = graph.getNodeAttributes(nodeId);
|
|
678
|
-
attrs.x += forces[nodeId].x * damping;
|
|
679
|
-
attrs.y += forces[nodeId].y * damping;
|
|
680
|
-
});
|
|
1625
|
+
catch {
|
|
1626
|
+
// Skip unreadable scope directories
|
|
1627
|
+
}
|
|
1628
|
+
continue;
|
|
681
1629
|
}
|
|
1630
|
+
checkPackageAgainstDatabase(nodeModulesPath, entry, findings);
|
|
682
1631
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1632
|
+
}
|
|
1633
|
+
else {
|
|
1634
|
+
console.warn('Warning: node_modules directory not found. Skipping local package scan.\n');
|
|
1635
|
+
}
|
|
1636
|
+
// Phase 2: Run npm audit
|
|
1637
|
+
console.log('Running npm audit...');
|
|
1638
|
+
const auditResult = shell.exec('npm audit --json', {
|
|
1639
|
+
windowsHide: true,
|
|
1640
|
+
silent: true,
|
|
1641
|
+
});
|
|
1642
|
+
if (auditResult.code !== 0 && auditResult.stdout.trim() === '') {
|
|
1643
|
+
console.warn('Warning: npm audit failed (missing package-lock.json or no network). Showing known-malicious results only.\n');
|
|
1644
|
+
}
|
|
1645
|
+
else {
|
|
1646
|
+
try {
|
|
1647
|
+
const auditData = JSON.parse(auditResult.stdout);
|
|
1648
|
+
const vulnerabilities = auditData.vulnerabilities ?? {};
|
|
1649
|
+
for (const [pkgName, vulnInfo] of Object.entries(vulnerabilities)) {
|
|
1650
|
+
// Skip if already flagged by our known-malicious list
|
|
1651
|
+
if (findings.some((f) => f.name === pkgName))
|
|
1652
|
+
continue;
|
|
1653
|
+
const severity = vulnInfo.severity ?? 'moderate';
|
|
1654
|
+
const via = Array.isArray(vulnInfo.via)
|
|
1655
|
+
? vulnInfo.via
|
|
1656
|
+
.map((v) => (typeof v === 'string' ? v : (v.title ?? v.name ?? '')))
|
|
1657
|
+
.filter(Boolean)
|
|
1658
|
+
.join('; ')
|
|
1659
|
+
: String(vulnInfo.via ?? '');
|
|
1660
|
+
const installedVer = vulnInfo.range ?? vulnInfo.version ?? 'unknown';
|
|
1661
|
+
findings.push({
|
|
1662
|
+
name: pkgName,
|
|
1663
|
+
installedVersion: installedVer,
|
|
1664
|
+
severity: severity,
|
|
1665
|
+
source: 'npm-audit',
|
|
1666
|
+
description: via || `Vulnerability reported by npm audit (${severity})`,
|
|
704
1667
|
});
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
catch {
|
|
1671
|
+
console.warn('Warning: Could not parse npm audit output.\n');
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
// Sort findings: critical first, then high, moderate, low
|
|
1675
|
+
const severityOrder = { critical: 0, high: 1, moderate: 2, low: 3 };
|
|
1676
|
+
findings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
1677
|
+
// Phase 3: Terminal output
|
|
1678
|
+
printSecurityScanResults(findings);
|
|
1679
|
+
// Phase 4: Serve HTML report
|
|
1680
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
1681
|
+
let projectName = 'Unknown Project';
|
|
1682
|
+
try {
|
|
1683
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
1684
|
+
projectName = packageJson.name ?? projectName;
|
|
1685
|
+
}
|
|
1686
|
+
catch {
|
|
1687
|
+
// Ignore
|
|
1688
|
+
}
|
|
1689
|
+
const criticalCount = findings.filter((f) => f.severity === 'critical').length;
|
|
1690
|
+
const highCount = findings.filter((f) => f.severity === 'high').length;
|
|
1691
|
+
const moderateCount = findings.filter((f) => f.severity === 'moderate').length;
|
|
1692
|
+
const lowCount = findings.filter((f) => f.severity === 'low').length;
|
|
1693
|
+
const data = {
|
|
1694
|
+
projectName,
|
|
1695
|
+
findings,
|
|
1696
|
+
totalCount: findings.length,
|
|
1697
|
+
criticalCount,
|
|
1698
|
+
highCount,
|
|
1699
|
+
moderateCount,
|
|
1700
|
+
lowCount,
|
|
1701
|
+
hasCritical: criticalCount > 0,
|
|
1702
|
+
hasHigh: highCount > 0,
|
|
1703
|
+
hasModerate: moderateCount > 0,
|
|
1704
|
+
hasLow: lowCount > 0,
|
|
1705
|
+
hasFindings: findings.length > 0,
|
|
1706
|
+
};
|
|
1707
|
+
const html = mustache.render(getSecurityScanTemplate(), data);
|
|
1708
|
+
const app = fastify({ logger: false });
|
|
1709
|
+
app.get('/', (_req, resp) => resp.type('text/html').send(html));
|
|
1710
|
+
app.listen({ port }, (err, address) => {
|
|
1711
|
+
if (err)
|
|
1712
|
+
throw err;
|
|
1713
|
+
console.log(`\nSecurity report available at: ${address}`);
|
|
1714
|
+
if (shouldOpenBrowser)
|
|
1715
|
+
open(address);
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
function checkPackageAgainstDatabase(nodeModulesPath, packageName, findings) {
|
|
1719
|
+
const pkgJsonPath = path.join(nodeModulesPath, packageName, 'package.json');
|
|
1720
|
+
if (!fs.existsSync(pkgJsonPath))
|
|
1721
|
+
return;
|
|
1722
|
+
let installedVersion;
|
|
1723
|
+
try {
|
|
1724
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));
|
|
1725
|
+
installedVersion = pkgJson.version ?? '0.0.0';
|
|
1726
|
+
}
|
|
1727
|
+
catch {
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
for (const entry of knownMaliciousPackages) {
|
|
1731
|
+
if (entry.name !== packageName)
|
|
1732
|
+
continue;
|
|
1733
|
+
if (entry.badVersions === '*') {
|
|
1734
|
+
findings.push({
|
|
1735
|
+
name: packageName,
|
|
1736
|
+
installedVersion,
|
|
1737
|
+
severity: entry.severity,
|
|
1738
|
+
source: 'known-malicious',
|
|
1739
|
+
description: entry.description,
|
|
705
1740
|
});
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
{
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
renderEdgeLabels: false,
|
|
715
|
-
defaultNodeColor: '#3b82f6',
|
|
716
|
-
defaultEdgeColor: '#94a3b8'
|
|
1741
|
+
}
|
|
1742
|
+
else if (entry.badVersions.includes(installedVersion)) {
|
|
1743
|
+
findings.push({
|
|
1744
|
+
name: packageName,
|
|
1745
|
+
installedVersion,
|
|
1746
|
+
severity: entry.severity,
|
|
1747
|
+
source: 'known-malicious',
|
|
1748
|
+
description: entry.description,
|
|
717
1749
|
});
|
|
718
1750
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
function printSecurityScanResults(findings) {
|
|
1754
|
+
if (findings.length === 0) {
|
|
1755
|
+
console.log('\x1b[32m✔ No known malicious packages or vulnerabilities found.\x1b[0m\n');
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
const critical = findings.filter((f) => f.severity === 'critical');
|
|
1759
|
+
const high = findings.filter((f) => f.severity === 'high');
|
|
1760
|
+
const moderate = findings.filter((f) => f.severity === 'moderate');
|
|
1761
|
+
const low = findings.filter((f) => f.severity === 'low');
|
|
1762
|
+
console.log(`\x1b[1mSecurity Scan Results:\x1b[0m`);
|
|
1763
|
+
console.log(`─────────────────────────────────────────────`);
|
|
1764
|
+
if (critical.length > 0)
|
|
1765
|
+
console.log(` \x1b[31m● ${critical.length} critical\x1b[0m`);
|
|
1766
|
+
if (high.length > 0)
|
|
1767
|
+
console.log(` \x1b[33m● ${high.length} high\x1b[0m`);
|
|
1768
|
+
if (moderate.length > 0)
|
|
1769
|
+
console.log(` \x1b[36m● ${moderate.length} moderate\x1b[0m`);
|
|
1770
|
+
if (low.length > 0)
|
|
1771
|
+
console.log(` \x1b[37m● ${low.length} low\x1b[0m`);
|
|
1772
|
+
console.log(`─────────────────────────────────────────────\n`);
|
|
1773
|
+
for (const finding of findings) {
|
|
1774
|
+
const color = finding.severity === 'critical'
|
|
1775
|
+
? '\x1b[31m'
|
|
1776
|
+
: finding.severity === 'high'
|
|
1777
|
+
? '\x1b[33m'
|
|
1778
|
+
: finding.severity === 'moderate'
|
|
1779
|
+
? '\x1b[36m'
|
|
1780
|
+
: '\x1b[37m';
|
|
1781
|
+
const sourceTag = finding.source === 'known-malicious' ? '[KNOWN MALICIOUS]' : '[npm audit]';
|
|
1782
|
+
console.log(` ${color}${finding.severity.toUpperCase()}\x1b[0m ${finding.name}@${finding.installedVersion} ${sourceTag}`);
|
|
1783
|
+
console.log(` ${finding.description}`);
|
|
1784
|
+
console.log();
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
// ─── Security Scan HTML Template ────────────────────────────────────
|
|
1788
|
+
function getSecurityScanTemplate() {
|
|
1789
|
+
return `
|
|
1790
|
+
<html>
|
|
1791
|
+
<head>
|
|
1792
|
+
<title>{{projectName}} - Security Scan</title>
|
|
1793
|
+
<style>
|
|
1794
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1795
|
+
|
|
1796
|
+
body {
|
|
1797
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
1798
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
|
1799
|
+
min-height: 100vh;
|
|
1800
|
+
padding: 2rem;
|
|
1801
|
+
color: #e2e8f0;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
.container { max-width: 1200px; margin: 0 auto; }
|
|
1805
|
+
|
|
1806
|
+
h1 {
|
|
1807
|
+
color: white;
|
|
1808
|
+
margin-bottom: 0.5rem;
|
|
1809
|
+
font-size: 2.5rem;
|
|
1810
|
+
text-align: center;
|
|
1811
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
.subtitle {
|
|
1815
|
+
text-align: center;
|
|
1816
|
+
color: #94a3b8;
|
|
1817
|
+
margin-bottom: 2rem;
|
|
1818
|
+
font-size: 1.1rem;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
.summary-cards {
|
|
1822
|
+
display: flex;
|
|
1823
|
+
gap: 1rem;
|
|
1824
|
+
margin-bottom: 2rem;
|
|
1825
|
+
flex-wrap: wrap;
|
|
1826
|
+
justify-content: center;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
.summary-card {
|
|
1830
|
+
padding: 1.25rem 2rem;
|
|
1831
|
+
border-radius: 12px;
|
|
1832
|
+
text-align: center;
|
|
1833
|
+
min-width: 140px;
|
|
1834
|
+
backdrop-filter: blur(10px);
|
|
1835
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
.summary-card .count {
|
|
1839
|
+
font-size: 2.5rem;
|
|
1840
|
+
font-weight: 700;
|
|
1841
|
+
line-height: 1;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
.summary-card .label {
|
|
1845
|
+
font-size: 0.8rem;
|
|
1846
|
+
text-transform: uppercase;
|
|
1847
|
+
letter-spacing: 0.1em;
|
|
1848
|
+
margin-top: 0.5rem;
|
|
1849
|
+
opacity: 0.9;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
.card-critical { background: rgba(239, 68, 68, 0.2); border-color: rgba(239, 68, 68, 0.4); }
|
|
1853
|
+
.card-critical .count { color: #ef4444; }
|
|
1854
|
+
.card-critical .label { color: #fca5a5; }
|
|
1855
|
+
|
|
1856
|
+
.card-high { background: rgba(245, 158, 11, 0.2); border-color: rgba(245, 158, 11, 0.4); }
|
|
1857
|
+
.card-high .count { color: #f59e0b; }
|
|
1858
|
+
.card-high .label { color: #fcd34d; }
|
|
1859
|
+
|
|
1860
|
+
.card-moderate { background: rgba(59, 130, 246, 0.2); border-color: rgba(59, 130, 246, 0.4); }
|
|
1861
|
+
.card-moderate .count { color: #3b82f6; }
|
|
1862
|
+
.card-moderate .label { color: #93c5fd; }
|
|
1863
|
+
|
|
1864
|
+
.card-low { background: rgba(107, 114, 128, 0.2); border-color: rgba(107, 114, 128, 0.4); }
|
|
1865
|
+
.card-low .count { color: #9ca3af; }
|
|
1866
|
+
.card-low .label { color: #d1d5db; }
|
|
1867
|
+
|
|
1868
|
+
.findings-list {
|
|
1869
|
+
background: rgba(255, 255, 255, 0.05);
|
|
1870
|
+
border-radius: 12px;
|
|
1871
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
1872
|
+
overflow: hidden;
|
|
1873
|
+
backdrop-filter: blur(10px);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
.finding-item {
|
|
1877
|
+
padding: 1.25rem 1.5rem;
|
|
1878
|
+
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
1879
|
+
transition: background-color 0.2s;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
.finding-item:last-child { border-bottom: none; }
|
|
1883
|
+
.finding-item:hover { background: rgba(255,255,255,0.04); }
|
|
1884
|
+
|
|
1885
|
+
.finding-header {
|
|
1886
|
+
display: flex;
|
|
1887
|
+
align-items: center;
|
|
1888
|
+
gap: 1rem;
|
|
1889
|
+
margin-bottom: 0.5rem;
|
|
1890
|
+
flex-wrap: wrap;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
.severity-badge {
|
|
1894
|
+
padding: 0.2rem 0.75rem;
|
|
1895
|
+
border-radius: 9999px;
|
|
1896
|
+
font-size: 0.7rem;
|
|
1897
|
+
font-weight: 700;
|
|
1898
|
+
text-transform: uppercase;
|
|
1899
|
+
letter-spacing: 0.05em;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
.severity-critical { background: #ef4444; color: white; }
|
|
1903
|
+
.severity-high { background: #f59e0b; color: #1a1a2e; }
|
|
1904
|
+
.severity-moderate { background: #3b82f6; color: white; }
|
|
1905
|
+
.severity-low { background: #6b7280; color: white; }
|
|
1906
|
+
|
|
1907
|
+
.finding-name {
|
|
1908
|
+
font-size: 1.2rem;
|
|
1909
|
+
font-weight: 600;
|
|
1910
|
+
color: #f1f5f9;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
.finding-version {
|
|
1914
|
+
font-family: 'Courier New', monospace;
|
|
1915
|
+
font-size: 0.85rem;
|
|
1916
|
+
padding: 0.15rem 0.6rem;
|
|
1917
|
+
background: rgba(255,255,255,0.1);
|
|
1918
|
+
border-radius: 4px;
|
|
1919
|
+
color: #cbd5e1;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
.source-tag {
|
|
1923
|
+
font-size: 0.7rem;
|
|
1924
|
+
padding: 0.15rem 0.6rem;
|
|
1925
|
+
border-radius: 4px;
|
|
1926
|
+
font-weight: 600;
|
|
1927
|
+
text-transform: uppercase;
|
|
1928
|
+
letter-spacing: 0.05em;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
.source-known-malicious { background: rgba(239, 68, 68, 0.2); color: #fca5a5; border: 1px solid rgba(239, 68, 68, 0.3); }
|
|
1932
|
+
.source-npm-audit { background: rgba(139, 92, 246, 0.2); color: #c4b5fd; border: 1px solid rgba(139, 92, 246, 0.3); }
|
|
1933
|
+
|
|
1934
|
+
.finding-description {
|
|
1935
|
+
color: #94a3b8;
|
|
1936
|
+
font-size: 0.9rem;
|
|
1937
|
+
line-height: 1.5;
|
|
1938
|
+
padding-left: 0.25rem;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
.no-findings {
|
|
1942
|
+
padding: 4rem 2rem;
|
|
1943
|
+
text-align: center;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
.no-findings .checkmark {
|
|
1947
|
+
font-size: 4rem;
|
|
1948
|
+
margin-bottom: 1rem;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
.no-findings h2 {
|
|
1952
|
+
color: #22c55e;
|
|
1953
|
+
font-size: 1.5rem;
|
|
1954
|
+
margin-bottom: 0.5rem;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
.no-findings p {
|
|
1958
|
+
color: #94a3b8;
|
|
1959
|
+
}
|
|
1960
|
+
</style>
|
|
1961
|
+
</head>
|
|
1962
|
+
<body>
|
|
1963
|
+
<div class="container">
|
|
1964
|
+
<h1>Security Scan</h1>
|
|
1965
|
+
<p class="subtitle">{{projectName}}</p>
|
|
1966
|
+
|
|
1967
|
+
{{#hasFindings}}
|
|
1968
|
+
<div class="summary-cards">
|
|
1969
|
+
{{#hasCritical}}
|
|
1970
|
+
<div class="summary-card card-critical">
|
|
1971
|
+
<div class="count">{{criticalCount}}</div>
|
|
1972
|
+
<div class="label">Critical</div>
|
|
1973
|
+
</div>
|
|
1974
|
+
{{/hasCritical}}
|
|
1975
|
+
{{#hasHigh}}
|
|
1976
|
+
<div class="summary-card card-high">
|
|
1977
|
+
<div class="count">{{highCount}}</div>
|
|
1978
|
+
<div class="label">High</div>
|
|
1979
|
+
</div>
|
|
1980
|
+
{{/hasHigh}}
|
|
1981
|
+
{{#hasModerate}}
|
|
1982
|
+
<div class="summary-card card-moderate">
|
|
1983
|
+
<div class="count">{{moderateCount}}</div>
|
|
1984
|
+
<div class="label">Moderate</div>
|
|
1985
|
+
</div>
|
|
1986
|
+
{{/hasModerate}}
|
|
1987
|
+
{{#hasLow}}
|
|
1988
|
+
<div class="summary-card card-low">
|
|
1989
|
+
<div class="count">{{lowCount}}</div>
|
|
1990
|
+
<div class="label">Low</div>
|
|
1991
|
+
</div>
|
|
1992
|
+
{{/hasLow}}
|
|
1993
|
+
</div>
|
|
1994
|
+
|
|
1995
|
+
<div class="findings-list">
|
|
1996
|
+
{{#findings}}
|
|
1997
|
+
<div class="finding-item">
|
|
1998
|
+
<div class="finding-header">
|
|
1999
|
+
<span class="severity-badge severity-{{severity}}">{{severity}}</span>
|
|
2000
|
+
<span class="finding-name">{{name}}</span>
|
|
2001
|
+
<span class="finding-version">{{installedVersion}}</span>
|
|
2002
|
+
<span class="source-tag source-{{source}}">{{source}}</span>
|
|
2003
|
+
</div>
|
|
2004
|
+
<div class="finding-description">{{description}}</div>
|
|
2005
|
+
</div>
|
|
2006
|
+
{{/findings}}
|
|
2007
|
+
</div>
|
|
2008
|
+
{{/hasFindings}}
|
|
2009
|
+
|
|
2010
|
+
{{^hasFindings}}
|
|
2011
|
+
<div class="findings-list">
|
|
2012
|
+
<div class="no-findings">
|
|
2013
|
+
<div class="checkmark">✓</div>
|
|
2014
|
+
<h2>All Clear</h2>
|
|
2015
|
+
<p>No known malicious packages or vulnerabilities were found.</p>
|
|
2016
|
+
</div>
|
|
2017
|
+
</div>
|
|
2018
|
+
{{/hasFindings}}
|
|
2019
|
+
</div>
|
|
2020
|
+
</body>
|
|
2021
|
+
</html>
|
|
2022
|
+
`;
|
|
2023
|
+
}
|
|
2024
|
+
function getPeerDepsTemplate() {
|
|
2025
|
+
return `
|
|
2026
|
+
<html>
|
|
2027
|
+
<head>
|
|
2028
|
+
<title>{{projectName}} - Peer Dependencies</title>
|
|
2029
|
+
<style>
|
|
2030
|
+
* {
|
|
2031
|
+
margin: 0;
|
|
2032
|
+
padding: 0;
|
|
2033
|
+
box-sizing: border-box;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
body {
|
|
2037
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
2038
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
2039
|
+
min-height: 100vh;
|
|
2040
|
+
padding: 2rem;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
.container {
|
|
2044
|
+
max-width: 1200px;
|
|
2045
|
+
margin: 0 auto;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
h1 {
|
|
2049
|
+
color: white;
|
|
2050
|
+
margin-bottom: 2rem;
|
|
2051
|
+
font-size: 2.5rem;
|
|
2052
|
+
text-align: center;
|
|
2053
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
.peer-deps-list {
|
|
2057
|
+
background: white;
|
|
2058
|
+
border-radius: 12px;
|
|
2059
|
+
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
|
2060
|
+
overflow: hidden;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
.peer-dep-item {
|
|
2064
|
+
padding: 1.5rem;
|
|
2065
|
+
border-bottom: 1px solid #e5e7eb;
|
|
2066
|
+
transition: background-color 0.2s;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
.peer-dep-item:last-child {
|
|
2070
|
+
border-bottom: none;
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
.peer-dep-item:hover {
|
|
2074
|
+
background-color: #f9fafb;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
.peer-dep-header {
|
|
2078
|
+
display: flex;
|
|
2079
|
+
justify-content: space-between;
|
|
2080
|
+
align-items: center;
|
|
2081
|
+
margin-bottom: 0.75rem;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
.peer-dep-name {
|
|
2085
|
+
font-size: 1.25rem;
|
|
2086
|
+
font-weight: 600;
|
|
2087
|
+
color: #1f2937;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
.peer-dep-version {
|
|
2091
|
+
font-family: 'Courier New', monospace;
|
|
2092
|
+
padding: 0.25rem 0.75rem;
|
|
2093
|
+
background: #f3f4f6;
|
|
2094
|
+
border-radius: 6px;
|
|
2095
|
+
font-size: 0.875rem;
|
|
2096
|
+
color: #4b5563;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
.peer-dep-version.conflict {
|
|
2100
|
+
background: #fee2e2;
|
|
2101
|
+
color: #dc2626;
|
|
2102
|
+
font-weight: 600;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
.peer-dep-info {
|
|
2106
|
+
display: flex;
|
|
2107
|
+
justify-content: space-between;
|
|
2108
|
+
align-items: center;
|
|
2109
|
+
flex-wrap: wrap;
|
|
2110
|
+
gap: 1rem;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
.required-by {
|
|
2114
|
+
flex: 1;
|
|
2115
|
+
min-width: 200px;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
.required-by-label {
|
|
2119
|
+
font-size: 0.75rem;
|
|
2120
|
+
color: #6b7280;
|
|
2121
|
+
text-transform: uppercase;
|
|
2122
|
+
letter-spacing: 0.05em;
|
|
2123
|
+
margin-bottom: 0.25rem;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
.required-by-list {
|
|
2127
|
+
display: flex;
|
|
2128
|
+
flex-wrap: wrap;
|
|
2129
|
+
gap: 0.5rem;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
.required-by-tag {
|
|
2133
|
+
display: inline-block;
|
|
2134
|
+
padding: 0.25rem 0.5rem;
|
|
2135
|
+
background: #dbeafe;
|
|
2136
|
+
color: #1e40af;
|
|
2137
|
+
border-radius: 4px;
|
|
2138
|
+
font-size: 0.75rem;
|
|
2139
|
+
font-weight: 500;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
.install-btn {
|
|
2143
|
+
padding: 0.5rem 1.5rem;
|
|
2144
|
+
background: #3b82f6;
|
|
2145
|
+
color: white;
|
|
2146
|
+
border: none;
|
|
2147
|
+
border-radius: 6px;
|
|
2148
|
+
font-size: 0.875rem;
|
|
2149
|
+
font-weight: 500;
|
|
2150
|
+
cursor: pointer;
|
|
2151
|
+
transition: all 0.2s;
|
|
2152
|
+
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
.install-btn:hover {
|
|
2156
|
+
background: #2563eb;
|
|
2157
|
+
transform: translateY(-1px);
|
|
2158
|
+
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
.install-btn:active {
|
|
2162
|
+
transform: translateY(0);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
.install-btn:disabled {
|
|
2166
|
+
background: #9ca3af;
|
|
2167
|
+
cursor: not-allowed;
|
|
2168
|
+
transform: none;
|
|
2169
|
+
box-shadow: none;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
.installed-badge {
|
|
2173
|
+
display: flex;
|
|
2174
|
+
align-items: center;
|
|
2175
|
+
gap: 0.5rem;
|
|
2176
|
+
padding: 0.5rem 1rem;
|
|
2177
|
+
background: #d1fae5;
|
|
2178
|
+
color: #065f46;
|
|
2179
|
+
border-radius: 6px;
|
|
2180
|
+
font-size: 0.875rem;
|
|
2181
|
+
font-weight: 600;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
.installed-by-dep-badge {
|
|
2185
|
+
display: flex;
|
|
2186
|
+
align-items: center;
|
|
2187
|
+
gap: 0.5rem;
|
|
2188
|
+
padding: 0.5rem 1rem;
|
|
2189
|
+
background: #e0e7ff;
|
|
2190
|
+
color: #3730a3;
|
|
2191
|
+
border-radius: 6px;
|
|
2192
|
+
font-size: 0.875rem;
|
|
2193
|
+
font-weight: 600;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
.checkmark {
|
|
2197
|
+
color: #10b981;
|
|
2198
|
+
font-size: 1.25rem;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
.message {
|
|
2202
|
+
margin-top: 0.5rem;
|
|
2203
|
+
padding: 0.5rem;
|
|
2204
|
+
border-radius: 4px;
|
|
2205
|
+
font-size: 0.875rem;
|
|
2206
|
+
display: none;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
.message.success {
|
|
2210
|
+
background: #d1fae5;
|
|
2211
|
+
color: #065f46;
|
|
2212
|
+
display: block;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
.message.error {
|
|
2216
|
+
background: #fee2e2;
|
|
2217
|
+
color: #dc2626;
|
|
2218
|
+
display: block;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
.no-deps {
|
|
2222
|
+
padding: 3rem;
|
|
2223
|
+
text-align: center;
|
|
2224
|
+
color: #6b7280;
|
|
2225
|
+
font-size: 1.125rem;
|
|
2226
|
+
}
|
|
2227
|
+
</style>
|
|
2228
|
+
</head>
|
|
2229
|
+
<body>
|
|
2230
|
+
<div class="container">
|
|
2231
|
+
<h1>Peer Dependencies for {{projectName}}</h1>
|
|
2232
|
+
<div class="peer-deps-list">
|
|
2233
|
+
{{#peerDeps}}
|
|
2234
|
+
<div class="peer-dep-item">
|
|
2235
|
+
<div class="peer-dep-header">
|
|
2236
|
+
<span class="peer-dep-name">{{name}}</span>
|
|
2237
|
+
<span class="peer-dep-version {{#isConflict}}conflict{{/isConflict}}">
|
|
2238
|
+
{{#isConflict}}Conflict: {{/isConflict}}{{version}}
|
|
2239
|
+
</span>
|
|
2240
|
+
</div>
|
|
2241
|
+
<div class="peer-dep-info">
|
|
2242
|
+
<div class="required-by">
|
|
2243
|
+
<div class="required-by-label">Required by:</div>
|
|
2244
|
+
<div class="required-by-list">
|
|
2245
|
+
{{#requiredBy}}
|
|
2246
|
+
<span class="required-by-tag">{{.}}</span>
|
|
2247
|
+
{{/requiredBy}}
|
|
2248
|
+
</div>
|
|
2249
|
+
</div>
|
|
2250
|
+
<div class="action-container">
|
|
2251
|
+
{{#isInstalled}}
|
|
2252
|
+
<div class="installed-badge">
|
|
2253
|
+
Installed
|
|
2254
|
+
</div>
|
|
2255
|
+
{{/isInstalled}}
|
|
2256
|
+
{{#isInstalledByDependency}}
|
|
2257
|
+
<div class="installed-by-dep-badge">
|
|
2258
|
+
Installed by dependency
|
|
2259
|
+
</div>
|
|
2260
|
+
{{/isInstalledByDependency}}
|
|
2261
|
+
{{^isInstalled}}
|
|
2262
|
+
{{^isInstalledByDependency}}
|
|
2263
|
+
<button class="install-btn" onclick="installPackage('{{name}}', '{{version}}', this)">
|
|
2264
|
+
npm install
|
|
2265
|
+
</button>
|
|
2266
|
+
<div class="message" id="msg-{{name}}"></div>
|
|
2267
|
+
{{/isInstalledByDependency}}
|
|
2268
|
+
{{/isInstalled}}
|
|
2269
|
+
</div>
|
|
2270
|
+
</div>
|
|
2271
|
+
</div>
|
|
2272
|
+
{{/peerDeps}}
|
|
2273
|
+
{{^peerDeps}}
|
|
2274
|
+
<div class="no-deps">
|
|
2275
|
+
No peer dependencies found!
|
|
2276
|
+
</div>
|
|
2277
|
+
{{/peerDeps}}
|
|
2278
|
+
</div>
|
|
2279
|
+
</div>
|
|
2280
|
+
|
|
2281
|
+
<script>
|
|
2282
|
+
async function installPackage(packageName, version, button) {
|
|
2283
|
+
const messageEl = document.getElementById('msg-' + packageName);
|
|
2284
|
+
|
|
2285
|
+
button.disabled = true;
|
|
2286
|
+
button.textContent = 'Installing...';
|
|
2287
|
+
messageEl.className = 'message';
|
|
2288
|
+
messageEl.textContent = '';
|
|
2289
|
+
|
|
2290
|
+
try {
|
|
2291
|
+
const response = await fetch('/install', {
|
|
2292
|
+
method: 'POST',
|
|
2293
|
+
headers: {
|
|
2294
|
+
'Content-Type': 'application/json',
|
|
2295
|
+
},
|
|
2296
|
+
body: JSON.stringify({
|
|
2297
|
+
package: packageName,
|
|
2298
|
+
version: version
|
|
2299
|
+
})
|
|
2300
|
+
});
|
|
2301
|
+
|
|
2302
|
+
const result = await response.json();
|
|
2303
|
+
|
|
2304
|
+
if (result.success) {
|
|
2305
|
+
messageEl.className = 'message success';
|
|
2306
|
+
messageEl.textContent = result.message;
|
|
2307
|
+
button.style.display = 'none';
|
|
2308
|
+
|
|
2309
|
+
// Optionally reload after a delay
|
|
2310
|
+
setTimeout(() => {
|
|
2311
|
+
location.reload();
|
|
2312
|
+
}, 2000);
|
|
2313
|
+
} else {
|
|
2314
|
+
messageEl.className = 'message error';
|
|
2315
|
+
messageEl.textContent = result.message;
|
|
2316
|
+
button.disabled = false;
|
|
2317
|
+
button.textContent = 'npm install';
|
|
2318
|
+
}
|
|
2319
|
+
} catch (error) {
|
|
2320
|
+
messageEl.className = 'message error';
|
|
2321
|
+
messageEl.textContent = 'Failed to install package: ' + error.message;
|
|
2322
|
+
button.disabled = false;
|
|
2323
|
+
button.textContent = 'npm install';
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
</script>
|
|
2327
|
+
</body>
|
|
2328
|
+
</html>
|
|
2329
|
+
`;
|
|
2330
|
+
}
|
|
2331
|
+
function getTemplate() {
|
|
2332
|
+
return `
|
|
2333
|
+
<html>
|
|
2334
|
+
<head>
|
|
2335
|
+
<title>{{name}}'s Dependency Graph</title>
|
|
2336
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/2.4.0/sigma.min.js"></script>
|
|
2337
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphology/0.25.4/graphology.umd.min.js"></script>
|
|
2338
|
+
<script type="text/javascript">
|
|
2339
|
+
function applyForceLayout(graph, iterations) {
|
|
2340
|
+
var nodes = graph.nodes();
|
|
2341
|
+
var edges = graph.edges();
|
|
2342
|
+
|
|
2343
|
+
// Physics constants - lower repulsion for better spacing
|
|
2344
|
+
var repulsionStrength = 50;
|
|
2345
|
+
var attractionStrength = 0.01;
|
|
2346
|
+
var damping = 0.5;
|
|
2347
|
+
|
|
2348
|
+
for (var iter = 0; iter < iterations; iter++) {
|
|
2349
|
+
var forces = {};
|
|
2350
|
+
|
|
2351
|
+
// Initialize forces
|
|
2352
|
+
nodes.forEach(function(nodeId) {
|
|
2353
|
+
forces[nodeId] = { x: 0, y: 0 };
|
|
2354
|
+
});
|
|
2355
|
+
|
|
2356
|
+
// Repulsive forces between all nodes
|
|
2357
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
2358
|
+
for (var j = i + 1; j < nodes.length; j++) {
|
|
2359
|
+
var node1 = nodes[i];
|
|
2360
|
+
var node2 = nodes[j];
|
|
2361
|
+
var attrs1 = graph.getNodeAttributes(node1);
|
|
2362
|
+
var attrs2 = graph.getNodeAttributes(node2);
|
|
2363
|
+
|
|
2364
|
+
var dx = attrs2.x - attrs1.x;
|
|
2365
|
+
var dy = attrs2.y - attrs1.y;
|
|
2366
|
+
var distance = Math.sqrt(dx * dx + dy * dy) || 0.1;
|
|
2367
|
+
var force = repulsionStrength / (distance * distance);
|
|
2368
|
+
|
|
2369
|
+
var fx = (dx / distance) * force;
|
|
2370
|
+
var fy = (dy / distance) * force;
|
|
2371
|
+
|
|
2372
|
+
forces[node1].x -= fx;
|
|
2373
|
+
forces[node1].y -= fy;
|
|
2374
|
+
forces[node2].x += fx;
|
|
2375
|
+
forces[node2].y += fy;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
// Attractive forces along edges
|
|
2380
|
+
edges.forEach(function(edgeId) {
|
|
2381
|
+
var edge = graph.extremities(edgeId);
|
|
2382
|
+
var source = edge.source || edge[0];
|
|
2383
|
+
var target = edge.target || edge[1];
|
|
2384
|
+
var attrs1 = graph.getNodeAttributes(source);
|
|
2385
|
+
var attrs2 = graph.getNodeAttributes(target);
|
|
2386
|
+
|
|
2387
|
+
var dx = attrs2.x - attrs1.x;
|
|
2388
|
+
var dy = attrs2.y - attrs1.y;
|
|
2389
|
+
var distance = Math.sqrt(dx * dx + dy * dy) || 0.1;
|
|
2390
|
+
|
|
2391
|
+
var fx = dx * attractionStrength;
|
|
2392
|
+
var fy = dy * attractionStrength;
|
|
2393
|
+
|
|
2394
|
+
forces[source].x += fx;
|
|
2395
|
+
forces[source].y += fy;
|
|
2396
|
+
forces[target].x -= fx;
|
|
2397
|
+
forces[target].y -= fy;
|
|
2398
|
+
});
|
|
2399
|
+
|
|
2400
|
+
// Apply forces with damping
|
|
2401
|
+
nodes.forEach(function(nodeId) {
|
|
2402
|
+
var attrs = graph.getNodeAttributes(nodeId);
|
|
2403
|
+
attrs.x += forces[nodeId].x * damping;
|
|
2404
|
+
attrs.y += forces[nodeId].y * damping;
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
function renderGraph() {
|
|
2410
|
+
var nodes = {{{nodes}}};
|
|
2411
|
+
var edges = {{{edges}}};
|
|
2412
|
+
|
|
2413
|
+
var graph = new graphology.Graph();
|
|
2414
|
+
|
|
2415
|
+
nodes.forEach(function(node) {
|
|
2416
|
+
graph.addNode(node.key, {
|
|
2417
|
+
label: node.label,
|
|
2418
|
+
x: node.x,
|
|
2419
|
+
y: node.y,
|
|
2420
|
+
size: node.size,
|
|
2421
|
+
color: node.color
|
|
2422
|
+
});
|
|
2423
|
+
});
|
|
2424
|
+
|
|
2425
|
+
edges.forEach(function(edge, index) {
|
|
2426
|
+
graph.mergeEdge(edge.source, edge.target, {
|
|
2427
|
+
size: 2,
|
|
2428
|
+
color: '#94a3b8'
|
|
2429
|
+
});
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
// Apply custom force-directed layout
|
|
2433
|
+
{{#applyForceLayout}}
|
|
2434
|
+
applyForceLayout(graph, 100);
|
|
2435
|
+
{{/applyForceLayout}}
|
|
2436
|
+
|
|
2437
|
+
var container = document.getElementById("dep-graph");
|
|
2438
|
+
var renderer = new Sigma(graph, container, {
|
|
2439
|
+
renderEdgeLabels: false,
|
|
2440
|
+
defaultNodeColor: '#3b82f6',
|
|
2441
|
+
defaultEdgeColor: '#94a3b8'
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
</script>
|
|
2445
|
+
<style>
|
|
2446
|
+
body { margin: 0; padding: 0; }
|
|
2447
|
+
#dep-graph { width: 100vw; height: 100vh; background: #f8fafc; }
|
|
2448
|
+
</style>
|
|
2449
|
+
</head>
|
|
2450
|
+
<body onload="renderGraph()">
|
|
2451
|
+
<div id="dep-graph"></div>
|
|
2452
|
+
</body>
|
|
2453
|
+
</html>
|
|
729
2454
|
`;
|
|
730
2455
|
}
|