socket 0.14.39 → 0.14.40-alpha.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/dist/constants.js +10 -4
- package/dist/module-sync/cli.js +34 -6
- package/dist/module-sync/constants.d.ts +4 -2
- package/dist/module-sync/constants.d.ts.map +1 -1
- package/dist/module-sync/npm-injection.js +274 -291
- package/dist/module-sync/path-resolve.js +1 -4
- package/dist/module-sync/shadow-bin.js +4 -26
- package/dist/require/cli.js +33 -5
- package/dist/require/constants.d.ts.map +1 -1
- package/dist/require/npm-injection.js +274 -291
- package/dist/require/path-resolve.js +1 -4
- package/dist/require/shadow-bin.js +4 -26
- package/package.json +5 -3
|
@@ -15,210 +15,18 @@ var https = require('node:https');
|
|
|
15
15
|
var path = require('node:path');
|
|
16
16
|
var readline = require('node:readline');
|
|
17
17
|
var promises = require('node:timers/promises');
|
|
18
|
-
var prompts = require('@socketsecurity/registry/lib/prompts');
|
|
19
18
|
var yoctoSpinner = require('@socketregistry/yocto-spinner');
|
|
20
|
-
var vendor = require('./vendor.js');
|
|
21
|
-
var npa = _socketInterop(require('npm-package-arg'));
|
|
22
|
-
var semver = _socketInterop(require('semver'));
|
|
23
19
|
var config = require('@socketsecurity/config');
|
|
20
|
+
var registry = require('@socketsecurity/registry');
|
|
24
21
|
var objects = require('@socketsecurity/registry/lib/objects');
|
|
25
22
|
var packages = require('@socketsecurity/registry/lib/packages');
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var sdk = require('./sdk.js');
|
|
23
|
+
var prompts = require('@socketsecurity/registry/lib/prompts');
|
|
24
|
+
var npa = _socketInterop(require('npm-package-arg'));
|
|
25
|
+
var semver = _socketInterop(require('semver'));
|
|
30
26
|
var constants = require('./constants.js');
|
|
27
|
+
var sdk = require('./sdk.js');
|
|
31
28
|
var pathResolve = require('./path-resolve.js');
|
|
32
29
|
|
|
33
|
-
var version = "0.14.39";
|
|
34
|
-
|
|
35
|
-
const NEWLINE_CHAR_CODE = 10; /*'\n'*/
|
|
36
|
-
|
|
37
|
-
const TTY_IPC = process.env['SOCKET_SECURITY_TTY_IPC'];
|
|
38
|
-
const sock = path.join(os.tmpdir(), `socket-security-tty-${process.pid}.sock`);
|
|
39
|
-
process.env['SOCKET_SECURITY_TTY_IPC'] = sock;
|
|
40
|
-
function createNonStandardTTYServer() {
|
|
41
|
-
return {
|
|
42
|
-
async captureTTY(mutexFn) {
|
|
43
|
-
return await new Promise((resolve, reject) => {
|
|
44
|
-
const conn = net.createConnection({
|
|
45
|
-
path: TTY_IPC
|
|
46
|
-
}).on('error', reject);
|
|
47
|
-
let captured = false;
|
|
48
|
-
const buffs = [];
|
|
49
|
-
conn.on('data', function awaitCapture(chunk) {
|
|
50
|
-
buffs.push(chunk);
|
|
51
|
-
let lineBuff = Buffer.concat(buffs);
|
|
52
|
-
if (captured) return;
|
|
53
|
-
try {
|
|
54
|
-
const eolIndex = lineBuff.indexOf(NEWLINE_CHAR_CODE);
|
|
55
|
-
if (eolIndex !== -1) {
|
|
56
|
-
conn.removeListener('data', awaitCapture);
|
|
57
|
-
conn.push(lineBuff.slice(eolIndex + 1));
|
|
58
|
-
const {
|
|
59
|
-
capabilities: {
|
|
60
|
-
input: hasInput,
|
|
61
|
-
output: hasOutput
|
|
62
|
-
},
|
|
63
|
-
ipc_version: remote_ipc_version
|
|
64
|
-
} = JSON.parse(lineBuff.subarray(0, eolIndex).toString('utf8'));
|
|
65
|
-
lineBuff = null;
|
|
66
|
-
captured = true;
|
|
67
|
-
if (remote_ipc_version !== version) {
|
|
68
|
-
throw new Error('Mismatched STDIO tunnel IPC version, ensure you only have 1 version of socket CLI being called.');
|
|
69
|
-
}
|
|
70
|
-
const input = hasInput ? new node_stream.PassThrough() : null;
|
|
71
|
-
input?.pause();
|
|
72
|
-
if (input) conn.pipe(input);
|
|
73
|
-
const output = hasOutput ? new node_stream.PassThrough() : null;
|
|
74
|
-
if (output) {
|
|
75
|
-
output.pipe(conn)
|
|
76
|
-
// Make ora happy
|
|
77
|
-
;
|
|
78
|
-
output.isTTY = true;
|
|
79
|
-
output.cursorTo = function cursorTo(x, y, callback) {
|
|
80
|
-
readline.cursorTo(this, x, y, callback);
|
|
81
|
-
};
|
|
82
|
-
output.clearLine = function clearLine(dir, callback) {
|
|
83
|
-
readline.clearLine(this, dir, callback);
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
mutexFn(hasInput ? input : undefined, hasOutput ? output : undefined).then(resolve, reject).finally(() => {
|
|
87
|
-
conn.unref();
|
|
88
|
-
conn.end();
|
|
89
|
-
input?.end();
|
|
90
|
-
output?.end();
|
|
91
|
-
// process.exit(13)
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
} catch (e) {
|
|
95
|
-
reject(e);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function createIPCServer(captureState, npmlog) {
|
|
103
|
-
const input = process.stdin;
|
|
104
|
-
const output = process.stderr;
|
|
105
|
-
return new Promise((resolve, reject) => {
|
|
106
|
-
const server = net
|
|
107
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
108
|
-
.createServer(async conn => {
|
|
109
|
-
if (captureState.captured) {
|
|
110
|
-
await new Promise(resolve => {
|
|
111
|
-
captureState.pendingCaptures.push({
|
|
112
|
-
resolve() {
|
|
113
|
-
resolve();
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
} else {
|
|
118
|
-
captureState.captured = true;
|
|
119
|
-
}
|
|
120
|
-
const wasProgressEnabled = npmlog.progressEnabled;
|
|
121
|
-
npmlog.pause();
|
|
122
|
-
if (wasProgressEnabled) {
|
|
123
|
-
npmlog.disableProgress();
|
|
124
|
-
}
|
|
125
|
-
conn.write(`${JSON.stringify({
|
|
126
|
-
ipc_version: version,
|
|
127
|
-
capabilities: {
|
|
128
|
-
input: Boolean(input),
|
|
129
|
-
output: true
|
|
130
|
-
}
|
|
131
|
-
})}\n`);
|
|
132
|
-
conn.on('data', data => {
|
|
133
|
-
output.write(data);
|
|
134
|
-
}).on('error', e => {
|
|
135
|
-
output.write(`there was an error prompting from a sub shell (${e?.message}), socket npm closing`);
|
|
136
|
-
process.exit(1);
|
|
137
|
-
});
|
|
138
|
-
input.on('data', data => {
|
|
139
|
-
conn.write(data);
|
|
140
|
-
}).on('end', () => {
|
|
141
|
-
conn.unref();
|
|
142
|
-
conn.end();
|
|
143
|
-
if (wasProgressEnabled) {
|
|
144
|
-
npmlog.enableProgress();
|
|
145
|
-
}
|
|
146
|
-
npmlog.resume();
|
|
147
|
-
captureState.nextCapture();
|
|
148
|
-
});
|
|
149
|
-
}).listen(sock, () => resolve(server)).on('error', reject).unref();
|
|
150
|
-
process.on('exit', () => {
|
|
151
|
-
server.close();
|
|
152
|
-
tryUnlinkSync(sock);
|
|
153
|
-
});
|
|
154
|
-
resolve(server);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
function createStandardTTYServer(isInteractive, npmlog) {
|
|
158
|
-
const captureState = {
|
|
159
|
-
captured: false,
|
|
160
|
-
nextCapture: () => {
|
|
161
|
-
if (captureState.pendingCaptures.length > 0) {
|
|
162
|
-
const pendingCapture = captureState.pendingCaptures.shift();
|
|
163
|
-
pendingCapture?.resolve();
|
|
164
|
-
} else {
|
|
165
|
-
captureState.captured = false;
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
pendingCaptures: []
|
|
169
|
-
};
|
|
170
|
-
tryUnlinkSync(sock);
|
|
171
|
-
const input = isInteractive ? process.stdin : undefined;
|
|
172
|
-
const output = process.stderr;
|
|
173
|
-
let ipcServerPromise;
|
|
174
|
-
if (input) {
|
|
175
|
-
ipcServerPromise = createIPCServer(captureState, npmlog);
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
async captureTTY(mutexFn) {
|
|
179
|
-
await ipcServerPromise;
|
|
180
|
-
if (captureState.captured) {
|
|
181
|
-
const captured = new Promise(resolve => {
|
|
182
|
-
captureState.pendingCaptures.push({
|
|
183
|
-
resolve() {
|
|
184
|
-
resolve();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
await captured;
|
|
189
|
-
} else {
|
|
190
|
-
captureState.captured = true;
|
|
191
|
-
}
|
|
192
|
-
const wasProgressEnabled = npmlog.progressEnabled;
|
|
193
|
-
try {
|
|
194
|
-
npmlog.pause();
|
|
195
|
-
if (wasProgressEnabled) {
|
|
196
|
-
npmlog.disableProgress();
|
|
197
|
-
}
|
|
198
|
-
return await mutexFn(input, output);
|
|
199
|
-
} finally {
|
|
200
|
-
if (wasProgressEnabled) {
|
|
201
|
-
npmlog.enableProgress();
|
|
202
|
-
}
|
|
203
|
-
npmlog.resume();
|
|
204
|
-
captureState.nextCapture();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function tryUnlinkSync(filepath) {
|
|
210
|
-
try {
|
|
211
|
-
fs.unlinkSync(filepath);
|
|
212
|
-
} catch (e) {
|
|
213
|
-
if (sdk.isErrnoException(e) && e.code !== 'ENOENT') {
|
|
214
|
-
throw e;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
function createTTYServer(isInteractive, npmlog) {
|
|
219
|
-
return !isInteractive && TTY_IPC ? createNonStandardTTYServer() : createStandardTTYServer(isInteractive, npmlog);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
30
|
//#region UX Constants
|
|
223
31
|
|
|
224
32
|
const IGNORE_UX = {
|
|
@@ -239,10 +47,10 @@ const ERROR_UX = {
|
|
|
239
47
|
/**
|
|
240
48
|
* Iterates over all entries with ordered issue rule for deferral. Iterates over
|
|
241
49
|
* all issue rules and finds the first defined value that does not defer otherwise
|
|
242
|
-
* uses the defaultValue. Takes the value and converts into a UX workflow
|
|
50
|
+
* uses the defaultValue. Takes the value and converts into a UX workflow.
|
|
243
51
|
*/
|
|
244
52
|
function resolveAlertRuleUX(orderedRulesCollection, defaultValue) {
|
|
245
|
-
if (defaultValue === true || defaultValue
|
|
53
|
+
if (defaultValue === true || defaultValue === null || defaultValue === undefined) {
|
|
246
54
|
defaultValue = {
|
|
247
55
|
action: 'error'
|
|
248
56
|
};
|
|
@@ -280,12 +88,13 @@ function resolveAlertRuleUX(orderedRulesCollection, defaultValue) {
|
|
|
280
88
|
}
|
|
281
89
|
|
|
282
90
|
/**
|
|
283
|
-
* Negative form because it is narrowing the type
|
|
91
|
+
* Negative form because it is narrowing the type.
|
|
284
92
|
*/
|
|
285
93
|
function ruleValueDoesNotDefer(rule) {
|
|
286
94
|
if (rule === undefined) {
|
|
287
95
|
return false;
|
|
288
|
-
}
|
|
96
|
+
}
|
|
97
|
+
if (objects.isObject(rule)) {
|
|
289
98
|
const {
|
|
290
99
|
action
|
|
291
100
|
} = rule;
|
|
@@ -297,7 +106,7 @@ function ruleValueDoesNotDefer(rule) {
|
|
|
297
106
|
}
|
|
298
107
|
|
|
299
108
|
/**
|
|
300
|
-
* Handles booleans for backwards compatibility
|
|
109
|
+
* Handles booleans for backwards compatibility.
|
|
301
110
|
*/
|
|
302
111
|
function uxForDefinedNonDeferValue(ruleValue) {
|
|
303
112
|
if (typeof ruleValue === 'boolean') {
|
|
@@ -368,10 +177,12 @@ const {
|
|
|
368
177
|
API_V0_URL,
|
|
369
178
|
ENV,
|
|
370
179
|
LOOP_SENTINEL,
|
|
180
|
+
NPM,
|
|
371
181
|
NPM_REGISTRY_URL,
|
|
182
|
+
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
|
|
372
183
|
SOCKET_CLI_ISSUES_URL,
|
|
184
|
+
SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE,
|
|
373
185
|
SOCKET_PUBLIC_API_KEY,
|
|
374
|
-
UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE,
|
|
375
186
|
abortSignal,
|
|
376
187
|
rootPath
|
|
377
188
|
} = constants;
|
|
@@ -417,16 +228,7 @@ const log = tryRequire([path.join(npmNmPath, 'proc-log/lib/index.js'),
|
|
|
417
228
|
// The proc-log DefinitelyTyped definition is incorrect. The type definition
|
|
418
229
|
// is really that of its export log.
|
|
419
230
|
mod => mod.log], path.join(npmNmPath, 'npmlog/lib/log.js'));
|
|
420
|
-
|
|
421
|
-
console.error(`Unable to integrate with npm CLI logging infrastructure.\n\n${POTENTIAL_BUG_ERROR_MESSAGE}.`);
|
|
422
|
-
// The exit code 127 indicates that the command or binary being executed
|
|
423
|
-
// could not be found.
|
|
424
|
-
process.exit(127);
|
|
425
|
-
}
|
|
426
|
-
const pacote = tryRequire(path.join(npmNmPath, 'pacote'), 'pacote');
|
|
427
|
-
const {
|
|
428
|
-
tarball
|
|
429
|
-
} = pacote;
|
|
231
|
+
const pacote = require(path.join(npmNmPath, 'pacote'));
|
|
430
232
|
const translations = require(path.join(rootPath, 'translations.json'));
|
|
431
233
|
const Arborist = require(arboristClassPath);
|
|
432
234
|
const depValid = require(arboristDepValidPath);
|
|
@@ -437,9 +239,6 @@ const kCtorArgs = Symbol('ctorArgs');
|
|
|
437
239
|
const kRiskyReify = Symbol('riskyReify');
|
|
438
240
|
const formatter = new sdk.ColorOrMarkdown(false);
|
|
439
241
|
const pubToken = sdk.getDefaultKey() ?? SOCKET_PUBLIC_API_KEY;
|
|
440
|
-
const ttyServer = createTTYServer(vendor.isInteractive({
|
|
441
|
-
stream: process.stdin
|
|
442
|
-
}), log);
|
|
443
242
|
let _uxLookup;
|
|
444
243
|
async function uxLookup(settings) {
|
|
445
244
|
while (_uxLookup === undefined) {
|
|
@@ -450,6 +249,35 @@ async function uxLookup(settings) {
|
|
|
450
249
|
}
|
|
451
250
|
return _uxLookup(settings);
|
|
452
251
|
}
|
|
252
|
+
function packageAlertsToReport(alerts) {
|
|
253
|
+
let report = null;
|
|
254
|
+
for (const alert of alerts) {
|
|
255
|
+
if (!isAlertFixableCve(alert.raw)) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const {
|
|
259
|
+
name
|
|
260
|
+
} = alert;
|
|
261
|
+
if (!report) {
|
|
262
|
+
report = {};
|
|
263
|
+
}
|
|
264
|
+
if (!report[name]) {
|
|
265
|
+
report[name] = [];
|
|
266
|
+
}
|
|
267
|
+
const props = alert.raw?.props;
|
|
268
|
+
report[name].push({
|
|
269
|
+
id: -1,
|
|
270
|
+
url: props?.url,
|
|
271
|
+
title: props?.title,
|
|
272
|
+
severity: alert.raw?.severity?.toLowerCase(),
|
|
273
|
+
vulnerable_versions: props?.vulnerableVersionRange,
|
|
274
|
+
cwe: props?.cwes,
|
|
275
|
+
cvss: props?.csvs,
|
|
276
|
+
name
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return report;
|
|
280
|
+
}
|
|
453
281
|
async function* batchScan(pkgIds) {
|
|
454
282
|
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
455
283
|
method: 'POST',
|
|
@@ -527,17 +355,17 @@ function findSpecificOverrideSet(first, second) {
|
|
|
527
355
|
overrideSet = overrideSet.parent;
|
|
528
356
|
}
|
|
529
357
|
// The override sets are incomparable. Neither one contains the other.
|
|
530
|
-
log
|
|
358
|
+
log?.silly('Conflicting override sets', first, second);
|
|
531
359
|
return undefined;
|
|
532
360
|
}
|
|
533
361
|
function isAlertFixable(alert) {
|
|
362
|
+
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
|
|
363
|
+
}
|
|
364
|
+
function isAlertFixableCve(alert) {
|
|
534
365
|
const {
|
|
535
366
|
type
|
|
536
367
|
} = alert;
|
|
537
|
-
|
|
538
|
-
return !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
539
|
-
}
|
|
540
|
-
return type === 'socketUpgradeAvailable';
|
|
368
|
+
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
541
369
|
}
|
|
542
370
|
function maybeReadfileSync(filepath) {
|
|
543
371
|
try {
|
|
@@ -545,7 +373,7 @@ function maybeReadfileSync(filepath) {
|
|
|
545
373
|
} catch {}
|
|
546
374
|
return undefined;
|
|
547
375
|
}
|
|
548
|
-
async function getPackagesAlerts(safeArb,
|
|
376
|
+
async function getPackagesAlerts(safeArb, pkgs, output) {
|
|
549
377
|
const spinner = yoctoSpinner({
|
|
550
378
|
stream: output
|
|
551
379
|
});
|
|
@@ -598,17 +426,19 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
|
|
|
598
426
|
raw: alert,
|
|
599
427
|
fixable: isAlertFixable(alert)
|
|
600
428
|
});
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
alerts
|
|
610
|
-
|
|
611
|
-
|
|
429
|
+
if (!ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
430
|
+
// Before we ask about problematic issues, check to see if they
|
|
431
|
+
// already existed in the old version if they did, be quiet.
|
|
432
|
+
const existing = pkgs.find(p => p.existing?.startsWith(`${name}@`))?.existing;
|
|
433
|
+
if (existing) {
|
|
434
|
+
const oldArtifact =
|
|
435
|
+
// eslint-disable-next-line no-await-in-loop
|
|
436
|
+
(await batchScan([existing]).next()).value;
|
|
437
|
+
if (oldArtifact?.alerts?.length) {
|
|
438
|
+
alerts = alerts.filter(({
|
|
439
|
+
type
|
|
440
|
+
}) => !oldArtifact.alerts?.find(a => a.type === type));
|
|
441
|
+
}
|
|
612
442
|
}
|
|
613
443
|
}
|
|
614
444
|
}
|
|
@@ -616,7 +446,7 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
|
|
|
616
446
|
if (!blocked) {
|
|
617
447
|
const pkg = pkgs.find(p => p.pkgid === id);
|
|
618
448
|
if (pkg) {
|
|
619
|
-
await tarball.stream(id, stream => {
|
|
449
|
+
await pacote.tarball.stream(id, stream => {
|
|
620
450
|
stream.resume();
|
|
621
451
|
return stream.promise();
|
|
622
452
|
}, {
|
|
@@ -648,18 +478,20 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
|
|
|
648
478
|
spinner.text = remaining > 0 ? getText() : '';
|
|
649
479
|
packageAlerts.push(...alerts);
|
|
650
480
|
}
|
|
651
|
-
} catch (e) {
|
|
652
|
-
console.log('error', e);
|
|
653
481
|
} finally {
|
|
654
482
|
spinner.stop();
|
|
655
483
|
}
|
|
656
484
|
return packageAlerts;
|
|
657
485
|
}
|
|
658
486
|
function toRepoUrl(resolved) {
|
|
659
|
-
|
|
487
|
+
try {
|
|
488
|
+
return URL.parse(resolved)?.origin ?? '';
|
|
489
|
+
} catch {}
|
|
490
|
+
return '';
|
|
660
491
|
}
|
|
661
|
-
function walk(diff_
|
|
662
|
-
const
|
|
492
|
+
function walk(diff_) {
|
|
493
|
+
const needInfoOn = [];
|
|
494
|
+
const queue = [...diff_.children];
|
|
663
495
|
let pos = 0;
|
|
664
496
|
let {
|
|
665
497
|
length: queueLength
|
|
@@ -669,9 +501,6 @@ function walk(diff_, needInfoOn = []) {
|
|
|
669
501
|
throw new Error('Detected infinite loop while walking Arborist diff');
|
|
670
502
|
}
|
|
671
503
|
const diff = queue[pos++];
|
|
672
|
-
if (!diff) {
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
504
|
const {
|
|
676
505
|
action
|
|
677
506
|
} = diff;
|
|
@@ -700,10 +529,23 @@ function walk(diff_, needInfoOn = []) {
|
|
|
700
529
|
});
|
|
701
530
|
}
|
|
702
531
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
532
|
+
for (const child of diff.children) {
|
|
533
|
+
queue[queueLength++] = child;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
537
|
+
const {
|
|
538
|
+
unchanged
|
|
539
|
+
} = diff_;
|
|
540
|
+
for (let i = 0, {
|
|
541
|
+
length
|
|
542
|
+
} = unchanged; i < length; i += 1) {
|
|
543
|
+
const pkgNode = unchanged[i];
|
|
544
|
+
needInfoOn.push({
|
|
545
|
+
existing: pkgNode.pkgid,
|
|
546
|
+
pkgid: pkgNode.pkgid,
|
|
547
|
+
repository_url: toRepoUrl(pkgNode.resolved)
|
|
548
|
+
});
|
|
707
549
|
}
|
|
708
550
|
}
|
|
709
551
|
return needInfoOn;
|
|
@@ -713,7 +555,7 @@ function walk(diff_, needInfoOn = []) {
|
|
|
713
555
|
// have access to. So we have to recreate any functionality that relies on those
|
|
714
556
|
// private properties and use our own "safe" prefixed non-conflicting private
|
|
715
557
|
// properties. Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
716
|
-
// is based on https://github.com/npm/cli/blob/
|
|
558
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/edge.js.
|
|
717
559
|
//
|
|
718
560
|
// The npm application
|
|
719
561
|
// Copyright (c) npm, Inc. and Contributors
|
|
@@ -721,6 +563,7 @@ function walk(diff_, needInfoOn = []) {
|
|
|
721
563
|
//
|
|
722
564
|
// An edge in the dependency graph.
|
|
723
565
|
// Represents a dependency relationship of some kind.
|
|
566
|
+
const initializedSafeEdges = new WeakSet();
|
|
724
567
|
class SafeEdge extends Edge {
|
|
725
568
|
#safeAccept;
|
|
726
569
|
#safeError;
|
|
@@ -739,11 +582,15 @@ class SafeEdge extends Edge {
|
|
|
739
582
|
if (accept !== undefined) {
|
|
740
583
|
this.#safeAccept = accept || '*';
|
|
741
584
|
}
|
|
585
|
+
if (from.constructor !== SafeNode) {
|
|
586
|
+
Reflect.setPrototypeOf(from, SafeNode.prototype);
|
|
587
|
+
}
|
|
742
588
|
this.#safeError = null;
|
|
743
589
|
this.#safeExplanation = null;
|
|
744
590
|
this.#safeFrom = from;
|
|
745
591
|
this.#safeName = name;
|
|
746
592
|
this.#safeTo = null;
|
|
593
|
+
initializedSafeEdges.add(this);
|
|
747
594
|
this.reload(true);
|
|
748
595
|
}
|
|
749
596
|
get accept() {
|
|
@@ -875,8 +722,11 @@ class SafeEdge extends Edge {
|
|
|
875
722
|
return this.#safeExplanation;
|
|
876
723
|
}
|
|
877
724
|
reload(hard = false) {
|
|
725
|
+
if (!initializedSafeEdges.has(this)) {
|
|
726
|
+
// Skip if called during super constructor.
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
878
729
|
this.#safeExplanation = null;
|
|
879
|
-
|
|
880
730
|
// Patch adding newOverrideSet and oldOverrideSet is based on
|
|
881
731
|
// https://github.com/npm/cli/pull/7025.
|
|
882
732
|
let newOverrideSet;
|
|
@@ -899,17 +749,15 @@ class SafeEdge extends Edge {
|
|
|
899
749
|
}
|
|
900
750
|
const newTo = this.#safeFrom?.resolve(this.name);
|
|
901
751
|
if (newTo !== this.#safeTo) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
752
|
+
// Patch replacing
|
|
753
|
+
// if (this.#safeTo) {
|
|
754
|
+
// this.#safeTo.edgesIn.delete(this)
|
|
755
|
+
// }
|
|
756
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
757
|
+
this.#safeTo?.deleteEdgeIn(this);
|
|
908
758
|
this.#safeTo = newTo ?? null;
|
|
909
759
|
this.#safeError = null;
|
|
910
|
-
|
|
911
|
-
this.#safeTo.addEdgeIn(this);
|
|
912
|
-
}
|
|
760
|
+
this.#safeTo?.addEdgeIn(this);
|
|
913
761
|
} else if (hard) {
|
|
914
762
|
this.#safeError = null;
|
|
915
763
|
}
|
|
@@ -966,7 +814,7 @@ class SafeEdge extends Edge {
|
|
|
966
814
|
}
|
|
967
815
|
|
|
968
816
|
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
969
|
-
// is based on https://github.com/npm/cli/blob/
|
|
817
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
|
|
970
818
|
class SafeNode extends Node {
|
|
971
819
|
// Return true if it's safe to remove this node, because anything that is
|
|
972
820
|
// depending on it would be fine with the thing that they would resolve to if
|
|
@@ -1066,6 +914,8 @@ class SafeNode extends Node {
|
|
|
1066
914
|
}
|
|
1067
915
|
return result;
|
|
1068
916
|
}
|
|
917
|
+
|
|
918
|
+
// Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/7025.
|
|
1069
919
|
deleteEdgeIn(edge) {
|
|
1070
920
|
this.edgesIn.delete(edge);
|
|
1071
921
|
const {
|
|
@@ -1194,7 +1044,7 @@ class SafeNode extends Node {
|
|
|
1194
1044
|
}
|
|
1195
1045
|
// This is an error condition. We can only get here if the new override set
|
|
1196
1046
|
// is in conflict with the existing.
|
|
1197
|
-
log
|
|
1047
|
+
log?.silly('Conflicting override sets', this.name);
|
|
1198
1048
|
return false;
|
|
1199
1049
|
}
|
|
1200
1050
|
|
|
@@ -1234,7 +1084,7 @@ class SafeNode extends Node {
|
|
|
1234
1084
|
}
|
|
1235
1085
|
|
|
1236
1086
|
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
1237
|
-
// is based on https://github.com/npm/cli/blob/
|
|
1087
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:
|
|
1238
1088
|
class SafeOverrideSet extends OverrideSet {
|
|
1239
1089
|
// Patch adding childrenAreEqual is based on
|
|
1240
1090
|
// https://github.com/npm/cli/pull/7025.
|
|
@@ -1337,7 +1187,7 @@ class SafeOverrideSet extends OverrideSet {
|
|
|
1337
1187
|
}
|
|
1338
1188
|
|
|
1339
1189
|
// Implementation code not related to our custom behavior is based on
|
|
1340
|
-
// https://github.com/npm/cli/blob/
|
|
1190
|
+
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
|
|
1341
1191
|
class SafeArborist extends Arborist {
|
|
1342
1192
|
constructor(...ctorArgs) {
|
|
1343
1193
|
const mutedArguments = [{
|
|
@@ -1356,6 +1206,7 @@ class SafeArborist extends Arborist {
|
|
|
1356
1206
|
async [kRiskyReify](...args) {
|
|
1357
1207
|
// SafeArborist has suffered side effects and must be rebuilt from scratch.
|
|
1358
1208
|
const arb = new Arborist(...this[kCtorArgs]);
|
|
1209
|
+
arb.idealTree = this.idealTree;
|
|
1359
1210
|
const ret = await arb.reify(...args);
|
|
1360
1211
|
Object.assign(this, arb);
|
|
1361
1212
|
return ret;
|
|
@@ -1379,46 +1230,179 @@ class SafeArborist extends Arborist {
|
|
|
1379
1230
|
options.dryRun = true;
|
|
1380
1231
|
options['save'] = false;
|
|
1381
1232
|
options['saveBundle'] = false;
|
|
1382
|
-
// TODO: Make this deal
|
|
1233
|
+
// TODO: Make this deal with any refactor to private fields by punching the
|
|
1383
1234
|
// class itself.
|
|
1384
1235
|
await super.reify(...args);
|
|
1385
|
-
const diff = walk(this['diff']);
|
|
1386
1236
|
options.dryRun = old.dryRun;
|
|
1387
1237
|
options['save'] = old.save;
|
|
1388
1238
|
options['saveBundle'] = old.saveBundle;
|
|
1389
|
-
|
|
1390
|
-
if (
|
|
1239
|
+
const needInfoOn = walk(this['diff']);
|
|
1240
|
+
if (needInfoOn.findIndex(c => c.repository_url === NPM_REGISTRY_URL) === -1) {
|
|
1241
|
+
// Nothing to check, hmmm already installed or all private?
|
|
1391
1242
|
return await this[kRiskyReify](...args);
|
|
1392
1243
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
return true;
|
|
1400
|
-
}
|
|
1401
|
-
return await prompts.confirm({
|
|
1402
|
-
message: 'Accept risks of installing these packages?',
|
|
1403
|
-
default: false
|
|
1404
|
-
}, {
|
|
1405
|
-
input,
|
|
1406
|
-
output,
|
|
1407
|
-
signal: abortSignal
|
|
1408
|
-
});
|
|
1409
|
-
} else if ((await getPackagesAlerts(this, this['registry'], diff, output)).length > 0) {
|
|
1410
|
-
throw new Error('Socket npm Unable to prompt to accept risk, need TTY to do so');
|
|
1411
|
-
}
|
|
1244
|
+
const input = process.stdin;
|
|
1245
|
+
const output = process.stderr;
|
|
1246
|
+
let alerts;
|
|
1247
|
+
const proceed = ENV[SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE] || (await (async () => {
|
|
1248
|
+
alerts = await getPackagesAlerts(this, needInfoOn, output);
|
|
1249
|
+
if (!alerts.length || ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
1412
1250
|
return true;
|
|
1251
|
+
}
|
|
1252
|
+
return await prompts.confirm({
|
|
1253
|
+
message: 'Accept risks of installing these packages?',
|
|
1254
|
+
default: false
|
|
1255
|
+
}, {
|
|
1256
|
+
input,
|
|
1257
|
+
output,
|
|
1258
|
+
signal: abortSignal
|
|
1413
1259
|
});
|
|
1414
|
-
}
|
|
1260
|
+
})());
|
|
1415
1261
|
if (proceed) {
|
|
1262
|
+
const fix = !!alerts?.length && (ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE] || (await prompts.confirm({
|
|
1263
|
+
message: 'Try to fix alerts?',
|
|
1264
|
+
default: true
|
|
1265
|
+
}, {
|
|
1266
|
+
input,
|
|
1267
|
+
output,
|
|
1268
|
+
signal: abortSignal
|
|
1269
|
+
})));
|
|
1270
|
+
if (fix) {
|
|
1271
|
+
await updateAdvisoryDependencies(this, alerts);
|
|
1272
|
+
}
|
|
1416
1273
|
return await this[kRiskyReify](...args);
|
|
1417
1274
|
} else {
|
|
1418
1275
|
throw new Error('Socket npm exiting due to risks');
|
|
1419
1276
|
}
|
|
1420
1277
|
}
|
|
1421
1278
|
}
|
|
1279
|
+
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1280
|
+
const report = packageAlertsToReport(alerts);
|
|
1281
|
+
if (!report) {
|
|
1282
|
+
// No advisories to process.
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
await arb.buildIdealTree();
|
|
1286
|
+
const tree = arb.idealTree;
|
|
1287
|
+
for (const name of Object.keys(report)) {
|
|
1288
|
+
const advisories = report[name];
|
|
1289
|
+
const node = findPackageRecursively(tree, name);
|
|
1290
|
+
if (!node) {
|
|
1291
|
+
// Package not found in the tree.
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
const {
|
|
1295
|
+
version
|
|
1296
|
+
} = node;
|
|
1297
|
+
const majorVerNum = semver.major(version);
|
|
1298
|
+
|
|
1299
|
+
// Fetch packument to get available versions.
|
|
1300
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1301
|
+
const packument = await packages.fetchPackagePackument(name);
|
|
1302
|
+
const availableVersions = packument ? Object.keys(packument.versions) : [];
|
|
1303
|
+
for (const advisory of advisories) {
|
|
1304
|
+
const {
|
|
1305
|
+
vulnerable_versions
|
|
1306
|
+
} = advisory;
|
|
1307
|
+
// Find the highest non-vulnerable version within the same major range
|
|
1308
|
+
const targetVersion = findBestPatchVersion(name, availableVersions, majorVerNum, vulnerable_versions);
|
|
1309
|
+
const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
|
|
1310
|
+
// Check !targetVersion to make TypeScript happy.
|
|
1311
|
+
if (!targetVersion || !targetPackument) {
|
|
1312
|
+
// No suitable patch version found.
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Use Object.defineProperty to override the version.
|
|
1317
|
+
Object.defineProperty(node, 'version', {
|
|
1318
|
+
configurable: true,
|
|
1319
|
+
enumerable: true,
|
|
1320
|
+
get: () => targetVersion
|
|
1321
|
+
});
|
|
1322
|
+
node.package.version = targetVersion;
|
|
1323
|
+
// Update resolved and clear integrity for the new version.
|
|
1324
|
+
node.resolved = `https://registry.npmjs.org/${name}/-/${name}-${targetVersion}.tgz`;
|
|
1325
|
+
if (node.integrity) {
|
|
1326
|
+
delete node.integrity;
|
|
1327
|
+
}
|
|
1328
|
+
if ('deprecated' in targetPackument) {
|
|
1329
|
+
node.package['deprecated'] = targetPackument.deprecated;
|
|
1330
|
+
} else {
|
|
1331
|
+
delete node.package['deprecated'];
|
|
1332
|
+
}
|
|
1333
|
+
const newDeps = {
|
|
1334
|
+
...targetPackument.dependencies
|
|
1335
|
+
};
|
|
1336
|
+
const {
|
|
1337
|
+
dependencies: oldDeps
|
|
1338
|
+
} = node.package;
|
|
1339
|
+
node.package.dependencies = newDeps;
|
|
1340
|
+
if (oldDeps) {
|
|
1341
|
+
for (const oldDepName of Object.keys(oldDeps)) {
|
|
1342
|
+
if (!objects.hasOwn(newDeps, oldDepName)) {
|
|
1343
|
+
node.edgesOut.get(oldDepName)?.detach();
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
for (const newDepName of Object.keys(newDeps)) {
|
|
1348
|
+
if (!objects.hasOwn(oldDeps, newDepName)) {
|
|
1349
|
+
node.addEdgeOut(new Edge({
|
|
1350
|
+
from: node,
|
|
1351
|
+
name: newDepName,
|
|
1352
|
+
spec: newDeps[newDepName],
|
|
1353
|
+
type: 'prod'
|
|
1354
|
+
}));
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
function findPackageRecursively(tree, packageName) {
|
|
1361
|
+
const queue = [{
|
|
1362
|
+
node: tree,
|
|
1363
|
+
depth: 0
|
|
1364
|
+
}];
|
|
1365
|
+
let sentinel = 0;
|
|
1366
|
+
while (queue.length) {
|
|
1367
|
+
if (sentinel++ === LOOP_SENTINEL) {
|
|
1368
|
+
throw new Error('Detected infinite loop in findPackageRecursively');
|
|
1369
|
+
}
|
|
1370
|
+
const {
|
|
1371
|
+
depth,
|
|
1372
|
+
node: currentNode
|
|
1373
|
+
} = queue.pop();
|
|
1374
|
+
const node = currentNode.children.get(packageName);
|
|
1375
|
+
if (node) {
|
|
1376
|
+
// Found package.
|
|
1377
|
+
return node;
|
|
1378
|
+
}
|
|
1379
|
+
const children = [...currentNode.children.values()];
|
|
1380
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
1381
|
+
queue.push({
|
|
1382
|
+
node: children[i],
|
|
1383
|
+
depth: depth + 1
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
return null;
|
|
1388
|
+
}
|
|
1389
|
+
function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableRange) {
|
|
1390
|
+
const manifestVersion = registry.getManifestData(NPM, name)?.version;
|
|
1391
|
+
// Filter versions that are within the current major version and are not in the vulnerable range
|
|
1392
|
+
const eligibleVersions = availableVersions.filter(version => {
|
|
1393
|
+
const isSameMajor = semver.major(version) === currentMajorVersion;
|
|
1394
|
+
const isNotVulnerable = !semver.satisfies(version, vulnerableRange);
|
|
1395
|
+
if (isSameMajor && isNotVulnerable) {
|
|
1396
|
+
return true;
|
|
1397
|
+
}
|
|
1398
|
+
return !!manifestVersion;
|
|
1399
|
+
});
|
|
1400
|
+
if (eligibleVersions.length === 0) {
|
|
1401
|
+
return null;
|
|
1402
|
+
}
|
|
1403
|
+
// Use semver to find the max satisfying version.
|
|
1404
|
+
return semver.maxSatisfying(eligibleVersions, '*');
|
|
1405
|
+
}
|
|
1422
1406
|
function installSafeArborist() {
|
|
1423
1407
|
const cache = require.cache;
|
|
1424
1408
|
cache[arboristClassPath] = {
|
|
@@ -1435,7 +1419,10 @@ function installSafeArborist() {
|
|
|
1435
1419
|
};
|
|
1436
1420
|
}
|
|
1437
1421
|
void (async () => {
|
|
1438
|
-
const
|
|
1422
|
+
const {
|
|
1423
|
+
orgs,
|
|
1424
|
+
settings
|
|
1425
|
+
} = await (async () => {
|
|
1439
1426
|
try {
|
|
1440
1427
|
const socketSdk = await sdk.setupSdk(pubToken);
|
|
1441
1428
|
const orgResult = await socketSdk.getOrganizations();
|
|
@@ -1474,13 +1461,9 @@ void (async () => {
|
|
|
1474
1461
|
throw e;
|
|
1475
1462
|
}
|
|
1476
1463
|
})();
|
|
1477
|
-
const {
|
|
1478
|
-
orgs,
|
|
1479
|
-
settings
|
|
1480
|
-
} = remoteSettings;
|
|
1481
|
-
const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
|
|
1482
1464
|
|
|
1483
1465
|
// Remove any organizations not being enforced.
|
|
1466
|
+
const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
|
|
1484
1467
|
for (const {
|
|
1485
1468
|
0: i,
|
|
1486
1469
|
1: org
|