@socketsecurity/cli 0.14.40 → 0.14.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module-sync/cli.js +83 -129
- package/dist/module-sync/npm-injection.js +208 -257
- package/dist/module-sync/settings.d.ts +6 -1
- package/dist/module-sync/socket-url.d.ts +17 -1
- package/dist/module-sync/socket-url.js +81 -2
- package/dist/require/cli.js +83 -129
- package/package.json +26 -20
|
@@ -13,78 +13,45 @@ var path = require('node:path');
|
|
|
13
13
|
var process = require('node:process');
|
|
14
14
|
var semver = _socketInterop(require('semver'));
|
|
15
15
|
var registry = require('@socketsecurity/registry');
|
|
16
|
+
var arrays = require('@socketsecurity/registry/lib/arrays');
|
|
16
17
|
var objects = require('@socketsecurity/registry/lib/objects');
|
|
17
18
|
var packages = require('@socketsecurity/registry/lib/packages');
|
|
18
19
|
var prompts = require('@socketsecurity/registry/lib/prompts');
|
|
19
20
|
var spinner = require('@socketsecurity/registry/lib/spinner');
|
|
21
|
+
var constants = require('./constants.js');
|
|
20
22
|
var events = require('node:events');
|
|
21
23
|
var https = require('node:https');
|
|
22
24
|
var readline = require('node:readline');
|
|
23
|
-
var constants = require('./constants.js');
|
|
24
25
|
var socketUrl = require('./socket-url.js');
|
|
25
|
-
var fs = require('node:fs');
|
|
26
26
|
var promises = require('node:timers/promises');
|
|
27
|
-
var config = require('@socketsecurity/config');
|
|
28
27
|
var pathResolve = require('./path-resolve.js');
|
|
28
|
+
var fs = require('node:fs');
|
|
29
29
|
var npa = _socketInterop(require('npm-package-arg'));
|
|
30
30
|
|
|
31
31
|
const {
|
|
32
|
-
API_V0_URL,
|
|
33
32
|
LOOP_SENTINEL: LOOP_SENTINEL$2,
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
NPM_REGISTRY_URL: NPM_REGISTRY_URL$1,
|
|
34
|
+
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE: SOCKET_CLI_FIX_PACKAGE_LOCK_FILE$1
|
|
36
35
|
} = constants;
|
|
37
|
-
|
|
38
|
-
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
headers: {
|
|
41
|
-
Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
|
|
42
|
-
},
|
|
43
|
-
signal: abortSignal$2
|
|
44
|
-
}).end(JSON.stringify({
|
|
45
|
-
components: pkgIds.map(id => ({
|
|
46
|
-
purl: `pkg:npm/${id}`
|
|
47
|
-
}))
|
|
48
|
-
}));
|
|
49
|
-
const {
|
|
50
|
-
0: res
|
|
51
|
-
} = await events.once(req, 'response');
|
|
52
|
-
const ok = res.statusCode >= 200 && res.statusCode <= 299;
|
|
53
|
-
if (!ok) {
|
|
54
|
-
throw new Error(`Socket API Error: ${res.statusCode}`);
|
|
55
|
-
}
|
|
56
|
-
const rli = readline.createInterface(res);
|
|
57
|
-
for await (const line of rli) {
|
|
58
|
-
yield JSON.parse(line);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function isAlertFixable(alert) {
|
|
62
|
-
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
|
|
63
|
-
}
|
|
64
|
-
function isAlertFixableCve(alert) {
|
|
65
|
-
const {
|
|
66
|
-
type
|
|
67
|
-
} = alert;
|
|
68
|
-
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
69
|
-
}
|
|
70
|
-
function toRepoUrl(resolved) {
|
|
36
|
+
function getUrlOrigin(input) {
|
|
71
37
|
try {
|
|
72
|
-
return URL.parse(
|
|
38
|
+
return URL.parse(input)?.origin ?? '';
|
|
73
39
|
} catch {}
|
|
74
40
|
return '';
|
|
75
41
|
}
|
|
76
|
-
function
|
|
42
|
+
function getPackagesToQueryFromDiff(diff_, options) {
|
|
77
43
|
const {
|
|
78
44
|
// Lazily access constants.IPC.
|
|
79
|
-
|
|
45
|
+
includeUnchanged = constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE$1],
|
|
46
|
+
includeUnknownOrigin = false
|
|
80
47
|
} = {
|
|
81
48
|
__proto__: null,
|
|
82
49
|
...options
|
|
83
50
|
};
|
|
84
|
-
const
|
|
51
|
+
const details = [];
|
|
85
52
|
// `diff_` is `null` when `npm install --package-lock-only` is passed.
|
|
86
53
|
if (!diff_) {
|
|
87
|
-
return
|
|
54
|
+
return details;
|
|
88
55
|
}
|
|
89
56
|
const queue = [...diff_.children];
|
|
90
57
|
let pos = 0;
|
|
@@ -114,25 +81,28 @@ function walk(diff_, options) {
|
|
|
114
81
|
if (pkgNode?.package.version !== oldNode?.package.version) {
|
|
115
82
|
keep = true;
|
|
116
83
|
if (oldNode?.package.name && oldNode.package.name === pkgNode?.package.name) {
|
|
117
|
-
existing = oldNode
|
|
84
|
+
existing = oldNode;
|
|
118
85
|
}
|
|
119
86
|
}
|
|
120
87
|
} else {
|
|
121
88
|
keep = action !== 'REMOVE';
|
|
122
89
|
}
|
|
123
90
|
if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
91
|
+
const origin = getUrlOrigin(pkgNode.resolved);
|
|
92
|
+
if (includeUnknownOrigin || origin === NPM_REGISTRY_URL$1) {
|
|
93
|
+
details.push({
|
|
94
|
+
node: pkgNode,
|
|
95
|
+
origin,
|
|
96
|
+
existing
|
|
97
|
+
});
|
|
98
|
+
}
|
|
129
99
|
}
|
|
130
100
|
}
|
|
131
101
|
for (const child of diff.children) {
|
|
132
102
|
queue[queueLength++] = child;
|
|
133
103
|
}
|
|
134
104
|
}
|
|
135
|
-
if (
|
|
105
|
+
if (includeUnchanged) {
|
|
136
106
|
const {
|
|
137
107
|
unchanged
|
|
138
108
|
} = diff_;
|
|
@@ -140,14 +110,55 @@ function walk(diff_, options) {
|
|
|
140
110
|
length
|
|
141
111
|
} = unchanged; i < length; i += 1) {
|
|
142
112
|
const pkgNode = unchanged[i];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
113
|
+
const origin = getUrlOrigin(pkgNode.resolved);
|
|
114
|
+
if (includeUnknownOrigin || origin === NPM_REGISTRY_URL$1) {
|
|
115
|
+
details.push({
|
|
116
|
+
node: pkgNode,
|
|
117
|
+
origin,
|
|
118
|
+
existing: pkgNode
|
|
119
|
+
});
|
|
120
|
+
}
|
|
148
121
|
}
|
|
149
122
|
}
|
|
150
|
-
return
|
|
123
|
+
return details;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const {
|
|
127
|
+
API_V0_URL,
|
|
128
|
+
abortSignal: abortSignal$2
|
|
129
|
+
} = constants;
|
|
130
|
+
async function* batchScan(pkgIds) {
|
|
131
|
+
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: {
|
|
134
|
+
Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
|
|
135
|
+
},
|
|
136
|
+
signal: abortSignal$2
|
|
137
|
+
}).end(JSON.stringify({
|
|
138
|
+
components: pkgIds.map(id => ({
|
|
139
|
+
purl: `pkg:npm/${id}`
|
|
140
|
+
}))
|
|
141
|
+
}));
|
|
142
|
+
const {
|
|
143
|
+
0: res
|
|
144
|
+
} = await events.once(req, 'response');
|
|
145
|
+
const ok = res.statusCode >= 200 && res.statusCode <= 299;
|
|
146
|
+
if (!ok) {
|
|
147
|
+
throw new Error(`Socket API Error: ${res.statusCode}`);
|
|
148
|
+
}
|
|
149
|
+
const rli = readline.createInterface(res);
|
|
150
|
+
for await (const line of rli) {
|
|
151
|
+
yield JSON.parse(line);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function isArtifactAlertCveFixable(alert) {
|
|
155
|
+
const {
|
|
156
|
+
type
|
|
157
|
+
} = alert;
|
|
158
|
+
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
159
|
+
}
|
|
160
|
+
function isArtifactAlertFixable(alert) {
|
|
161
|
+
return alert.type === 'socketUpgradeAvailable' || isArtifactAlertCveFixable(alert);
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
const {
|
|
@@ -165,37 +176,6 @@ const WARN_UX = {
|
|
|
165
176
|
block: false,
|
|
166
177
|
display: true
|
|
167
178
|
};
|
|
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
|
-
}
|
|
199
179
|
|
|
200
180
|
// Iterates over all entries with ordered issue rule for deferral. Iterates over
|
|
201
181
|
// all issue rules and finds the first defined value that does not defer otherwise
|
|
@@ -374,7 +354,7 @@ void (async () => {
|
|
|
374
354
|
settings.entries.splice(i, 1);
|
|
375
355
|
}
|
|
376
356
|
}
|
|
377
|
-
const socketYml = findSocketYmlSync();
|
|
357
|
+
const socketYml = socketUrl.findSocketYmlSync();
|
|
378
358
|
if (socketYml) {
|
|
379
359
|
settings.entries.push({
|
|
380
360
|
start: socketYml.path,
|
|
@@ -417,7 +397,6 @@ const arboristDepValidPath = path.join(arboristPkgPath, 'lib/dep-valid.js');
|
|
|
417
397
|
const arboristEdgeClassPath = path.join(arboristPkgPath, 'lib/edge.js');
|
|
418
398
|
const arboristNodeClassPath = path.join(arboristPkgPath, 'lib/node.js');
|
|
419
399
|
const arboristOverrideSetClassPath = path.join(arboristPkgPath, 'lib/override-set.js');
|
|
420
|
-
const pacotePath = path.join(npmNmPath, 'pacote');
|
|
421
400
|
|
|
422
401
|
const depValid = require(arboristDepValidPath);
|
|
423
402
|
|
|
@@ -1083,7 +1062,7 @@ class SafeEdge extends Edge {
|
|
|
1083
1062
|
this.#safeError = null;
|
|
1084
1063
|
}
|
|
1085
1064
|
// Patch adding "else if" condition based on
|
|
1086
|
-
// https://github.com/npm/cli/pull/7025
|
|
1065
|
+
// https://github.com/npm/cli/pull/7025.
|
|
1087
1066
|
else if (oldOverrideSet) {
|
|
1088
1067
|
// Propagate the new override set to the target node.
|
|
1089
1068
|
this.#safeTo.updateOverridesEdgeInRemoved(oldOverrideSet);
|
|
@@ -1134,7 +1113,6 @@ class SafeEdge extends Edge {
|
|
|
1134
1113
|
}
|
|
1135
1114
|
}
|
|
1136
1115
|
|
|
1137
|
-
const pacote = require(pacotePath);
|
|
1138
1116
|
const {
|
|
1139
1117
|
LOOP_SENTINEL,
|
|
1140
1118
|
NPM,
|
|
@@ -1161,22 +1139,22 @@ function findBestPatchVersion(name, availableVersions, currentMajorVersion, vuln
|
|
|
1161
1139
|
// Use semver to find the max satisfying version.
|
|
1162
1140
|
return semver.maxSatisfying(eligibleVersions, '*');
|
|
1163
1141
|
}
|
|
1164
|
-
function
|
|
1142
|
+
function findPackageNodes(tree, packageName) {
|
|
1165
1143
|
const queue = [{
|
|
1166
1144
|
node: tree
|
|
1167
1145
|
}];
|
|
1146
|
+
const matches = [];
|
|
1168
1147
|
let sentinel = 0;
|
|
1169
1148
|
while (queue.length) {
|
|
1170
1149
|
if (sentinel++ === LOOP_SENTINEL) {
|
|
1171
|
-
throw new Error('Detected infinite loop in
|
|
1150
|
+
throw new Error('Detected infinite loop in findPackageNodes');
|
|
1172
1151
|
}
|
|
1173
1152
|
const {
|
|
1174
1153
|
node: currentNode
|
|
1175
1154
|
} = queue.pop();
|
|
1176
1155
|
const node = currentNode.children.get(packageName);
|
|
1177
1156
|
if (node) {
|
|
1178
|
-
|
|
1179
|
-
return node;
|
|
1157
|
+
matches.push(node);
|
|
1180
1158
|
}
|
|
1181
1159
|
const children = [...currentNode.children.values()];
|
|
1182
1160
|
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
@@ -1185,18 +1163,19 @@ function findPackage(tree, packageName) {
|
|
|
1185
1163
|
});
|
|
1186
1164
|
}
|
|
1187
1165
|
}
|
|
1188
|
-
return
|
|
1166
|
+
return matches;
|
|
1189
1167
|
}
|
|
1190
|
-
async function getPackagesAlerts(
|
|
1168
|
+
async function getPackagesAlerts(details, options) {
|
|
1191
1169
|
let {
|
|
1192
1170
|
length: remaining
|
|
1193
|
-
} =
|
|
1171
|
+
} = details;
|
|
1194
1172
|
const packageAlerts = [];
|
|
1195
1173
|
if (!remaining) {
|
|
1196
1174
|
return packageAlerts;
|
|
1197
1175
|
}
|
|
1198
1176
|
const {
|
|
1199
|
-
|
|
1177
|
+
includeExisting = false,
|
|
1178
|
+
includeUnfixable = true,
|
|
1200
1179
|
output
|
|
1201
1180
|
} = {
|
|
1202
1181
|
__proto__: null,
|
|
@@ -1208,7 +1187,7 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1208
1187
|
const getText = spinner$1 ? () => `Looking up data for ${remaining} packages` : () => '';
|
|
1209
1188
|
spinner$1?.start(getText());
|
|
1210
1189
|
try {
|
|
1211
|
-
for await (const artifact of batchScan(
|
|
1190
|
+
for await (const artifact of batchScan(arrays.arrayUnique(details.map(d => d.node.pkgid)))) {
|
|
1212
1191
|
if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
|
|
1213
1192
|
continue;
|
|
1214
1193
|
}
|
|
@@ -1217,7 +1196,6 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1217
1196
|
} = artifact;
|
|
1218
1197
|
const name = packages.resolvePackageName(artifact);
|
|
1219
1198
|
const id = `${name}@${artifact.version}`;
|
|
1220
|
-
let blocked = false;
|
|
1221
1199
|
let displayWarning = false;
|
|
1222
1200
|
let alerts = [];
|
|
1223
1201
|
for (const alert of artifact.alerts) {
|
|
@@ -1231,15 +1209,12 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1231
1209
|
type: alert.type
|
|
1232
1210
|
}
|
|
1233
1211
|
});
|
|
1234
|
-
if (ux.block) {
|
|
1235
|
-
blocked = true;
|
|
1236
|
-
}
|
|
1237
1212
|
if (ux.display && output) {
|
|
1238
1213
|
displayWarning = true;
|
|
1239
1214
|
}
|
|
1240
1215
|
if (ux.block || ux.display) {
|
|
1241
|
-
const
|
|
1242
|
-
if (
|
|
1216
|
+
const fixable = isArtifactAlertFixable(alert);
|
|
1217
|
+
if (includeUnfixable || fixable) {
|
|
1243
1218
|
alerts.push({
|
|
1244
1219
|
name,
|
|
1245
1220
|
version,
|
|
@@ -1247,38 +1222,27 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1247
1222
|
type: alert.type,
|
|
1248
1223
|
block: ux.block,
|
|
1249
1224
|
raw: alert,
|
|
1250
|
-
fixable
|
|
1225
|
+
fixable
|
|
1251
1226
|
});
|
|
1252
1227
|
}
|
|
1253
1228
|
// Lazily access constants.IPC.
|
|
1254
|
-
if (
|
|
1229
|
+
if (includeExisting && !constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
1255
1230
|
// Before we ask about problematic issues, check to see if they
|
|
1256
1231
|
// already existed in the old version if they did, be quiet.
|
|
1257
|
-
const existing =
|
|
1232
|
+
const existing = details.find(d => d.existing?.pkgid.startsWith(`${name}@`))?.existing;
|
|
1258
1233
|
if (existing) {
|
|
1259
1234
|
const oldArtifact =
|
|
1260
1235
|
// eslint-disable-next-line no-await-in-loop
|
|
1261
|
-
(await batchScan([existing]).next()).value;
|
|
1236
|
+
(await batchScan([existing.pkgid]).next()).value;
|
|
1262
1237
|
if (oldArtifact?.alerts?.length) {
|
|
1263
1238
|
alerts = alerts.filter(({
|
|
1264
1239
|
type
|
|
1265
|
-
}) => !oldArtifact.alerts
|
|
1240
|
+
}) => !oldArtifact.alerts.find(a => a.type === type));
|
|
1266
1241
|
}
|
|
1267
1242
|
}
|
|
1268
1243
|
}
|
|
1269
1244
|
}
|
|
1270
1245
|
}
|
|
1271
|
-
if (!blocked) {
|
|
1272
|
-
const pkg = pkgs.find(p => p.pkgid === id);
|
|
1273
|
-
if (pkg) {
|
|
1274
|
-
await pacote.tarball.stream(id, stream => {
|
|
1275
|
-
stream.resume();
|
|
1276
|
-
return stream.promise();
|
|
1277
|
-
}, {
|
|
1278
|
-
...safeArb[kCtorArgs][0]
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
1246
|
if (displayWarning && spinner$1) {
|
|
1283
1247
|
spinner$1.stop(`(socket) ${formatter.hyperlink(id, socketUrl.getSocketDevPackageOverviewUrl(NPM, name, version))} contains risks:`);
|
|
1284
1248
|
}
|
|
@@ -1324,23 +1288,23 @@ function getTranslations() {
|
|
|
1324
1288
|
}
|
|
1325
1289
|
return _translations;
|
|
1326
1290
|
}
|
|
1327
|
-
function
|
|
1328
|
-
let
|
|
1291
|
+
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1292
|
+
let alertsByPkg;
|
|
1329
1293
|
for (const alert of alerts) {
|
|
1330
|
-
if (!
|
|
1294
|
+
if (!isArtifactAlertCveFixable(alert.raw)) {
|
|
1331
1295
|
continue;
|
|
1332
1296
|
}
|
|
1297
|
+
if (!alertsByPkg) {
|
|
1298
|
+
alertsByPkg = {};
|
|
1299
|
+
}
|
|
1333
1300
|
const {
|
|
1334
1301
|
name
|
|
1335
1302
|
} = alert;
|
|
1336
|
-
if (!
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1339
|
-
if (!report[name]) {
|
|
1340
|
-
report[name] = [];
|
|
1303
|
+
if (!alertsByPkg[name]) {
|
|
1304
|
+
alertsByPkg[name] = [];
|
|
1341
1305
|
}
|
|
1342
1306
|
const props = alert.raw?.props;
|
|
1343
|
-
|
|
1307
|
+
alertsByPkg[name].push({
|
|
1344
1308
|
id: -1,
|
|
1345
1309
|
url: props?.url,
|
|
1346
1310
|
title: props?.title,
|
|
@@ -1351,92 +1315,89 @@ function packageAlertsToReport(alerts) {
|
|
|
1351
1315
|
name
|
|
1352
1316
|
});
|
|
1353
1317
|
}
|
|
1354
|
-
|
|
1355
|
-
}
|
|
1356
|
-
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1357
|
-
const report = packageAlertsToReport(alerts);
|
|
1358
|
-
if (!report) {
|
|
1318
|
+
if (!alertsByPkg) {
|
|
1359
1319
|
// No advisories to process.
|
|
1360
1320
|
return;
|
|
1361
1321
|
}
|
|
1362
1322
|
await arb.buildIdealTree();
|
|
1363
1323
|
const tree = arb.idealTree;
|
|
1364
|
-
for (const name of Object.keys(
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
if (!node) {
|
|
1368
|
-
// Package not found in the tree.
|
|
1324
|
+
for (const name of Object.keys(alertsByPkg)) {
|
|
1325
|
+
const nodes = findPackageNodes(tree, name);
|
|
1326
|
+
if (!nodes.length) {
|
|
1369
1327
|
continue;
|
|
1370
1328
|
}
|
|
1371
|
-
const {
|
|
1372
|
-
version
|
|
1373
|
-
} = node;
|
|
1374
|
-
const majorVerNum = semver.major(version);
|
|
1375
|
-
|
|
1376
1329
|
// Fetch packument to get available versions.
|
|
1377
1330
|
// eslint-disable-next-line no-await-in-loop
|
|
1378
1331
|
const packument = await packages.fetchPackagePackument(name);
|
|
1379
|
-
const
|
|
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.
|
|
1390
|
-
continue;
|
|
1391
|
-
}
|
|
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;
|
|
1404
|
-
}
|
|
1405
|
-
if ('deprecated' in targetPackument) {
|
|
1406
|
-
node.package['deprecated'] = targetPackument.deprecated;
|
|
1407
|
-
} else {
|
|
1408
|
-
delete node.package['deprecated'];
|
|
1409
|
-
}
|
|
1410
|
-
const newDeps = {
|
|
1411
|
-
...targetPackument.dependencies
|
|
1412
|
-
};
|
|
1332
|
+
for (const node of nodes) {
|
|
1413
1333
|
const {
|
|
1414
|
-
|
|
1415
|
-
} = node
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1334
|
+
version
|
|
1335
|
+
} = node;
|
|
1336
|
+
const majorVerNum = semver.major(version);
|
|
1337
|
+
const availableVersions = packument ? Object.keys(packument.versions) : [];
|
|
1338
|
+
const pkgAlerts = alertsByPkg[name];
|
|
1339
|
+
for (const alert of pkgAlerts) {
|
|
1340
|
+
const {
|
|
1341
|
+
vulnerable_versions
|
|
1342
|
+
} = alert;
|
|
1343
|
+
// Find the highest non-vulnerable version within the same major range
|
|
1344
|
+
const targetVersion = findBestPatchVersion(name, availableVersions, majorVerNum, vulnerable_versions);
|
|
1345
|
+
const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
|
|
1346
|
+
// Check !targetVersion to make TypeScript happy.
|
|
1347
|
+
if (!targetVersion || !targetPackument) {
|
|
1348
|
+
// No suitable patch version found.
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
// Use Object.defineProperty to override the version.
|
|
1352
|
+
Object.defineProperty(node, 'version', {
|
|
1353
|
+
configurable: true,
|
|
1354
|
+
enumerable: true,
|
|
1355
|
+
get: () => targetVersion
|
|
1356
|
+
});
|
|
1357
|
+
node.package.version = targetVersion;
|
|
1358
|
+
// Update resolved and clear integrity for the new version.
|
|
1359
|
+
node.resolved = `${NPM_REGISTRY_URL}/${name}/-/${name}-${targetVersion}.tgz`;
|
|
1360
|
+
if (node.integrity) {
|
|
1361
|
+
delete node.integrity;
|
|
1362
|
+
}
|
|
1363
|
+
if ('deprecated' in targetPackument) {
|
|
1364
|
+
node.package['deprecated'] = targetPackument.deprecated;
|
|
1365
|
+
} else {
|
|
1366
|
+
delete node.package['deprecated'];
|
|
1367
|
+
}
|
|
1368
|
+
const newDeps = {
|
|
1369
|
+
...targetPackument.dependencies
|
|
1370
|
+
};
|
|
1371
|
+
const {
|
|
1372
|
+
dependencies: oldDeps
|
|
1373
|
+
} = node.package;
|
|
1374
|
+
node.package.dependencies = newDeps;
|
|
1375
|
+
if (oldDeps) {
|
|
1376
|
+
for (const oldDepName of Object.keys(oldDeps)) {
|
|
1377
|
+
if (!objects.hasOwn(newDeps, oldDepName)) {
|
|
1378
|
+
node.edgesOut.get(oldDepName)?.detach();
|
|
1379
|
+
}
|
|
1421
1380
|
}
|
|
1422
1381
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1382
|
+
for (const newDepName of Object.keys(newDeps)) {
|
|
1383
|
+
if (!objects.hasOwn(oldDeps, newDepName)) {
|
|
1384
|
+
node.addEdgeOut(new Edge({
|
|
1385
|
+
from: node,
|
|
1386
|
+
name: newDepName,
|
|
1387
|
+
spec: newDeps[newDepName],
|
|
1388
|
+
type: 'prod'
|
|
1389
|
+
}));
|
|
1390
|
+
}
|
|
1432
1391
|
}
|
|
1433
1392
|
}
|
|
1434
1393
|
}
|
|
1435
1394
|
}
|
|
1436
1395
|
}
|
|
1437
1396
|
async function reify(...args) {
|
|
1438
|
-
|
|
1439
|
-
|
|
1397
|
+
// We are assuming `this[_diffTrees]()` has been called by `super.reify(...)`:
|
|
1398
|
+
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/reify.js#L141
|
|
1399
|
+
const needInfoOn = getPackagesToQueryFromDiff(this.diff);
|
|
1400
|
+
if (!needInfoOn.length) {
|
|
1440
1401
|
// Nothing to check, hmmm already installed or all private?
|
|
1441
1402
|
return await this[kRiskyReify](...args);
|
|
1442
1403
|
}
|
|
@@ -1449,64 +1410,54 @@ async function reify(...args) {
|
|
|
1449
1410
|
stderr: output,
|
|
1450
1411
|
stdin: input
|
|
1451
1412
|
} = process;
|
|
1452
|
-
let alerts
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1413
|
+
let alerts = bypassAlerts ? [] : await getPackagesAlerts(needInfoOn, {
|
|
1414
|
+
output
|
|
1415
|
+
});
|
|
1416
|
+
if (alerts.length && !bypassConfirms && !(await prompts.confirm({
|
|
1417
|
+
message: 'Accept risks of installing these packages?',
|
|
1418
|
+
default: false
|
|
1419
|
+
}, {
|
|
1420
|
+
input,
|
|
1421
|
+
output,
|
|
1422
|
+
signal: abortSignal
|
|
1423
|
+
}))) {
|
|
1424
|
+
throw new Error('Socket npm exiting due to risks');
|
|
1425
|
+
}
|
|
1426
|
+
if (!alerts.length || !bypassConfirms && !(await prompts.confirm({
|
|
1427
|
+
message: 'Try to fix alerts?',
|
|
1428
|
+
default: true
|
|
1429
|
+
}, {
|
|
1430
|
+
input,
|
|
1431
|
+
output,
|
|
1432
|
+
signal: abortSignal
|
|
1433
|
+
}))) {
|
|
1434
|
+
return await this[kRiskyReify](...args);
|
|
1435
|
+
}
|
|
1436
|
+
const prev = new Set(alerts.map(a => a.key));
|
|
1437
|
+
let ret;
|
|
1438
|
+
/* eslint-disable no-await-in-loop */
|
|
1439
|
+
while (alerts.length > 0) {
|
|
1440
|
+
await updateAdvisoryDependencies(this, alerts);
|
|
1441
|
+
ret = await this[kRiskyReify](...args);
|
|
1442
|
+
await this.loadActual();
|
|
1443
|
+
await this.buildIdealTree();
|
|
1444
|
+
alerts = (await getPackagesAlerts(getPackagesToQueryFromDiff(this.diff, {
|
|
1445
|
+
includeUnchanged: true
|
|
1446
|
+
}), {
|
|
1447
|
+
includeExisting: true,
|
|
1448
|
+
includeUnfixable: true
|
|
1449
|
+
})).filter(({
|
|
1450
|
+
key
|
|
1451
|
+
}) => {
|
|
1452
|
+
if (prev.has(key)) {
|
|
1453
|
+
return false;
|
|
1454
|
+
}
|
|
1455
|
+
prev.add(key);
|
|
1458
1456
|
return true;
|
|
1459
|
-
}
|
|
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
1457
|
});
|
|
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;
|
|
1505
|
-
}
|
|
1506
|
-
return await this[kRiskyReify](...args);
|
|
1507
|
-
} else {
|
|
1508
|
-
throw new Error('Socket npm exiting due to risks');
|
|
1509
1458
|
}
|
|
1459
|
+
/* eslint-enable no-await-in-loop */
|
|
1460
|
+
return ret;
|
|
1510
1461
|
}
|
|
1511
1462
|
|
|
1512
1463
|
const Arborist = require(arboristClassPath);
|
|
@@ -1556,8 +1507,6 @@ class SafeArborist extends Arborist {
|
|
|
1556
1507
|
options.dryRun = true;
|
|
1557
1508
|
options.save = false;
|
|
1558
1509
|
options.saveBundle = false;
|
|
1559
|
-
// TODO: Make this deal with any refactor to private fields by punching the
|
|
1560
|
-
// class itself.
|
|
1561
1510
|
await super.reify(...args);
|
|
1562
1511
|
options.dryRun = old.dryRun;
|
|
1563
1512
|
options.save = old.save;
|
|
@@ -1567,6 +1516,8 @@ class SafeArborist extends Arborist {
|
|
|
1567
1516
|
}
|
|
1568
1517
|
|
|
1569
1518
|
function installSafeArborist() {
|
|
1519
|
+
// Override '@npmcli/arborist' module exports with patched variants based on
|
|
1520
|
+
// https://github.com/npm/cli/pull/7025.
|
|
1570
1521
|
const cache = require.cache;
|
|
1571
1522
|
cache[arboristClassPath] = {
|
|
1572
1523
|
exports: SafeArborist
|