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