@socketsecurity/cli 0.14.40 → 0.14.42

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.
@@ -13,78 +13,42 @@ 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
- var pathResolve = require('./path-resolve.js');
27
+ var npmPaths = require('./npm-paths.js');
29
28
  var npa = _socketInterop(require('npm-package-arg'));
30
29
 
31
30
  const {
32
- API_V0_URL,
33
31
  LOOP_SENTINEL: LOOP_SENTINEL$2,
34
- SOCKET_CLI_FIX_PACKAGE_LOCK_FILE: SOCKET_CLI_FIX_PACKAGE_LOCK_FILE$1,
35
- abortSignal: abortSignal$2
32
+ NPM_REGISTRY_URL: NPM_REGISTRY_URL$1
36
33
  } = constants;
37
- async function* batchScan(pkgIds) {
38
- const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
39
- method: 'POST',
40
- headers: {
41
- Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
42
- },
43
- signal: abortSignal$2
44
- }).end(JSON.stringify({
45
- components: pkgIds.map(id => ({
46
- purl: `pkg:npm/${id}`
47
- }))
48
- }));
49
- const {
50
- 0: res
51
- } = await events.once(req, 'response');
52
- const ok = res.statusCode >= 200 && res.statusCode <= 299;
53
- if (!ok) {
54
- throw new Error(`Socket API Error: ${res.statusCode}`);
55
- }
56
- const rli = readline.createInterface(res);
57
- for await (const line of rli) {
58
- yield JSON.parse(line);
59
- }
60
- }
61
- function isAlertFixable(alert) {
62
- return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
63
- }
64
- function isAlertFixableCve(alert) {
65
- const {
66
- type
67
- } = alert;
68
- return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
69
- }
70
- function toRepoUrl(resolved) {
34
+ function getUrlOrigin(input) {
71
35
  try {
72
- return URL.parse(resolved)?.origin ?? '';
36
+ return URL.parse(input)?.origin ?? '';
73
37
  } catch {}
74
38
  return '';
75
39
  }
76
- function walk(diff_, options) {
40
+ function getPackagesToQueryFromDiff(diff_, options) {
77
41
  const {
78
- // Lazily access constants.IPC.
79
- fix = constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE$1]
42
+ includeUnchanged = false,
43
+ includeUnknownOrigin = false
80
44
  } = {
81
45
  __proto__: null,
82
46
  ...options
83
47
  };
84
- const needInfoOn = [];
48
+ const details = [];
85
49
  // `diff_` is `null` when `npm install --package-lock-only` is passed.
86
50
  if (!diff_) {
87
- return needInfoOn;
51
+ return details;
88
52
  }
89
53
  const queue = [...diff_.children];
90
54
  let pos = 0;
@@ -114,25 +78,28 @@ function walk(diff_, options) {
114
78
  if (pkgNode?.package.version !== oldNode?.package.version) {
115
79
  keep = true;
116
80
  if (oldNode?.package.name && oldNode.package.name === pkgNode?.package.name) {
117
- existing = oldNode.pkgid;
81
+ existing = oldNode;
118
82
  }
119
83
  }
120
84
  } else {
121
85
  keep = action !== 'REMOVE';
122
86
  }
123
87
  if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {
124
- needInfoOn.push({
125
- existing,
126
- pkgid: pkgNode.pkgid,
127
- repository_url: toRepoUrl(pkgNode.resolved)
128
- });
88
+ const origin = getUrlOrigin(pkgNode.resolved);
89
+ if (includeUnknownOrigin || origin === NPM_REGISTRY_URL$1) {
90
+ details.push({
91
+ node: pkgNode,
92
+ origin,
93
+ existing
94
+ });
95
+ }
129
96
  }
130
97
  }
131
98
  for (const child of diff.children) {
132
99
  queue[queueLength++] = child;
133
100
  }
134
101
  }
135
- if (fix) {
102
+ if (includeUnchanged) {
136
103
  const {
137
104
  unchanged
138
105
  } = diff_;
@@ -140,14 +107,55 @@ function walk(diff_, options) {
140
107
  length
141
108
  } = unchanged; i < length; i += 1) {
142
109
  const pkgNode = unchanged[i];
143
- needInfoOn.push({
144
- existing: pkgNode.pkgid,
145
- pkgid: pkgNode.pkgid,
146
- repository_url: toRepoUrl(pkgNode.resolved)
147
- });
110
+ const origin = getUrlOrigin(pkgNode.resolved);
111
+ if (includeUnknownOrigin || origin === NPM_REGISTRY_URL$1) {
112
+ details.push({
113
+ node: pkgNode,
114
+ origin,
115
+ existing: pkgNode
116
+ });
117
+ }
148
118
  }
149
119
  }
150
- return needInfoOn;
120
+ return details;
121
+ }
122
+
123
+ const {
124
+ API_V0_URL,
125
+ abortSignal: abortSignal$2
126
+ } = constants;
127
+ async function* batchScan(pkgIds) {
128
+ const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
129
+ method: 'POST',
130
+ headers: {
131
+ Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
132
+ },
133
+ signal: abortSignal$2
134
+ }).end(JSON.stringify({
135
+ components: pkgIds.map(id => ({
136
+ purl: `pkg:npm/${id}`
137
+ }))
138
+ }));
139
+ const {
140
+ 0: res
141
+ } = await events.once(req, 'response');
142
+ const ok = res.statusCode >= 200 && res.statusCode <= 299;
143
+ if (!ok) {
144
+ throw new Error(`Socket API Error: ${res.statusCode}`);
145
+ }
146
+ const rli = readline.createInterface(res);
147
+ for await (const line of rli) {
148
+ yield JSON.parse(line);
149
+ }
150
+ }
151
+ function isArtifactAlertCveFixable(alert) {
152
+ const {
153
+ type
154
+ } = alert;
155
+ return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'] && !!alert.props?.['vulnerableVersionRange'];
156
+ }
157
+ function isArtifactAlertFixable(alert) {
158
+ return alert.type === 'socketUpgradeAvailable' || isArtifactAlertCveFixable(alert);
151
159
  }
152
160
 
153
161
  const {
@@ -165,37 +173,6 @@ const WARN_UX = {
165
173
  block: false,
166
174
  display: true
167
175
  };
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
176
 
200
177
  // Iterates over all entries with ordered issue rule for deferral. Iterates over
201
178
  // all issue rules and finds the first defined value that does not defer otherwise
@@ -374,7 +351,7 @@ void (async () => {
374
351
  settings.entries.splice(i, 1);
375
352
  }
376
353
  }
377
- const socketYml = findSocketYmlSync();
354
+ const socketYml = socketUrl.findSocketYmlSync();
378
355
  if (socketYml) {
379
356
  settings.entries.push({
380
357
  start: socketYml.path,
@@ -394,32 +371,7 @@ void (async () => {
394
371
  _uxLookup = createAlertUXLookup(settings);
395
372
  })();
396
373
 
397
- const {
398
- NODE_MODULES,
399
- SOCKET_CLI_ISSUES_URL
400
- } = constants;
401
- const npmEntrypoint = fs.realpathSync.native(process.argv[1]);
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);
374
+ const depValid = require(npmPaths.getArboristDepValidPath());
423
375
 
424
376
  const {
425
377
  UNDEFINED_TOKEN
@@ -449,6 +401,7 @@ function tryRequire(...ids) {
449
401
  let _log = UNDEFINED_TOKEN;
450
402
  function getLogger() {
451
403
  if (_log === UNDEFINED_TOKEN) {
404
+ const npmNmPath = npmPaths.getNpmNodeModulesPath();
452
405
  _log = tryRequire([path.join(npmNmPath, 'proc-log/lib/index.js'),
453
406
  // The proc-log DefinitelyTyped definition is incorrect. The type definition
454
407
  // is really that of its export log.
@@ -460,7 +413,7 @@ function getLogger() {
460
413
  const {
461
414
  LOOP_SENTINEL: LOOP_SENTINEL$1
462
415
  } = constants;
463
- const OverrideSet = require(arboristOverrideSetClassPath);
416
+ const OverrideSet = require(npmPaths.getArboristOverrideSetClassPath());
464
417
 
465
418
  // Implementation code not related to patch https://github.com/npm/cli/pull/7025
466
419
  // is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:
@@ -597,7 +550,7 @@ class SafeOverrideSet extends OverrideSet {
597
550
  }
598
551
  }
599
552
 
600
- const Node = require(arboristNodeClassPath);
553
+ const Node = require(npmPaths.getArboristNodeClassPath());
601
554
 
602
555
  // Implementation code not related to patch https://github.com/npm/cli/pull/7025
603
556
  // is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
@@ -870,7 +823,7 @@ class SafeNode extends Node {
870
823
  }
871
824
  }
872
825
 
873
- const Edge = require(arboristEdgeClassPath);
826
+ const Edge = require(npmPaths.getArboristEdgeClassPath());
874
827
 
875
828
  // The Edge class makes heavy use of private properties which subclasses do NOT
876
829
  // have access to. So we have to recreate any functionality that relies on those
@@ -1083,7 +1036,7 @@ class SafeEdge extends Edge {
1083
1036
  this.#safeError = null;
1084
1037
  }
1085
1038
  // Patch adding "else if" condition based on
1086
- // https://github.com/npm/cli/pull/7025
1039
+ // https://github.com/npm/cli/pull/7025.
1087
1040
  else if (oldOverrideSet) {
1088
1041
  // Propagate the new override set to the target node.
1089
1042
  this.#safeTo.updateOverridesEdgeInRemoved(oldOverrideSet);
@@ -1134,22 +1087,25 @@ class SafeEdge extends Edge {
1134
1087
  }
1135
1088
  }
1136
1089
 
1137
- const pacote = require(pacotePath);
1138
1090
  const {
1139
1091
  LOOP_SENTINEL,
1140
1092
  NPM,
1141
1093
  NPM_REGISTRY_URL,
1142
1094
  SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
1143
1095
  SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE,
1144
- abortSignal
1096
+ abortSignal,
1097
+ kInternalsSymbol,
1098
+ [kInternalsSymbol]: {
1099
+ getIPC
1100
+ }
1145
1101
  } = constants;
1146
1102
  const formatter = new socketUrl.ColorOrMarkdown(false);
1147
- function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableRange) {
1103
+ function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableVersionRange, _firstPatchedVersionIdentifier) {
1148
1104
  const manifestVersion = registry.getManifestData(NPM, name)?.version;
1149
1105
  // Filter versions that are within the current major version and are not in the vulnerable range
1150
1106
  const eligibleVersions = availableVersions.filter(version => {
1151
1107
  const isSameMajor = semver.major(version) === currentMajorVersion;
1152
- const isNotVulnerable = !semver.satisfies(version, vulnerableRange);
1108
+ const isNotVulnerable = !semver.satisfies(version, vulnerableVersionRange);
1153
1109
  if (isSameMajor && isNotVulnerable) {
1154
1110
  return true;
1155
1111
  }
@@ -1161,22 +1117,22 @@ function findBestPatchVersion(name, availableVersions, currentMajorVersion, vuln
1161
1117
  // Use semver to find the max satisfying version.
1162
1118
  return semver.maxSatisfying(eligibleVersions, '*');
1163
1119
  }
1164
- function findPackage(tree, packageName) {
1120
+ function findPackageNodes(tree, packageName) {
1165
1121
  const queue = [{
1166
1122
  node: tree
1167
1123
  }];
1124
+ const matches = [];
1168
1125
  let sentinel = 0;
1169
1126
  while (queue.length) {
1170
1127
  if (sentinel++ === LOOP_SENTINEL) {
1171
- throw new Error('Detected infinite loop in findPackage');
1128
+ throw new Error('Detected infinite loop in findPackageNodes');
1172
1129
  }
1173
1130
  const {
1174
1131
  node: currentNode
1175
1132
  } = queue.pop();
1176
1133
  const node = currentNode.children.get(packageName);
1177
1134
  if (node) {
1178
- // Found package.
1179
- return node;
1135
+ matches.push(node);
1180
1136
  }
1181
1137
  const children = [...currentNode.children.values()];
1182
1138
  for (let i = children.length - 1; i >= 0; i -= 1) {
@@ -1185,18 +1141,19 @@ function findPackage(tree, packageName) {
1185
1141
  });
1186
1142
  }
1187
1143
  }
1188
- return null;
1144
+ return matches;
1189
1145
  }
1190
- async function getPackagesAlerts(safeArb, pkgs, options) {
1146
+ async function getPackagesAlerts(details, options) {
1191
1147
  let {
1192
1148
  length: remaining
1193
- } = pkgs;
1149
+ } = details;
1194
1150
  const packageAlerts = [];
1195
1151
  if (!remaining) {
1196
1152
  return packageAlerts;
1197
1153
  }
1198
1154
  const {
1199
- fixable,
1155
+ includeExisting = false,
1156
+ includeUnfixable = true,
1200
1157
  output
1201
1158
  } = {
1202
1159
  __proto__: null,
@@ -1208,7 +1165,7 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
1208
1165
  const getText = spinner$1 ? () => `Looking up data for ${remaining} packages` : () => '';
1209
1166
  spinner$1?.start(getText());
1210
1167
  try {
1211
- for await (const artifact of batchScan(pkgs.map(p => p.pkgid))) {
1168
+ for await (const artifact of batchScan(arrays.arrayUnique(details.map(d => d.node.pkgid)))) {
1212
1169
  if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
1213
1170
  continue;
1214
1171
  }
@@ -1217,7 +1174,6 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
1217
1174
  } = artifact;
1218
1175
  const name = packages.resolvePackageName(artifact);
1219
1176
  const id = `${name}@${artifact.version}`;
1220
- let blocked = false;
1221
1177
  let displayWarning = false;
1222
1178
  let alerts = [];
1223
1179
  for (const alert of artifact.alerts) {
@@ -1231,15 +1187,12 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
1231
1187
  type: alert.type
1232
1188
  }
1233
1189
  });
1234
- if (ux.block) {
1235
- blocked = true;
1236
- }
1237
1190
  if (ux.display && output) {
1238
1191
  displayWarning = true;
1239
1192
  }
1240
1193
  if (ux.block || ux.display) {
1241
- const isFixable = isAlertFixable(alert);
1242
- if (!fixable || isFixable) {
1194
+ const fixable = isArtifactAlertFixable(alert);
1195
+ if (includeUnfixable || fixable) {
1243
1196
  alerts.push({
1244
1197
  name,
1245
1198
  version,
@@ -1247,38 +1200,27 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
1247
1200
  type: alert.type,
1248
1201
  block: ux.block,
1249
1202
  raw: alert,
1250
- fixable: isFixable
1203
+ fixable
1251
1204
  });
1252
1205
  }
1253
1206
  // Lazily access constants.IPC.
1254
- if (!fixable && !constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
1207
+ if (includeExisting && !constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
1255
1208
  // Before we ask about problematic issues, check to see if they
1256
1209
  // already existed in the old version if they did, be quiet.
1257
- const existing = pkgs.find(p => p.existing?.startsWith(`${name}@`))?.existing;
1210
+ const existing = details.find(d => d.existing?.pkgid.startsWith(`${name}@`))?.existing;
1258
1211
  if (existing) {
1259
1212
  const oldArtifact =
1260
1213
  // eslint-disable-next-line no-await-in-loop
1261
- (await batchScan([existing]).next()).value;
1214
+ (await batchScan([existing.pkgid]).next()).value;
1262
1215
  if (oldArtifact?.alerts?.length) {
1263
1216
  alerts = alerts.filter(({
1264
1217
  type
1265
- }) => !oldArtifact.alerts?.find(a => a.type === type));
1218
+ }) => !oldArtifact.alerts.find(a => a.type === type));
1266
1219
  }
1267
1220
  }
1268
1221
  }
1269
1222
  }
1270
1223
  }
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
1224
  if (displayWarning && spinner$1) {
1283
1225
  spinner$1.stop(`(socket) ${formatter.hyperlink(id, socketUrl.getSocketDevPackageOverviewUrl(NPM, name, version))} contains risks:`);
1284
1226
  }
@@ -1309,7 +1251,7 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
1309
1251
  packageAlerts.push(...alerts);
1310
1252
  }
1311
1253
  } catch (e) {
1312
- pathResolve.debugLog(e);
1254
+ npmPaths.debugLog(e);
1313
1255
  } finally {
1314
1256
  spinner$1?.stop();
1315
1257
  }
@@ -1324,194 +1266,181 @@ function getTranslations() {
1324
1266
  }
1325
1267
  return _translations;
1326
1268
  }
1327
- function packageAlertsToReport(alerts) {
1328
- let report = null;
1269
+ async function updateAdvisoryDependencies(arb, alerts) {
1270
+ let patchDataByPkg;
1329
1271
  for (const alert of alerts) {
1330
- if (!isAlertFixableCve(alert.raw)) {
1272
+ if (!isArtifactAlertCveFixable(alert.raw)) {
1331
1273
  continue;
1332
1274
  }
1275
+ if (!patchDataByPkg) {
1276
+ patchDataByPkg = {};
1277
+ }
1333
1278
  const {
1334
1279
  name
1335
1280
  } = 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
1281
+ if (!patchDataByPkg[name]) {
1282
+ patchDataByPkg[name] = [];
1283
+ }
1284
+ const {
1285
+ firstPatchedVersionIdentifier,
1286
+ vulnerableVersionRange
1287
+ } = alert.raw.props;
1288
+ patchDataByPkg[name].push({
1289
+ firstPatchedVersionIdentifier,
1290
+ vulnerableVersionRange
1352
1291
  });
1353
1292
  }
1354
- return report;
1355
- }
1356
- async function updateAdvisoryDependencies(arb, alerts) {
1357
- const report = packageAlertsToReport(alerts);
1358
- if (!report) {
1293
+ if (!patchDataByPkg) {
1359
1294
  // No advisories to process.
1360
1295
  return;
1361
1296
  }
1362
1297
  await arb.buildIdealTree();
1363
1298
  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.
1299
+ for (const name of Object.keys(patchDataByPkg)) {
1300
+ const nodes = findPackageNodes(tree, name);
1301
+ if (!nodes.length) {
1369
1302
  continue;
1370
1303
  }
1371
- const {
1372
- version
1373
- } = node;
1374
- const majorVerNum = semver.major(version);
1375
-
1376
1304
  // Fetch packument to get available versions.
1377
1305
  // eslint-disable-next-line no-await-in-loop
1378
1306
  const packument = await packages.fetchPackagePackument(name);
1379
- const availableVersions = packument ? Object.keys(packument.versions) : [];
1380
- for (const advisory of advisories) {
1307
+ for (const node of nodes) {
1381
1308
  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
- };
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();
1309
+ version
1310
+ } = node;
1311
+ const majorVerNum = semver.major(version);
1312
+ const availableVersions = packument ? Object.keys(packument.versions) : [];
1313
+ const patchData = patchDataByPkg[name];
1314
+ for (const {
1315
+ firstPatchedVersionIdentifier,
1316
+ vulnerableVersionRange
1317
+ } of patchData) {
1318
+ // Find the highest non-vulnerable version within the same major range
1319
+ const targetVersion = findBestPatchVersion(name, availableVersions, majorVerNum, vulnerableVersionRange);
1320
+ const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
1321
+ // Check !targetVersion to make TypeScript happy.
1322
+ if (!targetVersion || !targetPackument) {
1323
+ // No suitable patch version found.
1324
+ continue;
1325
+ }
1326
+ // Use Object.defineProperty to override the version.
1327
+ Object.defineProperty(node, 'version', {
1328
+ configurable: true,
1329
+ enumerable: true,
1330
+ get: () => targetVersion
1331
+ });
1332
+ node.package.version = targetVersion;
1333
+ // Update resolved and clear integrity for the new version.
1334
+ node.resolved = `${NPM_REGISTRY_URL}/${name}/-/${name}-${targetVersion}.tgz`;
1335
+ if (node.integrity) {
1336
+ delete node.integrity;
1337
+ }
1338
+ if ('deprecated' in targetPackument) {
1339
+ node.package['deprecated'] = targetPackument.deprecated;
1340
+ } else {
1341
+ delete node.package['deprecated'];
1342
+ }
1343
+ const newDeps = {
1344
+ ...targetPackument.dependencies
1345
+ };
1346
+ const {
1347
+ dependencies: oldDeps
1348
+ } = node.package;
1349
+ node.package.dependencies = newDeps;
1350
+ if (oldDeps) {
1351
+ for (const oldDepName of Object.keys(oldDeps)) {
1352
+ if (!objects.hasOwn(newDeps, oldDepName)) {
1353
+ node.edgesOut.get(oldDepName)?.detach();
1354
+ }
1421
1355
  }
1422
1356
  }
1423
- }
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
- }));
1357
+ for (const newDepName of Object.keys(newDeps)) {
1358
+ if (!objects.hasOwn(oldDeps, newDepName)) {
1359
+ node.addEdgeOut(new Edge({
1360
+ from: node,
1361
+ name: newDepName,
1362
+ spec: newDeps[newDepName],
1363
+ type: 'prod'
1364
+ }));
1365
+ }
1432
1366
  }
1433
1367
  }
1434
1368
  }
1435
1369
  }
1436
1370
  }
1371
+ const kRiskyReify = Symbol('riskyReify');
1437
1372
  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) {
1373
+ const IPC = await getIPC();
1374
+ // We are assuming `this[_diffTrees]()` has been called by `super.reify(...)`:
1375
+ // https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/reify.js#L141
1376
+ let needInfoOn = getPackagesToQueryFromDiff(this.diff, {
1377
+ includeUnchanged: !!IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]
1378
+ });
1379
+ if (!needInfoOn.length) {
1440
1380
  // Nothing to check, hmmm already installed or all private?
1441
1381
  return await this[kRiskyReify](...args);
1442
1382
  }
1443
- // Lazily access constants.IPC.
1444
1383
  const {
1445
1384
  [SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]: bypassConfirms,
1446
1385
  [SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE]: bypassAlerts
1447
- } = constants.IPC;
1386
+ } = IPC;
1448
1387
  const {
1449
1388
  stderr: output,
1450
1389
  stdin: input
1451
1390
  } = process;
1452
- let alerts;
1453
- const proceed = bypassAlerts || (await (async () => {
1454
- alerts = await getPackagesAlerts(this, needInfoOn, {
1455
- output
1456
- });
1457
- if (bypassConfirms || !alerts.length) {
1458
- return true;
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
1391
+ let alerts = bypassAlerts ? [] : await getPackagesAlerts(needInfoOn, {
1392
+ output
1393
+ });
1394
+ if (alerts.length && !bypassConfirms && !(await prompts.confirm({
1395
+ message: 'Accept risks of installing these packages?',
1396
+ default: false
1397
+ }, {
1398
+ input,
1399
+ output,
1400
+ signal: abortSignal
1401
+ }))) {
1402
+ throw new Error('Socket npm exiting due to risks');
1403
+ }
1404
+ if (!alerts.length || !bypassConfirms && !(await prompts.confirm({
1405
+ message: 'Try to fix alerts?',
1406
+ default: true
1407
+ }, {
1408
+ input,
1409
+ output,
1410
+ signal: abortSignal
1411
+ }))) {
1412
+ return await this[kRiskyReify](...args);
1413
+ }
1414
+ const prev = new Set(alerts.map(a => a.key));
1415
+ let ret;
1416
+ /* eslint-disable no-await-in-loop */
1417
+ while (alerts.length > 0) {
1418
+ await updateAdvisoryDependencies(this, alerts);
1419
+ ret = await this[kRiskyReify](...args);
1420
+ await this.loadActual();
1421
+ await this.buildIdealTree();
1422
+ needInfoOn = getPackagesToQueryFromDiff(this.diff, {
1423
+ includeUnchanged: true
1467
1424
  });
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
- });
1425
+ alerts = (await getPackagesAlerts(needInfoOn, {
1426
+ includeExisting: true,
1427
+ includeUnfixable: true
1428
+ })).filter(({
1429
+ key
1430
+ }) => {
1431
+ const unseen = !prev.has(key);
1432
+ if (unseen) {
1433
+ prev.add(key);
1502
1434
  }
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');
1435
+ return unseen;
1436
+ });
1509
1437
  }
1438
+ /* eslint-enable no-await-in-loop */
1439
+ return ret;
1510
1440
  }
1511
1441
 
1512
- const Arborist = require(arboristClassPath);
1442
+ const Arborist = require(npmPaths.getArboristClassPath());
1513
1443
  const kCtorArgs = Symbol('ctorArgs');
1514
- const kRiskyReify = Symbol('riskyReify');
1515
1444
 
1516
1445
  // Implementation code not related to our custom behavior is based on
1517
1446
  // https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
@@ -1556,8 +1485,6 @@ class SafeArborist extends Arborist {
1556
1485
  options.dryRun = true;
1557
1486
  options.save = false;
1558
1487
  options.saveBundle = false;
1559
- // TODO: Make this deal with any refactor to private fields by punching the
1560
- // class itself.
1561
1488
  await super.reify(...args);
1562
1489
  options.dryRun = old.dryRun;
1563
1490
  options.save = old.save;
@@ -1567,17 +1494,19 @@ class SafeArborist extends Arborist {
1567
1494
  }
1568
1495
 
1569
1496
  function installSafeArborist() {
1497
+ // Override '@npmcli/arborist' module exports with patched variants based on
1498
+ // https://github.com/npm/cli/pull/7025.
1570
1499
  const cache = require.cache;
1571
- cache[arboristClassPath] = {
1500
+ cache[npmPaths.getArboristClassPath()] = {
1572
1501
  exports: SafeArborist
1573
1502
  };
1574
- cache[arboristEdgeClassPath] = {
1503
+ cache[npmPaths.getArboristEdgeClassPath()] = {
1575
1504
  exports: SafeEdge
1576
1505
  };
1577
- cache[arboristNodeClassPath] = {
1506
+ cache[npmPaths.getArboristNodeClassPath()] = {
1578
1507
  exports: SafeNode
1579
1508
  };
1580
- cache[arboristOverrideSetClassPath] = {
1509
+ cache[npmPaths.getArboristOverrideSetClassPath()] = {
1581
1510
  exports: SafeOverrideSet
1582
1511
  };
1583
1512
  }