socket 0.14.40-alpha.8 → 0.14.40
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/README.md +1 -1
- package/bin/cli.js +12 -6
- package/dist/{module-sync/constants.d.ts → constants.d.ts} +11 -2
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +19 -11
- package/dist/module-sync/cli.js +318 -255
- package/dist/module-sync/debug.d.ts +3 -0
- package/dist/module-sync/errors.d.ts +3 -1
- package/dist/module-sync/logging.d.ts +16 -0
- package/dist/module-sync/npm-injection.js +1026 -942
- package/dist/module-sync/path-resolve.d.ts +1 -1
- package/dist/module-sync/path-resolve.js +49 -1
- package/dist/module-sync/shadow-bin.d.ts +2 -2
- package/dist/module-sync/shadow-bin.js +19 -14
- package/dist/module-sync/socket-url.d.ts +24 -0
- package/dist/module-sync/socket-url.js +222 -0
- package/dist/require/cli.js +318 -253
- package/dist/require/npm-injection.js +2 -1504
- package/dist/require/path-resolve.js +2 -197
- package/dist/require/shadow-bin.js +2 -80
- package/dist/require/socket-url.js +3 -0
- package/dist/require/vendor.js +53 -400
- package/package.json +19 -19
- package/dist/module-sync/color-or-markdown.d.ts +0 -23
- package/dist/module-sync/constants.d.ts.map +0 -1
- package/dist/module-sync/sdk.d.ts +0 -8
- package/dist/module-sync/sdk.js +0 -214
- package/dist/require/constants.d.ts.map +0 -1
- package/dist/require/sdk.js +0 -212
|
@@ -9,26 +9,154 @@ function _socketInterop(e) {
|
|
|
9
9
|
return c ? e.default : e
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
var events = require('node:events');
|
|
13
|
-
var fs = require('node:fs');
|
|
14
|
-
var https = require('node:https');
|
|
15
12
|
var path = require('node:path');
|
|
16
|
-
var
|
|
17
|
-
var promises = require('node:timers/promises');
|
|
18
|
-
var npa = _socketInterop(require('npm-package-arg'));
|
|
13
|
+
var process = require('node:process');
|
|
19
14
|
var semver = _socketInterop(require('semver'));
|
|
20
|
-
var config = require('@socketsecurity/config');
|
|
21
15
|
var registry = require('@socketsecurity/registry');
|
|
22
16
|
var objects = require('@socketsecurity/registry/lib/objects');
|
|
23
17
|
var packages = require('@socketsecurity/registry/lib/packages');
|
|
24
18
|
var prompts = require('@socketsecurity/registry/lib/prompts');
|
|
25
19
|
var spinner = require('@socketsecurity/registry/lib/spinner');
|
|
20
|
+
var events = require('node:events');
|
|
21
|
+
var https = require('node:https');
|
|
22
|
+
var readline = require('node:readline');
|
|
26
23
|
var constants = require('./constants.js');
|
|
27
|
-
var
|
|
24
|
+
var socketUrl = require('./socket-url.js');
|
|
25
|
+
var fs = require('node:fs');
|
|
26
|
+
var promises = require('node:timers/promises');
|
|
27
|
+
var config = require('@socketsecurity/config');
|
|
28
28
|
var pathResolve = require('./path-resolve.js');
|
|
29
|
+
var npa = _socketInterop(require('npm-package-arg'));
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
const {
|
|
32
|
+
API_V0_URL,
|
|
33
|
+
LOOP_SENTINEL: LOOP_SENTINEL$2,
|
|
34
|
+
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE: SOCKET_CLI_FIX_PACKAGE_LOCK_FILE$1,
|
|
35
|
+
abortSignal: abortSignal$2
|
|
36
|
+
} = constants;
|
|
37
|
+
async function* batchScan(pkgIds) {
|
|
38
|
+
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
|
|
42
|
+
},
|
|
43
|
+
signal: abortSignal$2
|
|
44
|
+
}).end(JSON.stringify({
|
|
45
|
+
components: pkgIds.map(id => ({
|
|
46
|
+
purl: `pkg:npm/${id}`
|
|
47
|
+
}))
|
|
48
|
+
}));
|
|
49
|
+
const {
|
|
50
|
+
0: res
|
|
51
|
+
} = await events.once(req, 'response');
|
|
52
|
+
const ok = res.statusCode >= 200 && res.statusCode <= 299;
|
|
53
|
+
if (!ok) {
|
|
54
|
+
throw new Error(`Socket API Error: ${res.statusCode}`);
|
|
55
|
+
}
|
|
56
|
+
const rli = readline.createInterface(res);
|
|
57
|
+
for await (const line of rli) {
|
|
58
|
+
yield JSON.parse(line);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function isAlertFixable(alert) {
|
|
62
|
+
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
|
|
63
|
+
}
|
|
64
|
+
function isAlertFixableCve(alert) {
|
|
65
|
+
const {
|
|
66
|
+
type
|
|
67
|
+
} = alert;
|
|
68
|
+
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
69
|
+
}
|
|
70
|
+
function toRepoUrl(resolved) {
|
|
71
|
+
try {
|
|
72
|
+
return URL.parse(resolved)?.origin ?? '';
|
|
73
|
+
} catch {}
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
function walk(diff_, options) {
|
|
77
|
+
const {
|
|
78
|
+
// Lazily access constants.IPC.
|
|
79
|
+
fix = constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE$1]
|
|
80
|
+
} = {
|
|
81
|
+
__proto__: null,
|
|
82
|
+
...options
|
|
83
|
+
};
|
|
84
|
+
const needInfoOn = [];
|
|
85
|
+
// `diff_` is `null` when `npm install --package-lock-only` is passed.
|
|
86
|
+
if (!diff_) {
|
|
87
|
+
return needInfoOn;
|
|
88
|
+
}
|
|
89
|
+
const queue = [...diff_.children];
|
|
90
|
+
let pos = 0;
|
|
91
|
+
let {
|
|
92
|
+
length: queueLength
|
|
93
|
+
} = queue;
|
|
94
|
+
while (pos < queueLength) {
|
|
95
|
+
if (pos === LOOP_SENTINEL$2) {
|
|
96
|
+
throw new Error('Detected infinite loop while walking Arborist diff');
|
|
97
|
+
}
|
|
98
|
+
const diff = queue[pos++];
|
|
99
|
+
const {
|
|
100
|
+
action
|
|
101
|
+
} = diff;
|
|
102
|
+
if (action) {
|
|
103
|
+
// The `pkgNode`, i.e. the `ideal` node, will be `undefined` if the diff
|
|
104
|
+
// action is 'REMOVE'
|
|
105
|
+
// The `oldNode`, i.e. the `actual` node, will be `undefined` if the diff
|
|
106
|
+
// action is 'ADD'.
|
|
107
|
+
const {
|
|
108
|
+
actual: oldNode,
|
|
109
|
+
ideal: pkgNode
|
|
110
|
+
} = diff;
|
|
111
|
+
let existing;
|
|
112
|
+
let keep = false;
|
|
113
|
+
if (action === 'CHANGE') {
|
|
114
|
+
if (pkgNode?.package.version !== oldNode?.package.version) {
|
|
115
|
+
keep = true;
|
|
116
|
+
if (oldNode?.package.name && oldNode.package.name === pkgNode?.package.name) {
|
|
117
|
+
existing = oldNode.pkgid;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
keep = action !== 'REMOVE';
|
|
122
|
+
}
|
|
123
|
+
if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {
|
|
124
|
+
needInfoOn.push({
|
|
125
|
+
existing,
|
|
126
|
+
pkgid: pkgNode.pkgid,
|
|
127
|
+
repository_url: toRepoUrl(pkgNode.resolved)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const child of diff.children) {
|
|
132
|
+
queue[queueLength++] = child;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (fix) {
|
|
136
|
+
const {
|
|
137
|
+
unchanged
|
|
138
|
+
} = diff_;
|
|
139
|
+
for (let i = 0, {
|
|
140
|
+
length
|
|
141
|
+
} = unchanged; i < length; i += 1) {
|
|
142
|
+
const pkgNode = unchanged[i];
|
|
143
|
+
needInfoOn.push({
|
|
144
|
+
existing: pkgNode.pkgid,
|
|
145
|
+
pkgid: pkgNode.pkgid,
|
|
146
|
+
repository_url: toRepoUrl(pkgNode.resolved)
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return needInfoOn;
|
|
151
|
+
}
|
|
31
152
|
|
|
153
|
+
const {
|
|
154
|
+
abortSignal: abortSignal$1
|
|
155
|
+
} = constants;
|
|
156
|
+
const ERROR_UX = {
|
|
157
|
+
block: true,
|
|
158
|
+
display: true
|
|
159
|
+
};
|
|
32
160
|
const IGNORE_UX = {
|
|
33
161
|
block: false,
|
|
34
162
|
display: false
|
|
@@ -37,18 +165,41 @@ const WARN_UX = {
|
|
|
37
165
|
block: false,
|
|
38
166
|
display: true
|
|
39
167
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
168
|
+
function findSocketYmlSync() {
|
|
169
|
+
let prevDir = null;
|
|
170
|
+
let dir = process.cwd();
|
|
171
|
+
while (dir !== prevDir) {
|
|
172
|
+
let ymlPath = path.join(dir, 'socket.yml');
|
|
173
|
+
let yml = maybeReadfileSync(ymlPath);
|
|
174
|
+
if (yml === undefined) {
|
|
175
|
+
ymlPath = path.join(dir, 'socket.yaml');
|
|
176
|
+
yml = maybeReadfileSync(ymlPath);
|
|
177
|
+
}
|
|
178
|
+
if (typeof yml === 'string') {
|
|
179
|
+
try {
|
|
180
|
+
return {
|
|
181
|
+
path: ymlPath,
|
|
182
|
+
parsed: config.parseSocketConfig(yml)
|
|
183
|
+
};
|
|
184
|
+
} catch {
|
|
185
|
+
throw new Error(`Found file but was unable to parse ${ymlPath}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
prevDir = dir;
|
|
189
|
+
dir = path.join(dir, '..');
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
function maybeReadfileSync(filepath) {
|
|
194
|
+
try {
|
|
195
|
+
return fs.readFileSync(filepath, 'utf8');
|
|
196
|
+
} catch {}
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
46
199
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* uses the defaultValue. Takes the value and converts into a UX workflow.
|
|
51
|
-
*/
|
|
200
|
+
// Iterates over all entries with ordered issue rule for deferral. Iterates over
|
|
201
|
+
// all issue rules and finds the first defined value that does not defer otherwise
|
|
202
|
+
// uses the defaultValue. Takes the value and converts into a UX workflow.
|
|
52
203
|
function resolveAlertRuleUX(orderedRulesCollection, defaultValue) {
|
|
53
204
|
if (defaultValue === true || defaultValue === null || defaultValue === undefined) {
|
|
54
205
|
defaultValue = {
|
|
@@ -87,9 +238,7 @@ function resolveAlertRuleUX(orderedRulesCollection, defaultValue) {
|
|
|
87
238
|
};
|
|
88
239
|
}
|
|
89
240
|
|
|
90
|
-
|
|
91
|
-
* Negative form because it is narrowing the type.
|
|
92
|
-
*/
|
|
241
|
+
// Negative form because it is narrowing the type.
|
|
93
242
|
function ruleValueDoesNotDefer(rule) {
|
|
94
243
|
if (rule === undefined) {
|
|
95
244
|
return false;
|
|
@@ -105,9 +254,7 @@ function ruleValueDoesNotDefer(rule) {
|
|
|
105
254
|
return true;
|
|
106
255
|
}
|
|
107
256
|
|
|
108
|
-
|
|
109
|
-
* Handles booleans for backwards compatibility.
|
|
110
|
-
*/
|
|
257
|
+
// Handles booleans for backwards compatibility.
|
|
111
258
|
function uxForDefinedNonDeferValue(ruleValue) {
|
|
112
259
|
if (typeof ruleValue === 'boolean') {
|
|
113
260
|
return ruleValue ? ERROR_UX : IGNORE_UX;
|
|
@@ -122,10 +269,6 @@ function uxForDefinedNonDeferValue(ruleValue) {
|
|
|
122
269
|
}
|
|
123
270
|
return ERROR_UX;
|
|
124
271
|
}
|
|
125
|
-
//#endregion
|
|
126
|
-
|
|
127
|
-
//#region exports
|
|
128
|
-
|
|
129
272
|
function createAlertUXLookup(settings) {
|
|
130
273
|
const cachedUX = new Map();
|
|
131
274
|
return context => {
|
|
@@ -171,24 +314,116 @@ function createAlertUXLookup(settings) {
|
|
|
171
314
|
return ux;
|
|
172
315
|
};
|
|
173
316
|
}
|
|
174
|
-
|
|
317
|
+
let _uxLookup;
|
|
318
|
+
async function uxLookup(settings) {
|
|
319
|
+
while (_uxLookup === undefined) {
|
|
320
|
+
// eslint-disable-next-line no-await-in-loop
|
|
321
|
+
await promises.setTimeout(1, {
|
|
322
|
+
signal: abortSignal$1
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return _uxLookup(settings);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Start initializing the AlertUxLookupResult immediately.
|
|
329
|
+
void (async () => {
|
|
330
|
+
const {
|
|
331
|
+
orgs,
|
|
332
|
+
settings
|
|
333
|
+
} = await (async () => {
|
|
334
|
+
try {
|
|
335
|
+
const socketSdk = await socketUrl.setupSdk(socketUrl.getPublicToken());
|
|
336
|
+
const orgResult = await socketSdk.getOrganizations();
|
|
337
|
+
if (!orgResult.success) {
|
|
338
|
+
throw new Error(`Failed to fetch Socket organization info: ${orgResult.error.message}`);
|
|
339
|
+
}
|
|
340
|
+
const orgs = [];
|
|
341
|
+
for (const org of Object.values(orgResult.data.organizations)) {
|
|
342
|
+
if (org) {
|
|
343
|
+
orgs.push(org);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const result = await socketSdk.postSettings(orgs.map(org => ({
|
|
347
|
+
organization: org.id
|
|
348
|
+
})));
|
|
349
|
+
if (!result.success) {
|
|
350
|
+
throw new Error(`Failed to fetch API key settings: ${result.error.message}`);
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
orgs,
|
|
354
|
+
settings: result.data
|
|
355
|
+
};
|
|
356
|
+
} catch (e) {
|
|
357
|
+
const cause = objects.isObject(e) && 'cause' in e ? e.cause : undefined;
|
|
358
|
+
if (socketUrl.isErrnoException(cause) && (cause.code === 'ENOTFOUND' || cause.code === 'ECONNREFUSED')) {
|
|
359
|
+
throw new Error('Unable to connect to socket.dev, ensure internet connectivity before retrying', {
|
|
360
|
+
cause: e
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
throw e;
|
|
364
|
+
}
|
|
365
|
+
})();
|
|
366
|
+
|
|
367
|
+
// Remove any organizations not being enforced.
|
|
368
|
+
const enforcedOrgs = socketUrl.getSetting('enforcedOrgs') ?? [];
|
|
369
|
+
for (const {
|
|
370
|
+
0: i,
|
|
371
|
+
1: org
|
|
372
|
+
} of orgs.entries()) {
|
|
373
|
+
if (!enforcedOrgs.includes(org.id)) {
|
|
374
|
+
settings.entries.splice(i, 1);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const socketYml = findSocketYmlSync();
|
|
378
|
+
if (socketYml) {
|
|
379
|
+
settings.entries.push({
|
|
380
|
+
start: socketYml.path,
|
|
381
|
+
settings: {
|
|
382
|
+
[socketYml.path]: {
|
|
383
|
+
deferTo: null,
|
|
384
|
+
// TODO: TypeScript complains about the type not matching. We should
|
|
385
|
+
// figure out why are providing
|
|
386
|
+
// issueRules: { [issueName: string]: boolean }
|
|
387
|
+
// but expecting
|
|
388
|
+
// issueRules: { [issueName: string]: { action: 'defer' | 'error' | 'ignore' | 'monitor' | 'warn' } }
|
|
389
|
+
issueRules: socketYml.parsed.issueRules
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
_uxLookup = createAlertUXLookup(settings);
|
|
395
|
+
})();
|
|
175
396
|
|
|
176
397
|
const {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
LOOP_SENTINEL,
|
|
180
|
-
NPM,
|
|
181
|
-
NPM_REGISTRY_URL,
|
|
182
|
-
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
|
|
183
|
-
SOCKET_CLI_ISSUES_URL,
|
|
184
|
-
SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE,
|
|
185
|
-
SOCKET_PUBLIC_API_KEY,
|
|
186
|
-
abortSignal,
|
|
187
|
-
rootPath
|
|
398
|
+
NODE_MODULES,
|
|
399
|
+
SOCKET_CLI_ISSUES_URL
|
|
188
400
|
} = constants;
|
|
189
|
-
const
|
|
190
|
-
const npmEntrypoint = fs.realpathSync(process.argv[1]);
|
|
401
|
+
const npmEntrypoint = fs.realpathSync.native(process.argv[1]);
|
|
191
402
|
const npmRootPath = pathResolve.findRoot(path.dirname(npmEntrypoint));
|
|
403
|
+
if (npmRootPath === undefined) {
|
|
404
|
+
console.error(`Unable to find npm CLI install directory.
|
|
405
|
+
Searched parent directories of ${npmEntrypoint}.
|
|
406
|
+
|
|
407
|
+
This is may be a bug with socket-npm related to changes to the npm CLI.
|
|
408
|
+
Please report to ${SOCKET_CLI_ISSUES_URL}.`);
|
|
409
|
+
// The exit code 127 indicates that the command or binary being executed
|
|
410
|
+
// could not be found.
|
|
411
|
+
process.exit(127);
|
|
412
|
+
}
|
|
413
|
+
const npmNmPath = path.join(npmRootPath, NODE_MODULES);
|
|
414
|
+
const arboristPkgPath = path.join(npmNmPath, '@npmcli/arborist');
|
|
415
|
+
const arboristClassPath = path.join(arboristPkgPath, 'lib/arborist/index.js');
|
|
416
|
+
const arboristDepValidPath = path.join(arboristPkgPath, 'lib/dep-valid.js');
|
|
417
|
+
const arboristEdgeClassPath = path.join(arboristPkgPath, 'lib/edge.js');
|
|
418
|
+
const arboristNodeClassPath = path.join(arboristPkgPath, 'lib/node.js');
|
|
419
|
+
const arboristOverrideSetClassPath = path.join(arboristPkgPath, 'lib/override-set.js');
|
|
420
|
+
const pacotePath = path.join(npmNmPath, 'pacote');
|
|
421
|
+
|
|
422
|
+
const depValid = require(arboristDepValidPath);
|
|
423
|
+
|
|
424
|
+
const {
|
|
425
|
+
UNDEFINED_TOKEN
|
|
426
|
+
} = constants;
|
|
192
427
|
function tryRequire(...ids) {
|
|
193
428
|
for (const data of ids) {
|
|
194
429
|
let id;
|
|
@@ -211,354 +446,432 @@ function tryRequire(...ids) {
|
|
|
211
446
|
}
|
|
212
447
|
return undefined;
|
|
213
448
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
449
|
+
let _log = UNDEFINED_TOKEN;
|
|
450
|
+
function getLogger() {
|
|
451
|
+
if (_log === UNDEFINED_TOKEN) {
|
|
452
|
+
_log = tryRequire([path.join(npmNmPath, 'proc-log/lib/index.js'),
|
|
453
|
+
// The proc-log DefinitelyTyped definition is incorrect. The type definition
|
|
454
|
+
// is really that of its export log.
|
|
455
|
+
mod => mod.log], path.join(npmNmPath, 'npmlog/lib/log.js'));
|
|
456
|
+
}
|
|
457
|
+
return _log;
|
|
219
458
|
}
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const Node = require(arboristNodeClassPath);
|
|
237
|
-
const OverrideSet = require(arboristOverrideSetClassPatch);
|
|
238
|
-
const kCtorArgs = Symbol('ctorArgs');
|
|
239
|
-
const kRiskyReify = Symbol('riskyReify');
|
|
240
|
-
const formatter = new sdk.ColorOrMarkdown(false);
|
|
241
|
-
const pubToken = sdk.getDefaultKey() ?? SOCKET_PUBLIC_API_KEY;
|
|
242
|
-
let _uxLookup;
|
|
243
|
-
async function uxLookup(settings) {
|
|
244
|
-
while (_uxLookup === undefined) {
|
|
245
|
-
// eslint-disable-next-line no-await-in-loop
|
|
246
|
-
await promises.setTimeout(1, {
|
|
247
|
-
signal: abortSignal
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
return _uxLookup(settings);
|
|
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
|
-
}
|
|
281
|
-
async function* batchScan(pkgIds) {
|
|
282
|
-
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
283
|
-
method: 'POST',
|
|
284
|
-
headers: {
|
|
285
|
-
Authorization: `Basic ${Buffer.from(`${pubToken}:`).toString('base64url')}`
|
|
286
|
-
},
|
|
287
|
-
signal: abortSignal
|
|
288
|
-
}).end(JSON.stringify({
|
|
289
|
-
components: pkgIds.map(id => ({
|
|
290
|
-
purl: `pkg:npm/${id}`
|
|
291
|
-
}))
|
|
292
|
-
}));
|
|
293
|
-
const {
|
|
294
|
-
0: res
|
|
295
|
-
} = await events.once(req, 'response');
|
|
296
|
-
const ok = res.statusCode >= 200 && res.statusCode <= 299;
|
|
297
|
-
if (!ok) {
|
|
298
|
-
throw new Error(`Socket API Error: ${res.statusCode}`);
|
|
299
|
-
}
|
|
300
|
-
const rli = readline.createInterface(res);
|
|
301
|
-
for await (const line of rli) {
|
|
302
|
-
yield JSON.parse(line);
|
|
459
|
+
|
|
460
|
+
const {
|
|
461
|
+
LOOP_SENTINEL: LOOP_SENTINEL$1
|
|
462
|
+
} = constants;
|
|
463
|
+
const OverrideSet = require(arboristOverrideSetClassPath);
|
|
464
|
+
|
|
465
|
+
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
466
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:
|
|
467
|
+
class SafeOverrideSet extends OverrideSet {
|
|
468
|
+
// Patch adding doOverrideSetsConflict is based on
|
|
469
|
+
// https://github.com/npm/cli/pull/7025.
|
|
470
|
+
static doOverrideSetsConflict(first, second) {
|
|
471
|
+
// If override sets contain one another then we can try to use the more specific
|
|
472
|
+
// one. However, if neither one is more specific, then we consider them to be
|
|
473
|
+
// in conflict.
|
|
474
|
+
return this.findSpecificOverrideSet(first, second) === undefined;
|
|
303
475
|
}
|
|
304
|
-
}
|
|
305
476
|
|
|
306
|
-
// Patch adding
|
|
307
|
-
// https://github.com/npm/cli/pull/7025.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
let prevDir = null;
|
|
316
|
-
let dir = process.cwd();
|
|
317
|
-
while (dir !== prevDir) {
|
|
318
|
-
let ymlPath = path.join(dir, 'socket.yml');
|
|
319
|
-
let yml = maybeReadfileSync(ymlPath);
|
|
320
|
-
if (yml === undefined) {
|
|
321
|
-
ymlPath = path.join(dir, 'socket.yaml');
|
|
322
|
-
yml = maybeReadfileSync(ymlPath);
|
|
477
|
+
// Patch adding findSpecificOverrideSet is based on
|
|
478
|
+
// https://github.com/npm/cli/pull/7025.
|
|
479
|
+
static findSpecificOverrideSet(first, second) {
|
|
480
|
+
let overrideSet = second;
|
|
481
|
+
while (overrideSet) {
|
|
482
|
+
if (overrideSet.isEqual(first)) {
|
|
483
|
+
return second;
|
|
484
|
+
}
|
|
485
|
+
overrideSet = overrideSet.parent;
|
|
323
486
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
parsed: config.parseSocketConfig(yml)
|
|
329
|
-
};
|
|
330
|
-
} catch {
|
|
331
|
-
throw new Error(`Found file but was unable to parse ${ymlPath}`);
|
|
487
|
+
overrideSet = first;
|
|
488
|
+
while (overrideSet) {
|
|
489
|
+
if (overrideSet.isEqual(second)) {
|
|
490
|
+
return first;
|
|
332
491
|
}
|
|
492
|
+
overrideSet = overrideSet.parent;
|
|
333
493
|
}
|
|
334
|
-
|
|
335
|
-
|
|
494
|
+
// The override sets are incomparable. Neither one contains the other.
|
|
495
|
+
const log = getLogger();
|
|
496
|
+
log?.silly('Conflicting override sets', first, second);
|
|
497
|
+
return undefined;
|
|
336
498
|
}
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
499
|
|
|
340
|
-
// Patch adding
|
|
341
|
-
// https://github.com/npm/cli/pull/7025.
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
while (overrideSet) {
|
|
352
|
-
if (overrideSet.isEqual(second)) {
|
|
353
|
-
return first;
|
|
354
|
-
}
|
|
355
|
-
overrideSet = overrideSet.parent;
|
|
356
|
-
}
|
|
357
|
-
// The override sets are incomparable. Neither one contains the other.
|
|
358
|
-
log?.silly('Conflicting override sets', first, second);
|
|
359
|
-
return undefined;
|
|
360
|
-
}
|
|
361
|
-
function isAlertFixable(alert) {
|
|
362
|
-
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
|
|
363
|
-
}
|
|
364
|
-
function isAlertFixableCve(alert) {
|
|
365
|
-
const {
|
|
366
|
-
type
|
|
367
|
-
} = alert;
|
|
368
|
-
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
369
|
-
}
|
|
370
|
-
function maybeReadfileSync(filepath) {
|
|
371
|
-
try {
|
|
372
|
-
return fs.readFileSync(filepath, 'utf8');
|
|
373
|
-
} catch {}
|
|
374
|
-
return undefined;
|
|
375
|
-
}
|
|
376
|
-
async function getPackagesAlerts(safeArb, pkgs, output) {
|
|
377
|
-
const spinner$1 = new spinner.Spinner({
|
|
378
|
-
stream: output
|
|
379
|
-
});
|
|
380
|
-
let {
|
|
381
|
-
length: remaining
|
|
382
|
-
} = pkgs;
|
|
383
|
-
const packageAlerts = [];
|
|
384
|
-
if (!remaining) {
|
|
385
|
-
spinner$1.success('No changes detected');
|
|
386
|
-
return packageAlerts;
|
|
387
|
-
}
|
|
388
|
-
const getText = () => `Looking up data for ${remaining} packages`;
|
|
389
|
-
spinner$1.start(getText());
|
|
390
|
-
try {
|
|
391
|
-
for await (const artifact of batchScan(pkgs.map(p => p.pkgid))) {
|
|
392
|
-
if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
|
|
393
|
-
continue;
|
|
500
|
+
// Patch adding childrenAreEqual is based on
|
|
501
|
+
// https://github.com/npm/cli/pull/7025.
|
|
502
|
+
childrenAreEqual(otherOverrideSet) {
|
|
503
|
+
const queue = [[this, otherOverrideSet]];
|
|
504
|
+
let pos = 0;
|
|
505
|
+
let {
|
|
506
|
+
length: queueLength
|
|
507
|
+
} = queue;
|
|
508
|
+
while (pos < queueLength) {
|
|
509
|
+
if (pos === LOOP_SENTINEL$1) {
|
|
510
|
+
throw new Error('Detected infinite loop while comparing override sets');
|
|
394
511
|
}
|
|
395
512
|
const {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
name,
|
|
408
|
-
version
|
|
409
|
-
},
|
|
410
|
-
alert: {
|
|
411
|
-
type: alert.type
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
if (ux.block) {
|
|
415
|
-
blocked = true;
|
|
416
|
-
}
|
|
417
|
-
if (ux.display) {
|
|
418
|
-
displayWarning = true;
|
|
419
|
-
}
|
|
420
|
-
if (ux.block || ux.display) {
|
|
421
|
-
alerts.push({
|
|
422
|
-
name,
|
|
423
|
-
version,
|
|
424
|
-
type: alert.type,
|
|
425
|
-
block: ux.block,
|
|
426
|
-
raw: alert,
|
|
427
|
-
fixable: isAlertFixable(alert)
|
|
428
|
-
});
|
|
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
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (!blocked) {
|
|
447
|
-
const pkg = pkgs.find(p => p.pkgid === id);
|
|
448
|
-
if (pkg) {
|
|
449
|
-
await pacote.tarball.stream(id, stream => {
|
|
450
|
-
stream.resume();
|
|
451
|
-
return stream.promise();
|
|
452
|
-
}, {
|
|
453
|
-
...safeArb[kCtorArgs][0]
|
|
454
|
-
});
|
|
455
|
-
}
|
|
513
|
+
0: currSet,
|
|
514
|
+
1: currOtherSet
|
|
515
|
+
} = queue[pos++];
|
|
516
|
+
const {
|
|
517
|
+
children
|
|
518
|
+
} = currSet;
|
|
519
|
+
const {
|
|
520
|
+
children: otherChildren
|
|
521
|
+
} = currOtherSet;
|
|
522
|
+
if (children.size !== otherChildren.size) {
|
|
523
|
+
return false;
|
|
456
524
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const lines = new Set();
|
|
461
|
-
for (const alert of alerts) {
|
|
462
|
-
// Based data from { pageProps: { alertTypes } } of:
|
|
463
|
-
// https://socket.dev/_next/data/94666139314b6437ee4491a0864e72b264547585/en-US.json
|
|
464
|
-
const info = translations.alerts[alert.type];
|
|
465
|
-
const title = info?.title ?? alert.type;
|
|
466
|
-
const attributes = [...(alert.fixable ? ['fixable'] : []), ...(alert.block ? [] : ['non-blocking'])];
|
|
467
|
-
const maybeAttributes = attributes.length ? ` (${attributes.join('; ')})` : '';
|
|
468
|
-
const maybeDesc = info?.description ? ` - ${info.description}` : '';
|
|
469
|
-
// TODO: emoji seems to mis-align terminals sometimes
|
|
470
|
-
lines.add(` ${title}${maybeAttributes}${maybeDesc}\n`);
|
|
525
|
+
for (const key of children.keys()) {
|
|
526
|
+
if (!otherChildren.has(key)) {
|
|
527
|
+
return false;
|
|
471
528
|
}
|
|
472
|
-
|
|
473
|
-
|
|
529
|
+
const child = children.get(key);
|
|
530
|
+
const otherChild = otherChildren.get(key);
|
|
531
|
+
if (child.value !== otherChild.value) {
|
|
532
|
+
return false;
|
|
474
533
|
}
|
|
475
|
-
|
|
534
|
+
queue[queueLength++] = [child, otherChild];
|
|
476
535
|
}
|
|
477
|
-
remaining -= 1;
|
|
478
|
-
spinner$1.text = remaining > 0 ? getText() : '';
|
|
479
|
-
packageAlerts.push(...alerts);
|
|
480
536
|
}
|
|
481
|
-
|
|
482
|
-
spinner$1.stop();
|
|
537
|
+
return true;
|
|
483
538
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return URL.parse(resolved)?.origin ?? '';
|
|
489
|
-
} catch {}
|
|
490
|
-
return '';
|
|
491
|
-
}
|
|
492
|
-
function walk(diff_) {
|
|
493
|
-
const needInfoOn = [];
|
|
494
|
-
const queue = [...diff_.children];
|
|
495
|
-
let pos = 0;
|
|
496
|
-
let {
|
|
497
|
-
length: queueLength
|
|
498
|
-
} = queue;
|
|
499
|
-
while (pos < queueLength) {
|
|
500
|
-
if (pos === LOOP_SENTINEL) {
|
|
501
|
-
throw new Error('Detected infinite loop while walking Arborist diff');
|
|
502
|
-
}
|
|
503
|
-
const diff = queue[pos++];
|
|
504
|
-
const {
|
|
505
|
-
action
|
|
506
|
-
} = diff;
|
|
507
|
-
if (action) {
|
|
508
|
-
// The `oldNode`, i.e. `actual` node, may be `undefined` if there is no
|
|
509
|
-
// node_modules folder.
|
|
510
|
-
const {
|
|
511
|
-
actual: oldNode,
|
|
512
|
-
ideal: pkgNode
|
|
513
|
-
} = diff;
|
|
514
|
-
if (!oldNode) {
|
|
515
|
-
console.log('oldNode', oldNode, diff);
|
|
539
|
+
getEdgeRule(edge) {
|
|
540
|
+
for (const rule of this.ruleset.values()) {
|
|
541
|
+
if (rule.name !== edge.name) {
|
|
542
|
+
continue;
|
|
516
543
|
}
|
|
517
|
-
|
|
518
|
-
|
|
544
|
+
// If keySpec is * we found our override.
|
|
545
|
+
if (rule.keySpec === '*') {
|
|
546
|
+
return rule;
|
|
519
547
|
}
|
|
520
|
-
|
|
521
|
-
let
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
548
|
+
// Patch replacing
|
|
549
|
+
// let spec = npa(`${edge.name}@${edge.spec}`)
|
|
550
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
551
|
+
//
|
|
552
|
+
// We need to use the rawSpec here, because the spec has the overrides
|
|
553
|
+
// applied to it already.
|
|
554
|
+
let spec = npa(`${edge.name}@${edge.rawSpec}`);
|
|
555
|
+
if (spec.type === 'alias') {
|
|
556
|
+
spec = spec.subSpec;
|
|
557
|
+
}
|
|
558
|
+
if (spec.type === 'git') {
|
|
559
|
+
if (spec.gitRange && rule.keySpec && semver.intersects(spec.gitRange, rule.keySpec)) {
|
|
560
|
+
return rule;
|
|
528
561
|
}
|
|
529
|
-
|
|
530
|
-
keep = action !== 'REMOVE';
|
|
562
|
+
continue;
|
|
531
563
|
}
|
|
532
|
-
if (
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
});
|
|
564
|
+
if (spec.type === 'range' || spec.type === 'version') {
|
|
565
|
+
if (rule.keySpec && semver.intersects(spec.fetchSpec, rule.keySpec)) {
|
|
566
|
+
return rule;
|
|
567
|
+
}
|
|
568
|
+
continue;
|
|
538
569
|
}
|
|
570
|
+
// If we got this far, the spec type is one of tag, directory or file
|
|
571
|
+
// which means we have no real way to make version comparisons, so we
|
|
572
|
+
// just accept the override.
|
|
573
|
+
return rule;
|
|
539
574
|
}
|
|
540
|
-
|
|
541
|
-
|
|
575
|
+
return this;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Patch adding isEqual is based on
|
|
579
|
+
// https://github.com/npm/cli/pull/7025.
|
|
580
|
+
isEqual(otherOverrideSet) {
|
|
581
|
+
if (this === otherOverrideSet) {
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
if (!otherOverrideSet) {
|
|
585
|
+
return false;
|
|
542
586
|
}
|
|
587
|
+
if (this.key !== otherOverrideSet.key || this.value !== otherOverrideSet.value) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
if (!this.childrenAreEqual(otherOverrideSet)) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
if (!this.parent) {
|
|
594
|
+
return !otherOverrideSet.parent;
|
|
595
|
+
}
|
|
596
|
+
return this.parent.isEqual(otherOverrideSet.parent);
|
|
543
597
|
}
|
|
544
|
-
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const Node = require(arboristNodeClassPath);
|
|
601
|
+
|
|
602
|
+
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
603
|
+
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
|
|
604
|
+
class SafeNode extends Node {
|
|
605
|
+
// Return true if it's safe to remove this node, because anything that is
|
|
606
|
+
// depending on it would be fine with the thing that they would resolve to if
|
|
607
|
+
// it was removed, or nothing is depending on it in the first place.
|
|
608
|
+
canDedupe(preferDedupe = false) {
|
|
609
|
+
// Not allowed to mess with shrinkwraps or bundles.
|
|
610
|
+
if (this.inDepBundle || this.inShrinkwrap) {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
// It's a top level pkg, or a dep of one.
|
|
614
|
+
if (!this.resolveParent?.resolveParent) {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
// No one wants it, remove it.
|
|
618
|
+
if (this.edgesIn.size === 0) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
const other = this.resolveParent.resolveParent.resolve(this.name);
|
|
622
|
+
// Nothing else, need this one.
|
|
623
|
+
if (!other) {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
// If it's the same thing, then always fine to remove.
|
|
627
|
+
if (other.matches(this)) {
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
// If the other thing can't replace this, then skip it.
|
|
631
|
+
if (!other.canReplace(this)) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
// Patch replacing
|
|
635
|
+
// if (preferDedupe || semver.gte(other.version, this.version)) {
|
|
636
|
+
// return true
|
|
637
|
+
// }
|
|
638
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
639
|
+
//
|
|
640
|
+
// If we prefer dedupe, or if the version is equal, take the other.
|
|
641
|
+
if (preferDedupe || semver.eq(other.version, this.version)) {
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
// If our current version isn't the result of an override, then prefer to
|
|
645
|
+
// take the greater version.
|
|
646
|
+
if (!this.overridden && semver.gt(other.version, this.version)) {
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Is it safe to replace one node with another? check the edges to
|
|
653
|
+
// make sure no one will get upset. Note that the node might end up
|
|
654
|
+
// having its own unmet dependencies, if the new node has new deps.
|
|
655
|
+
// Note that there are cases where Arborist will opt to insert a node
|
|
656
|
+
// into the tree even though this function returns false! This is
|
|
657
|
+
// necessary when a root dependency is added or updated, or when a
|
|
658
|
+
// root dependency brings peer deps along with it. In that case, we
|
|
659
|
+
// will go ahead and create the invalid state, and then try to resolve
|
|
660
|
+
// it with more tree construction, because it's a user request.
|
|
661
|
+
canReplaceWith(node, ignorePeers) {
|
|
662
|
+
if (this.name !== node.name || this.packageName !== node.packageName) {
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
// Patch replacing
|
|
666
|
+
// if (node.overrides !== this.overrides) {
|
|
667
|
+
// return false
|
|
668
|
+
// }
|
|
669
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
670
|
+
//
|
|
671
|
+
// If this node has no dependencies, then it's irrelevant to check the
|
|
672
|
+
// override rules of the replacement node.
|
|
673
|
+
if (this.edgesOut.size) {
|
|
674
|
+
// XXX need to check for two root nodes?
|
|
675
|
+
if (node.overrides) {
|
|
676
|
+
if (!node.overrides.isEqual(this.overrides)) {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
if (this.overrides) {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
// To satisfy the patch we ensure `node.overrides === this.overrides`
|
|
686
|
+
// so that the condition we want to replace,
|
|
687
|
+
// if (this.overrides !== node.overrides) {
|
|
688
|
+
// , is not hit.`
|
|
689
|
+
const oldOverrideSet = this.overrides;
|
|
690
|
+
let result = true;
|
|
691
|
+
if (oldOverrideSet !== node.overrides) {
|
|
692
|
+
this.overrides = node.overrides;
|
|
693
|
+
}
|
|
694
|
+
try {
|
|
695
|
+
result = super.canReplaceWith(node, ignorePeers);
|
|
696
|
+
this.overrides = oldOverrideSet;
|
|
697
|
+
} catch (e) {
|
|
698
|
+
this.overrides = oldOverrideSet;
|
|
699
|
+
throw e;
|
|
700
|
+
}
|
|
701
|
+
return result;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/7025.
|
|
705
|
+
deleteEdgeIn(edge) {
|
|
706
|
+
this.edgesIn.delete(edge);
|
|
545
707
|
const {
|
|
546
|
-
|
|
547
|
-
} =
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
708
|
+
overrides
|
|
709
|
+
} = edge;
|
|
710
|
+
if (overrides) {
|
|
711
|
+
this.updateOverridesEdgeInRemoved(overrides);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
addEdgeIn(edge) {
|
|
715
|
+
// Patch replacing
|
|
716
|
+
// if (edge.overrides) {
|
|
717
|
+
// this.overrides = edge.overrides
|
|
718
|
+
// }
|
|
719
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
720
|
+
//
|
|
721
|
+
// We need to handle the case where the new edge in has an overrides field
|
|
722
|
+
// which is different from the current value.
|
|
723
|
+
if (!this.overrides || !this.overrides.isEqual(edge.overrides)) {
|
|
724
|
+
this.updateOverridesEdgeInAdded(edge.overrides);
|
|
725
|
+
}
|
|
726
|
+
this.edgesIn.add(edge);
|
|
727
|
+
// Try to get metadata from the yarn.lock file.
|
|
728
|
+
this.root.meta?.addEdge(edge);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// @ts-ignore: Incorrectly typed as a property instead of an accessor.
|
|
732
|
+
get overridden() {
|
|
733
|
+
// Patch replacing
|
|
734
|
+
// return !!(this.overrides && this.overrides.value && this.overrides.name === this.name)
|
|
735
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
736
|
+
if (!this.overrides || !this.overrides.value || this.overrides.name !== this.name) {
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
// The overrides rule is for a package with this name, but some override rules
|
|
740
|
+
// only apply to specific versions. To make sure this package was actually
|
|
741
|
+
// overridden, we check whether any edge going in had the rule applied to it,
|
|
742
|
+
// in which case its overrides set is different than its source node.
|
|
743
|
+
for (const edge of this.edgesIn) {
|
|
744
|
+
if (edge.overrides && edge.overrides.name === this.name && edge.overrides.value === this.version) {
|
|
745
|
+
if (!edge.overrides?.isEqual(edge.from?.overrides)) {
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Patch adding recalculateOutEdgesOverrides is based on
|
|
754
|
+
// https://github.com/npm/cli/pull/7025.
|
|
755
|
+
recalculateOutEdgesOverrides() {
|
|
756
|
+
// For each edge out propagate the new overrides through.
|
|
757
|
+
for (const edge of this.edgesOut.values()) {
|
|
758
|
+
edge.reload(true);
|
|
759
|
+
if (edge.to) {
|
|
760
|
+
edge.to.updateOverridesEdgeInAdded(edge.overrides);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// @ts-ignore: Incorrectly typed to accept null.
|
|
766
|
+
set root(newRoot) {
|
|
767
|
+
// Patch removing
|
|
768
|
+
// if (!this.overrides && this.parent && this.parent.overrides) {
|
|
769
|
+
// this.overrides = this.parent.overrides.getNodeRule(this)
|
|
770
|
+
// }
|
|
771
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
772
|
+
//
|
|
773
|
+
// The "root" setter is a really large and complex function. To satisfy the
|
|
774
|
+
// patch we add a dummy value to `this.overrides` so that the condition we
|
|
775
|
+
// want to remove,
|
|
776
|
+
// if (!this.overrides && this.parent && this.parent.overrides) {
|
|
777
|
+
// , is not hit.
|
|
778
|
+
if (!this.overrides) {
|
|
779
|
+
this.overrides = new SafeOverrideSet({
|
|
780
|
+
overrides: ''
|
|
556
781
|
});
|
|
557
782
|
}
|
|
783
|
+
try {
|
|
784
|
+
super.root = newRoot;
|
|
785
|
+
this.overrides = undefined;
|
|
786
|
+
} catch (e) {
|
|
787
|
+
this.overrides = undefined;
|
|
788
|
+
throw e;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Patch adding updateOverridesEdgeInAdded is based on
|
|
793
|
+
// https://github.com/npm/cli/pull/7025.
|
|
794
|
+
//
|
|
795
|
+
// This logic isn't perfect either. When we have two edges in that have
|
|
796
|
+
// different override sets, then we have to decide which set is correct. This
|
|
797
|
+
// function assumes the more specific override set is applicable, so if we have
|
|
798
|
+
// dependencies A->B->C and A->C and an override set that specifies what happens
|
|
799
|
+
// for C under A->B, this will work even if the new A->C edge comes along and
|
|
800
|
+
// tries to change the override set. The strictly correct logic is not to allow
|
|
801
|
+
// two edges with different overrides to point to the same node, because even
|
|
802
|
+
// if this node can satisfy both, one of its dependencies might need to be
|
|
803
|
+
// different depending on the edge leading to it. However, this might cause a
|
|
804
|
+
// lot of duplication, because the conflict in the dependencies might never
|
|
805
|
+
// actually happen.
|
|
806
|
+
updateOverridesEdgeInAdded(otherOverrideSet) {
|
|
807
|
+
if (!otherOverrideSet) {
|
|
808
|
+
// Assuming there are any overrides at all, the overrides field is never
|
|
809
|
+
// undefined for any node at the end state of the tree. So if the new edge's
|
|
810
|
+
// overrides is undefined it will be updated later. So we can wait with
|
|
811
|
+
// updating the node's overrides field.
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
if (!this.overrides) {
|
|
815
|
+
this.overrides = otherOverrideSet;
|
|
816
|
+
this.recalculateOutEdgesOverrides();
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
if (this.overrides.isEqual(otherOverrideSet)) {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
const newOverrideSet = SafeOverrideSet.findSpecificOverrideSet(this.overrides, otherOverrideSet);
|
|
823
|
+
if (newOverrideSet) {
|
|
824
|
+
if (this.overrides.isEqual(newOverrideSet)) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
this.overrides = newOverrideSet;
|
|
828
|
+
this.recalculateOutEdgesOverrides();
|
|
829
|
+
return true;
|
|
830
|
+
}
|
|
831
|
+
// This is an error condition. We can only get here if the new override set
|
|
832
|
+
// is in conflict with the existing.
|
|
833
|
+
const log = getLogger();
|
|
834
|
+
log?.silly('Conflicting override sets', this.name);
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Patch adding updateOverridesEdgeInRemoved is based on
|
|
839
|
+
// https://github.com/npm/cli/pull/7025.
|
|
840
|
+
updateOverridesEdgeInRemoved(otherOverrideSet) {
|
|
841
|
+
// If this edge's overrides isn't equal to this node's overrides,
|
|
842
|
+
// then removing it won't change newOverrideSet later.
|
|
843
|
+
if (!this.overrides || !this.overrides.isEqual(otherOverrideSet)) {
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
let newOverrideSet;
|
|
847
|
+
for (const edge of this.edgesIn) {
|
|
848
|
+
const {
|
|
849
|
+
overrides: edgeOverrides
|
|
850
|
+
} = edge;
|
|
851
|
+
if (newOverrideSet && edgeOverrides) {
|
|
852
|
+
newOverrideSet = SafeOverrideSet.findSpecificOverrideSet(edgeOverrides, newOverrideSet);
|
|
853
|
+
} else {
|
|
854
|
+
newOverrideSet = edgeOverrides;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (this.overrides.isEqual(newOverrideSet)) {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
this.overrides = newOverrideSet;
|
|
861
|
+
if (newOverrideSet) {
|
|
862
|
+
// Optimization: If there's any override set at all, then no non-extraneous
|
|
863
|
+
// node has an empty override set. So if we temporarily have no override set
|
|
864
|
+
// (for example, we removed all the edges in), there's no use updating all
|
|
865
|
+
// the edges out right now. Let's just wait until we have an actual override
|
|
866
|
+
// set later.
|
|
867
|
+
this.recalculateOutEdgesOverrides();
|
|
868
|
+
}
|
|
869
|
+
return true;
|
|
558
870
|
}
|
|
559
|
-
return needInfoOn;
|
|
560
871
|
}
|
|
561
872
|
|
|
873
|
+
const Edge = require(arboristEdgeClassPath);
|
|
874
|
+
|
|
562
875
|
// The Edge class makes heavy use of private properties which subclasses do NOT
|
|
563
876
|
// have access to. So we have to recreate any functionality that relies on those
|
|
564
877
|
// private properties and use our own "safe" prefixed non-conflicting private
|
|
@@ -622,7 +935,7 @@ class SafeEdge extends Edge {
|
|
|
622
935
|
}
|
|
623
936
|
// Patch adding "else if" condition is based on
|
|
624
937
|
// https://github.com/npm/cli/pull/7025.
|
|
625
|
-
else if (this.overrides && this.#safeTo.edgesOut.size && doOverrideSetsConflict(this.overrides, this.#safeTo.overrides)) {
|
|
938
|
+
else if (this.overrides && this.#safeTo.edgesOut.size && SafeOverrideSet.doOverrideSetsConflict(this.overrides, this.#safeTo.overrides)) {
|
|
626
939
|
// Any inconsistency between the edge's override set and the target's
|
|
627
940
|
// override set is potentially problematic. But we only say the edge is
|
|
628
941
|
// in error if the override sets are plainly conflicting. Note that if
|
|
@@ -793,501 +1106,252 @@ class SafeEdge extends Edge {
|
|
|
793
1106
|
}
|
|
794
1107
|
// Patch replacing
|
|
795
1108
|
// return depValid(node, this.spec, this.#accept, this.#from)
|
|
796
|
-
// is based on https://github.com/npm/cli/pull/7025.
|
|
797
|
-
//
|
|
798
|
-
// If there's no override we just use the spec.
|
|
799
|
-
if (!this.overrides?.keySpec) {
|
|
800
|
-
return depValid(node, this.spec, this.#safeAccept, this.#safeFrom);
|
|
801
|
-
}
|
|
802
|
-
// There's some override. If the target node satisfies the overriding spec
|
|
803
|
-
// then it's okay.
|
|
804
|
-
if (depValid(node, this.spec, this.#safeAccept, this.#safeFrom)) {
|
|
805
|
-
return true;
|
|
806
|
-
}
|
|
807
|
-
// If it doesn't, then it should at least satisfy the original spec.
|
|
808
|
-
if (!depValid(node, this.rawSpec, this.#safeAccept, this.#safeFrom)) {
|
|
809
|
-
return false;
|
|
810
|
-
}
|
|
811
|
-
// It satisfies the original spec, not the overriding spec. We need to make
|
|
812
|
-
// sure it doesn't use the overridden spec.
|
|
813
|
-
// For example, we might have an ^8.0.0 rawSpec, and an override that makes
|
|
814
|
-
// keySpec=8.23.0 and the override value spec=9.0.0.
|
|
815
|
-
// If the node is 9.0.0, then it's okay because it's consistent with spec.
|
|
816
|
-
// If the node is 8.24.0, then it's okay because it's consistent with the rawSpec.
|
|
817
|
-
// If the node is 8.23.0, then it's not okay because even though it's consistent
|
|
818
|
-
// with the rawSpec, it's also consistent with the keySpec.
|
|
819
|
-
// So we're looking for ^8.0.0 or 9.0.0 and not 8.23.0.
|
|
820
|
-
return !depValid(node, this.overrides.keySpec, this.#safeAccept, this.#safeFrom);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
825
|
-
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
|
|
826
|
-
class SafeNode extends Node {
|
|
827
|
-
// Return true if it's safe to remove this node, because anything that is
|
|
828
|
-
// depending on it would be fine with the thing that they would resolve to if
|
|
829
|
-
// it was removed, or nothing is depending on it in the first place.
|
|
830
|
-
canDedupe(preferDedupe = false) {
|
|
831
|
-
// Not allowed to mess with shrinkwraps or bundles.
|
|
832
|
-
if (this.inDepBundle || this.inShrinkwrap) {
|
|
833
|
-
return false;
|
|
834
|
-
}
|
|
835
|
-
// It's a top level pkg, or a dep of one.
|
|
836
|
-
if (!this.resolveParent?.resolveParent) {
|
|
837
|
-
return false;
|
|
838
|
-
}
|
|
839
|
-
// No one wants it, remove it.
|
|
840
|
-
if (this.edgesIn.size === 0) {
|
|
841
|
-
return true;
|
|
842
|
-
}
|
|
843
|
-
const other = this.resolveParent.resolveParent.resolve(this.name);
|
|
844
|
-
// Nothing else, need this one.
|
|
845
|
-
if (!other) {
|
|
846
|
-
return false;
|
|
847
|
-
}
|
|
848
|
-
// If it's the same thing, then always fine to remove.
|
|
849
|
-
if (other.matches(this)) {
|
|
850
|
-
return true;
|
|
851
|
-
}
|
|
852
|
-
// If the other thing can't replace this, then skip it.
|
|
853
|
-
if (!other.canReplace(this)) {
|
|
854
|
-
return false;
|
|
855
|
-
}
|
|
856
|
-
// Patch replacing
|
|
857
|
-
// if (preferDedupe || semver.gte(other.version, this.version)) {
|
|
858
|
-
// return true
|
|
859
|
-
// }
|
|
860
|
-
// is based on https://github.com/npm/cli/pull/7025.
|
|
861
|
-
//
|
|
862
|
-
// If we prefer dedupe, or if the version is equal, take the other.
|
|
863
|
-
if (preferDedupe || semver.eq(other.version, this.version)) {
|
|
864
|
-
return true;
|
|
865
|
-
}
|
|
866
|
-
// If our current version isn't the result of an override, then prefer to
|
|
867
|
-
// take the greater version.
|
|
868
|
-
if (!this.overridden && semver.gt(other.version, this.version)) {
|
|
869
|
-
return true;
|
|
870
|
-
}
|
|
871
|
-
return false;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// Is it safe to replace one node with another? check the edges to
|
|
875
|
-
// make sure no one will get upset. Note that the node might end up
|
|
876
|
-
// having its own unmet dependencies, if the new node has new deps.
|
|
877
|
-
// Note that there are cases where Arborist will opt to insert a node
|
|
878
|
-
// into the tree even though this function returns false! This is
|
|
879
|
-
// necessary when a root dependency is added or updated, or when a
|
|
880
|
-
// root dependency brings peer deps along with it. In that case, we
|
|
881
|
-
// will go ahead and create the invalid state, and then try to resolve
|
|
882
|
-
// it with more tree construction, because it's a user request.
|
|
883
|
-
canReplaceWith(node, ignorePeers) {
|
|
884
|
-
if (this.name !== node.name || this.packageName !== node.packageName) {
|
|
885
|
-
return false;
|
|
886
|
-
}
|
|
887
|
-
// Patch replacing
|
|
888
|
-
// if (node.overrides !== this.overrides) {
|
|
889
|
-
// return false
|
|
890
|
-
// }
|
|
891
|
-
// is based on https://github.com/npm/cli/pull/7025.
|
|
892
|
-
//
|
|
893
|
-
// If this node has no dependencies, then it's irrelevant to check the
|
|
894
|
-
// override rules of the replacement node.
|
|
895
|
-
if (this.edgesOut.size) {
|
|
896
|
-
// XXX need to check for two root nodes?
|
|
897
|
-
if (node.overrides) {
|
|
898
|
-
if (!node.overrides.isEqual(this.overrides)) {
|
|
899
|
-
return false;
|
|
900
|
-
}
|
|
901
|
-
} else {
|
|
902
|
-
if (this.overrides) {
|
|
903
|
-
return false;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
// To satisfy the patch we ensure `node.overrides === this.overrides`
|
|
908
|
-
// so that the condition we want to replace,
|
|
909
|
-
// if (this.overrides !== node.overrides) {
|
|
910
|
-
// , is not hit.`
|
|
911
|
-
const oldOverrideSet = this.overrides;
|
|
912
|
-
let result = true;
|
|
913
|
-
if (oldOverrideSet !== node.overrides) {
|
|
914
|
-
this.overrides = node.overrides;
|
|
915
|
-
}
|
|
916
|
-
try {
|
|
917
|
-
result = super.canReplaceWith(node, ignorePeers);
|
|
918
|
-
this.overrides = oldOverrideSet;
|
|
919
|
-
} catch (e) {
|
|
920
|
-
this.overrides = oldOverrideSet;
|
|
921
|
-
throw e;
|
|
922
|
-
}
|
|
923
|
-
return result;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
// Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/7025.
|
|
927
|
-
deleteEdgeIn(edge) {
|
|
928
|
-
this.edgesIn.delete(edge);
|
|
929
|
-
const {
|
|
930
|
-
overrides
|
|
931
|
-
} = edge;
|
|
932
|
-
if (overrides) {
|
|
933
|
-
this.updateOverridesEdgeInRemoved(overrides);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
addEdgeIn(edge) {
|
|
937
|
-
// Patch replacing
|
|
938
|
-
// if (edge.overrides) {
|
|
939
|
-
// this.overrides = edge.overrides
|
|
940
|
-
// }
|
|
941
|
-
// is based on https://github.com/npm/cli/pull/7025.
|
|
942
|
-
//
|
|
943
|
-
// We need to handle the case where the new edge in has an overrides field
|
|
944
|
-
// which is different from the current value.
|
|
945
|
-
if (!this.overrides || !this.overrides.isEqual(edge.overrides)) {
|
|
946
|
-
this.updateOverridesEdgeInAdded(edge.overrides);
|
|
947
|
-
}
|
|
948
|
-
this.edgesIn.add(edge);
|
|
949
|
-
// Try to get metadata from the yarn.lock file.
|
|
950
|
-
this.root.meta?.addEdge(edge);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// @ts-ignore: Incorrectly typed as a property instead of an accessor.
|
|
954
|
-
get overridden() {
|
|
955
|
-
// Patch replacing
|
|
956
|
-
// return !!(this.overrides && this.overrides.value && this.overrides.name === this.name)
|
|
957
|
-
// is based on https://github.com/npm/cli/pull/7025.
|
|
958
|
-
if (!this.overrides || !this.overrides.value || this.overrides.name !== this.name) {
|
|
959
|
-
return false;
|
|
960
|
-
}
|
|
961
|
-
// The overrides rule is for a package with this name, but some override rules
|
|
962
|
-
// only apply to specific versions. To make sure this package was actually
|
|
963
|
-
// overridden, we check whether any edge going in had the rule applied to it,
|
|
964
|
-
// in which case its overrides set is different than its source node.
|
|
965
|
-
for (const edge of this.edgesIn) {
|
|
966
|
-
if (edge.overrides && edge.overrides.name === this.name && edge.overrides.value === this.version) {
|
|
967
|
-
if (!edge.overrides?.isEqual(edge.from?.overrides)) {
|
|
968
|
-
return true;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
return false;
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// Patch adding recalculateOutEdgesOverrides is based on
|
|
976
|
-
// https://github.com/npm/cli/pull/7025.
|
|
977
|
-
recalculateOutEdgesOverrides() {
|
|
978
|
-
// For each edge out propagate the new overrides through.
|
|
979
|
-
for (const edge of this.edgesOut.values()) {
|
|
980
|
-
edge.reload(true);
|
|
981
|
-
if (edge.to) {
|
|
982
|
-
edge.to.updateOverridesEdgeInAdded(edge.overrides);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
// @ts-ignore: Incorrectly typed to accept null.
|
|
988
|
-
set root(newRoot) {
|
|
989
|
-
// Patch removing
|
|
990
|
-
// if (!this.overrides && this.parent && this.parent.overrides) {
|
|
991
|
-
// this.overrides = this.parent.overrides.getNodeRule(this)
|
|
992
|
-
// }
|
|
993
|
-
// is based on https://github.com/npm/cli/pull/7025.
|
|
994
|
-
//
|
|
995
|
-
// The "root" setter is a really large and complex function. To satisfy the
|
|
996
|
-
// patch we add a dummy value to `this.overrides` so that the condition we
|
|
997
|
-
// want to remove,
|
|
998
|
-
// if (!this.overrides && this.parent && this.parent.overrides) {
|
|
999
|
-
// , is not hit.
|
|
1000
|
-
if (!this.overrides) {
|
|
1001
|
-
this.overrides = new OverrideSet({
|
|
1002
|
-
overrides: ''
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
try {
|
|
1006
|
-
super.root = newRoot;
|
|
1007
|
-
this.overrides = undefined;
|
|
1008
|
-
} catch (e) {
|
|
1009
|
-
this.overrides = undefined;
|
|
1010
|
-
throw e;
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
// Patch adding updateOverridesEdgeInAdded is based on
|
|
1015
|
-
// https://github.com/npm/cli/pull/7025.
|
|
1016
|
-
//
|
|
1017
|
-
// This logic isn't perfect either. When we have two edges in that have
|
|
1018
|
-
// different override sets, then we have to decide which set is correct. This
|
|
1019
|
-
// function assumes the more specific override set is applicable, so if we have
|
|
1020
|
-
// dependencies A->B->C and A->C and an override set that specifies what happens
|
|
1021
|
-
// for C under A->B, this will work even if the new A->C edge comes along and
|
|
1022
|
-
// tries to change the override set. The strictly correct logic is not to allow
|
|
1023
|
-
// two edges with different overrides to point to the same node, because even
|
|
1024
|
-
// if this node can satisfy both, one of its dependencies might need to be
|
|
1025
|
-
// different depending on the edge leading to it. However, this might cause a
|
|
1026
|
-
// lot of duplication, because the conflict in the dependencies might never
|
|
1027
|
-
// actually happen.
|
|
1028
|
-
updateOverridesEdgeInAdded(otherOverrideSet) {
|
|
1029
|
-
if (!otherOverrideSet) {
|
|
1030
|
-
// Assuming there are any overrides at all, the overrides field is never
|
|
1031
|
-
// undefined for any node at the end state of the tree. So if the new edge's
|
|
1032
|
-
// overrides is undefined it will be updated later. So we can wait with
|
|
1033
|
-
// updating the node's overrides field.
|
|
1034
|
-
return false;
|
|
1109
|
+
// is based on https://github.com/npm/cli/pull/7025.
|
|
1110
|
+
//
|
|
1111
|
+
// If there's no override we just use the spec.
|
|
1112
|
+
if (!this.overrides?.keySpec) {
|
|
1113
|
+
return depValid(node, this.spec, this.#safeAccept, this.#safeFrom);
|
|
1035
1114
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1115
|
+
// There's some override. If the target node satisfies the overriding spec
|
|
1116
|
+
// then it's okay.
|
|
1117
|
+
if (depValid(node, this.spec, this.#safeAccept, this.#safeFrom)) {
|
|
1039
1118
|
return true;
|
|
1040
1119
|
}
|
|
1041
|
-
|
|
1120
|
+
// If it doesn't, then it should at least satisfy the original spec.
|
|
1121
|
+
if (!depValid(node, this.rawSpec, this.#safeAccept, this.#safeFrom)) {
|
|
1042
1122
|
return false;
|
|
1043
1123
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
// is in conflict with the existing.
|
|
1055
|
-
log?.silly('Conflicting override sets', this.name);
|
|
1056
|
-
return false;
|
|
1124
|
+
// It satisfies the original spec, not the overriding spec. We need to make
|
|
1125
|
+
// sure it doesn't use the overridden spec.
|
|
1126
|
+
// For example, we might have an ^8.0.0 rawSpec, and an override that makes
|
|
1127
|
+
// keySpec=8.23.0 and the override value spec=9.0.0.
|
|
1128
|
+
// If the node is 9.0.0, then it's okay because it's consistent with spec.
|
|
1129
|
+
// If the node is 8.24.0, then it's okay because it's consistent with the rawSpec.
|
|
1130
|
+
// If the node is 8.23.0, then it's not okay because even though it's consistent
|
|
1131
|
+
// with the rawSpec, it's also consistent with the keySpec.
|
|
1132
|
+
// So we're looking for ^8.0.0 or 9.0.0 and not 8.23.0.
|
|
1133
|
+
return !depValid(node, this.overrides.keySpec, this.#safeAccept, this.#safeFrom);
|
|
1057
1134
|
}
|
|
1135
|
+
}
|
|
1058
1136
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1137
|
+
const pacote = require(pacotePath);
|
|
1138
|
+
const {
|
|
1139
|
+
LOOP_SENTINEL,
|
|
1140
|
+
NPM,
|
|
1141
|
+
NPM_REGISTRY_URL,
|
|
1142
|
+
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
|
|
1143
|
+
SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE,
|
|
1144
|
+
abortSignal
|
|
1145
|
+
} = constants;
|
|
1146
|
+
const formatter = new socketUrl.ColorOrMarkdown(false);
|
|
1147
|
+
function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableRange) {
|
|
1148
|
+
const manifestVersion = registry.getManifestData(NPM, name)?.version;
|
|
1149
|
+
// Filter versions that are within the current major version and are not in the vulnerable range
|
|
1150
|
+
const eligibleVersions = availableVersions.filter(version => {
|
|
1151
|
+
const isSameMajor = semver.major(version) === currentMajorVersion;
|
|
1152
|
+
const isNotVulnerable = !semver.satisfies(version, vulnerableRange);
|
|
1153
|
+
if (isSameMajor && isNotVulnerable) {
|
|
1154
|
+
return true;
|
|
1066
1155
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1156
|
+
return !!manifestVersion;
|
|
1157
|
+
});
|
|
1158
|
+
if (eligibleVersions.length === 0) {
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
// Use semver to find the max satisfying version.
|
|
1162
|
+
return semver.maxSatisfying(eligibleVersions, '*');
|
|
1163
|
+
}
|
|
1164
|
+
function findPackage(tree, packageName) {
|
|
1165
|
+
const queue = [{
|
|
1166
|
+
node: tree
|
|
1167
|
+
}];
|
|
1168
|
+
let sentinel = 0;
|
|
1169
|
+
while (queue.length) {
|
|
1170
|
+
if (sentinel++ === LOOP_SENTINEL) {
|
|
1171
|
+
throw new Error('Detected infinite loop in findPackage');
|
|
1077
1172
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1173
|
+
const {
|
|
1174
|
+
node: currentNode
|
|
1175
|
+
} = queue.pop();
|
|
1176
|
+
const node = currentNode.children.get(packageName);
|
|
1177
|
+
if (node) {
|
|
1178
|
+
// Found package.
|
|
1179
|
+
return node;
|
|
1080
1180
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
// the edges out right now. Let's just wait until we have an actual override
|
|
1087
|
-
// set later.
|
|
1088
|
-
this.recalculateOutEdgesOverrides();
|
|
1181
|
+
const children = [...currentNode.children.values()];
|
|
1182
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
1183
|
+
queue.push({
|
|
1184
|
+
node: children[i]
|
|
1185
|
+
});
|
|
1089
1186
|
}
|
|
1090
|
-
return true;
|
|
1091
1187
|
}
|
|
1188
|
+
return null;
|
|
1092
1189
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
const queue = [[this, otherOverrideSet]];
|
|
1101
|
-
let pos = 0;
|
|
1102
|
-
let {
|
|
1103
|
-
length: queueLength
|
|
1104
|
-
} = queue;
|
|
1105
|
-
while (pos < queueLength) {
|
|
1106
|
-
if (pos === LOOP_SENTINEL) {
|
|
1107
|
-
throw new Error('Detected infinite loop while comparing override sets');
|
|
1108
|
-
}
|
|
1109
|
-
const {
|
|
1110
|
-
0: currSet,
|
|
1111
|
-
1: currOtherSet
|
|
1112
|
-
} = queue[pos++];
|
|
1113
|
-
const {
|
|
1114
|
-
children
|
|
1115
|
-
} = currSet;
|
|
1116
|
-
const {
|
|
1117
|
-
children: otherChildren
|
|
1118
|
-
} = currOtherSet;
|
|
1119
|
-
if (children.size !== otherChildren.size) {
|
|
1120
|
-
return false;
|
|
1121
|
-
}
|
|
1122
|
-
for (const key of children.keys()) {
|
|
1123
|
-
if (!otherChildren.has(key)) {
|
|
1124
|
-
return false;
|
|
1125
|
-
}
|
|
1126
|
-
const child = children.get(key);
|
|
1127
|
-
const otherChild = otherChildren.get(key);
|
|
1128
|
-
if (child.value !== otherChild.value) {
|
|
1129
|
-
return false;
|
|
1130
|
-
}
|
|
1131
|
-
queue[queueLength++] = [child, otherChild];
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
return true;
|
|
1190
|
+
async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
1191
|
+
let {
|
|
1192
|
+
length: remaining
|
|
1193
|
+
} = pkgs;
|
|
1194
|
+
const packageAlerts = [];
|
|
1195
|
+
if (!remaining) {
|
|
1196
|
+
return packageAlerts;
|
|
1135
1197
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1198
|
+
const {
|
|
1199
|
+
fixable,
|
|
1200
|
+
output
|
|
1201
|
+
} = {
|
|
1202
|
+
__proto__: null,
|
|
1203
|
+
...options
|
|
1204
|
+
};
|
|
1205
|
+
const spinner$1 = output ? new spinner.Spinner({
|
|
1206
|
+
stream: output
|
|
1207
|
+
}) : undefined;
|
|
1208
|
+
const getText = spinner$1 ? () => `Looking up data for ${remaining} packages` : () => '';
|
|
1209
|
+
spinner$1?.start(getText());
|
|
1210
|
+
try {
|
|
1211
|
+
for await (const artifact of batchScan(pkgs.map(p => p.pkgid))) {
|
|
1212
|
+
if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
|
|
1139
1213
|
continue;
|
|
1140
1214
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1215
|
+
const {
|
|
1216
|
+
version
|
|
1217
|
+
} = artifact;
|
|
1218
|
+
const name = packages.resolvePackageName(artifact);
|
|
1219
|
+
const id = `${name}@${artifact.version}`;
|
|
1220
|
+
let blocked = false;
|
|
1221
|
+
let displayWarning = false;
|
|
1222
|
+
let alerts = [];
|
|
1223
|
+
for (const alert of artifact.alerts) {
|
|
1224
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1225
|
+
const ux = await uxLookup({
|
|
1226
|
+
package: {
|
|
1227
|
+
name,
|
|
1228
|
+
version
|
|
1229
|
+
},
|
|
1230
|
+
alert: {
|
|
1231
|
+
type: alert.type
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
if (ux.block) {
|
|
1235
|
+
blocked = true;
|
|
1158
1236
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
if (
|
|
1163
|
-
|
|
1237
|
+
if (ux.display && output) {
|
|
1238
|
+
displayWarning = true;
|
|
1239
|
+
}
|
|
1240
|
+
if (ux.block || ux.display) {
|
|
1241
|
+
const isFixable = isAlertFixable(alert);
|
|
1242
|
+
if (!fixable || isFixable) {
|
|
1243
|
+
alerts.push({
|
|
1244
|
+
name,
|
|
1245
|
+
version,
|
|
1246
|
+
key: alert.key,
|
|
1247
|
+
type: alert.type,
|
|
1248
|
+
block: ux.block,
|
|
1249
|
+
raw: alert,
|
|
1250
|
+
fixable: isFixable
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
// Lazily access constants.IPC.
|
|
1254
|
+
if (!fixable && !constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
1255
|
+
// Before we ask about problematic issues, check to see if they
|
|
1256
|
+
// already existed in the old version if they did, be quiet.
|
|
1257
|
+
const existing = pkgs.find(p => p.existing?.startsWith(`${name}@`))?.existing;
|
|
1258
|
+
if (existing) {
|
|
1259
|
+
const oldArtifact =
|
|
1260
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1261
|
+
(await batchScan([existing]).next()).value;
|
|
1262
|
+
if (oldArtifact?.alerts?.length) {
|
|
1263
|
+
alerts = alerts.filter(({
|
|
1264
|
+
type
|
|
1265
|
+
}) => !oldArtifact.alerts?.find(a => a.type === type));
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1164
1269
|
}
|
|
1165
|
-
continue;
|
|
1166
1270
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1271
|
+
if (!blocked) {
|
|
1272
|
+
const pkg = pkgs.find(p => p.pkgid === id);
|
|
1273
|
+
if (pkg) {
|
|
1274
|
+
await pacote.tarball.stream(id, stream => {
|
|
1275
|
+
stream.resume();
|
|
1276
|
+
return stream.promise();
|
|
1277
|
+
}, {
|
|
1278
|
+
...safeArb[kCtorArgs][0]
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
if (displayWarning && spinner$1) {
|
|
1283
|
+
spinner$1.stop(`(socket) ${formatter.hyperlink(id, socketUrl.getSocketDevPackageOverviewUrl(NPM, name, version))} contains risks:`);
|
|
1284
|
+
}
|
|
1285
|
+
alerts.sort((a, b) => a.type < b.type ? -1 : 1);
|
|
1286
|
+
if (output) {
|
|
1287
|
+
const lines = new Set();
|
|
1288
|
+
const translations = getTranslations();
|
|
1289
|
+
for (const alert of alerts) {
|
|
1290
|
+
const attributes = [...(alert.fixable ? ['fixable'] : []), ...(alert.block ? [] : ['non-blocking'])];
|
|
1291
|
+
const maybeAttributes = attributes.length ? ` (${attributes.join('; ')})` : '';
|
|
1292
|
+
// Based data from { pageProps: { alertTypes } } of:
|
|
1293
|
+
// https://socket.dev/_next/data/94666139314b6437ee4491a0864e72b264547585/en-US.json
|
|
1294
|
+
const info = translations.alerts[alert.type];
|
|
1295
|
+
const title = info?.title ?? alert.type;
|
|
1296
|
+
const maybeDesc = info?.description ? ` - ${info.description}` : '';
|
|
1297
|
+
// TODO: emoji seems to mis-align terminals sometimes
|
|
1298
|
+
lines.add(` ${title}${maybeAttributes}${maybeDesc}\n`);
|
|
1299
|
+
}
|
|
1300
|
+
for (const line of lines) {
|
|
1301
|
+
output?.write(line);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
spinner$1?.start();
|
|
1305
|
+
remaining -= 1;
|
|
1306
|
+
if (spinner$1) {
|
|
1307
|
+
spinner$1.text = remaining > 0 ? getText() : '';
|
|
1308
|
+
}
|
|
1309
|
+
packageAlerts.push(...alerts);
|
|
1192
1310
|
}
|
|
1193
|
-
|
|
1311
|
+
} catch (e) {
|
|
1312
|
+
pathResolve.debugLog(e);
|
|
1313
|
+
} finally {
|
|
1314
|
+
spinner$1?.stop();
|
|
1194
1315
|
}
|
|
1316
|
+
return packageAlerts;
|
|
1195
1317
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
...ctorArgs[0],
|
|
1203
|
-
audit: true,
|
|
1204
|
-
dryRun: true,
|
|
1205
|
-
ignoreScripts: true,
|
|
1206
|
-
save: false,
|
|
1207
|
-
saveBundle: false,
|
|
1208
|
-
// progress: false,
|
|
1209
|
-
fund: false
|
|
1210
|
-
}, ctorArgs.slice(1)];
|
|
1211
|
-
super(...mutedArguments);
|
|
1212
|
-
this[kCtorArgs] = ctorArgs;
|
|
1213
|
-
}
|
|
1214
|
-
async [kRiskyReify](...args) {
|
|
1215
|
-
// SafeArborist has suffered side effects and must be rebuilt from scratch.
|
|
1216
|
-
const arb = new Arborist(...this[kCtorArgs]);
|
|
1217
|
-
arb.idealTree = this.idealTree;
|
|
1218
|
-
const ret = await arb.reify(...args);
|
|
1219
|
-
Object.assign(this, arb);
|
|
1220
|
-
return ret;
|
|
1318
|
+
let _translations;
|
|
1319
|
+
function getTranslations() {
|
|
1320
|
+
if (_translations === undefined) {
|
|
1321
|
+
_translations = require(
|
|
1322
|
+
// Lazily access constants.rootPath.
|
|
1323
|
+
path.join(constants.rootPath, 'translations.json'));
|
|
1221
1324
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
return await this[kRiskyReify](...args);
|
|
1325
|
+
return _translations;
|
|
1326
|
+
}
|
|
1327
|
+
function packageAlertsToReport(alerts) {
|
|
1328
|
+
let report = null;
|
|
1329
|
+
for (const alert of alerts) {
|
|
1330
|
+
if (!isAlertFixableCve(alert.raw)) {
|
|
1331
|
+
continue;
|
|
1230
1332
|
}
|
|
1231
|
-
const old = {
|
|
1232
|
-
...options,
|
|
1233
|
-
dryRun: false,
|
|
1234
|
-
save: Boolean(options['save'] ?? true),
|
|
1235
|
-
saveBundle: Boolean(options['saveBundle'] ?? false)
|
|
1236
|
-
};
|
|
1237
|
-
args[0] = options;
|
|
1238
|
-
options.dryRun = true;
|
|
1239
|
-
options['save'] = false;
|
|
1240
|
-
options['saveBundle'] = false;
|
|
1241
|
-
// TODO: Make this deal with any refactor to private fields by punching the
|
|
1242
|
-
// class itself.
|
|
1243
|
-
await super.reify(...args);
|
|
1244
|
-
options.dryRun = old.dryRun;
|
|
1245
|
-
options['save'] = old.save;
|
|
1246
|
-
options['saveBundle'] = old.saveBundle;
|
|
1247
1333
|
const {
|
|
1248
|
-
|
|
1249
|
-
} =
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
const needInfoOn = diff ? walk(diff) : [];
|
|
1253
|
-
if (needInfoOn.findIndex(c => c.repository_url === NPM_REGISTRY_URL) === -1) {
|
|
1254
|
-
// Nothing to check, hmmm already installed or all private?
|
|
1255
|
-
return await this[kRiskyReify](...args);
|
|
1334
|
+
name
|
|
1335
|
+
} = alert;
|
|
1336
|
+
if (!report) {
|
|
1337
|
+
report = {};
|
|
1256
1338
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
let alerts;
|
|
1260
|
-
const proceed = ENV[SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE] || (await (async () => {
|
|
1261
|
-
alerts = await getPackagesAlerts(this, needInfoOn, output);
|
|
1262
|
-
if (!alerts.length || ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
1263
|
-
return true;
|
|
1264
|
-
}
|
|
1265
|
-
return await prompts.confirm({
|
|
1266
|
-
message: 'Accept risks of installing these packages?',
|
|
1267
|
-
default: false
|
|
1268
|
-
}, {
|
|
1269
|
-
input,
|
|
1270
|
-
output,
|
|
1271
|
-
signal: abortSignal
|
|
1272
|
-
});
|
|
1273
|
-
})());
|
|
1274
|
-
if (proceed) {
|
|
1275
|
-
const fix = !!alerts?.length && (ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE] || (await prompts.confirm({
|
|
1276
|
-
message: 'Try to fix alerts?',
|
|
1277
|
-
default: true
|
|
1278
|
-
}, {
|
|
1279
|
-
input,
|
|
1280
|
-
output,
|
|
1281
|
-
signal: abortSignal
|
|
1282
|
-
})));
|
|
1283
|
-
if (fix) {
|
|
1284
|
-
await updateAdvisoryDependencies(this, alerts);
|
|
1285
|
-
}
|
|
1286
|
-
return await this[kRiskyReify](...args);
|
|
1287
|
-
} else {
|
|
1288
|
-
throw new Error('Socket npm exiting due to risks');
|
|
1339
|
+
if (!report[name]) {
|
|
1340
|
+
report[name] = [];
|
|
1289
1341
|
}
|
|
1342
|
+
const props = alert.raw?.props;
|
|
1343
|
+
report[name].push({
|
|
1344
|
+
id: -1,
|
|
1345
|
+
url: props?.url,
|
|
1346
|
+
title: props?.title,
|
|
1347
|
+
severity: alert.raw?.severity?.toLowerCase(),
|
|
1348
|
+
vulnerable_versions: props?.vulnerableVersionRange,
|
|
1349
|
+
cwe: props?.cwes,
|
|
1350
|
+
cvss: props?.csvs,
|
|
1351
|
+
name
|
|
1352
|
+
});
|
|
1290
1353
|
}
|
|
1354
|
+
return report;
|
|
1291
1355
|
}
|
|
1292
1356
|
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1293
1357
|
const report = packageAlertsToReport(alerts);
|
|
@@ -1299,7 +1363,7 @@ async function updateAdvisoryDependencies(arb, alerts) {
|
|
|
1299
1363
|
const tree = arb.idealTree;
|
|
1300
1364
|
for (const name of Object.keys(report)) {
|
|
1301
1365
|
const advisories = report[name];
|
|
1302
|
-
const node =
|
|
1366
|
+
const node = findPackage(tree, name);
|
|
1303
1367
|
if (!node) {
|
|
1304
1368
|
// Package not found in the tree.
|
|
1305
1369
|
continue;
|
|
@@ -1334,7 +1398,7 @@ async function updateAdvisoryDependencies(arb, alerts) {
|
|
|
1334
1398
|
});
|
|
1335
1399
|
node.package.version = targetVersion;
|
|
1336
1400
|
// Update resolved and clear integrity for the new version.
|
|
1337
|
-
node.resolved =
|
|
1401
|
+
node.resolved = `${NPM_REGISTRY_URL}/${name}/-/${name}-${targetVersion}.tgz`;
|
|
1338
1402
|
if (node.integrity) {
|
|
1339
1403
|
delete node.integrity;
|
|
1340
1404
|
}
|
|
@@ -1370,49 +1434,142 @@ async function updateAdvisoryDependencies(arb, alerts) {
|
|
|
1370
1434
|
}
|
|
1371
1435
|
}
|
|
1372
1436
|
}
|
|
1373
|
-
function
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1437
|
+
async function reify(...args) {
|
|
1438
|
+
const needInfoOn = await walk(this.diff);
|
|
1439
|
+
if (!needInfoOn.length || needInfoOn.findIndex(c => c.repository_url === NPM_REGISTRY_URL) === -1) {
|
|
1440
|
+
// Nothing to check, hmmm already installed or all private?
|
|
1441
|
+
return await this[kRiskyReify](...args);
|
|
1442
|
+
}
|
|
1443
|
+
// Lazily access constants.IPC.
|
|
1444
|
+
const {
|
|
1445
|
+
[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]: bypassConfirms,
|
|
1446
|
+
[SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE]: bypassAlerts
|
|
1447
|
+
} = constants.IPC;
|
|
1448
|
+
const {
|
|
1449
|
+
stderr: output,
|
|
1450
|
+
stdin: input
|
|
1451
|
+
} = process;
|
|
1452
|
+
let alerts;
|
|
1453
|
+
const proceed = bypassAlerts || (await (async () => {
|
|
1454
|
+
alerts = await getPackagesAlerts(this, needInfoOn, {
|
|
1455
|
+
output
|
|
1456
|
+
});
|
|
1457
|
+
if (bypassConfirms || !alerts.length) {
|
|
1458
|
+
return true;
|
|
1389
1459
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1460
|
+
return await prompts.confirm({
|
|
1461
|
+
message: 'Accept risks of installing these packages?',
|
|
1462
|
+
default: false
|
|
1463
|
+
}, {
|
|
1464
|
+
input,
|
|
1465
|
+
output,
|
|
1466
|
+
signal: abortSignal
|
|
1467
|
+
});
|
|
1468
|
+
})());
|
|
1469
|
+
if (proceed) {
|
|
1470
|
+
const fix = !!alerts?.length && bypassConfirms; /*||
|
|
1471
|
+
(await confirm(
|
|
1472
|
+
{
|
|
1473
|
+
message: 'Try to fix alerts?',
|
|
1474
|
+
default: true
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
input,
|
|
1478
|
+
output,
|
|
1479
|
+
signal: abortSignal
|
|
1480
|
+
}
|
|
1481
|
+
))*/
|
|
1482
|
+
if (fix) {
|
|
1483
|
+
let ret;
|
|
1484
|
+
const prev = new Set(alerts?.map(a => a.key));
|
|
1485
|
+
/* eslint-disable no-await-in-loop */
|
|
1486
|
+
while (alerts.length > 0) {
|
|
1487
|
+
await updateAdvisoryDependencies(this, alerts);
|
|
1488
|
+
ret = await this[kRiskyReify](...args);
|
|
1489
|
+
await this.loadActual();
|
|
1490
|
+
await this.buildIdealTree();
|
|
1491
|
+
alerts = await getPackagesAlerts(this, await walk(this.diff, {
|
|
1492
|
+
fix: true
|
|
1493
|
+
}), {
|
|
1494
|
+
fixable: true
|
|
1495
|
+
});
|
|
1496
|
+
alerts = alerts.filter(a => {
|
|
1497
|
+
const {
|
|
1498
|
+
key
|
|
1499
|
+
} = a;
|
|
1500
|
+
if (prev.has(key)) {
|
|
1501
|
+
return false;
|
|
1502
|
+
}
|
|
1503
|
+
prev.add(key);
|
|
1504
|
+
return true;
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
/* eslint-enable no-await-in-loop */
|
|
1508
|
+
return ret;
|
|
1395
1509
|
}
|
|
1510
|
+
return await this[kRiskyReify](...args);
|
|
1511
|
+
} else {
|
|
1512
|
+
throw new Error('Socket npm exiting due to risks');
|
|
1396
1513
|
}
|
|
1397
|
-
return null;
|
|
1398
1514
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1515
|
+
|
|
1516
|
+
const Arborist = require(arboristClassPath);
|
|
1517
|
+
const kCtorArgs = Symbol('ctorArgs');
|
|
1518
|
+
const kRiskyReify = Symbol('riskyReify');
|
|
1519
|
+
|
|
1520
|
+
// Implementation code not related to our custom behavior is based on
|
|
1521
|
+
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
|
|
1522
|
+
class SafeArborist extends Arborist {
|
|
1523
|
+
constructor(...ctorArgs) {
|
|
1524
|
+
super({
|
|
1525
|
+
...ctorArgs[0],
|
|
1526
|
+
audit: true,
|
|
1527
|
+
dryRun: true,
|
|
1528
|
+
ignoreScripts: true,
|
|
1529
|
+
save: false,
|
|
1530
|
+
saveBundle: false,
|
|
1531
|
+
// progress: false,
|
|
1532
|
+
fund: false
|
|
1533
|
+
}, ...ctorArgs.slice(1));
|
|
1534
|
+
this[kCtorArgs] = ctorArgs;
|
|
1535
|
+
}
|
|
1536
|
+
async [kRiskyReify](...args) {
|
|
1537
|
+
// SafeArborist has suffered side effects and must be rebuilt from scratch.
|
|
1538
|
+
const arb = new Arborist(...this[kCtorArgs]);
|
|
1539
|
+
arb.idealTree = this.idealTree;
|
|
1540
|
+
const ret = await arb.reify(...args);
|
|
1541
|
+
Object.assign(this, arb);
|
|
1542
|
+
return ret;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// @ts-ignore Incorrectly typed.
|
|
1546
|
+
async reify(...args) {
|
|
1547
|
+
const options = args[0] ? {
|
|
1548
|
+
...args[0]
|
|
1549
|
+
} : {};
|
|
1550
|
+
if (options.dryRun) {
|
|
1551
|
+
return await this[kRiskyReify](...args);
|
|
1407
1552
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1553
|
+
const old = {
|
|
1554
|
+
...options,
|
|
1555
|
+
dryRun: false,
|
|
1556
|
+
save: Boolean(options.save ?? true),
|
|
1557
|
+
saveBundle: Boolean(options.saveBundle ?? false)
|
|
1558
|
+
};
|
|
1559
|
+
args[0] = options;
|
|
1560
|
+
options.dryRun = true;
|
|
1561
|
+
options.save = false;
|
|
1562
|
+
options.saveBundle = false;
|
|
1563
|
+
// TODO: Make this deal with any refactor to private fields by punching the
|
|
1564
|
+
// class itself.
|
|
1565
|
+
await super.reify(...args);
|
|
1566
|
+
options.dryRun = old.dryRun;
|
|
1567
|
+
options.save = old.save;
|
|
1568
|
+
options.saveBundle = old.saveBundle;
|
|
1569
|
+
return await Reflect.apply(reify, this, args);
|
|
1412
1570
|
}
|
|
1413
|
-
// Use semver to find the max satisfying version.
|
|
1414
|
-
return semver.maxSatisfying(eligibleVersions, '*');
|
|
1415
1571
|
}
|
|
1572
|
+
|
|
1416
1573
|
function installSafeArborist() {
|
|
1417
1574
|
const cache = require.cache;
|
|
1418
1575
|
cache[arboristClassPath] = {
|
|
@@ -1424,82 +1581,9 @@ function installSafeArborist() {
|
|
|
1424
1581
|
cache[arboristNodeClassPath] = {
|
|
1425
1582
|
exports: SafeNode
|
|
1426
1583
|
};
|
|
1427
|
-
cache[
|
|
1584
|
+
cache[arboristOverrideSetClassPath] = {
|
|
1428
1585
|
exports: SafeOverrideSet
|
|
1429
1586
|
};
|
|
1430
1587
|
}
|
|
1431
|
-
void (async () => {
|
|
1432
|
-
const {
|
|
1433
|
-
orgs,
|
|
1434
|
-
settings
|
|
1435
|
-
} = await (async () => {
|
|
1436
|
-
try {
|
|
1437
|
-
const socketSdk = await sdk.setupSdk(pubToken);
|
|
1438
|
-
const orgResult = await socketSdk.getOrganizations();
|
|
1439
|
-
if (!orgResult.success) {
|
|
1440
|
-
throw new Error(`Failed to fetch Socket organization info: ${orgResult.error.message}`);
|
|
1441
|
-
}
|
|
1442
|
-
const orgs = [];
|
|
1443
|
-
for (const org of Object.values(orgResult.data.organizations)) {
|
|
1444
|
-
if (org) {
|
|
1445
|
-
orgs.push(org);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
const result = await socketSdk.postSettings(orgs.map(org => ({
|
|
1449
|
-
organization: org.id
|
|
1450
|
-
})));
|
|
1451
|
-
if (!result.success) {
|
|
1452
|
-
throw new Error(`Failed to fetch API key settings: ${result.error.message}`);
|
|
1453
|
-
}
|
|
1454
|
-
return {
|
|
1455
|
-
orgs,
|
|
1456
|
-
settings: result.data
|
|
1457
|
-
};
|
|
1458
|
-
} catch (e) {
|
|
1459
|
-
if (objects.isObject(e) && 'cause' in e) {
|
|
1460
|
-
const {
|
|
1461
|
-
cause
|
|
1462
|
-
} = e;
|
|
1463
|
-
if (sdk.isErrnoException(cause)) {
|
|
1464
|
-
if (cause.code === 'ENOTFOUND' || cause.code === 'ECONNREFUSED') {
|
|
1465
|
-
throw new Error('Unable to connect to socket.dev, ensure internet connectivity before retrying', {
|
|
1466
|
-
cause: e
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
throw e;
|
|
1472
|
-
}
|
|
1473
|
-
})();
|
|
1474
|
-
|
|
1475
|
-
// Remove any organizations not being enforced.
|
|
1476
|
-
const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
|
|
1477
|
-
for (const {
|
|
1478
|
-
0: i,
|
|
1479
|
-
1: org
|
|
1480
|
-
} of orgs.entries()) {
|
|
1481
|
-
if (!enforcedOrgs.includes(org.id)) {
|
|
1482
|
-
settings.entries.splice(i, 1);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
const socketYml = findSocketYmlSync();
|
|
1486
|
-
if (socketYml) {
|
|
1487
|
-
settings.entries.push({
|
|
1488
|
-
start: socketYml.path,
|
|
1489
|
-
settings: {
|
|
1490
|
-
[socketYml.path]: {
|
|
1491
|
-
deferTo: null,
|
|
1492
|
-
// TODO: TypeScript complains about the type not matching. We should
|
|
1493
|
-
// figure out why are providing
|
|
1494
|
-
// issueRules: { [issueName: string]: boolean }
|
|
1495
|
-
// but expecting
|
|
1496
|
-
// issueRules: { [issueName: string]: { action: 'defer' | 'error' | 'ignore' | 'monitor' | 'warn' } }
|
|
1497
|
-
issueRules: socketYml.parsed.issueRules
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
});
|
|
1501
|
-
}
|
|
1502
|
-
_uxLookup = createAlertUXLookup(settings);
|
|
1503
|
-
})();
|
|
1504
1588
|
|
|
1505
1589
|
installSafeArborist();
|