socket 0.14.39 → 0.14.40-alpha.0
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 +236 -269
- 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 +236 -269
- 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 isInteractive = _socketInterop(require('is-interactive'));
|
|
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(homedir.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(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
|
});
|
|
@@ -616,7 +444,7 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
|
|
|
616
444
|
if (!blocked) {
|
|
617
445
|
const pkg = pkgs.find(p => p.pkgid === id);
|
|
618
446
|
if (pkg) {
|
|
619
|
-
await tarball.stream(id, stream => {
|
|
447
|
+
await pacote.tarball.stream(id, stream => {
|
|
620
448
|
stream.resume();
|
|
621
449
|
return stream.promise();
|
|
622
450
|
}, {
|
|
@@ -648,8 +476,6 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
|
|
|
648
476
|
spinner.text = remaining > 0 ? getText() : '';
|
|
649
477
|
packageAlerts.push(...alerts);
|
|
650
478
|
}
|
|
651
|
-
} catch (e) {
|
|
652
|
-
console.log('error', e);
|
|
653
479
|
} finally {
|
|
654
480
|
spinner.stop();
|
|
655
481
|
}
|
|
@@ -713,7 +539,7 @@ function walk(diff_, needInfoOn = []) {
|
|
|
713
539
|
// have access to. So we have to recreate any functionality that relies on those
|
|
714
540
|
// private properties and use our own "safe" prefixed non-conflicting private
|
|
715
541
|
// properties. Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
716
|
-
// is based on https://github.com/npm/cli/blob/
|
|
542
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/edge.js.
|
|
717
543
|
//
|
|
718
544
|
// The npm application
|
|
719
545
|
// Copyright (c) npm, Inc. and Contributors
|
|
@@ -721,6 +547,7 @@ function walk(diff_, needInfoOn = []) {
|
|
|
721
547
|
//
|
|
722
548
|
// An edge in the dependency graph.
|
|
723
549
|
// Represents a dependency relationship of some kind.
|
|
550
|
+
const initializedSafeEdges = new WeakSet();
|
|
724
551
|
class SafeEdge extends Edge {
|
|
725
552
|
#safeAccept;
|
|
726
553
|
#safeError;
|
|
@@ -739,11 +566,15 @@ class SafeEdge extends Edge {
|
|
|
739
566
|
if (accept !== undefined) {
|
|
740
567
|
this.#safeAccept = accept || '*';
|
|
741
568
|
}
|
|
569
|
+
if (from.constructor !== SafeNode) {
|
|
570
|
+
Reflect.setPrototypeOf(from, SafeNode.prototype);
|
|
571
|
+
}
|
|
742
572
|
this.#safeError = null;
|
|
743
573
|
this.#safeExplanation = null;
|
|
744
574
|
this.#safeFrom = from;
|
|
745
575
|
this.#safeName = name;
|
|
746
576
|
this.#safeTo = null;
|
|
577
|
+
initializedSafeEdges.add(this);
|
|
747
578
|
this.reload(true);
|
|
748
579
|
}
|
|
749
580
|
get accept() {
|
|
@@ -875,8 +706,11 @@ class SafeEdge extends Edge {
|
|
|
875
706
|
return this.#safeExplanation;
|
|
876
707
|
}
|
|
877
708
|
reload(hard = false) {
|
|
709
|
+
if (!initializedSafeEdges.has(this)) {
|
|
710
|
+
// Skip if called during super constructor.
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
878
713
|
this.#safeExplanation = null;
|
|
879
|
-
|
|
880
714
|
// Patch adding newOverrideSet and oldOverrideSet is based on
|
|
881
715
|
// https://github.com/npm/cli/pull/7025.
|
|
882
716
|
let newOverrideSet;
|
|
@@ -899,17 +733,15 @@ class SafeEdge extends Edge {
|
|
|
899
733
|
}
|
|
900
734
|
const newTo = this.#safeFrom?.resolve(this.name);
|
|
901
735
|
if (newTo !== this.#safeTo) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
736
|
+
// Patch replacing
|
|
737
|
+
// if (this.#safeTo) {
|
|
738
|
+
// this.#safeTo.edgesIn.delete(this)
|
|
739
|
+
// }
|
|
740
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
741
|
+
this.#safeTo?.deleteEdgeIn(this);
|
|
908
742
|
this.#safeTo = newTo ?? null;
|
|
909
743
|
this.#safeError = null;
|
|
910
|
-
|
|
911
|
-
this.#safeTo.addEdgeIn(this);
|
|
912
|
-
}
|
|
744
|
+
this.#safeTo?.addEdgeIn(this);
|
|
913
745
|
} else if (hard) {
|
|
914
746
|
this.#safeError = null;
|
|
915
747
|
}
|
|
@@ -966,7 +798,7 @@ class SafeEdge extends Edge {
|
|
|
966
798
|
}
|
|
967
799
|
|
|
968
800
|
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
969
|
-
// is based on https://github.com/npm/cli/blob/
|
|
801
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
|
|
970
802
|
class SafeNode extends Node {
|
|
971
803
|
// Return true if it's safe to remove this node, because anything that is
|
|
972
804
|
// depending on it would be fine with the thing that they would resolve to if
|
|
@@ -1066,6 +898,8 @@ class SafeNode extends Node {
|
|
|
1066
898
|
}
|
|
1067
899
|
return result;
|
|
1068
900
|
}
|
|
901
|
+
|
|
902
|
+
// Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/7025.
|
|
1069
903
|
deleteEdgeIn(edge) {
|
|
1070
904
|
this.edgesIn.delete(edge);
|
|
1071
905
|
const {
|
|
@@ -1194,7 +1028,7 @@ class SafeNode extends Node {
|
|
|
1194
1028
|
}
|
|
1195
1029
|
// This is an error condition. We can only get here if the new override set
|
|
1196
1030
|
// is in conflict with the existing.
|
|
1197
|
-
log
|
|
1031
|
+
log?.silly('Conflicting override sets', this.name);
|
|
1198
1032
|
return false;
|
|
1199
1033
|
}
|
|
1200
1034
|
|
|
@@ -1234,7 +1068,7 @@ class SafeNode extends Node {
|
|
|
1234
1068
|
}
|
|
1235
1069
|
|
|
1236
1070
|
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
1237
|
-
// is based on https://github.com/npm/cli/blob/
|
|
1071
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:
|
|
1238
1072
|
class SafeOverrideSet extends OverrideSet {
|
|
1239
1073
|
// Patch adding childrenAreEqual is based on
|
|
1240
1074
|
// https://github.com/npm/cli/pull/7025.
|
|
@@ -1337,7 +1171,7 @@ class SafeOverrideSet extends OverrideSet {
|
|
|
1337
1171
|
}
|
|
1338
1172
|
|
|
1339
1173
|
// Implementation code not related to our custom behavior is based on
|
|
1340
|
-
// https://github.com/npm/cli/blob/
|
|
1174
|
+
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
|
|
1341
1175
|
class SafeArborist extends Arborist {
|
|
1342
1176
|
constructor(...ctorArgs) {
|
|
1343
1177
|
const mutedArguments = [{
|
|
@@ -1356,6 +1190,7 @@ class SafeArborist extends Arborist {
|
|
|
1356
1190
|
async [kRiskyReify](...args) {
|
|
1357
1191
|
// SafeArborist has suffered side effects and must be rebuilt from scratch.
|
|
1358
1192
|
const arb = new Arborist(...this[kCtorArgs]);
|
|
1193
|
+
arb.idealTree = this.idealTree;
|
|
1359
1194
|
const ret = await arb.reify(...args);
|
|
1360
1195
|
Object.assign(this, arb);
|
|
1361
1196
|
return ret;
|
|
@@ -1379,46 +1214,179 @@ class SafeArborist extends Arborist {
|
|
|
1379
1214
|
options.dryRun = true;
|
|
1380
1215
|
options['save'] = false;
|
|
1381
1216
|
options['saveBundle'] = false;
|
|
1382
|
-
// TODO: Make this deal
|
|
1217
|
+
// TODO: Make this deal with any refactor to private fields by punching the
|
|
1383
1218
|
// class itself.
|
|
1384
1219
|
await super.reify(...args);
|
|
1385
|
-
const diff = walk(this['diff']);
|
|
1386
1220
|
options.dryRun = old.dryRun;
|
|
1387
1221
|
options['save'] = old.save;
|
|
1388
1222
|
options['saveBundle'] = old.saveBundle;
|
|
1389
|
-
// Nothing to check,
|
|
1223
|
+
// Nothing to check, hmmm already installed or all private?
|
|
1224
|
+
const diff = walk(this['diff']);
|
|
1390
1225
|
if (diff.findIndex(c => c.repository_url === NPM_REGISTRY_URL) === -1) {
|
|
1391
1226
|
return await this[kRiskyReify](...args);
|
|
1392
1227
|
}
|
|
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
|
-
}
|
|
1228
|
+
const input = process.stdin;
|
|
1229
|
+
const output = process.stderr;
|
|
1230
|
+
let alerts;
|
|
1231
|
+
const proceed = ENV[SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE] || (await (async () => {
|
|
1232
|
+
alerts = await getPackagesAlerts(this, diff, output);
|
|
1233
|
+
if (!alerts.length || ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
1412
1234
|
return true;
|
|
1235
|
+
}
|
|
1236
|
+
return await prompts.confirm({
|
|
1237
|
+
message: 'Accept risks of installing these packages?',
|
|
1238
|
+
default: false
|
|
1239
|
+
}, {
|
|
1240
|
+
input,
|
|
1241
|
+
output,
|
|
1242
|
+
signal: abortSignal
|
|
1413
1243
|
});
|
|
1414
|
-
}
|
|
1244
|
+
})());
|
|
1415
1245
|
if (proceed) {
|
|
1246
|
+
const fix = !!alerts?.length && (ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE] || (await prompts.confirm({
|
|
1247
|
+
message: 'Try to fix alerts?',
|
|
1248
|
+
default: true
|
|
1249
|
+
}, {
|
|
1250
|
+
input,
|
|
1251
|
+
output,
|
|
1252
|
+
signal: abortSignal
|
|
1253
|
+
})));
|
|
1254
|
+
if (fix) {
|
|
1255
|
+
await updateAdvisoryDependencies(this, alerts);
|
|
1256
|
+
}
|
|
1416
1257
|
return await this[kRiskyReify](...args);
|
|
1417
1258
|
} else {
|
|
1418
1259
|
throw new Error('Socket npm exiting due to risks');
|
|
1419
1260
|
}
|
|
1420
1261
|
}
|
|
1421
1262
|
}
|
|
1263
|
+
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1264
|
+
const report = packageAlertsToReport(alerts);
|
|
1265
|
+
if (!report) {
|
|
1266
|
+
// No advisories to process.
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
await arb.buildIdealTree();
|
|
1270
|
+
const tree = arb.idealTree;
|
|
1271
|
+
for (const name of Object.keys(report)) {
|
|
1272
|
+
const advisories = report[name];
|
|
1273
|
+
const node = findPackageRecursively(tree, name);
|
|
1274
|
+
if (!node) {
|
|
1275
|
+
// Package not found in the tree.
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
const {
|
|
1279
|
+
version
|
|
1280
|
+
} = node;
|
|
1281
|
+
const majorVerNum = semver.major(version);
|
|
1282
|
+
|
|
1283
|
+
// Fetch packument to get available versions.
|
|
1284
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1285
|
+
const packument = await packages.fetchPackagePackument(name);
|
|
1286
|
+
const availableVersions = packument ? Object.keys(packument.versions) : [];
|
|
1287
|
+
for (const advisory of advisories) {
|
|
1288
|
+
const {
|
|
1289
|
+
vulnerable_versions
|
|
1290
|
+
} = advisory;
|
|
1291
|
+
// Find the highest non-vulnerable version within the same major range
|
|
1292
|
+
const targetVersion = findBestPatchVersion(name, availableVersions, majorVerNum, vulnerable_versions);
|
|
1293
|
+
const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
|
|
1294
|
+
// Check !targetVersion to make TypeScript happy.
|
|
1295
|
+
if (!targetVersion || !targetPackument) {
|
|
1296
|
+
// No suitable patch version found.
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Use Object.defineProperty to override the version.
|
|
1301
|
+
Object.defineProperty(node, 'version', {
|
|
1302
|
+
configurable: true,
|
|
1303
|
+
enumerable: true,
|
|
1304
|
+
get: () => targetVersion
|
|
1305
|
+
});
|
|
1306
|
+
node.package.version = targetVersion;
|
|
1307
|
+
// Update resolved and clear integrity for the new version.
|
|
1308
|
+
node.resolved = `https://registry.npmjs.org/${name}/-/${name}-${targetVersion}.tgz`;
|
|
1309
|
+
if (node.integrity) {
|
|
1310
|
+
delete node.integrity;
|
|
1311
|
+
}
|
|
1312
|
+
if ('deprecated' in targetPackument) {
|
|
1313
|
+
node.package['deprecated'] = targetPackument.deprecated;
|
|
1314
|
+
} else {
|
|
1315
|
+
delete node.package['deprecated'];
|
|
1316
|
+
}
|
|
1317
|
+
const newDeps = {
|
|
1318
|
+
...targetPackument.dependencies
|
|
1319
|
+
};
|
|
1320
|
+
const {
|
|
1321
|
+
dependencies: oldDeps
|
|
1322
|
+
} = node.package;
|
|
1323
|
+
node.package.dependencies = newDeps;
|
|
1324
|
+
if (oldDeps) {
|
|
1325
|
+
for (const oldDepName of Object.keys(oldDeps)) {
|
|
1326
|
+
if (!objects.hasOwn(newDeps, oldDepName)) {
|
|
1327
|
+
node.edgesOut.get(oldDepName)?.detach();
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
for (const newDepName of Object.keys(newDeps)) {
|
|
1332
|
+
if (!objects.hasOwn(oldDeps, newDepName)) {
|
|
1333
|
+
node.addEdgeOut(new Edge({
|
|
1334
|
+
from: node,
|
|
1335
|
+
name: newDepName,
|
|
1336
|
+
spec: newDeps[newDepName],
|
|
1337
|
+
type: 'prod'
|
|
1338
|
+
}));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
function findPackageRecursively(tree, packageName) {
|
|
1345
|
+
const queue = [{
|
|
1346
|
+
node: tree,
|
|
1347
|
+
depth: 0
|
|
1348
|
+
}];
|
|
1349
|
+
let sentinel = 0;
|
|
1350
|
+
while (queue.length) {
|
|
1351
|
+
if (sentinel++ === LOOP_SENTINEL) {
|
|
1352
|
+
throw new Error('Detected infinite loop in findPackageRecursively');
|
|
1353
|
+
}
|
|
1354
|
+
const {
|
|
1355
|
+
depth,
|
|
1356
|
+
node: currentNode
|
|
1357
|
+
} = queue.pop();
|
|
1358
|
+
const node = currentNode.children.get(packageName);
|
|
1359
|
+
if (node) {
|
|
1360
|
+
// Found package.
|
|
1361
|
+
return node;
|
|
1362
|
+
}
|
|
1363
|
+
const children = [...currentNode.children.values()];
|
|
1364
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
1365
|
+
queue.push({
|
|
1366
|
+
node: children[i],
|
|
1367
|
+
depth: depth + 1
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
1373
|
+
function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableRange) {
|
|
1374
|
+
const manifestVersion = registry.getManifestData(NPM, name)?.version;
|
|
1375
|
+
// Filter versions that are within the current major version and are not in the vulnerable range
|
|
1376
|
+
const eligibleVersions = availableVersions.filter(version => {
|
|
1377
|
+
const isSameMajor = semver.major(version) === currentMajorVersion;
|
|
1378
|
+
const isNotVulnerable = !semver.satisfies(version, vulnerableRange);
|
|
1379
|
+
if (isSameMajor && isNotVulnerable) {
|
|
1380
|
+
return true;
|
|
1381
|
+
}
|
|
1382
|
+
return !!manifestVersion;
|
|
1383
|
+
});
|
|
1384
|
+
if (eligibleVersions.length === 0) {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
// Use semver to find the max satisfying version.
|
|
1388
|
+
return semver.maxSatisfying(eligibleVersions, '*');
|
|
1389
|
+
}
|
|
1422
1390
|
function installSafeArborist() {
|
|
1423
1391
|
const cache = require.cache;
|
|
1424
1392
|
cache[arboristClassPath] = {
|
|
@@ -1435,7 +1403,10 @@ function installSafeArborist() {
|
|
|
1435
1403
|
};
|
|
1436
1404
|
}
|
|
1437
1405
|
void (async () => {
|
|
1438
|
-
const
|
|
1406
|
+
const {
|
|
1407
|
+
orgs,
|
|
1408
|
+
settings
|
|
1409
|
+
} = await (async () => {
|
|
1439
1410
|
try {
|
|
1440
1411
|
const socketSdk = await sdk.setupSdk(pubToken);
|
|
1441
1412
|
const orgResult = await socketSdk.getOrganizations();
|
|
@@ -1474,13 +1445,9 @@ void (async () => {
|
|
|
1474
1445
|
throw e;
|
|
1475
1446
|
}
|
|
1476
1447
|
})();
|
|
1477
|
-
const {
|
|
1478
|
-
orgs,
|
|
1479
|
-
settings
|
|
1480
|
-
} = remoteSettings;
|
|
1481
|
-
const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
|
|
1482
1448
|
|
|
1483
1449
|
// Remove any organizations not being enforced.
|
|
1450
|
+
const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
|
|
1484
1451
|
for (const {
|
|
1485
1452
|
0: i,
|
|
1486
1453
|
1: org
|