socket 0.14.39 → 0.14.40-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,210 +15,18 @@ var https = require('node:https');
15
15
  var path = require('node:path');
16
16
  var readline = require('node:readline');
17
17
  var promises = require('node:timers/promises');
18
- var prompts = require('@socketsecurity/registry/lib/prompts');
19
18
  var yoctoSpinner = require('@socketregistry/yocto-spinner');
20
- var isInteractive = _socketInterop(require('is-interactive'));
21
- var npa = _socketInterop(require('npm-package-arg'));
22
- var semver = _socketInterop(require('semver'));
23
19
  var config = require('@socketsecurity/config');
20
+ var registry = require('@socketsecurity/registry');
24
21
  var objects = require('@socketsecurity/registry/lib/objects');
25
22
  var packages = require('@socketsecurity/registry/lib/packages');
26
- var net = require('node:net');
27
- var homedir = require('node:os');
28
- var node_stream = require('node:stream');
29
- var sdk = require('./sdk.js');
23
+ var prompts = require('@socketsecurity/registry/lib/prompts');
24
+ var npa = _socketInterop(require('npm-package-arg'));
25
+ var semver = _socketInterop(require('semver'));
30
26
  var constants = require('./constants.js');
27
+ var sdk = require('./sdk.js');
31
28
  var pathResolve = require('./path-resolve.js');
32
29
 
33
- var version = "0.14.39";
34
-
35
- const NEWLINE_CHAR_CODE = 10; /*'\n'*/
36
-
37
- const TTY_IPC = process.env['SOCKET_SECURITY_TTY_IPC'];
38
- const sock = path.join(homedir.tmpdir(), `socket-security-tty-${process.pid}.sock`);
39
- process.env['SOCKET_SECURITY_TTY_IPC'] = sock;
40
- function createNonStandardTTYServer() {
41
- return {
42
- async captureTTY(mutexFn) {
43
- return await new Promise((resolve, reject) => {
44
- const conn = net.createConnection({
45
- path: TTY_IPC
46
- }).on('error', reject);
47
- let captured = false;
48
- const buffs = [];
49
- conn.on('data', function awaitCapture(chunk) {
50
- buffs.push(chunk);
51
- let lineBuff = Buffer.concat(buffs);
52
- if (captured) return;
53
- try {
54
- const eolIndex = lineBuff.indexOf(NEWLINE_CHAR_CODE);
55
- if (eolIndex !== -1) {
56
- conn.removeListener('data', awaitCapture);
57
- conn.push(lineBuff.slice(eolIndex + 1));
58
- const {
59
- capabilities: {
60
- input: hasInput,
61
- output: hasOutput
62
- },
63
- ipc_version: remote_ipc_version
64
- } = JSON.parse(lineBuff.subarray(0, eolIndex).toString('utf8'));
65
- lineBuff = null;
66
- captured = true;
67
- if (remote_ipc_version !== version) {
68
- throw new Error('Mismatched STDIO tunnel IPC version, ensure you only have 1 version of socket CLI being called.');
69
- }
70
- const input = hasInput ? new node_stream.PassThrough() : null;
71
- input?.pause();
72
- if (input) conn.pipe(input);
73
- const output = hasOutput ? new node_stream.PassThrough() : null;
74
- if (output) {
75
- output.pipe(conn)
76
- // Make ora happy
77
- ;
78
- output.isTTY = true;
79
- output.cursorTo = function cursorTo(x, y, callback) {
80
- readline.cursorTo(this, x, y, callback);
81
- };
82
- output.clearLine = function clearLine(dir, callback) {
83
- readline.clearLine(this, dir, callback);
84
- };
85
- }
86
- mutexFn(hasInput ? input : undefined, hasOutput ? output : undefined).then(resolve, reject).finally(() => {
87
- conn.unref();
88
- conn.end();
89
- input?.end();
90
- output?.end();
91
- // process.exit(13)
92
- });
93
- }
94
- } catch (e) {
95
- reject(e);
96
- }
97
- });
98
- });
99
- }
100
- };
101
- }
102
- function createIPCServer(captureState, npmlog) {
103
- const input = process.stdin;
104
- const output = process.stderr;
105
- return new Promise((resolve, reject) => {
106
- const server = net
107
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
108
- .createServer(async conn => {
109
- if (captureState.captured) {
110
- await new Promise(resolve => {
111
- captureState.pendingCaptures.push({
112
- resolve() {
113
- resolve();
114
- }
115
- });
116
- });
117
- } else {
118
- captureState.captured = true;
119
- }
120
- const wasProgressEnabled = npmlog.progressEnabled;
121
- npmlog.pause();
122
- if (wasProgressEnabled) {
123
- npmlog.disableProgress();
124
- }
125
- conn.write(`${JSON.stringify({
126
- ipc_version: version,
127
- capabilities: {
128
- input: Boolean(input),
129
- output: true
130
- }
131
- })}\n`);
132
- conn.on('data', data => {
133
- output.write(data);
134
- }).on('error', e => {
135
- output.write(`there was an error prompting from a sub shell (${e?.message}), socket npm closing`);
136
- process.exit(1);
137
- });
138
- input.on('data', data => {
139
- conn.write(data);
140
- }).on('end', () => {
141
- conn.unref();
142
- conn.end();
143
- if (wasProgressEnabled) {
144
- npmlog.enableProgress();
145
- }
146
- npmlog.resume();
147
- captureState.nextCapture();
148
- });
149
- }).listen(sock, () => resolve(server)).on('error', reject).unref();
150
- process.on('exit', () => {
151
- server.close();
152
- tryUnlinkSync(sock);
153
- });
154
- resolve(server);
155
- });
156
- }
157
- function createStandardTTYServer(isInteractive, npmlog) {
158
- const captureState = {
159
- captured: false,
160
- nextCapture: () => {
161
- if (captureState.pendingCaptures.length > 0) {
162
- const pendingCapture = captureState.pendingCaptures.shift();
163
- pendingCapture?.resolve();
164
- } else {
165
- captureState.captured = false;
166
- }
167
- },
168
- pendingCaptures: []
169
- };
170
- tryUnlinkSync(sock);
171
- const input = isInteractive ? process.stdin : undefined;
172
- const output = process.stderr;
173
- let ipcServerPromise;
174
- if (input) {
175
- ipcServerPromise = createIPCServer(captureState, npmlog);
176
- }
177
- return {
178
- async captureTTY(mutexFn) {
179
- await ipcServerPromise;
180
- if (captureState.captured) {
181
- const captured = new Promise(resolve => {
182
- captureState.pendingCaptures.push({
183
- resolve() {
184
- resolve();
185
- }
186
- });
187
- });
188
- await captured;
189
- } else {
190
- captureState.captured = true;
191
- }
192
- const wasProgressEnabled = npmlog.progressEnabled;
193
- try {
194
- npmlog.pause();
195
- if (wasProgressEnabled) {
196
- npmlog.disableProgress();
197
- }
198
- return await mutexFn(input, output);
199
- } finally {
200
- if (wasProgressEnabled) {
201
- npmlog.enableProgress();
202
- }
203
- npmlog.resume();
204
- captureState.nextCapture();
205
- }
206
- }
207
- };
208
- }
209
- function tryUnlinkSync(filepath) {
210
- try {
211
- fs.unlinkSync(filepath);
212
- } catch (e) {
213
- if (sdk.isErrnoException(e) && e.code !== 'ENOENT') {
214
- throw e;
215
- }
216
- }
217
- }
218
- function createTTYServer(isInteractive, npmlog) {
219
- return !isInteractive && TTY_IPC ? createNonStandardTTYServer() : createStandardTTYServer(isInteractive, npmlog);
220
- }
221
-
222
30
  //#region UX Constants
223
31
 
224
32
  const IGNORE_UX = {
@@ -239,10 +47,10 @@ const ERROR_UX = {
239
47
  /**
240
48
  * Iterates over all entries with ordered issue rule for deferral. Iterates over
241
49
  * all issue rules and finds the first defined value that does not defer otherwise
242
- * uses the defaultValue. Takes the value and converts into a UX workflow
50
+ * uses the defaultValue. Takes the value and converts into a UX workflow.
243
51
  */
244
52
  function resolveAlertRuleUX(orderedRulesCollection, defaultValue) {
245
- if (defaultValue === true || defaultValue == null) {
53
+ if (defaultValue === true || defaultValue === null || defaultValue === undefined) {
246
54
  defaultValue = {
247
55
  action: 'error'
248
56
  };
@@ -280,12 +88,13 @@ function resolveAlertRuleUX(orderedRulesCollection, defaultValue) {
280
88
  }
281
89
 
282
90
  /**
283
- * Negative form because it is narrowing the type
91
+ * Negative form because it is narrowing the type.
284
92
  */
285
93
  function ruleValueDoesNotDefer(rule) {
286
94
  if (rule === undefined) {
287
95
  return false;
288
- } else if (rule !== null && typeof rule === 'object') {
96
+ }
97
+ if (objects.isObject(rule)) {
289
98
  const {
290
99
  action
291
100
  } = rule;
@@ -297,7 +106,7 @@ function ruleValueDoesNotDefer(rule) {
297
106
  }
298
107
 
299
108
  /**
300
- * Handles booleans for backwards compatibility
109
+ * Handles booleans for backwards compatibility.
301
110
  */
302
111
  function uxForDefinedNonDeferValue(ruleValue) {
303
112
  if (typeof ruleValue === 'boolean') {
@@ -368,10 +177,12 @@ const {
368
177
  API_V0_URL,
369
178
  ENV,
370
179
  LOOP_SENTINEL,
180
+ NPM,
371
181
  NPM_REGISTRY_URL,
182
+ SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
372
183
  SOCKET_CLI_ISSUES_URL,
184
+ SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE,
373
185
  SOCKET_PUBLIC_API_KEY,
374
- UPDATE_SOCKET_OVERRIDES_IN_PACKAGE_LOCK_FILE,
375
186
  abortSignal,
376
187
  rootPath
377
188
  } = constants;
@@ -417,16 +228,7 @@ const log = tryRequire([path.join(npmNmPath, 'proc-log/lib/index.js'),
417
228
  // The proc-log DefinitelyTyped definition is incorrect. The type definition
418
229
  // is really that of its export log.
419
230
  mod => mod.log], path.join(npmNmPath, 'npmlog/lib/log.js'));
420
- 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;
231
+ const pacote = require(path.join(npmNmPath, 'pacote'));
430
232
  const translations = require(path.join(rootPath, 'translations.json'));
431
233
  const Arborist = require(arboristClassPath);
432
234
  const depValid = require(arboristDepValidPath);
@@ -437,9 +239,6 @@ const kCtorArgs = Symbol('ctorArgs');
437
239
  const kRiskyReify = Symbol('riskyReify');
438
240
  const formatter = new sdk.ColorOrMarkdown(false);
439
241
  const pubToken = sdk.getDefaultKey() ?? SOCKET_PUBLIC_API_KEY;
440
- const ttyServer = createTTYServer(isInteractive({
441
- stream: process.stdin
442
- }), log);
443
242
  let _uxLookup;
444
243
  async function uxLookup(settings) {
445
244
  while (_uxLookup === undefined) {
@@ -450,6 +249,35 @@ async function uxLookup(settings) {
450
249
  }
451
250
  return _uxLookup(settings);
452
251
  }
252
+ function packageAlertsToReport(alerts) {
253
+ let report = null;
254
+ for (const alert of alerts) {
255
+ if (!isAlertFixableCve(alert.raw)) {
256
+ continue;
257
+ }
258
+ const {
259
+ name
260
+ } = alert;
261
+ if (!report) {
262
+ report = {};
263
+ }
264
+ if (!report[name]) {
265
+ report[name] = [];
266
+ }
267
+ const props = alert.raw?.props;
268
+ report[name].push({
269
+ id: -1,
270
+ url: props?.url,
271
+ title: props?.title,
272
+ severity: alert.raw?.severity?.toLowerCase(),
273
+ vulnerable_versions: props?.vulnerableVersionRange,
274
+ cwe: props?.cwes,
275
+ cvss: props?.csvs,
276
+ name
277
+ });
278
+ }
279
+ return report;
280
+ }
453
281
  async function* batchScan(pkgIds) {
454
282
  const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
455
283
  method: 'POST',
@@ -527,17 +355,17 @@ function findSpecificOverrideSet(first, second) {
527
355
  overrideSet = overrideSet.parent;
528
356
  }
529
357
  // The override sets are incomparable. Neither one contains the other.
530
- log.silly('Conflicting override sets', first, second);
358
+ log?.silly('Conflicting override sets', first, second);
531
359
  return undefined;
532
360
  }
533
361
  function isAlertFixable(alert) {
362
+ return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
363
+ }
364
+ function isAlertFixableCve(alert) {
534
365
  const {
535
366
  type
536
367
  } = alert;
537
- if (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') {
538
- return !!alert.props?.['firstPatchedVersionIdentifier'];
539
- }
540
- return type === 'socketUpgradeAvailable';
368
+ return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
541
369
  }
542
370
  function maybeReadfileSync(filepath) {
543
371
  try {
@@ -545,7 +373,7 @@ function maybeReadfileSync(filepath) {
545
373
  } catch {}
546
374
  return undefined;
547
375
  }
548
- async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
376
+ async function getPackagesAlerts(safeArb, pkgs, output) {
549
377
  const spinner = yoctoSpinner({
550
378
  stream: output
551
379
  });
@@ -616,7 +444,7 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
616
444
  if (!blocked) {
617
445
  const pkg = pkgs.find(p => p.pkgid === id);
618
446
  if (pkg) {
619
- await tarball.stream(id, stream => {
447
+ await pacote.tarball.stream(id, stream => {
620
448
  stream.resume();
621
449
  return stream.promise();
622
450
  }, {
@@ -648,8 +476,6 @@ async function getPackagesAlerts(safeArb, _registry, pkgs, output) {
648
476
  spinner.text = remaining > 0 ? getText() : '';
649
477
  packageAlerts.push(...alerts);
650
478
  }
651
- } catch (e) {
652
- console.log('error', e);
653
479
  } finally {
654
480
  spinner.stop();
655
481
  }
@@ -713,7 +539,7 @@ function walk(diff_, needInfoOn = []) {
713
539
  // have access to. So we have to recreate any functionality that relies on those
714
540
  // private properties and use our own "safe" prefixed non-conflicting private
715
541
  // properties. Implementation code not related to patch https://github.com/npm/cli/pull/7025
716
- // is based on https://github.com/npm/cli/blob/v10.9.0/workspaces/arborist/lib/edge.js.
542
+ // is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/edge.js.
717
543
  //
718
544
  // The npm application
719
545
  // Copyright (c) npm, Inc. and Contributors
@@ -721,6 +547,7 @@ function walk(diff_, needInfoOn = []) {
721
547
  //
722
548
  // An edge in the dependency graph.
723
549
  // Represents a dependency relationship of some kind.
550
+ const initializedSafeEdges = new WeakSet();
724
551
  class SafeEdge extends Edge {
725
552
  #safeAccept;
726
553
  #safeError;
@@ -739,11 +566,15 @@ class SafeEdge extends Edge {
739
566
  if (accept !== undefined) {
740
567
  this.#safeAccept = accept || '*';
741
568
  }
569
+ if (from.constructor !== SafeNode) {
570
+ Reflect.setPrototypeOf(from, SafeNode.prototype);
571
+ }
742
572
  this.#safeError = null;
743
573
  this.#safeExplanation = null;
744
574
  this.#safeFrom = from;
745
575
  this.#safeName = name;
746
576
  this.#safeTo = null;
577
+ initializedSafeEdges.add(this);
747
578
  this.reload(true);
748
579
  }
749
580
  get accept() {
@@ -875,8 +706,11 @@ class SafeEdge extends Edge {
875
706
  return this.#safeExplanation;
876
707
  }
877
708
  reload(hard = false) {
709
+ if (!initializedSafeEdges.has(this)) {
710
+ // Skip if called during super constructor.
711
+ return;
712
+ }
878
713
  this.#safeExplanation = null;
879
-
880
714
  // Patch adding newOverrideSet and oldOverrideSet is based on
881
715
  // https://github.com/npm/cli/pull/7025.
882
716
  let newOverrideSet;
@@ -899,17 +733,15 @@ class SafeEdge extends Edge {
899
733
  }
900
734
  const newTo = this.#safeFrom?.resolve(this.name);
901
735
  if (newTo !== this.#safeTo) {
902
- 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
- }
736
+ // Patch replacing
737
+ // if (this.#safeTo) {
738
+ // this.#safeTo.edgesIn.delete(this)
739
+ // }
740
+ // is based on https://github.com/npm/cli/pull/7025.
741
+ this.#safeTo?.deleteEdgeIn(this);
908
742
  this.#safeTo = newTo ?? null;
909
743
  this.#safeError = null;
910
- if (this.#safeTo) {
911
- this.#safeTo.addEdgeIn(this);
912
- }
744
+ this.#safeTo?.addEdgeIn(this);
913
745
  } else if (hard) {
914
746
  this.#safeError = null;
915
747
  }
@@ -966,7 +798,7 @@ class SafeEdge extends Edge {
966
798
  }
967
799
 
968
800
  // Implementation code not related to patch https://github.com/npm/cli/pull/7025
969
- // is based on https://github.com/npm/cli/blob/v10.9.0/workspaces/arborist/lib/node.js:
801
+ // is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
970
802
  class SafeNode extends Node {
971
803
  // Return true if it's safe to remove this node, because anything that is
972
804
  // depending on it would be fine with the thing that they would resolve to if
@@ -1066,6 +898,8 @@ class SafeNode extends Node {
1066
898
  }
1067
899
  return result;
1068
900
  }
901
+
902
+ // Patch adding deleteEdgeIn is based on https://github.com/npm/cli/pull/7025.
1069
903
  deleteEdgeIn(edge) {
1070
904
  this.edgesIn.delete(edge);
1071
905
  const {
@@ -1194,7 +1028,7 @@ class SafeNode extends Node {
1194
1028
  }
1195
1029
  // This is an error condition. We can only get here if the new override set
1196
1030
  // is in conflict with the existing.
1197
- log.silly('Conflicting override sets', this.name);
1031
+ log?.silly('Conflicting override sets', this.name);
1198
1032
  return false;
1199
1033
  }
1200
1034
 
@@ -1234,7 +1068,7 @@ class SafeNode extends Node {
1234
1068
  }
1235
1069
 
1236
1070
  // Implementation code not related to patch https://github.com/npm/cli/pull/7025
1237
- // is based on https://github.com/npm/cli/blob/v10.9.0/workspaces/arborist/lib/override-set.js:
1071
+ // is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:
1238
1072
  class SafeOverrideSet extends OverrideSet {
1239
1073
  // Patch adding childrenAreEqual is based on
1240
1074
  // https://github.com/npm/cli/pull/7025.
@@ -1337,7 +1171,7 @@ class SafeOverrideSet extends OverrideSet {
1337
1171
  }
1338
1172
 
1339
1173
  // Implementation code not related to our custom behavior is based on
1340
- // https://github.com/npm/cli/blob/v10.9.0/workspaces/arborist/lib/arborist/index.js:
1174
+ // https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
1341
1175
  class SafeArborist extends Arborist {
1342
1176
  constructor(...ctorArgs) {
1343
1177
  const mutedArguments = [{
@@ -1356,6 +1190,7 @@ class SafeArborist extends Arborist {
1356
1190
  async [kRiskyReify](...args) {
1357
1191
  // SafeArborist has suffered side effects and must be rebuilt from scratch.
1358
1192
  const arb = new Arborist(...this[kCtorArgs]);
1193
+ arb.idealTree = this.idealTree;
1359
1194
  const ret = await arb.reify(...args);
1360
1195
  Object.assign(this, arb);
1361
1196
  return ret;
@@ -1379,46 +1214,179 @@ class SafeArborist extends Arborist {
1379
1214
  options.dryRun = true;
1380
1215
  options['save'] = false;
1381
1216
  options['saveBundle'] = false;
1382
- // TODO: Make this deal w/ any refactor to private fields by punching the
1217
+ // TODO: Make this deal with any refactor to private fields by punching the
1383
1218
  // class itself.
1384
1219
  await super.reify(...args);
1385
- const diff = walk(this['diff']);
1386
1220
  options.dryRun = old.dryRun;
1387
1221
  options['save'] = old.save;
1388
1222
  options['saveBundle'] = old.saveBundle;
1389
- // Nothing to check, mmm already installed or all private?
1223
+ // Nothing to check, hmmm already installed or all private?
1224
+ const diff = walk(this['diff']);
1390
1225
  if (diff.findIndex(c => c.repository_url === NPM_REGISTRY_URL) === -1) {
1391
1226
  return await this[kRiskyReify](...args);
1392
1227
  }
1393
- 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
- }
1228
+ const input = process.stdin;
1229
+ const output = process.stderr;
1230
+ let alerts;
1231
+ const proceed = ENV[SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE] || (await (async () => {
1232
+ alerts = await getPackagesAlerts(this, diff, output);
1233
+ if (!alerts.length || ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
1412
1234
  return true;
1235
+ }
1236
+ return await prompts.confirm({
1237
+ message: 'Accept risks of installing these packages?',
1238
+ default: false
1239
+ }, {
1240
+ input,
1241
+ output,
1242
+ signal: abortSignal
1413
1243
  });
1414
- }
1244
+ })());
1415
1245
  if (proceed) {
1246
+ const fix = !!alerts?.length && (ENV[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE] || (await prompts.confirm({
1247
+ message: 'Try to fix alerts?',
1248
+ default: true
1249
+ }, {
1250
+ input,
1251
+ output,
1252
+ signal: abortSignal
1253
+ })));
1254
+ if (fix) {
1255
+ await updateAdvisoryDependencies(this, alerts);
1256
+ }
1416
1257
  return await this[kRiskyReify](...args);
1417
1258
  } else {
1418
1259
  throw new Error('Socket npm exiting due to risks');
1419
1260
  }
1420
1261
  }
1421
1262
  }
1263
+ async function updateAdvisoryDependencies(arb, alerts) {
1264
+ const report = packageAlertsToReport(alerts);
1265
+ if (!report) {
1266
+ // No advisories to process.
1267
+ return;
1268
+ }
1269
+ await arb.buildIdealTree();
1270
+ const tree = arb.idealTree;
1271
+ for (const name of Object.keys(report)) {
1272
+ const advisories = report[name];
1273
+ const node = findPackageRecursively(tree, name);
1274
+ if (!node) {
1275
+ // Package not found in the tree.
1276
+ continue;
1277
+ }
1278
+ const {
1279
+ version
1280
+ } = node;
1281
+ const majorVerNum = semver.major(version);
1282
+
1283
+ // Fetch packument to get available versions.
1284
+ // eslint-disable-next-line no-await-in-loop
1285
+ const packument = await packages.fetchPackagePackument(name);
1286
+ const availableVersions = packument ? Object.keys(packument.versions) : [];
1287
+ for (const advisory of advisories) {
1288
+ const {
1289
+ vulnerable_versions
1290
+ } = advisory;
1291
+ // Find the highest non-vulnerable version within the same major range
1292
+ const targetVersion = findBestPatchVersion(name, availableVersions, majorVerNum, vulnerable_versions);
1293
+ const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
1294
+ // Check !targetVersion to make TypeScript happy.
1295
+ if (!targetVersion || !targetPackument) {
1296
+ // No suitable patch version found.
1297
+ continue;
1298
+ }
1299
+
1300
+ // Use Object.defineProperty to override the version.
1301
+ Object.defineProperty(node, 'version', {
1302
+ configurable: true,
1303
+ enumerable: true,
1304
+ get: () => targetVersion
1305
+ });
1306
+ node.package.version = targetVersion;
1307
+ // Update resolved and clear integrity for the new version.
1308
+ node.resolved = `https://registry.npmjs.org/${name}/-/${name}-${targetVersion}.tgz`;
1309
+ if (node.integrity) {
1310
+ delete node.integrity;
1311
+ }
1312
+ if ('deprecated' in targetPackument) {
1313
+ node.package['deprecated'] = targetPackument.deprecated;
1314
+ } else {
1315
+ delete node.package['deprecated'];
1316
+ }
1317
+ const newDeps = {
1318
+ ...targetPackument.dependencies
1319
+ };
1320
+ const {
1321
+ dependencies: oldDeps
1322
+ } = node.package;
1323
+ node.package.dependencies = newDeps;
1324
+ if (oldDeps) {
1325
+ for (const oldDepName of Object.keys(oldDeps)) {
1326
+ if (!objects.hasOwn(newDeps, oldDepName)) {
1327
+ node.edgesOut.get(oldDepName)?.detach();
1328
+ }
1329
+ }
1330
+ }
1331
+ for (const newDepName of Object.keys(newDeps)) {
1332
+ if (!objects.hasOwn(oldDeps, newDepName)) {
1333
+ node.addEdgeOut(new Edge({
1334
+ from: node,
1335
+ name: newDepName,
1336
+ spec: newDeps[newDepName],
1337
+ type: 'prod'
1338
+ }));
1339
+ }
1340
+ }
1341
+ }
1342
+ }
1343
+ }
1344
+ function findPackageRecursively(tree, packageName) {
1345
+ const queue = [{
1346
+ node: tree,
1347
+ depth: 0
1348
+ }];
1349
+ let sentinel = 0;
1350
+ while (queue.length) {
1351
+ if (sentinel++ === LOOP_SENTINEL) {
1352
+ throw new Error('Detected infinite loop in findPackageRecursively');
1353
+ }
1354
+ const {
1355
+ depth,
1356
+ node: currentNode
1357
+ } = queue.pop();
1358
+ const node = currentNode.children.get(packageName);
1359
+ if (node) {
1360
+ // Found package.
1361
+ return node;
1362
+ }
1363
+ const children = [...currentNode.children.values()];
1364
+ for (let i = children.length - 1; i >= 0; i -= 1) {
1365
+ queue.push({
1366
+ node: children[i],
1367
+ depth: depth + 1
1368
+ });
1369
+ }
1370
+ }
1371
+ return null;
1372
+ }
1373
+ function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableRange) {
1374
+ const manifestVersion = registry.getManifestData(NPM, name)?.version;
1375
+ // Filter versions that are within the current major version and are not in the vulnerable range
1376
+ const eligibleVersions = availableVersions.filter(version => {
1377
+ const isSameMajor = semver.major(version) === currentMajorVersion;
1378
+ const isNotVulnerable = !semver.satisfies(version, vulnerableRange);
1379
+ if (isSameMajor && isNotVulnerable) {
1380
+ return true;
1381
+ }
1382
+ return !!manifestVersion;
1383
+ });
1384
+ if (eligibleVersions.length === 0) {
1385
+ return null;
1386
+ }
1387
+ // Use semver to find the max satisfying version.
1388
+ return semver.maxSatisfying(eligibleVersions, '*');
1389
+ }
1422
1390
  function installSafeArborist() {
1423
1391
  const cache = require.cache;
1424
1392
  cache[arboristClassPath] = {
@@ -1435,7 +1403,10 @@ function installSafeArborist() {
1435
1403
  };
1436
1404
  }
1437
1405
  void (async () => {
1438
- const remoteSettings = await (async () => {
1406
+ const {
1407
+ orgs,
1408
+ settings
1409
+ } = await (async () => {
1439
1410
  try {
1440
1411
  const socketSdk = await sdk.setupSdk(pubToken);
1441
1412
  const orgResult = await socketSdk.getOrganizations();
@@ -1474,13 +1445,9 @@ void (async () => {
1474
1445
  throw e;
1475
1446
  }
1476
1447
  })();
1477
- const {
1478
- orgs,
1479
- settings
1480
- } = remoteSettings;
1481
- const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
1482
1448
 
1483
1449
  // Remove any organizations not being enforced.
1450
+ const enforcedOrgs = sdk.getSetting('enforcedOrgs') ?? [];
1484
1451
  for (const {
1485
1452
  0: i,
1486
1453
  1: org