@socketsecurity/cli-with-sentry 0.14.57 → 0.14.58

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.
@@ -28,6 +28,7 @@ var shadowNpmInject = require('./shadow-npm-inject.js');
28
28
  var constants = require('./constants.js');
29
29
  var meow = _socketInterop(require('meow'));
30
30
  var objects = require('@socketsecurity/registry/lib/objects');
31
+ var path$1 = require('@socketsecurity/registry/lib/path');
31
32
  var regexps = require('@socketsecurity/registry/lib/regexps');
32
33
  var commonTags = _socketInterop(require('common-tags'));
33
34
  var fs$1 = require('node:fs/promises');
@@ -42,27 +43,30 @@ var util = require('node:util');
42
43
  var registry = require('@socketsecurity/registry');
43
44
  var npm = require('@socketsecurity/registry/lib/npm');
44
45
  var packages = require('@socketsecurity/registry/lib/packages');
45
- var registryConstants = require('@socketsecurity/registry/lib/constants');
46
- var isInteractive = require('@socketregistry/is-interactive/index.cjs');
47
- var terminalLink = _socketInterop(require('terminal-link'));
46
+ var lockfileFile = _socketInterop(require('@pnpm/lockfile-file'));
47
+ var lockfile_detectDepTypes = _socketInterop(require('@pnpm/lockfile.detect-dep-types'));
48
+ var debug = require('@socketsecurity/registry/lib/debug');
48
49
  var spawn = require('@socketsecurity/registry/lib/spawn');
49
- var npa = _socketInterop(require('npm-package-arg'));
50
- var semver = _socketInterop(require('semver'));
51
- var tinyglobby = _socketInterop(require('tinyglobby'));
52
- var promises = require('@socketsecurity/registry/lib/promises');
50
+ var shadowNpmPaths = require('./shadow-npm-paths.js');
53
51
  var browserslist = _socketInterop(require('browserslist'));
52
+ var semver = _socketInterop(require('semver'));
54
53
  var which = _socketInterop(require('which'));
55
54
  var index_cjs = require('@socketregistry/hyrious__bun.lockb/index.cjs');
56
55
  var sorts = require('@socketsecurity/registry/lib/sorts');
57
56
  var strings = require('@socketsecurity/registry/lib/strings');
57
+ var registryConstants = require('@socketsecurity/registry/lib/constants');
58
+ var isInteractive = require('@socketregistry/is-interactive/index.cjs');
59
+ var terminalLink = _socketInterop(require('terminal-link'));
60
+ var npa = _socketInterop(require('npm-package-arg'));
61
+ var tinyglobby = _socketInterop(require('tinyglobby'));
62
+ var promises = require('@socketsecurity/registry/lib/promises');
58
63
  var yaml = _socketInterop(require('yaml'));
59
- var debug = require('@socketsecurity/registry/lib/debug');
60
- var shadowNpmPaths = require('./shadow-npm-paths.js');
61
64
  var betterAjvErrors = _socketInterop(require('@apideck/better-ajv-errors'));
62
65
  var config$A = require('@socketsecurity/config');
63
66
  var assert = require('node:assert');
64
67
  var readline = require('node:readline/promises');
65
68
  var open = _socketInterop(require('open'));
69
+ var BoxWidget = _socketInterop(require('blessed/lib/widgets/box'));
66
70
  var TableWidget = _socketInterop(require('blessed-contrib/lib/widget/table'));
67
71
  var readline$1 = require('node:readline');
68
72
 
@@ -324,7 +328,7 @@ class Core {
324
328
  }) {
325
329
  const introducedBy = [];
326
330
  if (pkg.direct) {
327
- let manifests = pkg.manifestFiles.map(({
331
+ const manifests = pkg.manifestFiles.map(({
328
332
  file
329
333
  }) => file).join(';');
330
334
  introducedBy.push(['direct', manifests]);
@@ -335,7 +339,7 @@ class Core {
335
339
  continue;
336
340
  }
337
341
  const topPurl = `${topPackage.type}/${topPackage.name}@${topPackage.version}`;
338
- let manifests = topPackage.manifestFiles.map(({
342
+ const manifests = topPackage.manifestFiles.map(({
339
343
  file
340
344
  }) => file).join(';');
341
345
  introducedBy.push([topPurl, manifests]);
@@ -767,7 +771,7 @@ function processSecurityComment({
767
771
  const result = [];
768
772
  let start = false;
769
773
  let ignoreAll = false;
770
- let ignoredPackages = [];
774
+ const ignoredPackages = [];
771
775
  for (const ignoreComment of ignoreComments) {
772
776
  const parsed = parseIgnoreCommand(ignoreComment.body?.split('\n').at(0) ?? '');
773
777
  if (parsed.ignoreAll) {
@@ -791,7 +795,7 @@ function processSecurityComment({
791
795
  const [_, _title, packageLink, _introducedBy, _manifest, _ci] = line.split('|');
792
796
 
793
797
  // Parsing package link [npm/pkg](url)
794
- let [_ecosystem, pkg] = packageLink.slice(1, packageLink.indexOf(']')).split('/', 2);
798
+ const [_ecosystem, pkg] = packageLink.slice(1, packageLink.indexOf(']')).split('/', 2);
795
799
  const [pkgName, pkgVersion] = pkg.split('@');
796
800
 
797
801
  // Checking if this package should be ignored
@@ -1114,8 +1118,8 @@ function createSources(alert) {
1114
1118
  manifests.push(addStr);
1115
1119
  }
1116
1120
  }
1117
- let manifestList = manifests.join('');
1118
- let sourceList = sources.join('');
1121
+ const manifestList = manifests.join('');
1122
+ const sourceList = sources.join('');
1119
1123
  const manifestStr = `<ul>${manifestList}</ul>`;
1120
1124
  const sourcesStr = `<ul>${sourceList}</ul>`;
1121
1125
  return [manifestStr, sourcesStr];
@@ -1260,8 +1264,8 @@ async function runAction(githubEventBefore, githubEventAfter) {
1260
1264
  const securityComment = securityCommentTemplate(diff);
1261
1265
  let newSecurityComment = true;
1262
1266
  let newOverviewComment = true;
1263
- let updateOldSecurityComment = comments.security !== undefined;
1264
- let updateOldOverviewComment = comments.overview !== undefined;
1267
+ const updateOldSecurityComment = comments.security !== undefined;
1268
+ const updateOldOverviewComment = comments.overview !== undefined;
1265
1269
  if (diff.newAlerts.length === 0) {
1266
1270
  if (!updateOldSecurityComment) {
1267
1271
  newSecurityComment = false;
@@ -1403,8 +1407,7 @@ const validationFlags = {
1403
1407
 
1404
1408
  const {
1405
1409
  DRY_RUN_LABEL: DRY_RUN_LABEL$1,
1406
- REDACTED,
1407
- SOCKET_CLI_SHOW_BANNER
1410
+ REDACTED
1408
1411
  } = constants;
1409
1412
  async function meowWithSubcommands(subcommands, options) {
1410
1413
  const {
@@ -1438,11 +1441,7 @@ async function meowWithSubcommands(subcommands, options) {
1438
1441
  };
1439
1442
  // ...else we provide basic instructions and help.
1440
1443
 
1441
- // Temp disable until we clear the --json and --markdown usage
1442
- // Lazily access constants.ENV[SOCKET_CLI_SHOW_BANNER].
1443
- if (constants.ENV[SOCKET_CLI_SHOW_BANNER]) {
1444
- logger.logger.log(getAsciiHeader(name));
1445
- }
1444
+ emitBanner(name);
1446
1445
  const cli = meow(`
1447
1446
  Usage
1448
1447
  $ ${name} <command>
@@ -1496,11 +1495,7 @@ function meowOrExit({
1496
1495
  parentName
1497
1496
  }) {
1498
1497
  const command = `${parentName} ${config.commandName}`;
1499
- // Temp disable until we clear the --json and --markdown usage.
1500
- // Lazily access constants.ENV[SOCKET_CLI_SHOW_BANNER].
1501
- if (constants.ENV[SOCKET_CLI_SHOW_BANNER]) {
1502
- logger.logger.log(getAsciiHeader(command));
1503
- }
1498
+ emitBanner(command);
1504
1499
 
1505
1500
  // This exits if .printHelp() is called either by meow itself or by us.
1506
1501
  const cli = meow({
@@ -1517,13 +1512,24 @@ function meowOrExit({
1517
1512
  }
1518
1513
  return cli;
1519
1514
  }
1515
+ function emitBanner(name) {
1516
+ // Print a banner at the top of each command.
1517
+ // This helps with brand recognition and marketing.
1518
+ // It also helps with debugging since it contains version and command details.
1519
+ // Note: print over stderr to preserve stdout for flags like --json and
1520
+ // --markdown. If we don't do this, you can't use --json in particular
1521
+ // and pipe the result to other tools. By emiting the banner over stderr
1522
+ // you can do something like `socket scan view xyz | jq | process`.
1523
+ // The spinner also emits over stderr for example.
1524
+ logger.logger.error(getAsciiHeader(name));
1525
+ }
1520
1526
  function getAsciiHeader(command) {
1521
1527
  const cliVersion = // The '@rollup/plugin-replace' will replace "process.env['SOCKET_CLI_VERSION_HASH']".
1522
- "0.14.57:6783de7:236c7308:pub";
1528
+ "0.14.58:f270068:05655527:pub";
1523
1529
  const nodeVersion = process.version;
1524
1530
  const apiToken = shadowNpmInject.getSetting('apiToken');
1525
1531
  const shownToken = apiToken ? getLastFiveOfApiToken(apiToken) : 'no';
1526
- const relCwd = process.cwd().replace(new RegExp(`^${regexps.escapeRegExp(constants.homePath)}`, 'i'), '~/');
1532
+ const relCwd = path$1.normalizePath(process.cwd().replace(new RegExp(`^${regexps.escapeRegExp(constants.homePath)}(?:${path.sep}|$)`, 'i'), '~/'));
1527
1533
  const body = `
1528
1534
  _____ _ _ /---------------
1529
1535
  | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}
@@ -1722,7 +1728,7 @@ async function outputAnalyticsWithToken({
1722
1728
  // A message should already have been printed if we have no data here
1723
1729
  if (!data) return;
1724
1730
  if (outputKind === 'json') {
1725
- let serialized = renderJson(data);
1731
+ const serialized = renderJson(data);
1726
1732
  if (!serialized) return;
1727
1733
  if (filePath && filePath !== '-') {
1728
1734
  try {
@@ -2194,6 +2200,9 @@ const config$x = {
2194
2200
  Usage
2195
2201
  $ ${command} <org slug>
2196
2202
 
2203
+ This feature requires an Enterprise Plan. To learn more about getting access
2204
+ to this feature and many more, please visit https://socket.dev/pricing
2205
+
2197
2206
  Options
2198
2207
  ${getFlagListOutput(config.flags, 6)}
2199
2208
 
@@ -2249,22 +2258,22 @@ async function run$x(argv, importMeta, {
2249
2258
  }
2250
2259
 
2251
2260
  const {
2252
- NPM: NPM$e,
2261
+ NPM: NPM$g,
2253
2262
  NPX: NPX$3,
2254
- PNPM: PNPM$8
2263
+ PNPM: PNPM$a
2255
2264
  } = constants;
2256
- const nodejsPlatformTypes = new Set(['javascript', 'js', 'nodejs', NPM$e, PNPM$8, 'ts', 'tsx', 'typescript']);
2265
+ const nodejsPlatformTypes = new Set(['javascript', 'js', 'nodejs', NPM$g, PNPM$a, 'ts', 'tsx', 'typescript']);
2257
2266
  async function runCycloneDX(yargv) {
2258
2267
  let cleanupPackageLock = false;
2259
2268
  if (yargv.type !== 'yarn' && nodejsPlatformTypes.has(yargv.type) && fs.existsSync('./yarn.lock')) {
2260
2269
  if (fs.existsSync('./package-lock.json')) {
2261
- yargv.type = NPM$e;
2270
+ yargv.type = NPM$g;
2262
2271
  } else {
2263
2272
  // Use synp to create a package-lock.json from the yarn.lock,
2264
2273
  // based on the node_modules folder, for a more accurate SBOM.
2265
2274
  try {
2266
2275
  await shadowBin(NPX$3, ['synp@1.9.14', '--', '--source-file', './yarn.lock'], 2);
2267
- yargv.type = NPM$e;
2276
+ yargv.type = NPM$g;
2268
2277
  cleanupPackageLock = true;
2269
2278
  } catch {}
2270
2279
  }
@@ -2784,99 +2793,105 @@ const cmdDiffScan = {
2784
2793
  }
2785
2794
  };
2786
2795
 
2787
- // import { detect } from '../../utils/package-environment-detector'
2788
-
2789
2796
  const {
2790
- NPM: NPM$d
2797
+ NPM: NPM$f
2791
2798
  } = constants;
2792
2799
  function isTopLevel(tree, node) {
2793
2800
  return tree.children.get(node.name) === node;
2794
2801
  }
2795
- async function runFix() {
2796
- // Lazily access constants.spinner.
2802
+ async function npmFix(_pkgEnvDetails, cwd, options) {
2797
2803
  const {
2798
2804
  spinner
2799
- } = constants;
2800
- spinner.start();
2801
- const cwd = process.cwd();
2802
- const editablePkgJson = await packages.readPackageJson(cwd, {
2803
- editable: true
2804
- });
2805
- // const agentDetails = await detect()
2806
-
2805
+ } = {
2806
+ __proto__: null,
2807
+ ...options
2808
+ };
2809
+ spinner?.start();
2807
2810
  const arb = new shadowNpmInject.SafeArborist({
2808
2811
  path: cwd,
2809
2812
  ...shadowNpmInject.SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES
2810
2813
  });
2811
2814
  await arb.reify();
2812
- const alerts = await shadowNpmInject.getPackagesAlerts(arb, {
2815
+ const alertsMap = await shadowNpmInject.getAlertsMapFromArborist(arb, {
2813
2816
  consolidate: true,
2814
- includeExisting: true,
2815
- includeUnfixable: false
2817
+ include: {
2818
+ existing: true,
2819
+ unfixable: false,
2820
+ upgrade: false
2821
+ }
2816
2822
  });
2817
- const infoByPkg = shadowNpmInject.getCveInfoByPackage(alerts);
2823
+ const infoByPkg = shadowNpmInject.getCveInfoByAlertsMap(alertsMap);
2824
+ if (!infoByPkg) {
2825
+ spinner?.stop();
2826
+ return;
2827
+ }
2818
2828
  await arb.buildIdealTree();
2819
- if (infoByPkg) {
2820
- for (const {
2821
- 0: name,
2822
- 1: infos
2823
- } of infoByPkg) {
2824
- let revertToIdealTree = arb.idealTree;
2825
- arb.idealTree = null;
2826
- // eslint-disable-next-line no-await-in-loop
2827
- await arb.buildIdealTree();
2828
- const tree = arb.idealTree;
2829
- const hasUpgrade = !!registry.getManifestData(NPM$d, name);
2830
- if (hasUpgrade) {
2831
- spinner.info(`Skipping ${name}. Socket Optimize package exists.`);
2832
- continue;
2833
- }
2834
- const nodes = shadowNpmInject.findPackageNodes(tree, name);
2835
- const packument = nodes.length && infos.length ?
2836
- // eslint-disable-next-line no-await-in-loop
2837
- await packages.fetchPackagePackument(name) : null;
2838
- if (packument) {
2839
- for (let i = 0, {
2840
- length: nodesLength
2841
- } = nodes; i < nodesLength; i += 1) {
2842
- const node = nodes[i];
2843
- for (let j = 0, {
2844
- length: infosLength
2845
- } = infos; j < infosLength; j += 1) {
2846
- const {
2847
- firstPatchedVersionIdentifier,
2848
- vulnerableVersionRange
2849
- } = infos[j];
2850
- const {
2851
- version: oldVersion
2852
- } = node;
2853
- if (shadowNpmInject.updateNode(node, packument, vulnerableVersionRange)) {
2854
- try {
2855
- // eslint-disable-next-line no-await-in-loop
2856
- await npm.runScript('test', [], {
2857
- spinner,
2858
- stdio: 'ignore'
2859
- });
2860
- spinner.info(`Patched ${name} ${oldVersion} -> ${node.version}`);
2861
- if (isTopLevel(tree, node)) {
2862
- for (const depField of ['dependencies', 'optionalDependencies', 'peerDependencies']) {
2863
- const oldVersion = editablePkgJson.content[depField]?.[name];
2864
- if (oldVersion) {
2865
- const decorator = /^[~^]/.exec(oldVersion)?.[0] ?? '';
2866
- editablePkgJson.content[depField][name] = `${decorator}${node.version}`;
2867
- }
2868
- }
2829
+ const editablePkgJson = await packages.readPackageJson(cwd, {
2830
+ editable: true
2831
+ });
2832
+ for (const {
2833
+ 0: name,
2834
+ 1: infos
2835
+ } of infoByPkg) {
2836
+ const revertToIdealTree = arb.idealTree;
2837
+ arb.idealTree = null;
2838
+ // eslint-disable-next-line no-await-in-loop
2839
+ await arb.buildIdealTree();
2840
+ const tree = arb.idealTree;
2841
+ const hasUpgrade = !!registry.getManifestData(NPM$f, name);
2842
+ if (hasUpgrade) {
2843
+ spinner?.info(`Skipping ${name}. Socket Optimize package exists.`);
2844
+ continue;
2845
+ }
2846
+ const nodes = shadowNpmInject.findPackageNodes(tree, name);
2847
+ const packument = nodes.length && infos.length ?
2848
+ // eslint-disable-next-line no-await-in-loop
2849
+ await packages.fetchPackagePackument(name) : null;
2850
+ if (!packument) {
2851
+ continue;
2852
+ }
2853
+ for (let i = 0, {
2854
+ length: nodesLength
2855
+ } = nodes; i < nodesLength; i += 1) {
2856
+ const node = nodes[i];
2857
+ for (let j = 0, {
2858
+ length: infosLength
2859
+ } = infos; j < infosLength; j += 1) {
2860
+ const {
2861
+ firstPatchedVersionIdentifier,
2862
+ vulnerableVersionRange
2863
+ } = infos[j];
2864
+ const {
2865
+ version: oldVersion
2866
+ } = node;
2867
+ if (shadowNpmInject.updateNode(node, packument, vulnerableVersionRange)) {
2868
+ try {
2869
+ // eslint-disable-next-line no-await-in-loop
2870
+ await npm.runScript('test', [], {
2871
+ spinner,
2872
+ stdio: 'ignore'
2873
+ });
2874
+ spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`);
2875
+ if (isTopLevel(tree, node)) {
2876
+ for (const depField of ['dependencies', 'optionalDependencies', 'peerDependencies']) {
2877
+ const {
2878
+ content: pkgJson
2879
+ } = editablePkgJson;
2880
+ const oldVersion = pkgJson[depField]?.[name];
2881
+ if (oldVersion) {
2882
+ const decorator = /^[~^]/.exec(oldVersion)?.[0] ?? '';
2883
+ pkgJson[depField][name] = `${decorator}${node.version}`;
2869
2884
  }
2870
- // eslint-disable-next-line no-await-in-loop
2871
- await editablePkgJson.save();
2872
- } catch {
2873
- spinner.error(`Reverting ${name} to ${oldVersion}`);
2874
- arb.idealTree = revertToIdealTree;
2875
2885
  }
2876
- } else {
2877
- spinner.error(`Could not patch ${name} ${oldVersion}`);
2878
2886
  }
2887
+ // eslint-disable-next-line no-await-in-loop
2888
+ await editablePkgJson.save();
2889
+ } catch {
2890
+ spinner?.error(`Reverting ${name} to ${oldVersion}`);
2891
+ arb.idealTree = revertToIdealTree;
2879
2892
  }
2893
+ } else {
2894
+ spinner?.error(`Could not patch ${name} ${oldVersion}`);
2880
2895
  }
2881
2896
  }
2882
2897
  }
@@ -2886,161 +2901,676 @@ async function runFix() {
2886
2901
  });
2887
2902
  arb2.idealTree = arb.idealTree;
2888
2903
  await arb2.reify();
2889
- spinner.stop();
2904
+ spinner?.stop();
2890
2905
  }
2891
2906
 
2892
- const {
2893
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
2894
- } = constants;
2895
- const config$t = {
2896
- commandName: 'fix',
2897
- description: 'Fix "fixable" Socket alerts',
2898
- hidden: true,
2899
- flags: {
2900
- ...commonFlags
2901
- },
2902
- help: (command, config) => `
2903
- Usage
2904
- $ ${command}
2905
-
2906
- Options
2907
- ${getFlagListOutput(config.flags, 6)}
2908
- `
2909
- };
2910
- const cmdFix = {
2911
- description: config$t.description,
2912
- hidden: config$t.hidden,
2913
- run: run$t
2914
- };
2915
- async function run$t(argv, importMeta, {
2916
- parentName
2917
- }) {
2918
- const cli = meowOrExit({
2919
- argv,
2920
- config: config$t,
2921
- importMeta,
2922
- parentName
2923
- });
2924
- if (cli.flags['dryRun']) {
2925
- logger.logger.log(DRY_RUN_BAIL_TEXT$s);
2926
- return;
2907
+ async function getAlertsMapFromPnpmLockfile(lockfile, options) {
2908
+ const {
2909
+ spinner
2910
+ } = {
2911
+ __proto__: null,
2912
+ ...options
2913
+ };
2914
+ const depTypes = lockfile_detectDepTypes.detectDepTypes(lockfile);
2915
+ const pkgIds = Object.keys(depTypes);
2916
+ let {
2917
+ length: remaining
2918
+ } = pkgIds;
2919
+ const alertsByPkgId = new Map();
2920
+ if (!remaining) {
2921
+ return alertsByPkgId;
2922
+ }
2923
+ const getText = () => `Looking up data for ${remaining} packages`;
2924
+ spinner?.start(getText());
2925
+ const toAlertsMapOptions = {
2926
+ overrides: lockfile.overrides,
2927
+ ...options
2928
+ };
2929
+ for await (const artifact of shadowNpmInject.batchScan(pkgIds)) {
2930
+ await shadowNpmInject.addArtifactToAlertsMap(artifact, alertsByPkgId, toAlertsMapOptions);
2931
+ remaining -= 1;
2932
+ if (spinner && remaining > 0) {
2933
+ spinner.start();
2934
+ spinner.setText(getText());
2935
+ }
2927
2936
  }
2928
- await runFix();
2937
+ spinner?.stop();
2938
+ return alertsByPkgId;
2929
2939
  }
2930
2940
 
2931
- function objectSome(obj) {
2932
- for (const key in obj) {
2933
- if (obj[key]) {
2934
- return true;
2941
+ function cmdFlagsToString(args) {
2942
+ const result = [];
2943
+ for (let i = 0, {
2944
+ length
2945
+ } = args; i < length; i += 1) {
2946
+ if (args[i].startsWith('--')) {
2947
+ // Check if the next item exists and is NOT another flag.
2948
+ if (i + 1 < length && !args[i + 1].startsWith('--')) {
2949
+ result.push(`${args[i]}=${args[i + 1]}`);
2950
+ i += 1;
2951
+ } else {
2952
+ result.push(args[i]);
2953
+ }
2935
2954
  }
2936
2955
  }
2937
- return false;
2956
+ return result.join(' ');
2938
2957
  }
2939
- function pick(input, keys) {
2940
- const result = {};
2941
- for (const key of keys) {
2942
- result[key] = input[key];
2958
+
2959
+ const {
2960
+ SOCKET_IPC_HANDSHAKE
2961
+ } = constants;
2962
+ function safeNpmInstall(options) {
2963
+ const {
2964
+ agentExecPath = shadowNpmPaths.getNpmBinPath(),
2965
+ args = [],
2966
+ ipc,
2967
+ spinner,
2968
+ ...spawnOptions
2969
+ } = {
2970
+ __proto__: null,
2971
+ ...options
2972
+ };
2973
+ const useIpc = objects.isObject(ipc);
2974
+ const useDebug = debug.isDebug();
2975
+ const terminatorPos = args.indexOf('--');
2976
+ const npmArgs = (terminatorPos === -1 ? args : args.slice(0, terminatorPos)).filter(a => !npm.isAuditFlag(a) && !npm.isFundFlag(a) && !npm.isProgressFlag(a));
2977
+ const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
2978
+ const isSilent = !useDebug && !npmArgs.some(npm.isLoglevelFlag);
2979
+ const logLevelArgs = isSilent ? ['--loglevel', 'error'] : [];
2980
+ const spawnPromise = spawn.spawn(
2981
+ // Lazily access constants.execPath.
2982
+ constants.execPath, [
2983
+ // Lazily access constants.nodeHardenFlags.
2984
+ ...constants.nodeHardenFlags,
2985
+ // Lazily access constants.nodeNoWarningsFlags.
2986
+ ...constants.nodeNoWarningsFlags, '--require',
2987
+ // Lazily access constants.distShadowNpmInjectPath.
2988
+ constants.distShadowNpmInjectPath, agentExecPath, 'install',
2989
+ // Avoid code paths for 'audit' and 'fund'.
2990
+ '--no-audit', '--no-fund',
2991
+ // Add `--no-progress` flag to fix input being swallowed by the spinner
2992
+ // when running the command with recent versions of npm.
2993
+ '--no-progress',
2994
+ // Add '--loglevel=error' if a loglevel flag is not provided and the
2995
+ // SOCKET_CLI_DEBUG environment variable is not truthy.
2996
+ ...logLevelArgs, ...npmArgs, ...otherArgs], {
2997
+ spinner,
2998
+ // Set stdio to include 'ipc'.
2999
+ // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166
3000
+ // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.
3001
+ stdio: useIpc ? [0, 1, 2, 'ipc'] : 'inherit',
3002
+ ...spawnOptions,
3003
+ env: {
3004
+ ...process$1.env,
3005
+ ...spawnOptions.env
3006
+ }
3007
+ });
3008
+ if (useIpc) {
3009
+ spawnPromise.process.send({
3010
+ [SOCKET_IPC_HANDSHAKE]: ipc
3011
+ });
2943
3012
  }
2944
- return result;
3013
+ return spawnPromise;
2945
3014
  }
2946
3015
 
2947
- function stringJoinWithSeparateFinalSeparator(list, separator = ' and ') {
2948
- const values = list.filter(Boolean);
3016
+ const {
3017
+ NPM: NPM$e
3018
+ } = constants;
3019
+ function runAgentInstall(pkgEnvDetails, options) {
2949
3020
  const {
2950
- length
2951
- } = values;
2952
- if (!length) {
2953
- return '';
2954
- }
2955
- if (length === 1) {
2956
- return values[0];
3021
+ agent,
3022
+ agentExecPath
3023
+ } = pkgEnvDetails;
3024
+ // All package managers support the "install" command.
3025
+ if (agent === NPM$e) {
3026
+ return safeNpmInstall({
3027
+ agentExecPath,
3028
+ ...options
3029
+ });
2957
3030
  }
2958
- const finalValue = values.pop();
2959
- return `${values.join(', ')}${separator}${finalValue}`;
3031
+ const {
3032
+ args = [],
3033
+ spinner,
3034
+ ...spawnOptions
3035
+ } = {
3036
+ __proto__: null,
3037
+ ...options
3038
+ };
3039
+ return spawn.spawn(agentExecPath, ['install', ...args], {
3040
+ spinner,
3041
+ stdio: debug.isDebug() ? 'inherit' : 'ignore',
3042
+ ...spawnOptions,
3043
+ env: {
3044
+ ...process.env,
3045
+ NODE_OPTIONS: cmdFlagsToString([
3046
+ // Lazily access constants.nodeHardenFlags.
3047
+ ...constants.nodeHardenFlags,
3048
+ // Lazily access constants.nodeNoWarningsFlags.
3049
+ ...constants.nodeNoWarningsFlags]),
3050
+ ...spawnOptions.env
3051
+ }
3052
+ });
2960
3053
  }
2961
3054
 
2962
- // Ordered from most severe to least.
2963
- const SEVERITIES_BY_ORDER = ['critical', 'high', 'middle', 'low'];
2964
- function getDesiredSeverities(lowestToInclude) {
2965
- const result = [];
2966
- for (const severity of SEVERITIES_BY_ORDER) {
2967
- result.push(severity);
2968
- if (severity === lowestToInclude) {
2969
- break;
2970
- }
3055
+ const {
3056
+ NPM: NPM$d,
3057
+ OVERRIDES: OVERRIDES$2,
3058
+ PNPM: PNPM$9
3059
+ } = constants;
3060
+ async function pnpmFix(pkgEnvDetails, cwd, options) {
3061
+ const {
3062
+ spinner
3063
+ } = {
3064
+ __proto__: null,
3065
+ ...options
3066
+ };
3067
+ spinner?.start();
3068
+ const lockfile = await lockfileFile.readWantedLockfile(cwd, {
3069
+ ignoreIncompatible: false
3070
+ });
3071
+ if (!lockfile) {
3072
+ spinner?.stop();
3073
+ return;
2971
3074
  }
2972
- return result;
2973
- }
2974
- function formatSeverityCount(severityCount) {
2975
- const summary = [];
2976
- for (const severity of SEVERITIES_BY_ORDER) {
2977
- if (severityCount[severity]) {
2978
- summary.push(`${severityCount[severity]} ${severity}`);
3075
+ const alertsMap = await getAlertsMapFromPnpmLockfile(lockfile, {
3076
+ consolidate: true,
3077
+ include: {
3078
+ existing: true,
3079
+ unfixable: false,
3080
+ upgrade: false
2979
3081
  }
3082
+ });
3083
+ const infoByPkg = shadowNpmInject.getCveInfoByAlertsMap(alertsMap);
3084
+ if (!infoByPkg) {
3085
+ spinner?.stop();
3086
+ return;
2980
3087
  }
2981
- return stringJoinWithSeparateFinalSeparator(summary);
2982
- }
2983
- function getSeverityCount(issues, lowestToInclude) {
2984
- const severityCount = pick({
2985
- low: 0,
2986
- middle: 0,
2987
- high: 0,
2988
- critical: 0
2989
- }, getDesiredSeverities(lowestToInclude));
2990
- for (const issue of issues) {
2991
- const {
2992
- value
2993
- } = issue;
2994
- if (!value) {
3088
+ const arb = new shadowNpmInject.SafeArborist({
3089
+ path: cwd,
3090
+ ...shadowNpmInject.SAFE_ARBORIST_REIFY_OPTIONS_OVERRIDES
3091
+ });
3092
+ await arb.loadActual();
3093
+ const editablePkgJson = await packages.readPackageJson(cwd, {
3094
+ editable: true
3095
+ });
3096
+ const {
3097
+ content: pkgJson
3098
+ } = editablePkgJson;
3099
+ for (const {
3100
+ 0: name,
3101
+ 1: infos
3102
+ } of infoByPkg) {
3103
+ const tree = arb.actualTree;
3104
+ const hasUpgrade = !!registry.getManifestData(NPM$d, name);
3105
+ if (hasUpgrade) {
3106
+ spinner?.info(`Skipping ${name}. Socket Optimize package exists.`);
2995
3107
  continue;
2996
3108
  }
2997
- if (severityCount[value.severity] !== undefined) {
2998
- severityCount[value.severity] += 1;
3109
+ const nodes = shadowNpmInject.findPackageNodes(tree, name);
3110
+ const packument = nodes.length && infos.length ?
3111
+ // eslint-disable-next-line no-await-in-loop
3112
+ await packages.fetchPackagePackument(name) : null;
3113
+ if (!packument) {
3114
+ continue;
2999
3115
  }
3000
- }
3001
- return severityCount;
3002
- }
3116
+ for (let i = 0, {
3117
+ length: nodesLength
3118
+ } = nodes; i < nodesLength; i += 1) {
3119
+ const node = nodes[i];
3120
+ for (let j = 0, {
3121
+ length: infosLength
3122
+ } = infos; j < infosLength; j += 1) {
3123
+ const {
3124
+ firstPatchedVersionIdentifier,
3125
+ vulnerableVersionRange
3126
+ } = infos[j];
3127
+ const {
3128
+ version: oldVersion
3129
+ } = node;
3130
+ const availableVersions = Object.keys(packument.versions);
3131
+ // Find the highest non-vulnerable version within the same major range
3132
+ const targetVersion = shadowNpmInject.findBestPatchVersion(node, availableVersions, vulnerableVersionRange);
3133
+ const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
3134
+ if (targetPackument) {
3135
+ const oldPnpm = pkgJson[PNPM$9];
3136
+ const oldOverrides = oldPnpm?.[OVERRIDES$2];
3137
+ try {
3138
+ editablePkgJson.update({
3139
+ [PNPM$9]: {
3140
+ ...oldPnpm,
3141
+ [OVERRIDES$2]: {
3142
+ [`${node.name}@${vulnerableVersionRange}`]: `^${targetVersion}`,
3143
+ ...oldOverrides
3144
+ }
3145
+ }
3146
+ });
3147
+ spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`);
3003
3148
 
3004
- async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
3005
- const socketSdk = await shadowNpmInject.setupSdk(shadowNpmInject.getPublicToken());
3006
- const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package');
3007
- const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score');
3008
- if (result.success === false) {
3009
- return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result);
3010
- }
3011
- if (scoreResult.success === false) {
3012
- return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult);
3149
+ // eslint-disable-next-line no-await-in-loop
3150
+ await editablePkgJson.save();
3151
+ // eslint-disable-next-line no-await-in-loop
3152
+ await runAgentInstall(pkgEnvDetails, {
3153
+ spinner
3154
+ });
3155
+ } catch {
3156
+ spinner?.error(`Reverting ${name} to ${oldVersion}`);
3157
+ }
3158
+ } else {
3159
+ spinner?.error(`Could not patch ${name} ${oldVersion}`);
3160
+ }
3161
+ }
3162
+ }
3013
3163
  }
3014
- const severityCount = getSeverityCount(result.data, includeAllIssues ? undefined : 'high');
3015
- return {
3016
- data: result.data,
3017
- severityCount,
3018
- score: scoreResult.data
3019
- };
3164
+ spinner?.stop();
3020
3165
  }
3021
3166
 
3022
3167
  const {
3023
- NPM: NPM$c
3024
- } = registryConstants;
3025
- function formatPackageInfo({
3026
- data,
3027
- score,
3028
- severityCount
3029
- }, {
3030
- name,
3031
- outputKind,
3032
- pkgName,
3033
- pkgVersion
3034
- }) {
3035
- if (outputKind === 'json') {
3036
- logger.logger.log(JSON.stringify(data, undefined, 2));
3037
- return;
3168
+ BINARY_LOCK_EXT,
3169
+ BUN: BUN$6,
3170
+ LOCK_EXT: LOCK_EXT$1,
3171
+ NPM: NPM$c,
3172
+ PNPM: PNPM$8,
3173
+ VLT: VLT$6,
3174
+ YARN,
3175
+ YARN_BERRY: YARN_BERRY$6,
3176
+ YARN_CLASSIC: YARN_CLASSIC$6
3177
+ } = constants;
3178
+ const AGENTS = [BUN$6, NPM$c, PNPM$8, YARN_BERRY$6, YARN_CLASSIC$6, VLT$6];
3179
+ const binByAgent = {
3180
+ __proto__: null,
3181
+ [BUN$6]: BUN$6,
3182
+ [NPM$c]: NPM$c,
3183
+ [PNPM$8]: PNPM$8,
3184
+ [YARN_BERRY$6]: YARN,
3185
+ [YARN_CLASSIC$6]: YARN,
3186
+ [VLT$6]: VLT$6
3187
+ };
3188
+ async function getAgentExecPath(agent) {
3189
+ const binName = binByAgent[agent];
3190
+ return (await which(binName, {
3191
+ nothrow: true
3192
+ })) ?? binName;
3193
+ }
3194
+ async function getAgentVersion(agentExecPath, cwd) {
3195
+ let result;
3196
+ try {
3197
+ result = semver.coerce(
3198
+ // All package managers support the "--version" flag.
3199
+ (await spawn.spawn(agentExecPath, ['--version'], {
3200
+ cwd
3201
+ })).stdout) ?? undefined;
3202
+ } catch {}
3203
+ return result;
3204
+ }
3205
+
3206
+ // The order of LOCKS properties IS significant as it affects iteration order.
3207
+ const LOCKS = {
3208
+ [`bun${LOCK_EXT$1}`]: BUN$6,
3209
+ [`bun${BINARY_LOCK_EXT}`]: BUN$6,
3210
+ // If both package-lock.json and npm-shrinkwrap.json are present in the root
3211
+ // of a project, npm-shrinkwrap.json will take precedence and package-lock.json
3212
+ // will be ignored.
3213
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson
3214
+ 'npm-shrinkwrap.json': NPM$c,
3215
+ 'package-lock.json': NPM$c,
3216
+ 'pnpm-lock.yaml': PNPM$8,
3217
+ 'pnpm-lock.yml': PNPM$8,
3218
+ [`yarn${LOCK_EXT$1}`]: YARN_CLASSIC$6,
3219
+ 'vlt-lock.json': VLT$6,
3220
+ // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:
3221
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles
3222
+ //
3223
+ // Unlike the other LOCKS keys this key contains a directory AND filename so
3224
+ // it has to be handled differently.
3225
+ 'node_modules/.package-lock.json': NPM$c
3226
+ };
3227
+ const readLockFileByAgent = (() => {
3228
+ function wrapReader(reader) {
3229
+ return async (...args) => {
3230
+ try {
3231
+ return await reader(...args);
3232
+ } catch {}
3233
+ return undefined;
3234
+ };
3235
+ }
3236
+ const binaryReader = wrapReader(shadowNpmInject.readFileBinary);
3237
+ const defaultReader = wrapReader(async lockPath => await shadowNpmInject.readFileUtf8(lockPath));
3238
+ return {
3239
+ [BUN$6]: wrapReader(async (lockPath, agentExecPath) => {
3240
+ const ext = path.extname(lockPath);
3241
+ if (ext === LOCK_EXT$1) {
3242
+ return await defaultReader(lockPath);
3243
+ }
3244
+ if (ext === BINARY_LOCK_EXT) {
3245
+ const lockBuffer = await binaryReader(lockPath);
3246
+ if (lockBuffer) {
3247
+ try {
3248
+ return index_cjs.parse(lockBuffer);
3249
+ } catch {}
3250
+ }
3251
+ // To print a Yarn lockfile to your console without writing it to disk
3252
+ // use `bun bun.lockb`.
3253
+ // https://bun.sh/guides/install/yarnlock
3254
+ return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3255
+ }
3256
+ return undefined;
3257
+ }),
3258
+ [NPM$c]: defaultReader,
3259
+ [PNPM$8]: defaultReader,
3260
+ [VLT$6]: defaultReader,
3261
+ [YARN_BERRY$6]: defaultReader,
3262
+ [YARN_CLASSIC$6]: defaultReader
3263
+ };
3264
+ })();
3265
+ async function detectPackageEnvironment({
3266
+ cwd = process$1.cwd(),
3267
+ onUnknown
3268
+ } = {}) {
3269
+ let lockPath = await shadowNpmInject.findUp(Object.keys(LOCKS), {
3270
+ cwd
3271
+ });
3272
+ let lockName = lockPath ? path.basename(lockPath) : undefined;
3273
+ const isHiddenLockFile = lockName === '.package-lock.json';
3274
+ const pkgJsonPath = lockPath ? path.resolve(lockPath, `${isHiddenLockFile ? '../' : ''}../package.json`) : await shadowNpmInject.findUp('package.json', {
3275
+ cwd
3276
+ });
3277
+ const pkgPath = pkgJsonPath && fs.existsSync(pkgJsonPath) ? path.dirname(pkgJsonPath) : undefined;
3278
+ const editablePkgJson = pkgPath ? await packages.readPackageJson(pkgPath, {
3279
+ editable: true
3280
+ }) : undefined;
3281
+ const pkgJson = editablePkgJson?.content;
3282
+ // Read Corepack `packageManager` field in package.json:
3283
+ // https://nodejs.org/api/packages.html#packagemanager
3284
+ const pkgManager = strings.isNonEmptyString(pkgJson?.packageManager) ? pkgJson.packageManager : undefined;
3285
+ let agent;
3286
+ let agentVersion;
3287
+ if (pkgManager) {
3288
+ const atSignIndex = pkgManager.lastIndexOf('@');
3289
+ if (atSignIndex !== -1) {
3290
+ const name = pkgManager.slice(0, atSignIndex);
3291
+ const version = pkgManager.slice(atSignIndex + 1);
3292
+ if (version && AGENTS.includes(name)) {
3293
+ agent = name;
3294
+ agentVersion = semver.coerce(version) ?? undefined;
3295
+ }
3296
+ }
3297
+ }
3298
+ if (agent === undefined && !isHiddenLockFile && typeof pkgJsonPath === 'string' && typeof lockName === 'string') {
3299
+ agent = LOCKS[lockName];
3300
+ }
3301
+ if (agent === undefined) {
3302
+ agent = NPM$c;
3303
+ onUnknown?.(pkgManager);
3304
+ }
3305
+ const agentExecPath = await getAgentExecPath(agent);
3306
+ const npmExecPath = agent === NPM$c ? agentExecPath : await getAgentExecPath(NPM$c);
3307
+ if (agentVersion === undefined) {
3308
+ agentVersion = await getAgentVersion(agentExecPath, cwd);
3309
+ }
3310
+ if (agent === YARN_CLASSIC$6 && (agentVersion?.major ?? 0) > 1) {
3311
+ agent = YARN_BERRY$6;
3312
+ }
3313
+ const targets = {
3314
+ browser: false,
3315
+ node: true
3316
+ };
3317
+ let lockSrc;
3318
+ // Lazily access constants.maintainedNodeVersions.
3319
+ let minimumNodeVersion = constants.maintainedNodeVersions.last;
3320
+ if (pkgJson) {
3321
+ const browserField = pkgJson.browser;
3322
+ if (strings.isNonEmptyString(browserField) || objects.isObjectObject(browserField)) {
3323
+ targets.browser = true;
3324
+ }
3325
+ const nodeRange = pkgJson.engines?.['node'];
3326
+ if (strings.isNonEmptyString(nodeRange)) {
3327
+ const coerced = semver.coerce(nodeRange);
3328
+ if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3329
+ minimumNodeVersion = coerced.version;
3330
+ }
3331
+ }
3332
+ const browserslistQuery = pkgJson['browserslist'];
3333
+ if (Array.isArray(browserslistQuery)) {
3334
+ const browserslistTargets = browserslist(browserslistQuery).map(s => s.toLowerCase()).sort(sorts.naturalCompare);
3335
+ const browserslistNodeTargets = browserslistTargets.filter(v => v.startsWith('node ')).map(v => v.slice(5 /*'node '.length*/));
3336
+ if (!targets.browser && browserslistTargets.length) {
3337
+ targets.browser = browserslistTargets.length !== browserslistNodeTargets.length;
3338
+ }
3339
+ if (browserslistNodeTargets.length) {
3340
+ const coerced = semver.coerce(browserslistNodeTargets[0]);
3341
+ if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3342
+ minimumNodeVersion = coerced.version;
3343
+ }
3344
+ }
3345
+ }
3346
+ // Lazily access constants.maintainedNodeVersions.
3347
+ targets.node = constants.maintainedNodeVersions.some(v => semver.satisfies(v, `>=${minimumNodeVersion}`));
3348
+ lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent[agent](lockPath, agentExecPath) : undefined;
3349
+ } else {
3350
+ lockName = undefined;
3351
+ lockPath = undefined;
3352
+ }
3353
+ return {
3354
+ agent,
3355
+ agentExecPath,
3356
+ agentVersion,
3357
+ lockName,
3358
+ lockPath,
3359
+ lockSrc,
3360
+ minimumNodeVersion,
3361
+ npmExecPath,
3362
+ pkgJson: editablePkgJson,
3363
+ pkgPath,
3364
+ supported: targets.browser || targets.node,
3365
+ targets
3366
+ };
3367
+ }
3368
+
3369
+ const {
3370
+ BUN: BUN$5,
3371
+ VLT: VLT$5,
3372
+ YARN_BERRY: YARN_BERRY$5
3373
+ } = constants;
3374
+ const COMMAND_TITLE$2 = 'Socket Optimize';
3375
+ async function detectAndValidatePackageEnvironment(cwd, options) {
3376
+ const {
3377
+ logger,
3378
+ prod
3379
+ } = {
3380
+ __proto__: null,
3381
+ ...options
3382
+ };
3383
+ const details = await detectPackageEnvironment({
3384
+ cwd,
3385
+ onUnknown(pkgManager) {
3386
+ logger?.warn(`${COMMAND_TITLE$2}: Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`);
3387
+ }
3388
+ });
3389
+ if (!details.supported) {
3390
+ logger?.fail(`${COMMAND_TITLE$2}: No supported Node or browser range detected`);
3391
+ return;
3392
+ }
3393
+ if (details.agent === VLT$5) {
3394
+ logger?.fail(`${COMMAND_TITLE$2}: ${details.agent} does not support overrides. Soon, though ⚡`);
3395
+ return;
3396
+ }
3397
+ const lockName = details.lockName ?? 'lock file';
3398
+ if (details.lockName === undefined || details.lockSrc === undefined) {
3399
+ logger?.fail(`${COMMAND_TITLE$2}: No ${lockName} found`);
3400
+ return;
3401
+ }
3402
+ if (details.lockSrc.trim() === '') {
3403
+ logger?.fail(`${COMMAND_TITLE$2}: ${lockName} is empty`);
3404
+ return;
3405
+ }
3406
+ if (details.pkgPath === undefined) {
3407
+ logger?.fail(`${COMMAND_TITLE$2}: No package.json found`);
3408
+ return;
3409
+ }
3410
+ if (prod && (details.agent === BUN$5 || details.agent === YARN_BERRY$5)) {
3411
+ logger?.fail(`${COMMAND_TITLE$2}: --prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.toString()}` : ''}`);
3412
+ return;
3413
+ }
3414
+ if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
3415
+ logger?.warn(`${COMMAND_TITLE$2}: Package ${lockName} found at ${details.lockPath}`);
3416
+ }
3417
+ return details;
3418
+ }
3419
+
3420
+ const {
3421
+ NPM: NPM$b,
3422
+ PNPM: PNPM$7
3423
+ } = constants;
3424
+ async function runFix() {
3425
+ // Lazily access constants.spinner.
3426
+ const {
3427
+ spinner
3428
+ } = constants;
3429
+ spinner.start();
3430
+ const cwd = process.cwd();
3431
+ const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {
3432
+ logger: logger.logger
3433
+ });
3434
+ if (!pkgEnvDetails) {
3435
+ spinner.stop();
3436
+ return;
3437
+ }
3438
+ switch (pkgEnvDetails.agent) {
3439
+ case NPM$b:
3440
+ {
3441
+ await npmFix(pkgEnvDetails, cwd);
3442
+ break;
3443
+ }
3444
+ case PNPM$7:
3445
+ {
3446
+ await pnpmFix(pkgEnvDetails, cwd);
3447
+ break;
3448
+ }
3449
+ }
3450
+ spinner.successAndStop('Socket.dev fix successful');
3451
+ }
3452
+
3453
+ const {
3454
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3455
+ } = constants;
3456
+ const config$t = {
3457
+ commandName: 'fix',
3458
+ description: 'Fix "fixable" Socket alerts',
3459
+ hidden: true,
3460
+ flags: {
3461
+ ...commonFlags
3462
+ },
3463
+ help: (command, config) => `
3464
+ Usage
3465
+ $ ${command}
3466
+
3467
+ Options
3468
+ ${getFlagListOutput(config.flags, 6)}
3469
+ `
3470
+ };
3471
+ const cmdFix = {
3472
+ description: config$t.description,
3473
+ hidden: config$t.hidden,
3474
+ run: run$t
3475
+ };
3476
+ async function run$t(argv, importMeta, {
3477
+ parentName
3478
+ }) {
3479
+ const cli = meowOrExit({
3480
+ argv,
3481
+ config: config$t,
3482
+ importMeta,
3483
+ parentName
3484
+ });
3485
+ if (cli.flags['dryRun']) {
3486
+ logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3487
+ return;
3488
+ }
3489
+ await runFix();
3490
+ }
3491
+
3492
+ async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
3493
+ const socketSdk = await shadowNpmInject.setupSdk(shadowNpmInject.getPublicToken());
3494
+ const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package');
3495
+ const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score');
3496
+ if (result.success === false) {
3497
+ return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result);
3498
+ }
3499
+ if (scoreResult.success === false) {
3500
+ return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult);
3501
+ }
3502
+ const severityCount = shadowNpmInject.getSeverityCount(result.data, includeAllIssues ? undefined : 'high');
3503
+ return {
3504
+ data: result.data,
3505
+ severityCount,
3506
+ score: scoreResult.data
3507
+ };
3508
+ }
3509
+
3510
+ const {
3511
+ NPM: NPM$a
3512
+ } = registryConstants;
3513
+ function formatScore(score) {
3514
+ if (score > 80) {
3515
+ return colors.green(`${score}`);
3516
+ } else if (score < 80 && score > 60) {
3517
+ return colors.yellow(`${score}`);
3518
+ }
3519
+ return colors.red(`${score}`);
3520
+ }
3521
+ function logPackageIssuesDetails(packageData, outputMarkdown) {
3522
+ const issueDetails = packageData.filter(d => d.value?.severity === shadowNpmInject.SEVERITY.critical || d.value?.severity === shadowNpmInject.SEVERITY.high);
3523
+ const uniqueIssueDetails = issueDetails.reduce((acc, issue) => {
3524
+ const {
3525
+ type
3526
+ } = issue;
3527
+ if (type) {
3528
+ const details = acc.get(type);
3529
+ if (details) {
3530
+ details.count += 1;
3531
+ } else {
3532
+ acc.set(type, {
3533
+ label: issue.value?.label ?? '',
3534
+ count: 1
3535
+ });
3536
+ }
3537
+ }
3538
+ return acc;
3539
+ }, new Map());
3540
+ const format = new shadowNpmInject.ColorOrMarkdown(outputMarkdown);
3541
+ for (const [type, details] of uniqueIssueDetails.entries()) {
3542
+ const issueWithLink = format.hyperlink(details.label, shadowNpmInject.getSocketDevAlertUrl(type), {
3543
+ fallbackToUrl: true
3544
+ });
3545
+ if (details.count === 1) {
3546
+ logger.logger.log(`- ${issueWithLink}`);
3547
+ } else {
3548
+ logger.logger.log(`- ${issueWithLink}: ${details.count}`);
3549
+ }
3550
+ }
3551
+ }
3552
+ function logPackageInfo({
3553
+ data,
3554
+ score,
3555
+ severityCount
3556
+ }, {
3557
+ name,
3558
+ outputKind,
3559
+ pkgName,
3560
+ pkgVersion
3561
+ }) {
3562
+ if (outputKind === 'json') {
3563
+ logger.logger.log(JSON.stringify(data, undefined, 2));
3564
+ return;
3038
3565
  }
3039
3566
  if (outputKind === 'markdown') {
3040
- logger.logger.log(`\n# Package report for ${pkgName}\n`);
3041
- logger.logger.log('Package report card:\n');
3567
+ logger.logger.log(commonTags.stripIndents`
3568
+ # Package report for ${pkgName}
3569
+
3570
+ Package report card:
3571
+ `);
3042
3572
  } else {
3043
- logger.logger.log(`\nPackage report card for ${pkgName}:\n`);
3573
+ logger.logger.log(`Package report card for ${pkgName}:`);
3044
3574
  }
3045
3575
  const scoreResult = {
3046
3576
  'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
@@ -3049,19 +3579,20 @@ function formatPackageInfo({
3049
3579
  Vulnerabilities: Math.floor(score.vulnerability.score * 100),
3050
3580
  License: Math.floor(score.license.score * 100)
3051
3581
  };
3582
+ logger.logger.log('\n');
3052
3583
  Object.entries(scoreResult).map(score => logger.logger.log(`- ${score[0]}: ${formatScore(score[1])}`));
3053
3584
  logger.logger.log('\n');
3054
- if (objectSome(severityCount)) {
3585
+ if (objects.hasKeys(severityCount)) {
3055
3586
  if (outputKind === 'markdown') {
3056
3587
  logger.logger.log('# Issues\n');
3057
3588
  }
3058
- logger.logger.log(`Package has these issues: ${formatSeverityCount(severityCount)}\n`);
3059
- formatPackageIssuesDetails(data, outputKind === 'markdown');
3589
+ logger.logger.log(`Package has these issues: ${shadowNpmInject.formatSeverityCount(severityCount)}\n`);
3590
+ logPackageIssuesDetails(data, outputKind === 'markdown');
3060
3591
  } else {
3061
3592
  logger.logger.log('Package has no issues');
3062
3593
  }
3063
3594
  const format = new shadowNpmInject.ColorOrMarkdown(outputKind === 'markdown');
3064
- const url = shadowNpmInject.getSocketDevPackageOverviewUrl(NPM$c, pkgName, pkgVersion);
3595
+ const url = shadowNpmInject.getSocketDevPackageOverviewUrl(NPM$a, pkgName, pkgVersion);
3065
3596
  logger.logger.log('\n');
3066
3597
  if (pkgVersion === 'latest') {
3067
3598
  logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, {
@@ -3072,49 +3603,11 @@ function formatPackageInfo({
3072
3603
  fallbackToUrl: true
3073
3604
  })}`);
3074
3605
  }
3075
- if (outputKind !== 'markdown') {
3076
- logger.logger.log(colors.dim(`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`));
3077
- } else {
3078
- logger.logger.log('');
3079
- }
3080
- }
3081
- function formatPackageIssuesDetails(packageData, outputMarkdown) {
3082
- const issueDetails = packageData.filter(d => d.value?.severity === 'high' || d.value?.severity === 'critical');
3083
- const uniqueIssues = issueDetails.reduce((acc, issue) => {
3084
- const {
3085
- type
3086
- } = issue;
3087
- if (type) {
3088
- if (acc[type] === undefined) {
3089
- acc[type] = {
3090
- label: issue.value?.label,
3091
- count: 1
3092
- };
3093
- } else {
3094
- acc[type].count += 1;
3095
- }
3096
- }
3097
- return acc;
3098
- }, {});
3099
- const format = new shadowNpmInject.ColorOrMarkdown(outputMarkdown);
3100
- for (const issue of Object.keys(uniqueIssues)) {
3101
- const issueWithLink = format.hyperlink(`${uniqueIssues[issue]?.label}`, shadowNpmInject.getSocketDevAlertUrl(issue), {
3102
- fallbackToUrl: true
3103
- });
3104
- if (uniqueIssues[issue]?.count === 1) {
3105
- logger.logger.log(`- ${issueWithLink}`);
3106
- } else {
3107
- logger.logger.log(`- ${issueWithLink}: ${uniqueIssues[issue]?.count}`);
3108
- }
3109
- }
3110
- }
3111
- function formatScore(score) {
3112
- if (score > 80) {
3113
- return colors.green(`${score}`);
3114
- } else if (score < 80 && score > 60) {
3115
- return colors.yellow(`${score}`);
3116
- }
3117
- return colors.red(`${score}`);
3606
+ if (outputKind !== 'markdown') {
3607
+ logger.logger.log(colors.dim(`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`));
3608
+ } else {
3609
+ logger.logger.log('');
3610
+ }
3118
3611
  }
3119
3612
 
3120
3613
  async function getPackageInfo({
@@ -3133,13 +3626,13 @@ async function getPackageInfo({
3133
3626
  const packageData = await fetchPackageInfo(pkgName, pkgVersion, includeAllIssues);
3134
3627
  spinner.successAndStop('Data fetched');
3135
3628
  if (packageData) {
3136
- formatPackageInfo(packageData, {
3629
+ logPackageInfo(packageData, {
3137
3630
  name: commandName,
3138
3631
  outputKind,
3139
3632
  pkgName,
3140
3633
  pkgVersion
3141
3634
  });
3142
- if (strict && objectSome(packageData.severityCount)) {
3635
+ if (strict && objects.hasKeys(packageData.severityCount)) {
3143
3636
  // Let NodeJS exit gracefully but with exit(1)
3144
3637
  process$1.exitCode = 1;
3145
3638
  }
@@ -3341,8 +3834,8 @@ async function run$r(argv, importMeta, {
3341
3834
  importMeta,
3342
3835
  parentName
3343
3836
  });
3344
- let apiBaseUrl = cli.flags['apiBaseUrl'];
3345
- let apiProxy = cli.flags['apiProxy'];
3837
+ const apiBaseUrl = cli.flags['apiBaseUrl'];
3838
+ const apiProxy = cli.flags['apiProxy'];
3346
3839
  if (cli.flags['dryRun']) {
3347
3840
  logger.logger.log(DRY_RUN_BAIL_TEXT$q);
3348
3841
  return;
@@ -3811,6 +4304,9 @@ const config$o = {
3811
4304
 
3812
4305
  Support is beta. Please report issues or give us feedback on what's missing.
3813
4306
 
4307
+ This is only for SBT. If your Scala setup uses gradle, please see the help
4308
+ sections for \`socket manifest gradle\` or \`socket cdxgen\`.
4309
+
3814
4310
  Examples
3815
4311
 
3816
4312
  $ ${command} ./build.sbt
@@ -4184,21 +4680,21 @@ async function run$l(argv, importMeta, {
4184
4680
  }
4185
4681
 
4186
4682
  const {
4187
- NPM: NPM$b
4683
+ NPM: NPM$9
4188
4684
  } = constants;
4189
4685
  async function wrapNpm(argv) {
4190
4686
  // Lazily access constants.distShadowNpmBinPath.
4191
4687
  const shadowBin = require(constants.distShadowNpmBinPath);
4192
- await shadowBin(NPM$b, argv);
4688
+ await shadowBin(NPM$9, argv);
4193
4689
  }
4194
4690
 
4195
4691
  const {
4196
4692
  DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k,
4197
- NPM: NPM$a
4693
+ NPM: NPM$8
4198
4694
  } = constants;
4199
4695
  const config$k = {
4200
4696
  commandName: 'npm',
4201
- description: `${NPM$a} wrapper functionality`,
4697
+ description: `${NPM$8} wrapper functionality`,
4202
4698
  hidden: false,
4203
4699
  flags: {},
4204
4700
  help: (command, _config) => `
@@ -4312,273 +4808,20 @@ async function run$i(argv, importMeta, {
4312
4808
  }
4313
4809
 
4314
4810
  const {
4315
- BUN: BUN$6,
4316
- NPM: NPM$9,
4317
- PNPM: PNPM$7,
4318
- VLT: VLT$6,
4319
- YARN_BERRY: YARN_BERRY$6,
4320
- YARN_CLASSIC: YARN_CLASSIC$6
4321
- } = constants;
4322
- function matchHumanStdout(stdout, name) {
4323
- return stdout.includes(` ${name}@`);
4324
- }
4325
- function matchQueryStdout(stdout, name) {
4326
- return stdout.includes(`"${name}"`);
4327
- }
4328
- const depsIncludesByAgent = new Map([[BUN$6, matchHumanStdout], [NPM$9, matchQueryStdout], [PNPM$7, matchQueryStdout], [VLT$6, matchQueryStdout], [YARN_BERRY$6, matchHumanStdout], [YARN_CLASSIC$6, matchHumanStdout]]);
4329
-
4330
- const {
4331
- BINARY_LOCK_EXT,
4332
- BUN: BUN$5,
4333
- LOCK_EXT: LOCK_EXT$1,
4334
- NPM: NPM$8,
4811
+ BUN: BUN$4,
4812
+ NPM: NPM$7,
4335
4813
  PNPM: PNPM$6,
4336
- VLT: VLT$5,
4337
- YARN,
4338
- YARN_BERRY: YARN_BERRY$5,
4814
+ VLT: VLT$4,
4815
+ YARN_BERRY: YARN_BERRY$4,
4339
4816
  YARN_CLASSIC: YARN_CLASSIC$5
4340
4817
  } = constants;
4341
- const AGENTS = [BUN$5, NPM$8, PNPM$6, YARN_BERRY$5, YARN_CLASSIC$5, VLT$5];
4342
- const binByAgent = {
4343
- __proto__: null,
4344
- [BUN$5]: BUN$5,
4345
- [NPM$8]: NPM$8,
4346
- [PNPM$6]: PNPM$6,
4347
- [YARN_BERRY$5]: YARN,
4348
- [YARN_CLASSIC$5]: YARN,
4349
- [VLT$5]: VLT$5
4350
- };
4351
- async function getAgentExecPath(agent) {
4352
- const binName = binByAgent[agent];
4353
- return (await which(binName, {
4354
- nothrow: true
4355
- })) ?? binName;
4356
- }
4357
- async function getAgentVersion(agentExecPath, cwd) {
4358
- let result;
4359
- try {
4360
- result = semver.coerce(
4361
- // All package managers support the "--version" flag.
4362
- (await spawn.spawn(agentExecPath, ['--version'], {
4363
- cwd
4364
- })).stdout) ?? undefined;
4365
- } catch {}
4366
- return result;
4367
- }
4368
-
4369
- // The order of LOCKS properties IS significant as it affects iteration order.
4370
- const LOCKS = {
4371
- [`bun${LOCK_EXT$1}`]: BUN$5,
4372
- [`bun${BINARY_LOCK_EXT}`]: BUN$5,
4373
- // If both package-lock.json and npm-shrinkwrap.json are present in the root
4374
- // of a project, npm-shrinkwrap.json will take precedence and package-lock.json
4375
- // will be ignored.
4376
- // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson
4377
- 'npm-shrinkwrap.json': NPM$8,
4378
- 'package-lock.json': NPM$8,
4379
- 'pnpm-lock.yaml': PNPM$6,
4380
- 'pnpm-lock.yml': PNPM$6,
4381
- [`yarn${LOCK_EXT$1}`]: YARN_CLASSIC$5,
4382
- 'vlt-lock.json': VLT$5,
4383
- // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:
4384
- // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles
4385
- //
4386
- // Unlike the other LOCKS keys this key contains a directory AND filename so
4387
- // it has to be handled differently.
4388
- 'node_modules/.package-lock.json': NPM$8
4389
- };
4390
- const readLockFileByAgent = (() => {
4391
- function wrapReader(reader) {
4392
- return async (...args) => {
4393
- try {
4394
- return await reader(...args);
4395
- } catch {}
4396
- return undefined;
4397
- };
4398
- }
4399
- const binaryReader = wrapReader(shadowNpmInject.readFileBinary);
4400
- const defaultReader = wrapReader(async lockPath => await shadowNpmInject.readFileUtf8(lockPath));
4401
- return {
4402
- [BUN$5]: wrapReader(async (lockPath, agentExecPath) => {
4403
- const ext = path.extname(lockPath);
4404
- if (ext === LOCK_EXT$1) {
4405
- return await defaultReader(lockPath);
4406
- }
4407
- if (ext === BINARY_LOCK_EXT) {
4408
- const lockBuffer = await binaryReader(lockPath);
4409
- if (lockBuffer) {
4410
- try {
4411
- return index_cjs.parse(lockBuffer);
4412
- } catch {}
4413
- }
4414
- // To print a Yarn lockfile to your console without writing it to disk
4415
- // use `bun bun.lockb`.
4416
- // https://bun.sh/guides/install/yarnlock
4417
- return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
4418
- }
4419
- return undefined;
4420
- }),
4421
- [NPM$8]: defaultReader,
4422
- [PNPM$6]: defaultReader,
4423
- [VLT$5]: defaultReader,
4424
- [YARN_BERRY$5]: defaultReader,
4425
- [YARN_CLASSIC$5]: defaultReader
4426
- };
4427
- })();
4428
- async function detectPackageEnvironment({
4429
- cwd = process$1.cwd(),
4430
- onUnknown
4431
- } = {}) {
4432
- let lockPath = await shadowNpmInject.findUp(Object.keys(LOCKS), {
4433
- cwd
4434
- });
4435
- let lockName = lockPath ? path.basename(lockPath) : undefined;
4436
- const isHiddenLockFile = lockName === '.package-lock.json';
4437
- const pkgJsonPath = lockPath ? path.resolve(lockPath, `${isHiddenLockFile ? '../' : ''}../package.json`) : await shadowNpmInject.findUp('package.json', {
4438
- cwd
4439
- });
4440
- const pkgPath = pkgJsonPath && fs.existsSync(pkgJsonPath) ? path.dirname(pkgJsonPath) : undefined;
4441
- const editablePkgJson = pkgPath ? await packages.readPackageJson(pkgPath, {
4442
- editable: true
4443
- }) : undefined;
4444
- const pkgJson = editablePkgJson?.content;
4445
- // Read Corepack `packageManager` field in package.json:
4446
- // https://nodejs.org/api/packages.html#packagemanager
4447
- const pkgManager = strings.isNonEmptyString(pkgJson?.packageManager) ? pkgJson.packageManager : undefined;
4448
- let agent;
4449
- let agentVersion;
4450
- if (pkgManager) {
4451
- const atSignIndex = pkgManager.lastIndexOf('@');
4452
- if (atSignIndex !== -1) {
4453
- const name = pkgManager.slice(0, atSignIndex);
4454
- const version = pkgManager.slice(atSignIndex + 1);
4455
- if (version && AGENTS.includes(name)) {
4456
- agent = name;
4457
- agentVersion = semver.coerce(version) ?? undefined;
4458
- }
4459
- }
4460
- }
4461
- if (agent === undefined && !isHiddenLockFile && typeof pkgJsonPath === 'string' && typeof lockName === 'string') {
4462
- agent = LOCKS[lockName];
4463
- }
4464
- if (agent === undefined) {
4465
- agent = NPM$8;
4466
- onUnknown?.(pkgManager);
4467
- }
4468
- const agentExecPath = await getAgentExecPath(agent);
4469
- const npmExecPath = agent === NPM$8 ? agentExecPath : await getAgentExecPath(NPM$8);
4470
- if (agentVersion === undefined) {
4471
- agentVersion = await getAgentVersion(agentExecPath, cwd);
4472
- }
4473
- if (agent === YARN_CLASSIC$5 && (agentVersion?.major ?? 0) > 1) {
4474
- agent = YARN_BERRY$5;
4475
- }
4476
- const targets = {
4477
- browser: false,
4478
- node: true
4479
- };
4480
- let lockSrc;
4481
- // Lazily access constants.maintainedNodeVersions.
4482
- let minimumNodeVersion = constants.maintainedNodeVersions.previous;
4483
- if (pkgJson) {
4484
- const browserField = pkgJson.browser;
4485
- if (strings.isNonEmptyString(browserField) || objects.isObjectObject(browserField)) {
4486
- targets.browser = true;
4487
- }
4488
- const nodeRange = pkgJson.engines?.['node'];
4489
- if (strings.isNonEmptyString(nodeRange)) {
4490
- const coerced = semver.coerce(nodeRange);
4491
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
4492
- minimumNodeVersion = coerced.version;
4493
- }
4494
- }
4495
- const browserslistQuery = pkgJson['browserslist'];
4496
- if (Array.isArray(browserslistQuery)) {
4497
- const browserslistTargets = browserslist(browserslistQuery).map(s => s.toLowerCase()).sort(sorts.naturalCompare);
4498
- const browserslistNodeTargets = browserslistTargets.filter(v => v.startsWith('node ')).map(v => v.slice(5 /*'node '.length*/));
4499
- if (!targets.browser && browserslistTargets.length) {
4500
- targets.browser = browserslistTargets.length !== browserslistNodeTargets.length;
4501
- }
4502
- if (browserslistNodeTargets.length) {
4503
- const coerced = semver.coerce(browserslistNodeTargets[0]);
4504
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
4505
- minimumNodeVersion = coerced.version;
4506
- }
4507
- }
4508
- }
4509
- // Lazily access constants.maintainedNodeVersions.
4510
- targets.node = constants.maintainedNodeVersions.some(v => semver.satisfies(v, `>=${minimumNodeVersion}`));
4511
- lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent[agent](lockPath, agentExecPath) : undefined;
4512
- } else {
4513
- lockName = undefined;
4514
- lockPath = undefined;
4515
- }
4516
- return {
4517
- agent,
4518
- agentExecPath,
4519
- agentVersion,
4520
- lockName,
4521
- lockPath,
4522
- lockSrc,
4523
- minimumNodeVersion,
4524
- npmExecPath,
4525
- pkgJson: editablePkgJson,
4526
- pkgPath,
4527
- supported: targets.browser || targets.node,
4528
- targets
4529
- };
4818
+ function matchLsCmdViewHumanStdout(stdout, name) {
4819
+ return stdout.includes(` ${name}@`);
4530
4820
  }
4531
-
4532
- const {
4533
- BUN: BUN$4,
4534
- VLT: VLT$4,
4535
- YARN_BERRY: YARN_BERRY$4
4536
- } = constants;
4537
- const COMMAND_TITLE$2 = 'Socket Optimize';
4538
- async function detectAndValidatePackageEnvironment(cwd, options) {
4539
- const {
4540
- logger,
4541
- prod
4542
- } = {
4543
- __proto__: null,
4544
- ...options
4545
- };
4546
- const details = await detectPackageEnvironment({
4547
- cwd,
4548
- onUnknown(pkgManager) {
4549
- logger?.warn(`${COMMAND_TITLE$2}: Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`);
4550
- }
4551
- });
4552
- if (!details.supported) {
4553
- logger?.fail(`${COMMAND_TITLE$2}: No supported Node or browser range detected`);
4554
- return;
4555
- }
4556
- if (details.agent === VLT$4) {
4557
- logger?.fail(`${COMMAND_TITLE$2}: ${details.agent} does not support overrides. Soon, though ⚡`);
4558
- return;
4559
- }
4560
- const lockName = details.lockName ?? 'lock file';
4561
- if (details.lockName === undefined || details.lockSrc === undefined) {
4562
- logger?.fail(`${COMMAND_TITLE$2}: No ${lockName} found`);
4563
- return;
4564
- }
4565
- if (details.lockSrc.trim() === '') {
4566
- logger?.fail(`${COMMAND_TITLE$2}: ${lockName} is empty`);
4567
- return;
4568
- }
4569
- if (details.pkgPath === undefined) {
4570
- logger?.fail(`${COMMAND_TITLE$2}: No package.json found`);
4571
- return;
4572
- }
4573
- if (prod && (details.agent === BUN$4 || details.agent === YARN_BERRY$4)) {
4574
- logger?.fail(`${COMMAND_TITLE$2}: --prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.toString()}` : ''}`);
4575
- return;
4576
- }
4577
- if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
4578
- logger?.warn(`${COMMAND_TITLE$2}: Package ${lockName} found at ${details.lockPath}`);
4579
- }
4580
- return details;
4821
+ function matchQueryCmdStdout(stdout, name) {
4822
+ return stdout.includes(`"${name}"`);
4581
4823
  }
4824
+ const depsIncludesByAgent = new Map([[BUN$4, matchLsCmdViewHumanStdout], [NPM$7, matchQueryCmdStdout], [PNPM$6, matchQueryCmdStdout], [VLT$4, matchQueryCmdStdout], [YARN_BERRY$4, matchLsCmdViewHumanStdout], [YARN_CLASSIC$5, matchLsCmdViewHumanStdout]]);
4582
4825
 
4583
4826
  function getDependencyEntries(pkgJson) {
4584
4827
  const {
@@ -4606,7 +4849,7 @@ function getDependencyEntries(pkgJson) {
4606
4849
 
4607
4850
  const {
4608
4851
  BUN: BUN$3,
4609
- NPM: NPM$7,
4852
+ NPM: NPM$6,
4610
4853
  OVERRIDES: OVERRIDES$1,
4611
4854
  PNPM: PNPM$5,
4612
4855
  RESOLUTIONS: RESOLUTIONS$1,
@@ -4627,7 +4870,7 @@ function getOverridesDataBun(pkgJson) {
4627
4870
  function getOverridesDataNpm(pkgJson) {
4628
4871
  const overrides = pkgJson?.[OVERRIDES$1] ?? {};
4629
4872
  return {
4630
- type: NPM$7,
4873
+ type: NPM$6,
4631
4874
  overrides
4632
4875
  };
4633
4876
  }
@@ -4668,7 +4911,7 @@ function getOverridesDataClassic(pkgJson) {
4668
4911
  overrides
4669
4912
  };
4670
4913
  }
4671
- const overridesDataByAgent = new Map([[BUN$3, getOverridesDataBun], [NPM$7, getOverridesDataNpm], [PNPM$5, getOverridesDataPnpm], [VLT$3, getOverridesDataVlt], [YARN_BERRY$3, getOverridesDataYarn], [YARN_CLASSIC$4, getOverridesDataClassic]]);
4914
+ const overridesDataByAgent = new Map([[BUN$3, getOverridesDataBun], [NPM$6, getOverridesDataNpm], [PNPM$5, getOverridesDataPnpm], [VLT$3, getOverridesDataVlt], [YARN_BERRY$3, getOverridesDataYarn], [YARN_CLASSIC$4, getOverridesDataClassic]]);
4672
4915
 
4673
4916
  const {
4674
4917
  PNPM: PNPM$4
@@ -4716,26 +4959,26 @@ function workspacePatternToGlobPattern(workspace) {
4716
4959
  const {
4717
4960
  BUN: BUN$2,
4718
4961
  LOCK_EXT,
4719
- NPM: NPM$6,
4962
+ NPM: NPM$5,
4720
4963
  PNPM: PNPM$3,
4721
4964
  VLT: VLT$2,
4722
4965
  YARN_BERRY: YARN_BERRY$2,
4723
4966
  YARN_CLASSIC: YARN_CLASSIC$3
4724
4967
  } = constants;
4725
- function lockIncludesNpm(lockSrc, name) {
4968
+ function includesNpm(lockSrc, name) {
4726
4969
  // Detects the package name in the following cases:
4727
4970
  // "name":
4728
4971
  return lockSrc.includes(`"${name}":`);
4729
4972
  }
4730
- function lockIncludesBun(lockSrc, name, lockName) {
4973
+ function includesBun(lockSrc, name, lockName) {
4731
4974
  // This is a bit counterintuitive. When lockName ends with a .lockb
4732
4975
  // we treat it as a yarn.lock. When lockName ends with a .lock we
4733
4976
  // treat it as a package-lock.json. The bun.lock format is not identical
4734
4977
  // package-lock.json, however it close enough for npmLockIncludes to work.
4735
- const lockScanner = lockName?.endsWith(LOCK_EXT) ? lockIncludesNpm : lockIncludesYarn;
4736
- return lockScanner(lockSrc, name);
4978
+ const lockfileScanner = lockName?.endsWith(LOCK_EXT) ? includesNpm : includesYarn;
4979
+ return lockfileScanner(lockSrc, name);
4737
4980
  }
4738
- function lockIncludesPnpm(lockSrc, name) {
4981
+ function includesPnpm(lockSrc, name) {
4739
4982
  const escapedName = regexps.escapeRegExp(name);
4740
4983
  return new RegExp(
4741
4984
  // Detects the package name in the following cases:
@@ -4745,12 +4988,12 @@ function lockIncludesPnpm(lockSrc, name) {
4745
4988
  // name@
4746
4989
  `(?<=^\\s*)(?:(['/])${escapedName}\\1|${escapedName}(?=[:@]))`, 'm').test(lockSrc);
4747
4990
  }
4748
- function lockIncludesVlt(lockSrc, name) {
4991
+ function includesVlt(lockSrc, name) {
4749
4992
  // Detects the package name in the following cases:
4750
4993
  // "name"
4751
4994
  return lockSrc.includes(`"${name}"`);
4752
4995
  }
4753
- function lockIncludesYarn(lockSrc, name) {
4996
+ function includesYarn(lockSrc, name) {
4754
4997
  const escapedName = regexps.escapeRegExp(name);
4755
4998
  return new RegExp(
4756
4999
  // Detects the package name in the following cases:
@@ -4760,11 +5003,11 @@ function lockIncludesYarn(lockSrc, name) {
4760
5003
  // , name@
4761
5004
  `(?<=(?:^\\s*|,\\s*)"?)${escapedName}(?=@)`, 'm').test(lockSrc);
4762
5005
  }
4763
- const lockIncludesByAgent = new Map([[BUN$2, lockIncludesBun], [NPM$6, lockIncludesNpm], [PNPM$3, lockIncludesPnpm], [VLT$2, lockIncludesVlt], [YARN_BERRY$2, lockIncludesYarn], [YARN_CLASSIC$3, lockIncludesYarn]]);
5006
+ const lockfileIncludesByAgent = new Map([[BUN$2, includesBun], [NPM$5, includesNpm], [PNPM$3, includesPnpm], [VLT$2, includesVlt], [YARN_BERRY$2, includesYarn], [YARN_CLASSIC$3, includesYarn]]);
4764
5007
 
4765
5008
  const {
4766
5009
  BUN: BUN$1,
4767
- NPM: NPM$5,
5010
+ NPM: NPM$4,
4768
5011
  PNPM: PNPM$2,
4769
5012
  VLT: VLT$1,
4770
5013
  YARN_BERRY: YARN_BERRY$1,
@@ -4800,11 +5043,11 @@ function cleanupQueryStdout(stdout) {
4800
5043
  }
4801
5044
  return JSON.stringify([...names], null, 2);
4802
5045
  }
4803
- function parseableToQueryStdout(stdout) {
5046
+ function parsableToQueryStdout(stdout) {
4804
5047
  if (stdout === '') {
4805
5048
  return '';
4806
5049
  }
4807
- // Convert the parseable stdout into a json array of unique names.
5050
+ // Convert the parsable stdout into a json array of unique names.
4808
5051
  // The matchAll regexp looks for a forward (posix) or backward (win32) slash
4809
5052
  // and matches one or more non-slashes until the newline.
4810
5053
  const names = new Set(stdout.matchAll(/(?<=[/\\])[^/\\]+(?=\n)/g));
@@ -4834,7 +5077,7 @@ async function lsNpm(agentExecPath, cwd) {
4834
5077
  }
4835
5078
  async function lsPnpm(agentExecPath, cwd, options) {
4836
5079
  const npmExecPath = options?.npmExecPath;
4837
- if (npmExecPath && npmExecPath !== NPM$5) {
5080
+ if (npmExecPath && npmExecPath !== NPM$4) {
4838
5081
  const result = await npmQuery(npmExecPath, cwd);
4839
5082
  if (result) {
4840
5083
  return result;
@@ -4842,15 +5085,19 @@ async function lsPnpm(agentExecPath, cwd, options) {
4842
5085
  }
4843
5086
  let stdout = '';
4844
5087
  try {
4845
- stdout = (await spawn.spawn(agentExecPath, ['ls', '--parseable', '--prod', '--depth', 'Infinity'], {
5088
+ stdout = (await spawn.spawn(agentExecPath,
5089
+ // Pnpm uses the alternative spelling of parsable.
5090
+ // https://en.wiktionary.org/wiki/parsable
5091
+ ['ls', '--parseable', '--prod', '--depth', 'Infinity'], {
4846
5092
  cwd
4847
5093
  })).stdout;
4848
5094
  } catch {}
4849
- return parseableToQueryStdout(stdout);
5095
+ return parsableToQueryStdout(stdout);
4850
5096
  }
4851
5097
  async function lsVlt(agentExecPath, cwd) {
4852
5098
  let stdout = '';
4853
5099
  try {
5100
+ // See https://docs.vlt.sh/cli/commands/list#options.
4854
5101
  stdout = (await spawn.spawn(agentExecPath, ['ls', '--view', 'human', ':not(.dev)'], {
4855
5102
  cwd
4856
5103
  })).stdout;
@@ -4881,20 +5128,39 @@ async function lsYarnClassic(agentExecPath, cwd) {
4881
5128
  } catch {}
4882
5129
  return '';
4883
5130
  }
4884
- const lsByAgent = {
4885
- // @ts-ignore
4886
- __proto__: null,
4887
- [BUN$1]: lsBun,
4888
- [NPM$5]: lsNpm,
4889
- [PNPM$2]: lsPnpm,
4890
- [VLT$1]: lsVlt,
4891
- [YARN_BERRY$1]: lsYarnBerry,
4892
- [YARN_CLASSIC$2]: lsYarnClassic
4893
- };
5131
+ const lsByAgent = new Map([[BUN$1, lsBun], [NPM$4, lsNpm], [PNPM$2, lsPnpm], [VLT$1, lsVlt], [YARN_BERRY$1, lsYarnBerry], [YARN_CLASSIC$2, lsYarnClassic]]);
5132
+
5133
+ const {
5134
+ NPM: NPM$3
5135
+ } = constants;
5136
+ const COMMAND_TITLE$1 = 'Socket Optimize';
5137
+ async function updateLockfile(pkgEnvDetails, options) {
5138
+ const {
5139
+ logger,
5140
+ spinner
5141
+ } = {
5142
+ __proto__: null,
5143
+ ...options
5144
+ };
5145
+ spinner?.start(`Updating ${pkgEnvDetails.lockName}...`);
5146
+ try {
5147
+ await runAgentInstall(pkgEnvDetails, {
5148
+ spinner
5149
+ });
5150
+ spinner?.stop();
5151
+ if (pkgEnvDetails.agent === NPM$3) {
5152
+ logger?.log(`💡 Re-run ${COMMAND_TITLE$1} whenever ${pkgEnvDetails.lockName} changes.\n This can be skipped once npm v11.2.0 is released.`);
5153
+ }
5154
+ } catch (e) {
5155
+ spinner?.stop();
5156
+ logger?.fail(`${COMMAND_TITLE$1}: ${pkgEnvDetails.agent} install failed to update ${pkgEnvDetails.lockName}`);
5157
+ logger?.error(e);
5158
+ }
5159
+ }
4894
5160
 
4895
5161
  const {
4896
5162
  BUN,
4897
- NPM: NPM$4,
5163
+ NPM: NPM$2,
4898
5164
  OVERRIDES,
4899
5165
  PNPM: PNPM$1,
4900
5166
  RESOLUTIONS,
@@ -4914,7 +5180,9 @@ function getHighestEntryIndex(entries, keys) {
4914
5180
  return getEntryIndexes(entries, keys).at(-1) ?? -1;
4915
5181
  }
4916
5182
  function updatePkgJson(editablePkgJson, field, value) {
4917
- const pkgJson = editablePkgJson.content;
5183
+ const {
5184
+ content: pkgJson
5185
+ } = editablePkgJson;
4918
5186
  const oldValue = pkgJson[field];
4919
5187
  if (oldValue) {
4920
5188
  // The field already exists so we simply update the field value.
@@ -4998,124 +5266,7 @@ function updateResolutions(editablePkgJson, overrides) {
4998
5266
  function pnpmUpdatePkgJson(editablePkgJson, overrides) {
4999
5267
  updatePkgJson(editablePkgJson, PNPM_FIELD_NAME, overrides);
5000
5268
  }
5001
- const updateManifestByAgent = new Map([[BUN, updateResolutions], [NPM$4, updateOverrides], [PNPM$1, pnpmUpdatePkgJson], [VLT, updateOverrides], [YARN_BERRY, updateResolutions], [YARN_CLASSIC$1, updateResolutions]]);
5002
-
5003
- const {
5004
- SOCKET_IPC_HANDSHAKE
5005
- } = constants;
5006
- function safeNpmInstall(options) {
5007
- const {
5008
- args = [],
5009
- ipc,
5010
- spinner,
5011
- ...spawnOptions
5012
- } = {
5013
- __proto__: null,
5014
- ...options
5015
- };
5016
- const terminatorPos = args.indexOf('--');
5017
- const npmArgs = (terminatorPos === -1 ? args : args.slice(0, terminatorPos)).filter(a => !npm.isAuditFlag(a) && !npm.isFundFlag(a) && !npm.isProgressFlag(a));
5018
- const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
5019
- const useIpc = objects.isObject(ipc);
5020
- const useDebug = debug.isDebug();
5021
- const isSilent = !useDebug && !npmArgs.some(npm.isLoglevelFlag);
5022
- const spawnPromise = spawn.spawn(
5023
- // Lazily access constants.execPath.
5024
- constants.execPath, [
5025
- // Lazily access constants.nodeNoWarningsFlags.
5026
- ...constants.nodeNoWarningsFlags, '--require',
5027
- // Lazily access constants.distShadowNpmInjectPath.
5028
- constants.distShadowNpmInjectPath, shadowNpmPaths.getNpmBinPath(), 'install',
5029
- // Even though the '--silent' flag is passed npm will still run through
5030
- // code paths for 'audit' and 'fund' unless '--no-audit' and '--no-fund'
5031
- // flags are passed.
5032
- '--no-audit', '--no-fund',
5033
- // Add `--no-progress` and `--silent` flags to fix input being swallowed
5034
- // by the spinner when running the command with recent versions of npm.
5035
- '--no-progress',
5036
- // Add the '--silent' flag if a loglevel flag is not provided and the
5037
- // SOCKET_CLI_DEBUG environment variable is not truthy.
5038
- ...(isSilent ? ['--silent'] : []), ...npmArgs, ...otherArgs], {
5039
- spinner,
5040
- // Set stdio to include 'ipc'.
5041
- // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166
5042
- // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.
5043
- stdio: isSilent ?
5044
- // 'ignore'
5045
- useIpc ? ['ignore', 'ignore', 'ignore', 'ipc'] : 'ignore' :
5046
- // 'inherit'
5047
- useIpc ? [0, 1, 2, 'ipc'] : 'inherit',
5048
- ...spawnOptions,
5049
- env: {
5050
- ...process$1.env,
5051
- ...spawnOptions.env
5052
- }
5053
- });
5054
- if (useIpc) {
5055
- spawnPromise.process.send({
5056
- [SOCKET_IPC_HANDSHAKE]: ipc
5057
- });
5058
- }
5059
- return spawnPromise;
5060
- }
5061
-
5062
- const {
5063
- NPM: NPM$3,
5064
- abortSignal
5065
- } = constants;
5066
- function runAgentInstall(agent, agentExecPath, options) {
5067
- // All package managers support the "install" command.
5068
- if (agent === NPM$3) {
5069
- return safeNpmInstall(options);
5070
- }
5071
- const {
5072
- args = [],
5073
- spinner,
5074
- ...spawnOptions
5075
- } = {
5076
- __proto__: null,
5077
- ...options
5078
- };
5079
- const isSilent = !debug.isDebug();
5080
- return spawn.spawn(agentExecPath, ['install', ...args], {
5081
- signal: abortSignal,
5082
- spinner,
5083
- stdio: isSilent ? 'ignore' : 'inherit',
5084
- ...spawnOptions,
5085
- env: {
5086
- ...process.env,
5087
- ...spawnOptions.env
5088
- }
5089
- });
5090
- }
5091
-
5092
- const {
5093
- NPM: NPM$2
5094
- } = constants;
5095
- const COMMAND_TITLE$1 = 'Socket Optimize';
5096
- async function updatePackageLockJson(pkgEnvDetails, options) {
5097
- const {
5098
- logger,
5099
- spinner
5100
- } = {
5101
- __proto__: null,
5102
- ...options
5103
- };
5104
- spinner?.start(`Updating ${pkgEnvDetails.lockName}...`);
5105
- try {
5106
- await runAgentInstall(pkgEnvDetails.agent, pkgEnvDetails.agentExecPath, {
5107
- spinner
5108
- });
5109
- spinner?.stop();
5110
- if (pkgEnvDetails.agent === NPM$2) {
5111
- logger?.log(`💡 Re-run ${COMMAND_TITLE$1} whenever ${pkgEnvDetails.lockName} changes.\n This can be skipped once npm v11.2.0 is released.`);
5112
- }
5113
- } catch (e) {
5114
- spinner?.stop();
5115
- logger?.fail(`${COMMAND_TITLE$1}: ${pkgEnvDetails.agent} install failed to update ${pkgEnvDetails.lockName}`);
5116
- logger?.error(e);
5117
- }
5118
- }
5269
+ const updateManifestByAgent = new Map([[BUN, updateResolutions], [NPM$2, updateOverrides], [PNPM$1, pnpmUpdatePkgJson], [VLT, updateOverrides], [YARN_BERRY, updateResolutions], [YARN_CLASSIC$1, updateResolutions]]);
5119
5270
 
5120
5271
  const {
5121
5272
  NPM: NPM$1,
@@ -5124,51 +5275,6 @@ const {
5124
5275
  } = constants;
5125
5276
  const COMMAND_TITLE = 'Socket Optimize';
5126
5277
  const manifestNpmOverrides = registry.getManifestData(NPM$1);
5127
- async function applyOptimization(cwd, pin, prod) {
5128
- const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {
5129
- logger: logger.logger,
5130
- prod
5131
- });
5132
- if (!pkgEnvDetails) {
5133
- return;
5134
- }
5135
- // Lazily access constants.spinner.
5136
- const {
5137
- spinner
5138
- } = constants;
5139
- spinner.start('Socket optimizing...');
5140
- const state = await addOverrides(pkgEnvDetails.pkgPath, pkgEnvDetails, {
5141
- logger: logger.logger,
5142
- pin,
5143
- prod,
5144
- spinner
5145
- });
5146
- spinner.stop();
5147
- const addedCount = state.added.size;
5148
- const updatedCount = state.updated.size;
5149
- const pkgJsonChanged = addedCount > 0 || updatedCount > 0;
5150
- if (pkgJsonChanged) {
5151
- if (updatedCount > 0) {
5152
- logger.logger?.log(`${createActionMessage('Updated', updatedCount, state.updatedInWorkspaces.size)}${addedCount ? '.' : '🚀'}`);
5153
- }
5154
- if (addedCount > 0) {
5155
- logger.logger?.log(`${createActionMessage('Added', addedCount, state.addedInWorkspaces.size)} 🚀`);
5156
- }
5157
- } else {
5158
- logger.logger?.log('Congratulations! Already Socket.dev optimized 🎉');
5159
- }
5160
- if (pkgEnvDetails.agent === NPM$1 || pkgJsonChanged) {
5161
- // Always update package-lock.json until the npm overrides PR lands:
5162
- // https://github.com/npm/cli/pull/8089
5163
- await updatePackageLockJson(pkgEnvDetails, {
5164
- logger: logger.logger,
5165
- spinner
5166
- });
5167
- }
5168
- }
5169
- function createActionMessage(verb, overrideCount, workspaceCount) {
5170
- return `${verb} ${overrideCount} Socket.dev optimized ${words.pluralize('override', overrideCount)}${workspaceCount ? ` in ${workspaceCount} ${words.pluralize('workspace', workspaceCount)}` : ''}`;
5171
- }
5172
5278
  async function addOverrides(pkgPath, pkgEnvDetails, options) {
5173
5279
  const {
5174
5280
  agent,
@@ -5214,14 +5320,14 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5214
5320
  state.warnedPnpmWorkspaceRequiresNpm = true;
5215
5321
  logger?.warn(`${COMMAND_TITLE}: pnpm workspace support requires \`npm ls\`, falling back to \`pnpm list\``);
5216
5322
  }
5217
- const thingToScan = isLockScanned ? lockSrc : await lsByAgent[agent](agentExecPath, pkgPath, {
5323
+ const thingToScan = isLockScanned ? lockSrc : await lsByAgent.get(agent)(agentExecPath, pkgPath, {
5218
5324
  npmExecPath
5219
5325
  });
5220
5326
  // The AgentDepsIncludesFn and AgentLockIncludesFn types overlap in their
5221
5327
  // first two parameters. AgentLockIncludesFn accepts an optional third
5222
5328
  // parameter which AgentDepsIncludesFn will ignore so we cast thingScanner
5223
5329
  // as an AgentLockIncludesFn type.
5224
- const thingScanner = isLockScanned ? lockIncludesByAgent.get(agent) : depsIncludesByAgent.get(agent);
5330
+ const thingScanner = isLockScanned ? lockfileIncludesByAgent.get(agent) : depsIncludesByAgent.get(agent);
5225
5331
  const depEntries = getDependencyEntries(pkgJson);
5226
5332
  const overridesDataObjects = [];
5227
5333
  if (pkgJson['private'] || isWorkspace) {
@@ -5347,6 +5453,51 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5347
5453
  }
5348
5454
  return state;
5349
5455
  }
5456
+ function createActionMessage(verb, overrideCount, workspaceCount) {
5457
+ return `${verb} ${overrideCount} Socket.dev optimized ${words.pluralize('override', overrideCount)}${workspaceCount ? ` in ${workspaceCount} ${words.pluralize('workspace', workspaceCount)}` : ''}`;
5458
+ }
5459
+ async function applyOptimization(cwd, pin, prod) {
5460
+ const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {
5461
+ logger: logger.logger,
5462
+ prod
5463
+ });
5464
+ if (!pkgEnvDetails) {
5465
+ return;
5466
+ }
5467
+ // Lazily access constants.spinner.
5468
+ const {
5469
+ spinner
5470
+ } = constants;
5471
+ spinner.start('Socket optimizing...');
5472
+ const state = await addOverrides(pkgEnvDetails.pkgPath, pkgEnvDetails, {
5473
+ logger: logger.logger,
5474
+ pin,
5475
+ prod,
5476
+ spinner
5477
+ });
5478
+ spinner.stop();
5479
+ const addedCount = state.added.size;
5480
+ const updatedCount = state.updated.size;
5481
+ const pkgJsonChanged = addedCount > 0 || updatedCount > 0;
5482
+ if (pkgJsonChanged) {
5483
+ if (updatedCount > 0) {
5484
+ logger.logger?.log(`${createActionMessage('Updated', updatedCount, state.updatedInWorkspaces.size)}${addedCount ? '.' : '🚀'}`);
5485
+ }
5486
+ if (addedCount > 0) {
5487
+ logger.logger?.log(`${createActionMessage('Added', addedCount, state.addedInWorkspaces.size)} 🚀`);
5488
+ }
5489
+ } else {
5490
+ logger.logger?.log('Congratulations! Already Socket.dev optimized 🎉');
5491
+ }
5492
+ if (pkgEnvDetails.agent === NPM$1 || pkgJsonChanged) {
5493
+ // Always update package-lock.json until the npm overrides PR lands:
5494
+ // https://github.com/npm/cli/pull/8089
5495
+ await updateLockfile(pkgEnvDetails, {
5496
+ logger: logger.logger,
5497
+ spinner
5498
+ });
5499
+ }
5500
+ }
5350
5501
 
5351
5502
  const {
5352
5503
  DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
@@ -5727,8 +5878,8 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5727
5878
  spinner.error('Report result deemed unhealthy for project');
5728
5879
  }
5729
5880
  } else if (!result.data.healthy) {
5730
- const severityCount = getSeverityCount(result.data.issues, includeAllIssues ? undefined : 'high');
5731
- const issueSummary = formatSeverityCount(severityCount);
5881
+ const severityCount = shadowNpmInject.getSeverityCount(result.data.issues, includeAllIssues ? undefined : 'high');
5882
+ const issueSummary = shadowNpmInject.formatSeverityCount(severityCount);
5732
5883
  spinner.success(`Report has these issues: ${issueSummary}`);
5733
5884
  } else {
5734
5885
  spinner.success('Report has no issues');
@@ -7029,7 +7180,7 @@ async function run$6(argv, importMeta, {
7029
7180
  });
7030
7181
  const [orgSlug = '', ...targets] = cli.input;
7031
7182
  const cwd = cli.flags['cwd'] && cli.flags['cwd'] !== 'process.cwd()' ? String(cli.flags['cwd']) : process$1.cwd();
7032
- let {
7183
+ const {
7033
7184
  branch: branchName,
7034
7185
  repo: repoName
7035
7186
  } = cli.flags;
@@ -7624,11 +7775,36 @@ const cmdScan = {
7624
7775
  }
7625
7776
  };
7626
7777
 
7778
+ // Note: Widgets does not seem to actually work as code :'(
7779
+
7627
7780
  async function getThreatFeed({
7781
+ direction,
7782
+ ecosystem,
7783
+ filter,
7784
+ outputKind,
7785
+ page,
7786
+ perPage
7787
+ }) {
7788
+ const apiToken = shadowNpmInject.getDefaultToken();
7789
+ if (!apiToken) {
7790
+ throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7791
+ }
7792
+ await getThreatFeedWithToken({
7793
+ apiToken,
7794
+ direction,
7795
+ ecosystem,
7796
+ filter,
7797
+ outputKind,
7798
+ page,
7799
+ perPage
7800
+ });
7801
+ }
7802
+ async function getThreatFeedWithToken({
7628
7803
  apiToken,
7629
7804
  direction,
7805
+ ecosystem,
7630
7806
  filter,
7631
- outputJson,
7807
+ outputKind,
7632
7808
  page,
7633
7809
  perPage
7634
7810
  }) {
@@ -7636,17 +7812,12 @@ async function getThreatFeed({
7636
7812
  const {
7637
7813
  spinner
7638
7814
  } = constants;
7639
- spinner.start('Looking up the threat feed');
7640
- const formattedQueryParams = formatQueryParams({
7641
- per_page: perPage,
7642
- page,
7643
- direction,
7644
- filter
7645
- }).join('&');
7646
- const response = await queryAPI(`threat-feed?${formattedQueryParams}`, apiToken);
7815
+ const queryParams = new URLSearchParams([['direction', direction], ['ecosystem', ecosystem], ['filter', filter], ['page', page], ['per_page', String(perPage)]]);
7816
+ spinner.start('Fetching Threat Feed data...');
7817
+ const response = await queryAPI(`threat-feed?${queryParams}`, apiToken);
7647
7818
  const data = await response.json();
7648
- spinner.stop();
7649
- if (outputJson) {
7819
+ spinner.stop('Threat feed data fetched');
7820
+ if (outputKind === 'json') {
7650
7821
  logger.logger.log(data);
7651
7822
  return;
7652
7823
  }
@@ -7659,47 +7830,98 @@ async function getThreatFeed({
7659
7830
  interactive: 'true',
7660
7831
  label: 'Threat feed',
7661
7832
  width: '100%',
7662
- height: '100%',
7833
+ height: '70%',
7834
+ // Changed from 100% to 70%
7663
7835
  border: {
7664
7836
  type: 'line',
7665
7837
  fg: 'cyan'
7666
7838
  },
7667
- columnSpacing: 3,
7668
- //in chars
7669
- columnWidth: [9, 30, 10, 17, 13, 100] /*in chars*/
7839
+ columnWidth: [10, 30, 20, 18, 15, 200],
7840
+ // TODO: the truncation doesn't seem to work too well yet but when we add
7841
+ // `pad` alignment fails, when we extend columnSpacing alignment fails
7842
+ columnSpacing: 1,
7843
+ truncate: '_'
7844
+ });
7845
+
7846
+ // Create details box at the bottom
7847
+ const detailsBox = new BoxWidget({
7848
+ bottom: 0,
7849
+ height: '30%',
7850
+ width: '100%',
7851
+ border: {
7852
+ type: 'line',
7853
+ fg: 'cyan'
7854
+ },
7855
+ label: 'Details',
7856
+ content: 'Use arrow keys to navigate. Press Enter to select a threat. Press q to exit.',
7857
+ style: {
7858
+ fg: 'white'
7859
+ }
7670
7860
  });
7671
7861
 
7672
7862
  // allow control the table with the keyboard
7673
7863
  table.focus();
7674
7864
  screen.append(table);
7865
+ screen.append(detailsBox);
7675
7866
  const formattedOutput = formatResults(data.results);
7867
+ const descriptions = data.results.map(d => d.description);
7676
7868
  table.setData({
7677
- headers: ['Ecosystem', 'Name', 'Version', 'Threat type', 'Detected at', 'Details'],
7869
+ headers: [' Ecosystem', ' Name', ' Version', ' Threat type', ' Detected at', ' Details'],
7678
7870
  data: formattedOutput
7679
7871
  });
7872
+
7873
+ // Update details box when selection changes
7874
+ table.rows.on('select item', () => {
7875
+ const selectedIndex = table.rows.selected;
7876
+ if (selectedIndex !== undefined && selectedIndex >= 0) {
7877
+ const selectedRow = formattedOutput[selectedIndex];
7878
+ if (selectedRow) {
7879
+ // Note: the spacing works around issues with the table; it refuses to pad!
7880
+ detailsBox.setContent(`Ecosystem: ${selectedRow[0]}\n` + `Name: ${selectedRow[1]}\n` + `Version:${selectedRow[2]}\n` + `Threat type:${selectedRow[3]}\n` + `Detected at:${selectedRow[4]}\n` + `Details: ${selectedRow[5]}\n` + `Description: ${descriptions[selectedIndex]}`);
7881
+ screen.render();
7882
+ }
7883
+ }
7884
+ });
7680
7885
  screen.render();
7681
7886
  screen.key(['escape', 'q', 'C-c'], () => process$1.exit(0));
7887
+ screen.key(['return'], () => {
7888
+ const selectedIndex = table.rows.selected;
7889
+ screen.destroy();
7890
+ const selectedRow = formattedOutput[selectedIndex];
7891
+ console.log(selectedRow);
7892
+ });
7682
7893
  }
7683
7894
  function formatResults(data) {
7684
7895
  return data.map(d => {
7685
7896
  const ecosystem = d.purl.split('pkg:')[1].split('/')[0];
7686
7897
  const name = d.purl.split('/')[1].split('@')[0];
7687
7898
  const version = d.purl.split('@')[1];
7688
- const timeStart = new Date(d.createdAt).getMilliseconds();
7689
- const timeEnd = Date.now();
7690
- const diff = getHourDiff(timeStart, timeEnd);
7691
- const hourDiff = diff > 0 ? `${diff} hours ago` : `${getMinDiff(timeStart, timeEnd)} minutes ago`;
7692
- return [ecosystem, decodeURIComponent(name), version, d.threatType, hourDiff, d.locationHtmlUrl];
7899
+ const timeDiff = msAtHome(d.createdAt);
7900
+
7901
+ // Note: the spacing works around issues with the table; it refuses to pad!
7902
+ return [ecosystem, decodeURIComponent(name), ` ${version}`, ` ${d.threatType}`, ` ${timeDiff}`, d.locationHtmlUrl];
7693
7903
  });
7694
7904
  }
7695
- function formatQueryParams(params) {
7696
- return Object.entries(params).map(entry => `${entry[0]}=${entry[1]}`);
7697
- }
7698
- function getHourDiff(start, end) {
7699
- return Math.floor((end - start) / 3600000);
7700
- }
7701
- function getMinDiff(start, end) {
7702
- return Math.floor((end - start) / 60000);
7905
+ function msAtHome(isoTimeStamp) {
7906
+ const timeStart = Date.parse(isoTimeStamp);
7907
+ const timeEnd = Date.now();
7908
+ const rtf = new Intl.RelativeTimeFormat('en', {
7909
+ numeric: 'always',
7910
+ style: 'short'
7911
+ });
7912
+ const delta = timeEnd - timeStart;
7913
+ if (delta < 60 * 60 * 1000) {
7914
+ return rtf.format(-Math.round(delta / (60 * 1000)), 'minute');
7915
+ // return Math.round(delta / (60 * 1000)) + ' min ago'
7916
+ } else if (delta < 24 * 60 * 60 * 1000) {
7917
+ return rtf.format(-(delta / (60 * 60 * 1000)).toFixed(1), 'hour');
7918
+ // return (delta / (60 * 60 * 1000)).toFixed(1) + ' hr ago'
7919
+ } else if (delta < 7 * 24 * 60 * 60 * 1000) {
7920
+ return rtf.format(-(delta / (24 * 60 * 60 * 1000)).toFixed(1), 'day');
7921
+ // return (delta / (24 * 60 * 60 * 1000)).toFixed(1) + ' day ago'
7922
+ } else {
7923
+ return isoTimeStamp.slice(0, 10);
7924
+ }
7703
7925
  }
7704
7926
 
7705
7927
  const {
@@ -7707,7 +7929,7 @@ const {
7707
7929
  } = constants;
7708
7930
  const config$1 = {
7709
7931
  commandName: 'threat-feed',
7710
- description: 'Look up the threat feed',
7932
+ description: '[beta] View the threat feed',
7711
7933
  hidden: false,
7712
7934
  flags: {
7713
7935
  ...commonFlags,
@@ -7730,6 +7952,12 @@ const config$1 = {
7730
7952
  default: 'desc',
7731
7953
  description: 'Order asc or desc by the createdAt attribute.'
7732
7954
  },
7955
+ eco: {
7956
+ type: 'string',
7957
+ shortFlag: 'e',
7958
+ default: '',
7959
+ description: 'Only show threats for a particular ecosystem'
7960
+ },
7733
7961
  filter: {
7734
7962
  type: 'string',
7735
7963
  shortFlag: 'f',
@@ -7741,9 +7969,35 @@ const config$1 = {
7741
7969
  Usage
7742
7970
  $ ${command}
7743
7971
 
7972
+ This feature requires a Threat Feed license. Please contact
7973
+ sales@socket.dev if you are interested in purchasing this access.
7974
+
7744
7975
  Options
7745
7976
  ${getFlagListOutput(config.flags, 6)}
7746
7977
 
7978
+ Valid filters:
7979
+
7980
+ - anom Anomaly
7981
+ - c Do not filter
7982
+ - fp False Positives
7983
+ - joke Joke / Fake
7984
+ - mal Malware and Possible Malware [default]
7985
+ - secret Secrets
7986
+ - spy Telemetry
7987
+ - tp False Positives and Unreviewed
7988
+ - typo Typo-squat
7989
+ - u Unreviewed
7990
+ - vuln Vulnerability
7991
+
7992
+ Valid ecosystems:
7993
+
7994
+ - gem
7995
+ - golang
7996
+ - maven
7997
+ - npm
7998
+ - nuget
7999
+ - pypi
8000
+
7747
8001
  Examples
7748
8002
  $ ${command}
7749
8003
  $ ${command} --perPage=5 --page=2 --direction=asc --filter=joke
@@ -7767,17 +8021,13 @@ async function run$1(argv, importMeta, {
7767
8021
  logger.logger.log(DRY_RUN_BAIL_TEXT$1);
7768
8022
  return;
7769
8023
  }
7770
- const apiToken = shadowNpmInject.getDefaultToken();
7771
- if (!apiToken) {
7772
- throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7773
- }
7774
8024
  await getThreatFeed({
7775
- apiToken,
7776
8025
  direction: String(cli.flags['direction'] || 'desc'),
8026
+ ecosystem: String(cli.flags['eco'] || ''),
7777
8027
  filter: String(cli.flags['filter'] || 'mal'),
7778
- outputJson: Boolean(cli.flags['json']),
7779
- page: String(cli.flags['filter'] || '1'),
7780
- perPage: Number(cli.flags['per_page'] || 0)
8028
+ outputKind: cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print',
8029
+ page: String(cli.flags['page'] || '1'),
8030
+ perPage: Number(cli.flags['perPage']) || 30
7781
8031
  });
7782
8032
  }
7783
8033
 
@@ -8054,5 +8304,5 @@ void (async () => {
8054
8304
  await shadowNpmInject.captureException(e);
8055
8305
  }
8056
8306
  })();
8057
- //# debugId=6f2331ca-147d-40b1-aa4e-e5b6a5c2eba0
8307
+ //# debugId=e7fc426e-8da9-4a73-b05c-6a96ab758857
8058
8308
  //# sourceMappingURL=cli.js.map