@socketsecurity/cli-with-sentry 1.0.111 → 1.1.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.
Files changed (38) hide show
  1. package/dist/cli.js +148 -1295
  2. package/dist/cli.js.map +1 -1
  3. package/dist/constants.js +6 -5
  4. package/dist/constants.js.map +1 -1
  5. package/dist/shadow-npm-bin.js +10 -4
  6. package/dist/shadow-npm-bin.js.map +1 -1
  7. package/dist/shadow-npm-inject.js +21 -240
  8. package/dist/shadow-npm-inject.js.map +1 -1
  9. package/dist/tsconfig.dts.tsbuildinfo +1 -1
  10. package/dist/types/commands/fix/cmd-fix.d.mts.map +1 -1
  11. package/dist/types/commands/fix/coana-fix.d.mts +1 -1
  12. package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
  13. package/dist/types/commands/fix/handle-fix.d.mts +1 -1
  14. package/dist/types/commands/fix/handle-fix.d.mts.map +1 -1
  15. package/dist/types/commands/fix/types.d.mts +18 -0
  16. package/dist/types/commands/fix/types.d.mts.map +1 -0
  17. package/dist/types/constants.d.mts.map +1 -1
  18. package/dist/types/shadow/npm/arborist/lib/arborist/index.d.mts.map +1 -1
  19. package/dist/types/shadow/npm/arborist-helpers.d.mts +1 -1
  20. package/dist/types/shadow/npm/arborist-helpers.d.mts.map +1 -1
  21. package/dist/types/shadow/npm/bin.d.mts.map +1 -1
  22. package/dist/types/utils/alerts-map.d.mts.map +1 -1
  23. package/dist/types/utils/fs.d.mts +3 -1
  24. package/dist/types/utils/fs.d.mts.map +1 -1
  25. package/dist/utils.js +990 -1222
  26. package/dist/utils.js.map +1 -1
  27. package/dist/vendor.js +111951 -119286
  28. package/package.json +6 -6
  29. package/dist/types/commands/fix/agent-fix.d.mts +0 -42
  30. package/dist/types/commands/fix/agent-fix.d.mts.map +0 -1
  31. package/dist/types/commands/fix/get-actual-tree.d.mts +0 -3
  32. package/dist/types/commands/fix/get-actual-tree.d.mts.map +0 -1
  33. package/dist/types/commands/fix/npm-fix.d.mts +0 -7
  34. package/dist/types/commands/fix/npm-fix.d.mts.map +0 -1
  35. package/dist/types/commands/fix/pnpm-fix.d.mts +0 -7
  36. package/dist/types/commands/fix/pnpm-fix.d.mts.map +0 -1
  37. package/dist/types/commands/fix/shared.d.mts +0 -10
  38. package/dist/types/commands/fix/shared.d.mts.map +0 -1
package/dist/utils.js CHANGED
@@ -19,12 +19,11 @@ var spawn = require('../external/@socketsecurity/registry/lib/spawn');
19
19
  var fs = require('../external/@socketsecurity/registry/lib/fs');
20
20
  var shadowNpmBin = require('./shadow-npm-bin.js');
21
21
  var fs$1 = require('node:fs');
22
- var registry = require('../external/@socketsecurity/registry');
23
- var packages = require('../external/@socketsecurity/registry/lib/packages');
22
+ var promises = require('node:timers/promises');
24
23
  var npm = require('../external/@socketsecurity/registry/lib/npm');
24
+ var packages = require('../external/@socketsecurity/registry/lib/packages');
25
25
  var streams = require('../external/@socketsecurity/registry/lib/streams');
26
26
  var globs = require('../external/@socketsecurity/registry/lib/globs');
27
- var promises = require('node:timers/promises');
28
27
 
29
28
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
30
29
  const sensitiveConfigKeyLookup = new Set(['apiToken']);
@@ -2266,13 +2265,6 @@ async function globWithGitIgnore(patterns, options) {
2266
2265
  }
2267
2266
  return filtered;
2268
2267
  }
2269
- async function globStreamNodeModules(cwd = process.cwd()) {
2270
- return vendor.outExports.globStream('**/node_modules', {
2271
- absolute: true,
2272
- cwd,
2273
- onlyDirectories: true
2274
- });
2275
- }
2276
2268
  async function globWorkspace(agent, cwd = process.cwd()) {
2277
2269
  const workspaceGlobs = await getWorkspaceGlobs(agent, cwd);
2278
2270
  return workspaceGlobs.length ? await vendor.outExports.glob(workspaceGlobs, {
@@ -2496,6 +2488,52 @@ function isHelpFlag(cmdArg) {
2496
2488
  return helpFlags.has(cmdArg);
2497
2489
  }
2498
2490
 
2491
+ async function findUp(name, options) {
2492
+ const opts = {
2493
+ __proto__: null,
2494
+ ...options
2495
+ };
2496
+ const {
2497
+ cwd = process.cwd(),
2498
+ signal = constants.abortSignal
2499
+ } = opts;
2500
+ let {
2501
+ onlyDirectories = false,
2502
+ onlyFiles = true
2503
+ } = opts;
2504
+ if (onlyDirectories) {
2505
+ onlyFiles = false;
2506
+ }
2507
+ if (onlyFiles) {
2508
+ onlyDirectories = false;
2509
+ }
2510
+ let dir = path.resolve(cwd);
2511
+ const {
2512
+ root
2513
+ } = path.parse(dir);
2514
+ const names = [name].flat();
2515
+ while (dir && dir !== root) {
2516
+ for (const name of names) {
2517
+ if (signal?.aborted) {
2518
+ return undefined;
2519
+ }
2520
+ const thePath = path.join(dir, name);
2521
+ try {
2522
+ // eslint-disable-next-line no-await-in-loop
2523
+ const stats = await fs$1.promises.stat(thePath);
2524
+ if (!onlyDirectories && (stats.isFile() || stats.isSymbolicLink())) {
2525
+ return thePath;
2526
+ }
2527
+ if (!onlyFiles && stats.isDirectory()) {
2528
+ return thePath;
2529
+ }
2530
+ } catch {}
2531
+ }
2532
+ dir = path.dirname(dir);
2533
+ }
2534
+ return undefined;
2535
+ }
2536
+
2499
2537
  function extractTier1ReachabilityScanId(socketFactsFile) {
2500
2538
  const json = fs.readJsonSync(socketFactsFile, {
2501
2539
  throws: false
@@ -2678,1332 +2716,1072 @@ async function writeSocketJson(cwd, sockJson) {
2678
2716
  };
2679
2717
  }
2680
2718
 
2681
- function isArtifactAlertCve(alert) {
2682
- const {
2683
- type
2684
- } = alert;
2685
- return type === constants.ALERT_TYPE_CVE || type === constants.ALERT_TYPE_MEDIUM_CVE || type === constants.ALERT_TYPE_MILD_CVE || type === constants.ALERT_TYPE_CRITICAL_CVE;
2686
- }
2687
-
2688
- function createEnum(obj) {
2689
- return Object.freeze({
2690
- __proto__: null,
2691
- ...obj
2692
- });
2719
+ const RangeStyles = ['caret', 'gt', 'gte', 'lt', 'lte', 'pin', 'preserve', 'tilde'];
2720
+ function getMajor(version) {
2721
+ try {
2722
+ const coerced = vendor.semverExports.coerce(version);
2723
+ return coerced ? vendor.semverExports.major(coerced) : null;
2724
+ } catch {}
2725
+ return null;
2693
2726
  }
2694
2727
 
2695
- const ALERT_FIX_TYPE = createEnum({
2696
- cve: 'cve',
2697
- remove: 'remove',
2698
- upgrade: 'upgrade'
2699
- });
2700
-
2701
- const ALERT_SEVERITY = createEnum({
2702
- critical: 'critical',
2703
- high: 'high',
2704
- middle: 'middle',
2705
- low: 'low'
2706
- });
2707
-
2708
- class ColorOrMarkdown {
2709
- constructor(useMarkdown) {
2710
- this.useMarkdown = !!useMarkdown;
2711
- }
2712
- bold(text) {
2713
- return this.useMarkdown ? `**${text}**` : vendor.yoctocolorsCjsExports.bold(`${text}`);
2714
- }
2715
- header(text, level = 1) {
2716
- return this.useMarkdown ? `\n${''.padStart(level, '#')} ${text}\n` : vendor.yoctocolorsCjsExports.underline(`\n${level === 1 ? vendor.yoctocolorsCjsExports.bold(text) : text}\n`);
2717
- }
2718
- hyperlink(text, url, {
2719
- fallback = true,
2720
- fallbackToUrl
2721
- } = {}) {
2722
- if (url) {
2723
- return this.useMarkdown ? `[${text}](${url})` : vendor.terminalLinkExports(text, url, {
2724
- fallback: fallbackToUrl ? (_text, url) => url : fallback
2725
- });
2726
- }
2727
- return text;
2728
- }
2729
- indent(...args) {
2730
- return vendor.indentStringExports(...args);
2731
- }
2732
- italic(text) {
2733
- return this.useMarkdown ? `_${text}_` : vendor.yoctocolorsCjsExports.italic(`${text}`);
2728
+ const COMPLETION_CMD_PREFIX = 'complete -F _socket_completion';
2729
+ function getCompletionSourcingCommand() {
2730
+ // Note: this is exported to distPath in .config/rollup.dist.config.mjs
2731
+ const completionScriptExportPath = path.join(constants.distPath, 'socket-completion.bash');
2732
+ if (!fs$1.existsSync(completionScriptExportPath)) {
2733
+ return {
2734
+ ok: false,
2735
+ message: 'Tab Completion script not found',
2736
+ cause: `Expected to find completion script at \`${completionScriptExportPath}\` but it was not there`
2737
+ };
2734
2738
  }
2735
- json(value) {
2736
- return this.useMarkdown ? '```json\n' + JSON.stringify(value) + '\n```' : JSON.stringify(value);
2739
+ return {
2740
+ ok: true,
2741
+ data: `source ${completionScriptExportPath}`
2742
+ };
2743
+ }
2744
+ function getBashrcDetails(targetCommandName) {
2745
+ const sourcingCommand = getCompletionSourcingCommand();
2746
+ if (!sourcingCommand.ok) {
2747
+ return sourcingCommand;
2737
2748
  }
2738
- list(items) {
2739
- const indentedContent = items.map(item => this.indent(item).trimStart());
2740
- return this.useMarkdown ? `* ${indentedContent.join('\n* ')}\n` : `${indentedContent.join('\n')}\n`;
2749
+ const {
2750
+ socketAppDataPath
2751
+ } = constants;
2752
+ if (!socketAppDataPath) {
2753
+ return {
2754
+ ok: false,
2755
+ message: 'Could not determine config directory',
2756
+ cause: 'Failed to get config path'
2757
+ };
2741
2758
  }
2742
- }
2743
2759
 
2744
- function toFilterConfig(obj) {
2745
- const normalized = {
2746
- __proto__: null
2747
- };
2748
- const keys = require$$10.isObject(obj) ? Object.keys(obj) : [];
2749
- for (const key of keys) {
2750
- const value = obj[key];
2751
- if (typeof value === 'boolean' || Array.isArray(value)) {
2752
- normalized[key] = value;
2760
+ // _socket_completion is the function defined in our completion bash script
2761
+ const completionCommand = `${COMPLETION_CMD_PREFIX} ${targetCommandName}`;
2762
+
2763
+ // Location of completion script in config after installing
2764
+ const completionScriptPath = path.join(path.dirname(socketAppDataPath), 'completion', 'socket-completion.bash');
2765
+ const bashrcContent = `# Socket CLI completion for "${targetCommandName}"
2766
+ if [ -f "${completionScriptPath}" ]; then
2767
+ # Load the tab completion script
2768
+ source "${completionScriptPath}"
2769
+ # Tell bash to use this function for tab completion of this function
2770
+ ${completionCommand}
2771
+ fi
2772
+ `;
2773
+ return {
2774
+ ok: true,
2775
+ data: {
2776
+ sourcingCommand: sourcingCommand.data,
2777
+ completionCommand,
2778
+ toAddToBashrc: bashrcContent,
2779
+ targetName: targetCommandName,
2780
+ targetPath: completionScriptPath
2753
2781
  }
2754
- }
2755
- return normalized;
2782
+ };
2756
2783
  }
2757
2784
 
2758
- const RangeStyles = ['caret', 'gt', 'gte', 'lt', 'lte', 'pin', 'preserve', 'tilde'];
2759
- function applyRange(refRange, version, style = 'preserve') {
2760
- switch (style) {
2761
- case 'caret':
2762
- return `^${version}`;
2763
- case 'gt':
2764
- return `>${version}`;
2765
- case 'gte':
2766
- return `>=${version}`;
2767
- case 'lt':
2768
- return `<${version}`;
2769
- case 'lte':
2770
- return `<=${version}`;
2771
- case 'preserve':
2772
- {
2773
- const range = new vendor.semverExports.Range(refRange);
2774
- const {
2775
- raw
2776
- } = range;
2777
- const comparators = range.set.flat();
2778
- const {
2779
- length
2780
- } = comparators;
2781
- if (length === 1) {
2782
- const char = /^[<>]=?/.exec(raw)?.[0];
2783
- if (char) {
2784
- return `${char}${version}`;
2785
- }
2786
- } else if (length === 2) {
2787
- const char = /^[~^]/.exec(raw)?.[0];
2788
- if (char) {
2789
- return `${char}${version}`;
2790
- }
2791
- }
2792
- return version;
2793
- }
2794
- case 'tilde':
2795
- return `~${version}`;
2796
- case 'pin':
2797
- default:
2798
- return version;
2785
+ const {
2786
+ kInternalsSymbol,
2787
+ [kInternalsSymbol]: {
2788
+ getSentry
2789
+ }
2790
+ } = constants;
2791
+ class AuthError extends Error {}
2792
+ class InputError extends Error {
2793
+ constructor(message, body) {
2794
+ super(message);
2795
+ this.body = body;
2799
2796
  }
2800
2797
  }
2801
- function getMajor(version) {
2802
- try {
2803
- const coerced = vendor.semverExports.coerce(version);
2804
- return coerced ? vendor.semverExports.major(coerced) : null;
2805
- } catch {}
2806
- return null;
2798
+ async function captureException(exception, hint) {
2799
+ const result = captureExceptionSync(exception, hint);
2800
+ // "Sleep" for a second, just in case, hopefully enough time to initiate fetch.
2801
+ await promises.setTimeout(1000);
2802
+ return result;
2803
+ }
2804
+ function captureExceptionSync(exception, hint) {
2805
+ const Sentry = getSentry();
2806
+ if (!Sentry) {
2807
+ return '';
2808
+ }
2809
+ require$$9.debugFn('notice', 'send: exception to Sentry');
2810
+ return Sentry.captureException(exception, hint);
2807
2811
  }
2808
- function getMinVersion(range) {
2812
+
2813
+ function npa(...args) {
2809
2814
  try {
2810
- return vendor.semverExports.minVersion(range);
2815
+ return Reflect.apply(vendor.npaExports, undefined, args);
2811
2816
  } catch {}
2812
2817
  return null;
2813
2818
  }
2814
2819
 
2815
- const require$1 = Module.createRequire(require('node:url').pathToFileURL(__filename).href);
2816
- let _translations;
2817
- function getTranslations() {
2818
- if (_translations === undefined) {
2819
- _translations = /*@__PURE__*/require$1(path.join(constants.rootPath, 'translations.json'));
2820
- }
2821
- return _translations;
2822
- }
2823
-
2824
- const ALERT_SEVERITY_COLOR = createEnum({
2825
- critical: 'magenta',
2826
- high: 'red',
2827
- middle: 'yellow',
2828
- low: 'white'
2829
- });
2830
- const ALERT_SEVERITY_ORDER = createEnum({
2831
- critical: 0,
2832
- high: 1,
2833
- middle: 2,
2834
- low: 3,
2835
- none: 4
2836
- });
2837
- const MIN_ABOVE_THE_FOLD_COUNT = 3;
2838
- const MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1;
2839
- const format = new ColorOrMarkdown(false);
2840
- function getHiddenRiskCounts(hiddenAlerts) {
2841
- const riskCounts = {
2842
- critical: 0,
2843
- high: 0,
2844
- middle: 0,
2845
- low: 0
2820
+ function shadowNpmInstall(options) {
2821
+ const {
2822
+ agentExecPath = getNpmBinPath(),
2823
+ args = [],
2824
+ ipc,
2825
+ spinner,
2826
+ ...spawnOpts
2827
+ } = {
2828
+ __proto__: null,
2829
+ ...options
2846
2830
  };
2847
- for (const alert of hiddenAlerts) {
2848
- switch (getAlertSeverityOrder(alert)) {
2849
- case ALERT_SEVERITY_ORDER.critical:
2850
- riskCounts.critical += 1;
2851
- break;
2852
- case ALERT_SEVERITY_ORDER.high:
2853
- riskCounts.high += 1;
2854
- break;
2855
- case ALERT_SEVERITY_ORDER.middle:
2856
- riskCounts.middle += 1;
2857
- break;
2858
- case ALERT_SEVERITY_ORDER.low:
2859
- riskCounts.low += 1;
2860
- break;
2831
+ const useDebug = require$$9.isDebug('stdio');
2832
+ const terminatorPos = args.indexOf('--');
2833
+ const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos);
2834
+ const binArgs = rawBinArgs.filter(a => !npm.isNpmAuditFlag(a) && !npm.isNpmFundFlag(a) && !npm.isNpmProgressFlag(a));
2835
+ const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
2836
+ const progressArg = rawBinArgs.findLast(npm.isNpmProgressFlag) !== '--no-progress';
2837
+ const isSilent = !useDebug && !binArgs.some(npm.isNpmLoglevelFlag);
2838
+ const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : [];
2839
+ const useIpc = require$$10.isObject(ipc);
2840
+
2841
+ // Include 'ipc' in the spawnOpts.stdio when an options.ipc object is provided.
2842
+ // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166
2843
+ // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.
2844
+ let stdio = require$$10.getOwn(spawnOpts, 'stdio');
2845
+ if (typeof stdio === 'string') {
2846
+ stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio];
2847
+ } else if (Array.isArray(stdio)) {
2848
+ if (useIpc && !stdio.includes('ipc')) {
2849
+ stdio = stdio.concat('ipc');
2861
2850
  }
2851
+ } else {
2852
+ stdio = useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : 'pipe';
2862
2853
  }
2863
- return riskCounts;
2864
- }
2865
- function getHiddenRisksDescription(riskCounts) {
2866
- const descriptions = [];
2867
- if (riskCounts.critical) {
2868
- descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`);
2869
- }
2870
- if (riskCounts.high) {
2871
- descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`);
2872
- }
2873
- if (riskCounts.middle) {
2874
- descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`);
2875
- }
2876
- if (riskCounts.low) {
2877
- descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`);
2854
+ const spawnPromise = spawn.spawn(constants.execPath, [...constants.nodeNoWarningsFlags, ...constants.nodeDebugFlags, ...constants.nodeHardenFlags, ...constants.nodeMemoryFlags, ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD ? ['--require', constants.instrumentWithSentryPath] : []), '--require', constants.shadowNpmInjectPath, npm.resolveBinPathSync(agentExecPath), 'install',
2855
+ // Avoid code paths for 'audit' and 'fund'.
2856
+ '--no-audit', '--no-fund',
2857
+ // Add '--no-progress' to fix input being swallowed by the npm spinner.
2858
+ '--no-progress',
2859
+ // Add '--loglevel=silent' if a loglevel flag is not provided and the
2860
+ // SOCKET_CLI_DEBUG environment variable is not truthy.
2861
+ ...logLevelArgs, ...binArgs, ...otherArgs], {
2862
+ ...spawnOpts,
2863
+ env: {
2864
+ ...process.env,
2865
+ ...constants.processEnv,
2866
+ ...require$$10.getOwn(spawnOpts, 'env')
2867
+ },
2868
+ spinner,
2869
+ stdio
2870
+ });
2871
+ if (useIpc) {
2872
+ spawnPromise.process.send({
2873
+ [constants.SOCKET_IPC_HANDSHAKE]: {
2874
+ [constants.SOCKET_CLI_SHADOW_BIN]: 'npm',
2875
+ [constants.SOCKET_CLI_SHADOW_PROGRESS]: progressArg,
2876
+ ...ipc
2877
+ }
2878
+ });
2878
2879
  }
2879
- return `(${descriptions.join('; ')})`;
2880
+ return spawnPromise;
2880
2881
  }
2881
- async function addArtifactToAlertsMap(artifact, alertsByPurl, options) {
2882
- // Make TypeScript happy.
2883
- if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
2884
- return alertsByPurl;
2885
- }
2882
+
2883
+ function runAgentInstall(pkgEnvDetails, options) {
2886
2884
  const {
2887
- type: ecosystem,
2888
- version
2889
- } = artifact;
2885
+ agent,
2886
+ agentExecPath
2887
+ } = pkgEnvDetails;
2888
+ const isNpm = agent === 'npm';
2889
+ const isPnpm = agent === 'pnpm';
2890
+ // All package managers support the "install" command.
2891
+ if (isNpm) {
2892
+ return shadowNpmInstall({
2893
+ agentExecPath,
2894
+ ...options
2895
+ });
2896
+ }
2890
2897
  const {
2891
- consolidate = false,
2892
- overrides,
2893
- socketYml
2898
+ args = [],
2899
+ spinner,
2900
+ ...spawnOpts
2894
2901
  } = {
2895
2902
  __proto__: null,
2896
2903
  ...options
2897
2904
  };
2898
- const name = packages.resolvePackageName(artifact);
2899
- const filterConfig = toFilterConfig({
2900
- blocked: true,
2901
- critical: true,
2902
- cve: true,
2903
- ...require$$10.getOwn(options, 'filter')
2905
+ const skipNodeHardenFlags = isPnpm && pkgEnvDetails.agentVersion.major < 11;
2906
+ return spawn.spawn(agentExecPath, ['install', ...args], {
2907
+ shell: constants.WIN32,
2908
+ spinner,
2909
+ stdio: 'inherit',
2910
+ ...spawnOpts,
2911
+ env: {
2912
+ ...process.env,
2913
+ ...constants.processEnv,
2914
+ NODE_OPTIONS: cmdFlagsToString([...(skipNodeHardenFlags ? [] : constants.nodeHardenFlags), ...constants.nodeNoWarningsFlags]),
2915
+ ...require$$10.getOwn(spawnOpts, 'env')
2916
+ }
2904
2917
  });
2905
- const enabledState = {
2906
- __proto__: null,
2907
- ...socketYml?.issueRules
2908
- };
2909
- let sockPkgAlerts = [];
2910
- for (const alert of artifact.alerts) {
2911
- const action = alert.action ?? '';
2912
- const enabledFlag = enabledState[alert.type];
2913
- if (action === 'ignore' && enabledFlag !== true || enabledFlag === false) {
2914
- continue;
2918
+ }
2919
+
2920
+ const {
2921
+ BINARY_LOCK_EXT,
2922
+ BUN,
2923
+ HIDDEN_PACKAGE_LOCK_JSON,
2924
+ LOCK_EXT,
2925
+ NPM,
2926
+ NPM_BUGGY_OVERRIDES_PATCHED_VERSION,
2927
+ PACKAGE_JSON,
2928
+ PNPM,
2929
+ VLT,
2930
+ YARN,
2931
+ YARN_BERRY,
2932
+ YARN_CLASSIC
2933
+ } = constants;
2934
+ const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT];
2935
+ const binByAgent = new Map([[BUN, BUN], [NPM, NPM], [PNPM, PNPM], [YARN_BERRY, YARN], [YARN_CLASSIC, YARN], [VLT, VLT]]);
2936
+ const readLockFileByAgent = (() => {
2937
+ function wrapReader(reader) {
2938
+ return async (...args) => {
2939
+ try {
2940
+ return await reader(...args);
2941
+ } catch {}
2942
+ return undefined;
2943
+ };
2944
+ }
2945
+ const binaryReader = wrapReader(fs.readFileBinary);
2946
+ const defaultReader = wrapReader(async lockPath => await fs.readFileUtf8(lockPath));
2947
+ return new Map([[BUN, wrapReader(async (lockPath, agentExecPath, cwd = process.cwd()) => {
2948
+ const ext = path.extname(lockPath);
2949
+ if (ext === LOCK_EXT) {
2950
+ return await defaultReader(lockPath);
2915
2951
  }
2916
- const blocked = action === 'error';
2917
- const critical = alert.severity === ALERT_SEVERITY.critical;
2918
- const cve = isArtifactAlertCve(alert);
2919
- const fixType = alert.fix?.type ?? '';
2920
- const fixableCve = fixType === ALERT_FIX_TYPE.cve;
2921
- const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade;
2922
- const fixable = fixableCve || fixableUpgrade;
2923
- const upgradable = fixableUpgrade && !require$$10.hasOwn(overrides, name);
2924
- if (filterConfig.blocked && blocked || filterConfig.critical && critical || filterConfig.cve && cve || filterConfig.fixable && fixable || filterConfig.upgradable && upgradable) {
2925
- sockPkgAlerts.push({
2926
- name,
2927
- version,
2928
- key: alert.key,
2929
- type: alert.type,
2930
- blocked,
2931
- critical,
2932
- ecosystem,
2933
- fixable,
2934
- raw: alert,
2935
- upgradable
2936
- });
2952
+ if (ext === BINARY_LOCK_EXT) {
2953
+ const lockBuffer = await binaryReader(lockPath);
2954
+ if (lockBuffer) {
2955
+ try {
2956
+ return vendor.hyrious__bun_lockbExports.parse(lockBuffer);
2957
+ } catch {}
2958
+ }
2959
+ // To print a Yarn lockfile to your console without writing it to disk
2960
+ // use `bun bun.lockb`.
2961
+ // https://bun.sh/guides/install/yarnlock
2962
+ return (await spawn.spawn(agentExecPath, [lockPath], {
2963
+ cwd,
2964
+ shell: constants.WIN32
2965
+ })).stdout;
2937
2966
  }
2967
+ return undefined;
2968
+ })], [NPM, defaultReader], [PNPM, defaultReader], [VLT, defaultReader], [YARN_BERRY, defaultReader], [YARN_CLASSIC, defaultReader]]);
2969
+ })();
2970
+
2971
+ // The order of LOCKS properties IS significant as it affects iteration order.
2972
+ const LOCKS = {
2973
+ [`bun${LOCK_EXT}`]: BUN,
2974
+ [`bun${BINARY_LOCK_EXT}`]: BUN,
2975
+ // If both package-lock.json and npm-shrinkwrap.json are present in the root
2976
+ // of a project, npm-shrinkwrap.json will take precedence and package-lock.json
2977
+ // will be ignored.
2978
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson
2979
+ 'npm-shrinkwrap.json': NPM,
2980
+ 'package-lock.json': NPM,
2981
+ 'pnpm-lock.yaml': PNPM,
2982
+ 'pnpm-lock.yml': PNPM,
2983
+ [`yarn${LOCK_EXT}`]: YARN_CLASSIC,
2984
+ 'vlt-lock.json': VLT,
2985
+ // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:
2986
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles
2987
+ //
2988
+ // Unlike the other LOCKS keys this key contains a directory AND filename so
2989
+ // it has to be handled differently.
2990
+ 'node_modules/.package-lock.json': NPM
2991
+ };
2992
+ async function getAgentExecPath(agent) {
2993
+ const binName = binByAgent.get(agent);
2994
+ if (binName === NPM) {
2995
+ return constants.npmExecPath;
2938
2996
  }
2939
- if (!sockPkgAlerts.length) {
2940
- return alertsByPurl;
2997
+ return (await vendor.libExports$1(binName, {
2998
+ nothrow: true
2999
+ })) ?? binName;
3000
+ }
3001
+ async function getAgentVersion(agent, agentExecPath, cwd) {
3002
+ let result;
3003
+ const quotedCmd = `\`${agent} --version\``;
3004
+ require$$9.debugFn('stdio', `spawn: ${quotedCmd}`);
3005
+ try {
3006
+ result =
3007
+ // Coerce version output into a valid semver version by passing it through
3008
+ // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
3009
+ // and tildes (~).
3010
+ vendor.semverExports.coerce(
3011
+ // All package managers support the "--version" flag.
3012
+ (await spawn.spawn(agentExecPath, ['--version'], {
3013
+ cwd,
3014
+ shell: constants.WIN32
3015
+ })).stdout) ?? undefined;
3016
+ } catch (e) {
3017
+ require$$9.debugFn('error', `caught: ${quotedCmd} failed`);
3018
+ require$$9.debugDir('inspect', {
3019
+ error: e
3020
+ });
2941
3021
  }
2942
- const purl = `pkg:${ecosystem}/${name}@${version}`;
2943
- const major = getMajor(version);
2944
- if (consolidate) {
2945
- const highestForCve = new Map();
2946
- const highestForUpgrade = new Map();
2947
- const unfixableAlerts = [];
2948
- for (const sockPkgAlert of sockPkgAlerts) {
2949
- const alert = sockPkgAlert.raw;
2950
- const fixType = alert.fix?.type ?? '';
2951
- if (fixType === ALERT_FIX_TYPE.cve) {
2952
- // An alert with alert.fix.type of 'cve' should have a
2953
- // alert.props.firstPatchedVersionIdentifier property value.
2954
- // We're just being cautious.
2955
- const firstPatchedVersionIdentifier = alert.props?.firstPatchedVersionIdentifier;
2956
- const patchedMajor = firstPatchedVersionIdentifier ? getMajor(firstPatchedVersionIdentifier) : null;
2957
- if (typeof patchedMajor === 'number') {
2958
- // Consolidate to the highest "first patched version" by each major
2959
- // version number.
2960
- const highest = highestForCve.get(patchedMajor)?.version ?? '0.0.0';
2961
- if (vendor.semverExports.gt(firstPatchedVersionIdentifier, highest)) {
2962
- highestForCve.set(patchedMajor, {
2963
- alert: sockPkgAlert,
2964
- version: firstPatchedVersionIdentifier
2965
- });
2966
- }
2967
- } else {
2968
- unfixableAlerts.push(sockPkgAlert);
2969
- }
2970
- } else if (fixType === ALERT_FIX_TYPE.upgrade) {
2971
- // For Socket Optimize upgrades we assume the highest version available
2972
- // is compatible. This may change in the future.
2973
- const highest = highestForUpgrade.get(major)?.version ?? '0.0.0';
2974
- if (vendor.semverExports.gt(version, highest)) {
2975
- highestForUpgrade.set(major, {
2976
- alert: sockPkgAlert,
2977
- version
2978
- });
2979
- }
2980
- } else {
2981
- unfixableAlerts.push(sockPkgAlert);
3022
+ return result;
3023
+ }
3024
+ async function detectPackageEnvironment({
3025
+ cwd = process.cwd(),
3026
+ onUnknown
3027
+ } = {}) {
3028
+ let lockPath = await findUp(Object.keys(LOCKS), {
3029
+ cwd
3030
+ });
3031
+ let lockName = lockPath ? path.basename(lockPath) : undefined;
3032
+ const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON;
3033
+ const pkgJsonPath = lockPath ? path.resolve(lockPath, `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`) : await findUp(PACKAGE_JSON, {
3034
+ cwd
3035
+ });
3036
+ const pkgPath = pkgJsonPath && fs$1.existsSync(pkgJsonPath) ? path.dirname(pkgJsonPath) : undefined;
3037
+ const editablePkgJson = pkgPath ? await packages.readPackageJson(pkgPath, {
3038
+ editable: true
3039
+ }) : undefined;
3040
+ // Read Corepack `packageManager` field in package.json:
3041
+ // https://nodejs.org/api/packages.html#packagemanager
3042
+ const pkgManager = strings.isNonEmptyString(editablePkgJson?.content?.packageManager) ? editablePkgJson.content.packageManager : undefined;
3043
+ let agent;
3044
+ if (pkgManager) {
3045
+ // A valid "packageManager" field value is "<package manager name>@<version>".
3046
+ // https://nodejs.org/api/packages.html#packagemanager
3047
+ const atSignIndex = pkgManager.lastIndexOf('@');
3048
+ if (atSignIndex !== -1) {
3049
+ const name = pkgManager.slice(0, atSignIndex);
3050
+ const version = pkgManager.slice(atSignIndex + 1);
3051
+ if (version && AGENTS.includes(name)) {
3052
+ agent = name;
2982
3053
  }
2983
3054
  }
2984
- sockPkgAlerts = [
2985
- // Sort CVE alerts by severity: critical, high, middle, then low.
2986
- ...Array.from(highestForCve.values()).map(d => d.alert).sort(alertSeverityComparator), ...Array.from(highestForUpgrade.values()).map(d => d.alert), ...unfixableAlerts];
2987
- } else {
2988
- sockPkgAlerts.sort((a, b) => sorts.naturalCompare(a.type, b.type));
2989
3055
  }
2990
- if (sockPkgAlerts.length) {
2991
- alertsByPurl.set(purl, sockPkgAlerts);
3056
+ if (agent === undefined && !isHiddenLockFile && typeof pkgJsonPath === 'string' && typeof lockName === 'string') {
3057
+ agent = LOCKS[lockName];
3058
+ }
3059
+ if (agent === undefined) {
3060
+ agent = NPM;
3061
+ onUnknown?.(pkgManager);
3062
+ }
3063
+ const agentExecPath = await getAgentExecPath(agent);
3064
+ const agentVersion = await getAgentVersion(agent, agentExecPath, cwd);
3065
+ if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {
3066
+ agent = YARN_BERRY;
2992
3067
  }
2993
- return alertsByPurl;
2994
- }
2995
- function alertsHaveBlocked(alerts) {
2996
- return alerts.find(a => a.blocked) !== undefined;
2997
- }
2998
- function alertsHaveSeverity(alerts, severity) {
2999
- return alerts.find(a => a.raw.severity === severity) !== undefined;
3000
- }
3001
- function alertSeverityComparator(a, b) {
3002
- // Put the most severe first.
3003
- return getAlertSeverityOrder(a) - getAlertSeverityOrder(b);
3004
- }
3005
- function getAlertSeverityOrder(alert) {
3006
- // The more severe, the lower the sort number.
3007
3068
  const {
3008
- severity
3009
- } = alert.raw;
3010
- return severity === ALERT_SEVERITY.critical ? 0 : severity === ALERT_SEVERITY.high ? 1 : severity === ALERT_SEVERITY.middle ? 2 : severity === ALERT_SEVERITY.low ? 3 : 4;
3011
- }
3012
- function getAlertsSeverityOrder(alerts) {
3013
- return alertsHaveBlocked(alerts) || alertsHaveSeverity(alerts, ALERT_SEVERITY.critical) ? 0 : alertsHaveSeverity(alerts, ALERT_SEVERITY.high) ? 1 : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle) ? 2 : alertsHaveSeverity(alerts, ALERT_SEVERITY.low) ? 3 : 4;
3014
- }
3015
- function getCveInfoFromAlertsMap(alertsMap, options) {
3016
- const filterConfig = toFilterConfig(require$$10.getOwn(options, 'filter'));
3017
- let infoByPartialPurl = null;
3018
- // eslint-disable-next-line no-unused-labels
3019
- for (const {
3020
- 0: purl,
3021
- 1: sockPkgAlerts
3022
- } of alertsMap) {
3023
- const purlObj = getPurlObject(purl);
3024
- const partialPurl = new vendor.packageurlJsExports.PackageURL(purlObj.type, purlObj.namespace, purlObj.name).toString();
3025
- const name = packages.resolvePackageName(purlObj);
3026
- sockPkgAlertsLoop: for (const sockPkgAlert of sockPkgAlerts) {
3027
- const alert = sockPkgAlert.raw;
3028
- if (alert.fix?.type !== ALERT_FIX_TYPE.cve || filterConfig.upgradable === false && registry.getManifestData(sockPkgAlert.ecosystem, name)) {
3029
- continue sockPkgAlertsLoop;
3030
- }
3031
- if (!infoByPartialPurl) {
3032
- infoByPartialPurl = new Map();
3069
+ maintainedNodeVersions
3070
+ } = constants;
3071
+ const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent);
3072
+ const minSupportedNodeMajor = vendor.semverExports.major(maintainedNodeVersions.last);
3073
+ const minSupportedNodeVersion = `${minSupportedNodeMajor}.0.0`;
3074
+ const minSupportedNodeRange = `>=${minSupportedNodeMajor}`;
3075
+ const nodeVersion = vendor.semverExports.coerce(process.version);
3076
+ let lockSrc;
3077
+ let pkgAgentRange;
3078
+ let pkgNodeRange;
3079
+ let pkgMinAgentVersion = minSupportedAgentVersion;
3080
+ let pkgMinNodeVersion = minSupportedNodeVersion;
3081
+ if (editablePkgJson?.content) {
3082
+ const {
3083
+ engines
3084
+ } = editablePkgJson.content;
3085
+ const engineAgentRange = engines?.[agent];
3086
+ const engineNodeRange = engines?.['node'];
3087
+ if (strings.isNonEmptyString(engineAgentRange)) {
3088
+ pkgAgentRange = engineAgentRange;
3089
+ // Roughly check agent range as semver.coerce will strip leading
3090
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3091
+ const coerced = vendor.semverExports.coerce(pkgAgentRange);
3092
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinAgentVersion)) {
3093
+ pkgMinAgentVersion = coerced.version;
3033
3094
  }
3034
- let infos = infoByPartialPurl.get(partialPurl);
3035
- if (!infos) {
3036
- infos = new Map();
3037
- infoByPartialPurl.set(partialPurl, infos);
3095
+ }
3096
+ if (strings.isNonEmptyString(engineNodeRange)) {
3097
+ pkgNodeRange = engineNodeRange;
3098
+ // Roughly check Node range as semver.coerce will strip leading
3099
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3100
+ const coerced = vendor.semverExports.coerce(pkgNodeRange);
3101
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
3102
+ pkgMinNodeVersion = coerced.version;
3038
3103
  }
3039
- const {
3040
- key
3041
- } = alert;
3042
- if (!infos.has(key)) {
3043
- // An alert with alert.fix.type of 'cve' should have a
3044
- // alert.props.firstPatchedVersionIdentifier property value.
3045
- // We're just being cautious.
3046
- const firstPatchedVersionIdentifier = alert.props?.firstPatchedVersionIdentifier;
3047
- const vulnerableVersionRange = alert.props?.vulnerableVersionRange;
3048
- let error;
3049
- if (firstPatchedVersionIdentifier && vulnerableVersionRange) {
3050
- try {
3051
- infos.set(key, {
3052
- firstPatchedVersionIdentifier,
3053
- vulnerableVersionRange: new vendor.semverExports.Range(
3054
- // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that
3055
- // semver.Range will parse it without erroring.
3056
- vulnerableVersionRange.replace(/, +/g, ' ').replace(/; +/g, ' || ')).format()
3057
- });
3058
- continue sockPkgAlertsLoop;
3059
- } catch (e) {
3060
- error = e;
3061
- }
3104
+ }
3105
+ const browserslistQuery = editablePkgJson.content['browserslist'];
3106
+ if (Array.isArray(browserslistQuery)) {
3107
+ // List Node targets in ascending version order.
3108
+ const browserslistNodeTargets = vendor.browserslistExports(browserslistQuery).filter(v => /^node /i.test(v)).map(v => v.slice(5 /*'node '.length*/)).sort(sorts.naturalCompare);
3109
+ if (browserslistNodeTargets.length) {
3110
+ // browserslistNodeTargets[0] is the lowest Node target version.
3111
+ const coerced = vendor.semverExports.coerce(browserslistNodeTargets[0]);
3112
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
3113
+ pkgMinNodeVersion = coerced.version;
3062
3114
  }
3063
- require$$9.debugFn('error', 'fail: invalid SocketPackageAlert');
3064
- require$$9.debugDir('inspect', {
3065
- alert,
3066
- error
3067
- });
3068
3115
  }
3069
3116
  }
3117
+ lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath, cwd) : undefined;
3118
+ } else {
3119
+ lockName = undefined;
3120
+ lockPath = undefined;
3070
3121
  }
3071
- return infoByPartialPurl;
3072
- }
3073
- function getSeverityLabel(severity) {
3074
- return severity === 'middle' ? 'moderate' : severity;
3122
+
3123
+ // Does the system agent version meet our minimum supported agent version?
3124
+ const agentSupported = !!agentVersion && vendor.semverExports.satisfies(agentVersion, `>=${minSupportedAgentVersion}`);
3125
+ // Does the system Node version meet our minimum supported Node version?
3126
+ const nodeSupported = vendor.semverExports.satisfies(nodeVersion, minSupportedNodeRange);
3127
+ const npmExecPath = agent === NPM ? agentExecPath : await getAgentExecPath(NPM);
3128
+ const npmBuggyOverrides = agent === NPM && !!agentVersion && vendor.semverExports.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION);
3129
+ const pkgMinAgentRange = `>=${pkgMinAgentVersion}`;
3130
+ const pkgMinNodeRange = `>=${vendor.semverExports.major(pkgMinNodeVersion)}`;
3131
+ return {
3132
+ agent,
3133
+ agentExecPath,
3134
+ agentSupported,
3135
+ agentVersion,
3136
+ editablePkgJson,
3137
+ features: {
3138
+ npmBuggyOverrides
3139
+ },
3140
+ lockName,
3141
+ lockPath,
3142
+ lockSrc,
3143
+ nodeSupported,
3144
+ nodeVersion,
3145
+ npmExecPath,
3146
+ pkgPath,
3147
+ pkgRequirements: {
3148
+ agent: pkgAgentRange ?? pkgMinAgentRange,
3149
+ node: pkgNodeRange ?? pkgMinNodeRange
3150
+ },
3151
+ pkgSupports: {
3152
+ // Does our minimum supported agent version meet the package's requirements?
3153
+ agent: vendor.semverExports.satisfies(minSupportedAgentVersion, pkgMinAgentRange),
3154
+ // Does our supported Node versions meet the package's requirements?
3155
+ node: maintainedNodeVersions.some(v => vendor.semverExports.satisfies(v, pkgMinNodeRange))
3156
+ }
3157
+ };
3075
3158
  }
3076
- function logAlertsMap(alertsMap, options) {
3159
+ async function detectAndValidatePackageEnvironment(cwd, options) {
3077
3160
  const {
3078
- hideAt = 'middle',
3079
- output = process.stderr
3161
+ cmdName = '',
3162
+ logger,
3163
+ prod
3080
3164
  } = {
3081
3165
  __proto__: null,
3082
3166
  ...options
3083
3167
  };
3084
- const translations = getTranslations();
3085
- const sortedEntries = Array.from(alertsMap.entries()).sort((a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1]));
3086
- const aboveTheFoldPurls = new Set();
3087
- const viewableAlertsByPurl = new Map();
3088
- const hiddenAlertsByPurl = new Map();
3089
- for (let i = 0, {
3090
- length
3091
- } = sortedEntries; i < length; i += 1) {
3092
- const {
3093
- 0: purl,
3094
- 1: alerts
3095
- } = sortedEntries[i];
3096
- const hiddenAlerts = [];
3097
- const viewableAlerts = alerts.filter(a => {
3098
- const keep = a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt];
3099
- if (!keep) {
3100
- hiddenAlerts.push(a);
3101
- }
3102
- return keep;
3103
- });
3104
- if (hiddenAlerts.length) {
3105
- hiddenAlertsByPurl.set(purl, hiddenAlerts.sort(alertSeverityComparator));
3106
- }
3107
- if (!viewableAlerts.length) {
3108
- continue;
3109
- }
3110
- viewableAlerts.sort(alertSeverityComparator);
3111
- viewableAlertsByPurl.set(purl, viewableAlerts);
3112
- if (viewableAlerts.find(a => a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle)) {
3113
- aboveTheFoldPurls.add(purl);
3168
+ const details = await detectPackageEnvironment({
3169
+ cwd,
3170
+ onUnknown(pkgManager) {
3171
+ logger?.warn(cmdPrefixMessage(cmdName, `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`));
3114
3172
  }
3173
+ });
3174
+ const {
3175
+ agent,
3176
+ nodeVersion,
3177
+ pkgRequirements
3178
+ } = details;
3179
+ const agentVersion = details.agentVersion ?? 'unknown';
3180
+ if (!details.agentSupported) {
3181
+ const minVersion = constants.minimumVersionByAgent.get(agent);
3182
+ return {
3183
+ ok: false,
3184
+ message: 'Version mismatch',
3185
+ cause: cmdPrefixMessage(cmdName, `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`)
3186
+ };
3115
3187
  }
3116
-
3117
- // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.
3118
- for (const {
3119
- 0: purl
3120
- } of viewableAlertsByPurl.entries()) {
3121
- if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {
3122
- break;
3123
- }
3124
- aboveTheFoldPurls.add(purl);
3125
- }
3126
- // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.
3127
- for (const {
3128
- 0: purl,
3129
- 1: hiddenAlerts
3130
- } of hiddenAlertsByPurl.entries()) {
3131
- if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {
3132
- break;
3133
- }
3134
- aboveTheFoldPurls.add(purl);
3135
- const viewableAlerts = viewableAlertsByPurl.get(purl) ?? [];
3136
- if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {
3137
- const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length;
3138
- let removedHiddenAlerts;
3139
- if (hiddenAlerts.length - neededCount > 0) {
3140
- removedHiddenAlerts = hiddenAlerts.splice(0, MIN_ABOVE_THE_FOLD_ALERT_COUNT);
3141
- } else {
3142
- removedHiddenAlerts = hiddenAlerts;
3143
- hiddenAlertsByPurl.delete(purl);
3144
- }
3145
- viewableAlertsByPurl.set(purl, [...viewableAlerts, ...removedHiddenAlerts]);
3146
- }
3188
+ if (!details.nodeSupported) {
3189
+ const minVersion = constants.maintainedNodeVersions.last;
3190
+ return {
3191
+ ok: false,
3192
+ message: 'Version mismatch',
3193
+ cause: cmdPrefixMessage(cmdName, `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`)
3194
+ };
3147
3195
  }
3148
- const mentionedPurlsWithHiddenAlerts = new Set();
3149
- for (let i = 0, prevAboveTheFold = true, entries = Array.from(viewableAlertsByPurl.entries()), {
3150
- length
3151
- } = entries; i < length; i += 1) {
3152
- const {
3153
- 0: purl,
3154
- 1: alerts
3155
- } = entries[i];
3156
- const lines = new Set();
3157
- for (const alert of alerts) {
3158
- const {
3159
- type
3160
- } = alert;
3161
- const severity = alert.raw.severity ?? '';
3162
- const attributes = [...(severity ? [vendor.yoctocolorsCjsExports[ALERT_SEVERITY_COLOR[severity]](getSeverityLabel(severity))] : []), ...(alert.blocked ? [vendor.yoctocolorsCjsExports.bold(vendor.yoctocolorsCjsExports.red('blocked'))] : []), ...(alert.fixable ? ['fixable'] : [])];
3163
- const maybeAttributes = attributes.length ? ` ${vendor.yoctocolorsCjsExports.italic(`(${attributes.join('; ')})`)}` : '';
3164
- // Based data from { pageProps: { alertTypes } } of:
3165
- // https://socket.dev/_next/data/9a6db8224b68b6da0eb9f7dbb17aff7e51568ac2/en-US.json
3166
- const info = translations.alerts[type];
3167
- const title = info?.title ?? type;
3168
- const maybeDesc = info?.description ? ` - ${info.description}` : '';
3169
- const content = `${title}${maybeAttributes}${maybeDesc}`;
3170
- // TODO: An added emoji seems to mis-align terminals sometimes.
3171
- lines.add(` ${content}`);
3172
- }
3173
- const purlObj = getPurlObject(purl);
3174
- const pkgName = packages.resolvePackageName(purlObj);
3175
- const hyperlink = format.hyperlink(pkgName, getSocketDevPackageOverviewUrl(purlObj.type, pkgName, purlObj.version));
3176
- const isAboveTheFold = aboveTheFoldPurls.has(purl);
3177
- if (isAboveTheFold) {
3178
- aboveTheFoldPurls.add(purl);
3179
- output.write(`${i ? '\n' : ''}${hyperlink}:\n`);
3180
- } else {
3181
- output.write(`${prevAboveTheFold ? '\n' : ''}${hyperlink}:\n`);
3182
- }
3183
- for (const line of lines) {
3184
- output.write(`${line}\n`);
3185
- }
3186
- const hiddenAlerts = hiddenAlertsByPurl.get(purl) ?? [];
3187
- const {
3188
- length: hiddenAlertsCount
3189
- } = hiddenAlerts;
3190
- if (hiddenAlertsCount) {
3191
- mentionedPurlsWithHiddenAlerts.add(purl);
3192
- if (hiddenAlertsCount === 1) {
3193
- output.write(` ${vendor.yoctocolorsCjsExports.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0].raw.severity ?? 'low')} risk alert`)}\n`);
3194
- } else {
3195
- output.write(` ${vendor.yoctocolorsCjsExports.dim(`+${hiddenAlertsCount} Hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\n`);
3196
- }
3197
- }
3198
- prevAboveTheFold = isAboveTheFold;
3196
+ if (!details.pkgSupports.agent) {
3197
+ return {
3198
+ ok: false,
3199
+ message: 'Engine mismatch',
3200
+ cause: cmdPrefixMessage(cmdName, `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`)
3201
+ };
3199
3202
  }
3200
- const additionalHiddenCount = hiddenAlertsByPurl.size - mentionedPurlsWithHiddenAlerts.size;
3201
- if (additionalHiddenCount) {
3202
- const totalRiskCounts = {
3203
- critical: 0,
3204
- high: 0,
3205
- middle: 0,
3206
- low: 0
3203
+ if (!details.pkgSupports.node) {
3204
+ return {
3205
+ ok: false,
3206
+ message: 'Version mismatch',
3207
+ cause: cmdPrefixMessage(cmdName, `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`)
3207
3208
  };
3208
- for (const {
3209
- 0: purl,
3210
- 1: alerts
3211
- } of hiddenAlertsByPurl.entries()) {
3212
- if (mentionedPurlsWithHiddenAlerts.has(purl)) {
3213
- continue;
3214
- }
3215
- const riskCounts = getHiddenRiskCounts(alerts);
3216
- totalRiskCounts.critical += riskCounts.critical;
3217
- totalRiskCounts.high += riskCounts.high;
3218
- totalRiskCounts.middle += riskCounts.middle;
3219
- totalRiskCounts.low += riskCounts.low;
3220
- }
3221
- output.write(`${aboveTheFoldPurls.size ? '\n' : ''}${vendor.yoctocolorsCjsExports.dim(`${aboveTheFoldPurls.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\n`);
3222
3209
  }
3223
- output.write('\n');
3224
- }
3225
-
3226
- function idToNpmPurl(id) {
3227
- return `pkg:npm/${id}`;
3228
- }
3229
- function idToPurl(id, type) {
3230
- return `pkg:${type}/${id}`;
3231
- }
3232
-
3233
- function extractOverridesFromPnpmLockSrc(lockfileContent) {
3234
- let match;
3235
- if (typeof lockfileContent === 'string') {
3236
- match = /^overrides:(?:\r?\n {2}.+)+(?:\r?\n)*/m.exec(lockfileContent)?.[0];
3237
- }
3238
- return match ?? '';
3239
- }
3240
- async function extractPurlsFromPnpmLockfile(lockfile) {
3241
- const packages = lockfile?.packages ?? {};
3242
- const seen = new Set();
3243
- const visit = pkgPath => {
3244
- if (seen.has(pkgPath)) {
3245
- return;
3246
- }
3247
- const pkg = packages[pkgPath];
3248
- if (!pkg) {
3249
- return;
3250
- }
3251
- seen.add(pkgPath);
3252
- const deps = {
3253
- __proto__: null,
3254
- ...pkg.dependencies,
3255
- ...pkg.optionalDependencies,
3256
- ...pkg.devDependencies
3210
+ const lockName = details.lockName ?? 'lock file';
3211
+ if (details.lockName === undefined || details.lockSrc === undefined) {
3212
+ return {
3213
+ ok: false,
3214
+ message: 'Missing lockfile',
3215
+ cause: cmdPrefixMessage(cmdName, `No ${lockName} found`)
3257
3216
  };
3258
- for (const depName in deps) {
3259
- const ref = deps[depName];
3260
- const subKey = isPnpmDepPath(ref) ? ref : `/${depName}@${ref}`;
3261
- visit(subKey);
3262
- }
3263
- };
3264
- for (const pkgPath of Object.keys(packages)) {
3265
- visit(pkgPath);
3266
3217
  }
3267
- return Array.from(seen).map(p => idToNpmPurl(stripPnpmPeerSuffix(stripLeadingPnpmDepPathSlash(p))));
3268
- }
3269
- function isPnpmDepPath(maybeDepPath) {
3270
- return maybeDepPath.length > 0 && maybeDepPath.charCodeAt(0) === 47; /*'/'*/
3271
- }
3272
- function parsePnpmLockfile(lockfileContent) {
3273
- let result;
3274
- if (typeof lockfileContent === 'string') {
3275
- try {
3276
- result = vendor.jsYaml.load(strings.stripBom(lockfileContent));
3277
- } catch {}
3218
+ if (details.lockSrc.trim() === '') {
3219
+ return {
3220
+ ok: false,
3221
+ message: 'Empty lockfile',
3222
+ cause: cmdPrefixMessage(cmdName, `${lockName} is empty`)
3223
+ };
3278
3224
  }
3279
- return require$$10.isObjectObject(result) ? result : null;
3280
- }
3281
- function parsePnpmLockfileVersion(version) {
3282
- try {
3283
- return vendor.semverExports.coerce(version);
3284
- } catch {}
3285
- return null;
3286
- }
3287
- function stripLeadingPnpmDepPathSlash(depPath) {
3288
- return isPnpmDepPath(depPath) ? depPath.slice(1) : depPath;
3289
- }
3290
- function stripPnpmPeerSuffix(depPath) {
3291
- const parenIndex = depPath.indexOf('(');
3292
- const index = parenIndex === -1 ? depPath.indexOf('_') : parenIndex;
3293
- return index === -1 ? depPath : depPath.slice(0, index);
3294
- }
3295
-
3296
- async function getAlertsMapFromPnpmLockfile(lockfile, options) {
3297
- const purls = await extractPurlsFromPnpmLockfile(lockfile);
3298
- return await getAlertsMapFromPurls(purls, {
3299
- overrides: lockfile.overrides,
3300
- ...options
3301
- });
3302
- }
3303
- async function getAlertsMapFromPurls(purls, options) {
3304
- const uniqPurls = arrays.arrayUnique(purls);
3305
- require$$9.debugDir('silly', {
3306
- purls: uniqPurls
3307
- });
3308
- let {
3309
- length: remaining
3310
- } = uniqPurls;
3311
- const alertsByPurl = new Map();
3312
- if (!remaining) {
3313
- return alertsByPurl;
3225
+ if (details.pkgPath === undefined) {
3226
+ return {
3227
+ ok: false,
3228
+ message: 'Missing package.json',
3229
+ cause: cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`)
3230
+ };
3314
3231
  }
3315
- const opts = {
3316
- __proto__: null,
3317
- consolidate: false,
3318
- nothrow: false,
3319
- ...options,
3320
- filter: toFilterConfig(require$$10.getOwn(options, 'filter'))
3321
- };
3322
- if (opts.onlyFixable) {
3323
- opts.filter.fixable = true;
3232
+ if (prod && (agent === BUN || agent === YARN_BERRY)) {
3233
+ return {
3234
+ ok: false,
3235
+ message: 'Bad input',
3236
+ cause: cmdPrefixMessage(cmdName, `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`)
3237
+ };
3324
3238
  }
3325
- const {
3326
- apiToken = getPublicApiToken(),
3327
- spinner
3328
- } = opts;
3329
- const getText = () => `Looking up data for ${remaining} packages`;
3330
- spinner?.start(getText());
3331
- const sockSdkCResult = await setupSdk({
3332
- apiToken
3333
- });
3334
- if (!sockSdkCResult.ok) {
3335
- spinner?.stop();
3336
- throw new Error('Auth error: Run `socket login` first');
3239
+ if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
3240
+ // Note: In tests we return <redacted> because otherwise snapshots will fail.
3241
+ logger?.warn(cmdPrefixMessage(cmdName, `Package ${lockName} found at ${constants.ENV.VITEST ? constants.REDACTED : details.lockPath}`));
3337
3242
  }
3338
- const sockSdk = sockSdkCResult.data;
3339
- const socketYml = findSocketYmlSync()?.parsed;
3340
- const alertsMapOptions = {
3341
- overrides: opts.overrides,
3342
- consolidate: opts.consolidate,
3343
- filter: opts.filter,
3344
- socketYml,
3345
- spinner
3243
+ return {
3244
+ ok: true,
3245
+ data: details
3346
3246
  };
3347
- for await (const batchResult of sockSdk.batchPackageStream({
3348
- components: uniqPurls.map(purl => ({
3349
- purl
3350
- }))
3351
- }, {
3352
- queryParams: {
3353
- alerts: 'true',
3354
- compact: 'true',
3355
- ...(opts.onlyFixable ? {
3356
- fixable: 'true '
3357
- } : {}),
3358
- ...(Array.isArray(opts.filter.actions) ? {
3359
- actions: opts.filter.actions.join(',')
3360
- } : {})
3361
- }
3362
- })) {
3363
- if (batchResult.success) {
3364
- const artifact = batchResult.data;
3365
- await addArtifactToAlertsMap(artifact, alertsByPurl, alertsMapOptions);
3366
- } else if (!opts.nothrow) {
3367
- spinner?.stop();
3368
- if (strings.isNonEmptyString(batchResult.error)) {
3369
- throw new Error(batchResult.error);
3370
- }
3371
- const statusCode = batchResult.status ?? 'unknown';
3372
- throw new Error(`Socket API server error (${statusCode}): No status message`);
3373
- } else {
3374
- spinner?.stop();
3375
- logger.logger.fail(`Received a ${batchResult.status} response from Socket API which we consider a permanent failure:`, batchResult.error, batchResult.cause ? `( ${batchResult.cause} )` : '');
3376
- require$$9.debugDir('inspect', {
3377
- batchResult
3378
- });
3379
- break;
3380
- }
3381
- remaining -= 1;
3382
- if (remaining > 0) {
3383
- spinner?.start(getText());
3384
- }
3385
- }
3386
- spinner?.stop();
3387
- return alertsByPurl;
3388
3247
  }
3389
3248
 
3390
- function npa(...args) {
3391
- try {
3392
- return Reflect.apply(vendor.npaExports, undefined, args);
3393
- } catch {}
3394
- return null;
3249
+ const ALL_ECOSYSTEMS = ['apk', 'bitbucket', 'cargo', 'chrome', 'cocoapods', 'composer', 'conan', 'conda', 'cran', 'deb', 'docker', 'gem', 'generic', 'github', 'golang', 'hackage', 'hex', 'huggingface', 'maven', 'mlflow', 'npm', 'nuget', 'oci', 'pub', 'pypi', 'qpkg', 'rpm', 'swift', 'swid', 'unknown'];
3250
+ new Set(ALL_ECOSYSTEMS);
3251
+ function getEcosystemChoicesForMeow() {
3252
+ return [...ALL_ECOSYSTEMS];
3395
3253
  }
3396
3254
 
3397
- async function removeNodeModules(cwd = process.cwd()) {
3398
- const stream = await globStreamNodeModules(cwd);
3399
- await streams.parallelEach(stream, p => fs.remove(p, {
3400
- force: true,
3401
- recursive: true
3402
- }), {
3403
- concurrency: 8
3255
+ function isArtifactAlertCve(alert) {
3256
+ const {
3257
+ type
3258
+ } = alert;
3259
+ return type === constants.ALERT_TYPE_CVE || type === constants.ALERT_TYPE_MEDIUM_CVE || type === constants.ALERT_TYPE_MILD_CVE || type === constants.ALERT_TYPE_CRITICAL_CVE;
3260
+ }
3261
+
3262
+ function createEnum(obj) {
3263
+ return Object.freeze({
3264
+ __proto__: null,
3265
+ ...obj
3404
3266
  });
3405
3267
  }
3406
- async function findUp(name, {
3407
- cwd = process.cwd(),
3408
- signal = constants.abortSignal
3409
- }) {
3410
- let dir = path.resolve(cwd);
3411
- const {
3412
- root
3413
- } = path.parse(dir);
3414
- const names = [name].flat();
3415
- while (dir && dir !== root) {
3416
- for (const name of names) {
3417
- if (signal?.aborted) {
3418
- return undefined;
3419
- }
3420
- const filePath = path.join(dir, name);
3421
- try {
3422
- // eslint-disable-next-line no-await-in-loop
3423
- const stats = await fs$1.promises.stat(filePath);
3424
- if (stats.isFile()) {
3425
- return filePath;
3426
- }
3427
- } catch {}
3268
+
3269
+ const ALERT_FIX_TYPE = createEnum({
3270
+ cve: 'cve',
3271
+ remove: 'remove',
3272
+ upgrade: 'upgrade'
3273
+ });
3274
+
3275
+ const ALERT_SEVERITY = createEnum({
3276
+ critical: 'critical',
3277
+ high: 'high',
3278
+ middle: 'middle',
3279
+ low: 'low'
3280
+ });
3281
+
3282
+ class ColorOrMarkdown {
3283
+ constructor(useMarkdown) {
3284
+ this.useMarkdown = !!useMarkdown;
3285
+ }
3286
+ bold(text) {
3287
+ return this.useMarkdown ? `**${text}**` : vendor.yoctocolorsCjsExports.bold(`${text}`);
3288
+ }
3289
+ header(text, level = 1) {
3290
+ return this.useMarkdown ? `\n${''.padStart(level, '#')} ${text}\n` : vendor.yoctocolorsCjsExports.underline(`\n${level === 1 ? vendor.yoctocolorsCjsExports.bold(text) : text}\n`);
3291
+ }
3292
+ hyperlink(text, url, {
3293
+ fallback = true,
3294
+ fallbackToUrl
3295
+ } = {}) {
3296
+ if (url) {
3297
+ return this.useMarkdown ? `[${text}](${url})` : vendor.terminalLinkExports(text, url, {
3298
+ fallback: fallbackToUrl ? (_text, url) => url : fallback
3299
+ });
3428
3300
  }
3429
- dir = path.dirname(dir);
3301
+ return text;
3302
+ }
3303
+ indent(...args) {
3304
+ return vendor.indentStringExports(...args);
3305
+ }
3306
+ italic(text) {
3307
+ return this.useMarkdown ? `_${text}_` : vendor.yoctocolorsCjsExports.italic(`${text}`);
3308
+ }
3309
+ json(value) {
3310
+ return this.useMarkdown ? '```json\n' + JSON.stringify(value) + '\n```' : JSON.stringify(value);
3311
+ }
3312
+ list(items) {
3313
+ const indentedContent = items.map(item => this.indent(item).trimStart());
3314
+ return this.useMarkdown ? `* ${indentedContent.join('\n* ')}\n` : `${indentedContent.join('\n')}\n`;
3430
3315
  }
3431
- return undefined;
3432
3316
  }
3433
3317
 
3434
- function shadowNpmInstall(options) {
3435
- const {
3436
- agentExecPath = getNpmBinPath(),
3437
- args = [],
3438
- ipc,
3439
- spinner,
3440
- ...spawnOpts
3441
- } = {
3442
- __proto__: null,
3443
- ...options
3318
+ function toFilterConfig(obj) {
3319
+ const normalized = {
3320
+ __proto__: null
3444
3321
  };
3445
- const useDebug = require$$9.isDebug('stdio');
3446
- const terminatorPos = args.indexOf('--');
3447
- const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos);
3448
- const binArgs = rawBinArgs.filter(a => !npm.isNpmAuditFlag(a) && !npm.isNpmFundFlag(a) && !npm.isNpmProgressFlag(a));
3449
- const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos);
3450
- const progressArg = rawBinArgs.findLast(npm.isNpmProgressFlag) !== '--no-progress';
3451
- const isSilent = !useDebug && !binArgs.some(npm.isNpmLoglevelFlag);
3452
- const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : [];
3453
- const useIpc = require$$10.isObject(ipc);
3454
-
3455
- // Include 'ipc' in the spawnOpts.stdio when an options.ipc object is provided.
3456
- // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166
3457
- // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.
3458
- let stdio = require$$10.getOwn(spawnOpts, 'stdio');
3459
- if (typeof stdio === 'string') {
3460
- stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio];
3461
- } else if (Array.isArray(stdio)) {
3462
- if (useIpc && !stdio.includes('ipc')) {
3463
- stdio = stdio.concat('ipc');
3322
+ const keys = require$$10.isObject(obj) ? Object.keys(obj) : [];
3323
+ for (const key of keys) {
3324
+ const value = obj[key];
3325
+ if (typeof value === 'boolean' || Array.isArray(value)) {
3326
+ normalized[key] = value;
3464
3327
  }
3465
- } else {
3466
- stdio = useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : 'pipe';
3467
- }
3468
- const spawnPromise = spawn.spawn(constants.execPath, [...constants.nodeNoWarningsFlags, ...constants.nodeDebugFlags, ...constants.nodeHardenFlags, ...constants.nodeMemoryFlags, ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD ? ['--require', constants.instrumentWithSentryPath] : []), '--require', constants.shadowNpmInjectPath, npm.resolveBinPathSync(agentExecPath), 'install',
3469
- // Avoid code paths for 'audit' and 'fund'.
3470
- '--no-audit', '--no-fund',
3471
- // Add '--no-progress' to fix input being swallowed by the npm spinner.
3472
- '--no-progress',
3473
- // Add '--loglevel=silent' if a loglevel flag is not provided and the
3474
- // SOCKET_CLI_DEBUG environment variable is not truthy.
3475
- ...logLevelArgs, ...binArgs, ...otherArgs], {
3476
- ...spawnOpts,
3477
- env: {
3478
- ...process.env,
3479
- ...constants.processEnv,
3480
- ...require$$10.getOwn(spawnOpts, 'env')
3481
- },
3482
- spinner,
3483
- stdio
3484
- });
3485
- if (useIpc) {
3486
- spawnPromise.process.send({
3487
- [constants.SOCKET_IPC_HANDSHAKE]: {
3488
- [constants.SOCKET_CLI_SHADOW_BIN]: 'npm',
3489
- [constants.SOCKET_CLI_SHADOW_PROGRESS]: progressArg,
3490
- ...ipc
3491
- }
3492
- });
3493
3328
  }
3494
- return spawnPromise;
3329
+ return normalized;
3495
3330
  }
3496
3331
 
3497
- function runAgentInstall(pkgEnvDetails, options) {
3498
- const {
3499
- agent,
3500
- agentExecPath
3501
- } = pkgEnvDetails;
3502
- const isNpm = agent === 'npm';
3503
- const isPnpm = agent === 'pnpm';
3504
- // All package managers support the "install" command.
3505
- if (isNpm) {
3506
- return shadowNpmInstall({
3507
- agentExecPath,
3508
- ...options
3509
- });
3332
+ const require$1 = Module.createRequire(require('node:url').pathToFileURL(__filename).href);
3333
+ let _translations;
3334
+ function getTranslations() {
3335
+ if (_translations === undefined) {
3336
+ _translations = /*@__PURE__*/require$1(path.join(constants.rootPath, 'translations.json'));
3510
3337
  }
3511
- const {
3512
- args = [],
3513
- spinner,
3514
- ...spawnOpts
3515
- } = {
3516
- __proto__: null,
3517
- ...options
3338
+ return _translations;
3339
+ }
3340
+
3341
+ const ALERT_SEVERITY_COLOR = createEnum({
3342
+ critical: 'magenta',
3343
+ high: 'red',
3344
+ middle: 'yellow',
3345
+ low: 'white'
3346
+ });
3347
+ const ALERT_SEVERITY_ORDER = createEnum({
3348
+ critical: 0,
3349
+ high: 1,
3350
+ middle: 2,
3351
+ low: 3,
3352
+ none: 4
3353
+ });
3354
+ const MIN_ABOVE_THE_FOLD_COUNT = 3;
3355
+ const MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1;
3356
+ const format = new ColorOrMarkdown(false);
3357
+ function getHiddenRiskCounts(hiddenAlerts) {
3358
+ const riskCounts = {
3359
+ critical: 0,
3360
+ high: 0,
3361
+ middle: 0,
3362
+ low: 0
3518
3363
  };
3519
- const skipNodeHardenFlags = isPnpm && pkgEnvDetails.agentVersion.major < 11;
3520
- return spawn.spawn(agentExecPath, ['install', ...args], {
3521
- shell: constants.WIN32,
3522
- spinner,
3523
- stdio: 'inherit',
3524
- ...spawnOpts,
3525
- env: {
3526
- ...process.env,
3527
- ...constants.processEnv,
3528
- NODE_OPTIONS: cmdFlagsToString([...(skipNodeHardenFlags ? [] : constants.nodeHardenFlags), ...constants.nodeNoWarningsFlags]),
3529
- ...require$$10.getOwn(spawnOpts, 'env')
3364
+ for (const alert of hiddenAlerts) {
3365
+ switch (getAlertSeverityOrder(alert)) {
3366
+ case ALERT_SEVERITY_ORDER.critical:
3367
+ riskCounts.critical += 1;
3368
+ break;
3369
+ case ALERT_SEVERITY_ORDER.high:
3370
+ riskCounts.high += 1;
3371
+ break;
3372
+ case ALERT_SEVERITY_ORDER.middle:
3373
+ riskCounts.middle += 1;
3374
+ break;
3375
+ case ALERT_SEVERITY_ORDER.low:
3376
+ riskCounts.low += 1;
3377
+ break;
3530
3378
  }
3531
- });
3379
+ }
3380
+ return riskCounts;
3532
3381
  }
3533
-
3534
- async function getNpmConfig(options) {
3382
+ function getHiddenRisksDescription(riskCounts) {
3383
+ const descriptions = [];
3384
+ if (riskCounts.critical) {
3385
+ descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`);
3386
+ }
3387
+ if (riskCounts.high) {
3388
+ descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`);
3389
+ }
3390
+ if (riskCounts.middle) {
3391
+ descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`);
3392
+ }
3393
+ if (riskCounts.low) {
3394
+ descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`);
3395
+ }
3396
+ return `(${descriptions.join('; ')})`;
3397
+ }
3398
+ async function addArtifactToAlertsMap(artifact, alertsByPurl, options) {
3399
+ // Make TypeScript happy.
3400
+ if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
3401
+ return alertsByPurl;
3402
+ }
3535
3403
  const {
3536
- cwd = process.cwd(),
3537
- env = process.env,
3538
- execPath = process.execPath,
3539
- nodeVersion = process.version,
3540
- npmCommand = 'install',
3541
- npmPath = getNpmDirPath(),
3542
- npmVersion,
3543
- platform = process.platform
3404
+ type: ecosystem,
3405
+ version
3406
+ } = artifact;
3407
+ const {
3408
+ consolidate = false,
3409
+ overrides,
3410
+ socketYml
3544
3411
  } = {
3545
3412
  __proto__: null,
3546
3413
  ...options
3547
3414
  };
3548
- const config = new vendor.libExports$2({
3549
- argv: [],
3550
- cwd,
3551
- definitions: vendor.definitionsExports.definitions,
3552
- execPath,
3553
- env: {
3554
- ...env
3555
- },
3556
- flatten: vendor.definitionsExports.flatten,
3557
- npmPath,
3558
- platform,
3559
- shorthands: vendor.definitionsExports.shorthands
3415
+ const name = packages.resolvePackageName(artifact);
3416
+ const filterConfig = toFilterConfig({
3417
+ blocked: true,
3418
+ critical: true,
3419
+ cve: true,
3420
+ ...require$$10.getOwn(options, 'filter')
3560
3421
  });
3561
- await config.load();
3562
- const flatConfig = {
3422
+ const enabledState = {
3563
3423
  __proto__: null,
3564
- ...config.flat
3424
+ ...socketYml?.issueRules
3565
3425
  };
3566
- if (nodeVersion) {
3567
- flatConfig.nodeVersion = nodeVersion;
3568
- }
3569
- if (npmCommand) {
3570
- flatConfig.npmCommand = npmCommand;
3571
- }
3572
- if (npmVersion) {
3573
- flatConfig.npmVersion = npmVersion.toString();
3574
- }
3575
- return flatConfig;
3576
- }
3577
-
3578
- async function readLockfile(lockfilePath) {
3579
- return fs$1.existsSync(lockfilePath) ? await fs.readFileUtf8(lockfilePath) : null;
3580
- }
3581
-
3582
- const {
3583
- BINARY_LOCK_EXT,
3584
- BUN,
3585
- HIDDEN_PACKAGE_LOCK_JSON,
3586
- LOCK_EXT,
3587
- NPM,
3588
- NPM_BUGGY_OVERRIDES_PATCHED_VERSION,
3589
- PACKAGE_JSON,
3590
- PNPM,
3591
- VLT,
3592
- YARN,
3593
- YARN_BERRY,
3594
- YARN_CLASSIC
3595
- } = constants;
3596
- const AGENTS = [BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT];
3597
- const binByAgent = new Map([[BUN, BUN], [NPM, NPM], [PNPM, PNPM], [YARN_BERRY, YARN], [YARN_CLASSIC, YARN], [VLT, VLT]]);
3598
- const readLockFileByAgent = (() => {
3599
- function wrapReader(reader) {
3600
- return async (...args) => {
3601
- try {
3602
- return await reader(...args);
3603
- } catch {}
3604
- return undefined;
3605
- };
3606
- }
3607
- const binaryReader = wrapReader(fs.readFileBinary);
3608
- const defaultReader = wrapReader(async lockPath => await fs.readFileUtf8(lockPath));
3609
- return new Map([[BUN, wrapReader(async (lockPath, agentExecPath, cwd = process.cwd()) => {
3610
- const ext = path.extname(lockPath);
3611
- if (ext === LOCK_EXT) {
3612
- return await defaultReader(lockPath);
3613
- }
3614
- if (ext === BINARY_LOCK_EXT) {
3615
- const lockBuffer = await binaryReader(lockPath);
3616
- if (lockBuffer) {
3617
- try {
3618
- return vendor.hyrious__bun_lockbExports.parse(lockBuffer);
3619
- } catch {}
3620
- }
3621
- // To print a Yarn lockfile to your console without writing it to disk
3622
- // use `bun bun.lockb`.
3623
- // https://bun.sh/guides/install/yarnlock
3624
- return (await spawn.spawn(agentExecPath, [lockPath], {
3625
- cwd,
3626
- shell: constants.WIN32
3627
- })).stdout;
3426
+ let sockPkgAlerts = [];
3427
+ for (const alert of artifact.alerts) {
3428
+ const action = alert.action ?? '';
3429
+ const enabledFlag = enabledState[alert.type];
3430
+ if (action === 'ignore' && enabledFlag !== true || enabledFlag === false) {
3431
+ continue;
3628
3432
  }
3629
- return undefined;
3630
- })], [NPM, defaultReader], [PNPM, defaultReader], [VLT, defaultReader], [YARN_BERRY, defaultReader], [YARN_CLASSIC, defaultReader]]);
3631
- })();
3632
-
3633
- // The order of LOCKS properties IS significant as it affects iteration order.
3634
- const LOCKS = {
3635
- [`bun${LOCK_EXT}`]: BUN,
3636
- [`bun${BINARY_LOCK_EXT}`]: BUN,
3637
- // If both package-lock.json and npm-shrinkwrap.json are present in the root
3638
- // of a project, npm-shrinkwrap.json will take precedence and package-lock.json
3639
- // will be ignored.
3640
- // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson
3641
- 'npm-shrinkwrap.json': NPM,
3642
- 'package-lock.json': NPM,
3643
- 'pnpm-lock.yaml': PNPM,
3644
- 'pnpm-lock.yml': PNPM,
3645
- [`yarn${LOCK_EXT}`]: YARN_CLASSIC,
3646
- 'vlt-lock.json': VLT,
3647
- // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:
3648
- // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles
3649
- //
3650
- // Unlike the other LOCKS keys this key contains a directory AND filename so
3651
- // it has to be handled differently.
3652
- 'node_modules/.package-lock.json': NPM
3653
- };
3654
- async function getAgentExecPath(agent) {
3655
- const binName = binByAgent.get(agent);
3656
- if (binName === NPM) {
3657
- return constants.npmExecPath;
3658
- }
3659
- return (await vendor.libExports$1(binName, {
3660
- nothrow: true
3661
- })) ?? binName;
3662
- }
3663
- async function getAgentVersion(agent, agentExecPath, cwd) {
3664
- let result;
3665
- const quotedCmd = `\`${agent} --version\``;
3666
- require$$9.debugFn('stdio', `spawn: ${quotedCmd}`);
3667
- try {
3668
- result =
3669
- // Coerce version output into a valid semver version by passing it through
3670
- // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
3671
- // and tildes (~).
3672
- vendor.semverExports.coerce(
3673
- // All package managers support the "--version" flag.
3674
- (await spawn.spawn(agentExecPath, ['--version'], {
3675
- cwd,
3676
- shell: constants.WIN32
3677
- })).stdout) ?? undefined;
3678
- } catch (e) {
3679
- require$$9.debugFn('error', `caught: ${quotedCmd} failed`);
3680
- require$$9.debugDir('inspect', {
3681
- error: e
3682
- });
3683
- }
3684
- return result;
3685
- }
3686
- async function detectPackageEnvironment({
3687
- cwd = process.cwd(),
3688
- onUnknown
3689
- } = {}) {
3690
- let lockPath = await findUp(Object.keys(LOCKS), {
3691
- cwd
3692
- });
3693
- let lockName = lockPath ? path.basename(lockPath) : undefined;
3694
- const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON;
3695
- const pkgJsonPath = lockPath ? path.resolve(lockPath, `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`) : await findUp(PACKAGE_JSON, {
3696
- cwd
3697
- });
3698
- const pkgPath = pkgJsonPath && fs$1.existsSync(pkgJsonPath) ? path.dirname(pkgJsonPath) : undefined;
3699
- const editablePkgJson = pkgPath ? await packages.readPackageJson(pkgPath, {
3700
- editable: true
3701
- }) : undefined;
3702
- // Read Corepack `packageManager` field in package.json:
3703
- // https://nodejs.org/api/packages.html#packagemanager
3704
- const pkgManager = strings.isNonEmptyString(editablePkgJson?.content?.packageManager) ? editablePkgJson.content.packageManager : undefined;
3705
- let agent;
3706
- if (pkgManager) {
3707
- // A valid "packageManager" field value is "<package manager name>@<version>".
3708
- // https://nodejs.org/api/packages.html#packagemanager
3709
- const atSignIndex = pkgManager.lastIndexOf('@');
3710
- if (atSignIndex !== -1) {
3711
- const name = pkgManager.slice(0, atSignIndex);
3712
- const version = pkgManager.slice(atSignIndex + 1);
3713
- if (version && AGENTS.includes(name)) {
3714
- agent = name;
3715
- }
3433
+ const blocked = action === 'error';
3434
+ const critical = alert.severity === ALERT_SEVERITY.critical;
3435
+ const cve = isArtifactAlertCve(alert);
3436
+ const fixType = alert.fix?.type ?? '';
3437
+ const fixableCve = fixType === ALERT_FIX_TYPE.cve;
3438
+ const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade;
3439
+ const fixable = fixableCve || fixableUpgrade;
3440
+ const upgradable = fixableUpgrade && !require$$10.hasOwn(overrides, name);
3441
+ if (filterConfig.blocked && blocked || filterConfig.critical && critical || filterConfig.cve && cve || filterConfig.fixable && fixable || filterConfig.upgradable && upgradable) {
3442
+ sockPkgAlerts.push({
3443
+ name,
3444
+ version,
3445
+ key: alert.key,
3446
+ type: alert.type,
3447
+ blocked,
3448
+ critical,
3449
+ ecosystem,
3450
+ fixable,
3451
+ raw: alert,
3452
+ upgradable
3453
+ });
3716
3454
  }
3717
3455
  }
3718
- if (agent === undefined && !isHiddenLockFile && typeof pkgJsonPath === 'string' && typeof lockName === 'string') {
3719
- agent = LOCKS[lockName];
3720
- }
3721
- if (agent === undefined) {
3722
- agent = NPM;
3723
- onUnknown?.(pkgManager);
3724
- }
3725
- const agentExecPath = await getAgentExecPath(agent);
3726
- const agentVersion = await getAgentVersion(agent, agentExecPath, cwd);
3727
- if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {
3728
- agent = YARN_BERRY;
3456
+ if (!sockPkgAlerts.length) {
3457
+ return alertsByPurl;
3729
3458
  }
3730
- const {
3731
- maintainedNodeVersions
3732
- } = constants;
3733
- const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent);
3734
- const minSupportedNodeMajor = vendor.semverExports.major(maintainedNodeVersions.last);
3735
- const minSupportedNodeVersion = `${minSupportedNodeMajor}.0.0`;
3736
- const minSupportedNodeRange = `>=${minSupportedNodeMajor}`;
3737
- const nodeVersion = vendor.semverExports.coerce(process.version);
3738
- let lockSrc;
3739
- let pkgAgentRange;
3740
- let pkgNodeRange;
3741
- let pkgMinAgentVersion = minSupportedAgentVersion;
3742
- let pkgMinNodeVersion = minSupportedNodeVersion;
3743
- if (editablePkgJson?.content) {
3744
- const {
3745
- engines
3746
- } = editablePkgJson.content;
3747
- const engineAgentRange = engines?.[agent];
3748
- const engineNodeRange = engines?.['node'];
3749
- if (strings.isNonEmptyString(engineAgentRange)) {
3750
- pkgAgentRange = engineAgentRange;
3751
- // Roughly check agent range as semver.coerce will strip leading
3752
- // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3753
- const coerced = vendor.semverExports.coerce(pkgAgentRange);
3754
- if (coerced && vendor.semverExports.lt(coerced, pkgMinAgentVersion)) {
3755
- pkgMinAgentVersion = coerced.version;
3756
- }
3757
- }
3758
- if (strings.isNonEmptyString(engineNodeRange)) {
3759
- pkgNodeRange = engineNodeRange;
3760
- // Roughly check Node range as semver.coerce will strip leading
3761
- // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3762
- const coerced = vendor.semverExports.coerce(pkgNodeRange);
3763
- if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
3764
- pkgMinNodeVersion = coerced.version;
3765
- }
3766
- }
3767
- const browserslistQuery = editablePkgJson.content['browserslist'];
3768
- if (Array.isArray(browserslistQuery)) {
3769
- // List Node targets in ascending version order.
3770
- const browserslistNodeTargets = vendor.browserslistExports(browserslistQuery).filter(v => /^node /i.test(v)).map(v => v.slice(5 /*'node '.length*/)).sort(sorts.naturalCompare);
3771
- if (browserslistNodeTargets.length) {
3772
- // browserslistNodeTargets[0] is the lowest Node target version.
3773
- const coerced = vendor.semverExports.coerce(browserslistNodeTargets[0]);
3774
- if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
3775
- pkgMinNodeVersion = coerced.version;
3459
+ const purl = `pkg:${ecosystem}/${name}@${version}`;
3460
+ const major = getMajor(version);
3461
+ if (consolidate) {
3462
+ const highestForCve = new Map();
3463
+ const highestForUpgrade = new Map();
3464
+ const unfixableAlerts = [];
3465
+ for (const sockPkgAlert of sockPkgAlerts) {
3466
+ const alert = sockPkgAlert.raw;
3467
+ const fixType = alert.fix?.type ?? '';
3468
+ if (fixType === ALERT_FIX_TYPE.cve) {
3469
+ // An alert with alert.fix.type of 'cve' should have a
3470
+ // alert.props.firstPatchedVersionIdentifier property value.
3471
+ // We're just being cautious.
3472
+ const firstPatchedVersionIdentifier = alert.props?.firstPatchedVersionIdentifier;
3473
+ const patchedMajor = firstPatchedVersionIdentifier ? getMajor(firstPatchedVersionIdentifier) : null;
3474
+ if (typeof patchedMajor === 'number') {
3475
+ // Consolidate to the highest "first patched version" by each major
3476
+ // version number.
3477
+ const highest = highestForCve.get(patchedMajor)?.version ?? '0.0.0';
3478
+ if (vendor.semverExports.gt(firstPatchedVersionIdentifier, highest)) {
3479
+ highestForCve.set(patchedMajor, {
3480
+ alert: sockPkgAlert,
3481
+ version: firstPatchedVersionIdentifier
3482
+ });
3483
+ }
3484
+ } else {
3485
+ unfixableAlerts.push(sockPkgAlert);
3486
+ }
3487
+ } else if (fixType === ALERT_FIX_TYPE.upgrade) {
3488
+ // For Socket Optimize upgrades we assume the highest version available
3489
+ // is compatible. This may change in the future.
3490
+ const highest = highestForUpgrade.get(major)?.version ?? '0.0.0';
3491
+ if (vendor.semverExports.gt(version, highest)) {
3492
+ highestForUpgrade.set(major, {
3493
+ alert: sockPkgAlert,
3494
+ version
3495
+ });
3776
3496
  }
3497
+ } else {
3498
+ unfixableAlerts.push(sockPkgAlert);
3777
3499
  }
3778
3500
  }
3779
- lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath, cwd) : undefined;
3501
+ sockPkgAlerts = [
3502
+ // Sort CVE alerts by severity: critical, high, middle, then low.
3503
+ ...Array.from(highestForCve.values()).map(d => d.alert).sort(alertSeverityComparator), ...Array.from(highestForUpgrade.values()).map(d => d.alert), ...unfixableAlerts];
3780
3504
  } else {
3781
- lockName = undefined;
3782
- lockPath = undefined;
3505
+ sockPkgAlerts.sort((a, b) => sorts.naturalCompare(a.type, b.type));
3783
3506
  }
3784
-
3785
- // Does the system agent version meet our minimum supported agent version?
3786
- const agentSupported = !!agentVersion && vendor.semverExports.satisfies(agentVersion, `>=${minSupportedAgentVersion}`);
3787
- // Does the system Node version meet our minimum supported Node version?
3788
- const nodeSupported = vendor.semverExports.satisfies(nodeVersion, minSupportedNodeRange);
3789
- const npmExecPath = agent === NPM ? agentExecPath : await getAgentExecPath(NPM);
3790
- const npmBuggyOverrides = agent === NPM && !!agentVersion && vendor.semverExports.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION);
3791
- const pkgMinAgentRange = `>=${pkgMinAgentVersion}`;
3792
- const pkgMinNodeRange = `>=${vendor.semverExports.major(pkgMinNodeVersion)}`;
3793
- return {
3794
- agent,
3795
- agentExecPath,
3796
- agentSupported,
3797
- agentVersion,
3798
- editablePkgJson,
3799
- features: {
3800
- npmBuggyOverrides
3801
- },
3802
- lockName,
3803
- lockPath,
3804
- lockSrc,
3805
- nodeSupported,
3806
- nodeVersion,
3807
- npmExecPath,
3808
- pkgPath,
3809
- pkgRequirements: {
3810
- agent: pkgAgentRange ?? pkgMinAgentRange,
3811
- node: pkgNodeRange ?? pkgMinNodeRange
3812
- },
3813
- pkgSupports: {
3814
- // Does our minimum supported agent version meet the package's requirements?
3815
- agent: vendor.semverExports.satisfies(minSupportedAgentVersion, pkgMinAgentRange),
3816
- // Does our supported Node versions meet the package's requirements?
3817
- node: maintainedNodeVersions.some(v => vendor.semverExports.satisfies(v, pkgMinNodeRange))
3818
- }
3819
- };
3507
+ if (sockPkgAlerts.length) {
3508
+ alertsByPurl.set(purl, sockPkgAlerts);
3509
+ }
3510
+ return alertsByPurl;
3820
3511
  }
3821
- async function detectAndValidatePackageEnvironment(cwd, options) {
3512
+ function alertsHaveBlocked(alerts) {
3513
+ return alerts.find(a => a.blocked) !== undefined;
3514
+ }
3515
+ function alertsHaveSeverity(alerts, severity) {
3516
+ return alerts.find(a => a.raw.severity === severity) !== undefined;
3517
+ }
3518
+ function alertSeverityComparator(a, b) {
3519
+ // Put the most severe first.
3520
+ return getAlertSeverityOrder(a) - getAlertSeverityOrder(b);
3521
+ }
3522
+ function getAlertSeverityOrder(alert) {
3523
+ // The more severe, the lower the sort number.
3822
3524
  const {
3823
- cmdName = '',
3824
- logger,
3825
- prod
3525
+ severity
3526
+ } = alert.raw;
3527
+ return severity === ALERT_SEVERITY.critical ? 0 : severity === ALERT_SEVERITY.high ? 1 : severity === ALERT_SEVERITY.middle ? 2 : severity === ALERT_SEVERITY.low ? 3 : 4;
3528
+ }
3529
+ function getAlertsSeverityOrder(alerts) {
3530
+ return alertsHaveBlocked(alerts) || alertsHaveSeverity(alerts, ALERT_SEVERITY.critical) ? 0 : alertsHaveSeverity(alerts, ALERT_SEVERITY.high) ? 1 : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle) ? 2 : alertsHaveSeverity(alerts, ALERT_SEVERITY.low) ? 3 : 4;
3531
+ }
3532
+ function getSeverityLabel(severity) {
3533
+ return severity === 'middle' ? 'moderate' : severity;
3534
+ }
3535
+ function logAlertsMap(alertsMap, options) {
3536
+ const {
3537
+ hideAt = 'middle',
3538
+ output = process.stderr
3826
3539
  } = {
3827
3540
  __proto__: null,
3828
3541
  ...options
3829
3542
  };
3830
- const details = await detectPackageEnvironment({
3831
- cwd,
3832
- onUnknown(pkgManager) {
3833
- logger?.warn(cmdPrefixMessage(cmdName, `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`));
3543
+ const translations = getTranslations();
3544
+ const sortedEntries = Array.from(alertsMap.entries()).sort((a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1]));
3545
+ const aboveTheFoldPurls = new Set();
3546
+ const viewableAlertsByPurl = new Map();
3547
+ const hiddenAlertsByPurl = new Map();
3548
+ for (let i = 0, {
3549
+ length
3550
+ } = sortedEntries; i < length; i += 1) {
3551
+ const {
3552
+ 0: purl,
3553
+ 1: alerts
3554
+ } = sortedEntries[i];
3555
+ const hiddenAlerts = [];
3556
+ const viewableAlerts = alerts.filter(a => {
3557
+ const keep = a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt];
3558
+ if (!keep) {
3559
+ hiddenAlerts.push(a);
3560
+ }
3561
+ return keep;
3562
+ });
3563
+ if (hiddenAlerts.length) {
3564
+ hiddenAlertsByPurl.set(purl, hiddenAlerts.sort(alertSeverityComparator));
3565
+ }
3566
+ if (!viewableAlerts.length) {
3567
+ continue;
3568
+ }
3569
+ viewableAlerts.sort(alertSeverityComparator);
3570
+ viewableAlertsByPurl.set(purl, viewableAlerts);
3571
+ if (viewableAlerts.find(a => a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle)) {
3572
+ aboveTheFoldPurls.add(purl);
3834
3573
  }
3835
- });
3836
- const {
3837
- agent,
3838
- nodeVersion,
3839
- pkgRequirements
3840
- } = details;
3841
- const agentVersion = details.agentVersion ?? 'unknown';
3842
- if (!details.agentSupported) {
3843
- const minVersion = constants.minimumVersionByAgent.get(agent);
3844
- return {
3845
- ok: false,
3846
- message: 'Version mismatch',
3847
- cause: cmdPrefixMessage(cmdName, `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`)
3848
- };
3849
- }
3850
- if (!details.nodeSupported) {
3851
- const minVersion = constants.maintainedNodeVersions.last;
3852
- return {
3853
- ok: false,
3854
- message: 'Version mismatch',
3855
- cause: cmdPrefixMessage(cmdName, `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`)
3856
- };
3857
- }
3858
- if (!details.pkgSupports.agent) {
3859
- return {
3860
- ok: false,
3861
- message: 'Engine mismatch',
3862
- cause: cmdPrefixMessage(cmdName, `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`)
3863
- };
3864
- }
3865
- if (!details.pkgSupports.node) {
3866
- return {
3867
- ok: false,
3868
- message: 'Version mismatch',
3869
- cause: cmdPrefixMessage(cmdName, `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`)
3870
- };
3871
- }
3872
- const lockName = details.lockName ?? 'lock file';
3873
- if (details.lockName === undefined || details.lockSrc === undefined) {
3874
- return {
3875
- ok: false,
3876
- message: 'Missing lockfile',
3877
- cause: cmdPrefixMessage(cmdName, `No ${lockName} found`)
3878
- };
3879
3574
  }
3880
- if (details.lockSrc.trim() === '') {
3881
- return {
3882
- ok: false,
3883
- message: 'Empty lockfile',
3884
- cause: cmdPrefixMessage(cmdName, `${lockName} is empty`)
3885
- };
3575
+
3576
+ // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.
3577
+ for (const {
3578
+ 0: purl
3579
+ } of viewableAlertsByPurl.entries()) {
3580
+ if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {
3581
+ break;
3582
+ }
3583
+ aboveTheFoldPurls.add(purl);
3886
3584
  }
3887
- if (details.pkgPath === undefined) {
3888
- return {
3889
- ok: false,
3890
- message: 'Missing package.json',
3891
- cause: cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`)
3892
- };
3585
+ // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.
3586
+ for (const {
3587
+ 0: purl,
3588
+ 1: hiddenAlerts
3589
+ } of hiddenAlertsByPurl.entries()) {
3590
+ if (aboveTheFoldPurls.size >= MIN_ABOVE_THE_FOLD_COUNT) {
3591
+ break;
3592
+ }
3593
+ aboveTheFoldPurls.add(purl);
3594
+ const viewableAlerts = viewableAlertsByPurl.get(purl) ?? [];
3595
+ if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {
3596
+ const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length;
3597
+ let removedHiddenAlerts;
3598
+ if (hiddenAlerts.length - neededCount > 0) {
3599
+ removedHiddenAlerts = hiddenAlerts.splice(0, MIN_ABOVE_THE_FOLD_ALERT_COUNT);
3600
+ } else {
3601
+ removedHiddenAlerts = hiddenAlerts;
3602
+ hiddenAlertsByPurl.delete(purl);
3603
+ }
3604
+ viewableAlertsByPurl.set(purl, [...viewableAlerts, ...removedHiddenAlerts]);
3605
+ }
3893
3606
  }
3894
- if (prod && (agent === BUN || agent === YARN_BERRY)) {
3895
- return {
3896
- ok: false,
3897
- message: 'Bad input',
3898
- cause: cmdPrefixMessage(cmdName, `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`)
3899
- };
3607
+ const mentionedPurlsWithHiddenAlerts = new Set();
3608
+ for (let i = 0, prevAboveTheFold = true, entries = Array.from(viewableAlertsByPurl.entries()), {
3609
+ length
3610
+ } = entries; i < length; i += 1) {
3611
+ const {
3612
+ 0: purl,
3613
+ 1: alerts
3614
+ } = entries[i];
3615
+ const lines = new Set();
3616
+ for (const alert of alerts) {
3617
+ const {
3618
+ type
3619
+ } = alert;
3620
+ const severity = alert.raw.severity ?? '';
3621
+ const attributes = [...(severity ? [vendor.yoctocolorsCjsExports[ALERT_SEVERITY_COLOR[severity]](getSeverityLabel(severity))] : []), ...(alert.blocked ? [vendor.yoctocolorsCjsExports.bold(vendor.yoctocolorsCjsExports.red('blocked'))] : []), ...(alert.fixable ? ['fixable'] : [])];
3622
+ const maybeAttributes = attributes.length ? ` ${vendor.yoctocolorsCjsExports.italic(`(${attributes.join('; ')})`)}` : '';
3623
+ // Based data from { pageProps: { alertTypes } } of:
3624
+ // https://socket.dev/_next/data/9a6db8224b68b6da0eb9f7dbb17aff7e51568ac2/en-US.json
3625
+ const info = translations.alerts[type];
3626
+ const title = info?.title ?? type;
3627
+ const maybeDesc = info?.description ? ` - ${info.description}` : '';
3628
+ const content = `${title}${maybeAttributes}${maybeDesc}`;
3629
+ // TODO: An added emoji seems to mis-align terminals sometimes.
3630
+ lines.add(` ${content}`);
3631
+ }
3632
+ const purlObj = getPurlObject(purl);
3633
+ const pkgName = packages.resolvePackageName(purlObj);
3634
+ const hyperlink = format.hyperlink(`${pkgName}@${purlObj.version}`, getSocketDevPackageOverviewUrl(purlObj.type, pkgName, purlObj.version));
3635
+ const isAboveTheFold = aboveTheFoldPurls.has(purl);
3636
+ if (isAboveTheFold) {
3637
+ aboveTheFoldPurls.add(purl);
3638
+ output.write(`${i ? '\n' : ''}${hyperlink}:\n`);
3639
+ } else {
3640
+ output.write(`${prevAboveTheFold ? '\n' : ''}${hyperlink}:\n`);
3641
+ }
3642
+ for (const line of lines) {
3643
+ output.write(`${line}\n`);
3644
+ }
3645
+ const hiddenAlerts = hiddenAlertsByPurl.get(purl) ?? [];
3646
+ const {
3647
+ length: hiddenAlertsCount
3648
+ } = hiddenAlerts;
3649
+ if (hiddenAlertsCount) {
3650
+ mentionedPurlsWithHiddenAlerts.add(purl);
3651
+ if (hiddenAlertsCount === 1) {
3652
+ output.write(` ${vendor.yoctocolorsCjsExports.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0].raw.severity ?? 'low')} risk alert`)}\n`);
3653
+ } else {
3654
+ output.write(` ${vendor.yoctocolorsCjsExports.dim(`+${hiddenAlertsCount} Hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\n`);
3655
+ }
3656
+ }
3657
+ prevAboveTheFold = isAboveTheFold;
3900
3658
  }
3901
- if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
3902
- // Note: In tests we return <redacted> because otherwise snapshots will fail.
3903
- logger?.warn(cmdPrefixMessage(cmdName, `Package ${lockName} found at ${constants.ENV.VITEST ? constants.REDACTED : details.lockPath}`));
3659
+ const additionalHiddenCount = hiddenAlertsByPurl.size - mentionedPurlsWithHiddenAlerts.size;
3660
+ if (additionalHiddenCount) {
3661
+ const totalRiskCounts = {
3662
+ critical: 0,
3663
+ high: 0,
3664
+ middle: 0,
3665
+ low: 0
3666
+ };
3667
+ for (const {
3668
+ 0: purl,
3669
+ 1: alerts
3670
+ } of hiddenAlertsByPurl.entries()) {
3671
+ if (mentionedPurlsWithHiddenAlerts.has(purl)) {
3672
+ continue;
3673
+ }
3674
+ const riskCounts = getHiddenRiskCounts(alerts);
3675
+ totalRiskCounts.critical += riskCounts.critical;
3676
+ totalRiskCounts.high += riskCounts.high;
3677
+ totalRiskCounts.middle += riskCounts.middle;
3678
+ totalRiskCounts.low += riskCounts.low;
3679
+ }
3680
+ output.write(`${aboveTheFoldPurls.size ? '\n' : ''}${vendor.yoctocolorsCjsExports.dim(`${aboveTheFoldPurls.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\n`);
3904
3681
  }
3905
- return {
3906
- ok: true,
3907
- data: details
3908
- };
3682
+ output.write('\n');
3909
3683
  }
3910
3684
 
3911
- const COMPLETION_CMD_PREFIX = 'complete -F _socket_completion';
3912
- function getCompletionSourcingCommand() {
3913
- // Note: this is exported to distPath in .config/rollup.dist.config.mjs
3914
- const completionScriptExportPath = path.join(constants.distPath, 'socket-completion.bash');
3915
- if (!fs$1.existsSync(completionScriptExportPath)) {
3916
- return {
3917
- ok: false,
3918
- message: 'Tab Completion script not found',
3919
- cause: `Expected to find completion script at \`${completionScriptExportPath}\` but it was not there`
3920
- };
3685
+ function idToNpmPurl(id) {
3686
+ return `pkg:npm/${id}`;
3687
+ }
3688
+
3689
+ async function getAlertsMapFromPurls(purls, options) {
3690
+ const uniqPurls = arrays.arrayUnique(purls);
3691
+ require$$9.debugDir('silly', {
3692
+ purls: uniqPurls
3693
+ });
3694
+ let {
3695
+ length: remaining
3696
+ } = uniqPurls;
3697
+ const alertsByPurl = new Map();
3698
+ if (!remaining) {
3699
+ return alertsByPurl;
3921
3700
  }
3922
- return {
3923
- ok: true,
3924
- data: `source ${completionScriptExportPath}`
3701
+ const opts = {
3702
+ __proto__: null,
3703
+ consolidate: false,
3704
+ nothrow: false,
3705
+ ...options,
3706
+ filter: toFilterConfig(require$$10.getOwn(options, 'filter'))
3925
3707
  };
3926
- }
3927
- function getBashrcDetails(targetCommandName) {
3928
- const sourcingCommand = getCompletionSourcingCommand();
3929
- if (!sourcingCommand.ok) {
3930
- return sourcingCommand;
3708
+ if (opts.onlyFixable) {
3709
+ opts.filter.fixable = true;
3931
3710
  }
3932
3711
  const {
3933
- socketAppDataPath
3934
- } = constants;
3935
- if (!socketAppDataPath) {
3936
- return {
3937
- ok: false,
3938
- message: 'Could not determine config directory',
3939
- cause: 'Failed to get config path'
3940
- };
3712
+ apiToken = getPublicApiToken(),
3713
+ spinner
3714
+ } = opts;
3715
+ const getText = () => `Looking up data for ${remaining} packages`;
3716
+ spinner?.start(getText());
3717
+ const sockSdkCResult = await setupSdk({
3718
+ apiToken
3719
+ });
3720
+ if (!sockSdkCResult.ok) {
3721
+ spinner?.stop();
3722
+ throw new Error('Auth error: Run `socket login` first');
3941
3723
  }
3942
-
3943
- // _socket_completion is the function defined in our completion bash script
3944
- const completionCommand = `${COMPLETION_CMD_PREFIX} ${targetCommandName}`;
3945
-
3946
- // Location of completion script in config after installing
3947
- const completionScriptPath = path.join(path.dirname(socketAppDataPath), 'completion', 'socket-completion.bash');
3948
- const bashrcContent = `# Socket CLI completion for "${targetCommandName}"
3949
- if [ -f "${completionScriptPath}" ]; then
3950
- # Load the tab completion script
3951
- source "${completionScriptPath}"
3952
- # Tell bash to use this function for tab completion of this function
3953
- ${completionCommand}
3954
- fi
3955
- `;
3956
- return {
3957
- ok: true,
3958
- data: {
3959
- sourcingCommand: sourcingCommand.data,
3960
- completionCommand,
3961
- toAddToBashrc: bashrcContent,
3962
- targetName: targetCommandName,
3963
- targetPath: completionScriptPath
3964
- }
3724
+ const sockSdk = sockSdkCResult.data;
3725
+ const socketYml = findSocketYmlSync()?.parsed;
3726
+ const alertsMapOptions = {
3727
+ consolidate: opts.consolidate,
3728
+ filter: opts.filter,
3729
+ overrides: opts.overrides,
3730
+ socketYml,
3731
+ spinner
3965
3732
  };
3966
- }
3967
-
3968
- const {
3969
- kInternalsSymbol,
3970
- [kInternalsSymbol]: {
3971
- getSentry
3972
- }
3973
- } = constants;
3974
- class AuthError extends Error {}
3975
- class InputError extends Error {
3976
- constructor(message, body) {
3977
- super(message);
3978
- this.body = body;
3979
- }
3980
- }
3981
- async function captureException(exception, hint) {
3982
- const result = captureExceptionSync(exception, hint);
3983
- // "Sleep" for a second, just in case, hopefully enough time to initiate fetch.
3984
- await promises.setTimeout(1000);
3985
- return result;
3986
- }
3987
- function captureExceptionSync(exception, hint) {
3988
- const Sentry = getSentry();
3989
- if (!Sentry) {
3990
- return '';
3733
+ try {
3734
+ for await (const batchResult of sockSdk.batchPackageStream({
3735
+ components: uniqPurls.map(purl => ({
3736
+ purl
3737
+ }))
3738
+ }, {
3739
+ queryParams: {
3740
+ alerts: 'true',
3741
+ compact: 'true',
3742
+ ...(opts.onlyFixable ? {
3743
+ fixable: 'true '
3744
+ } : {}),
3745
+ ...(Array.isArray(opts.filter.actions) ? {
3746
+ actions: opts.filter.actions.join(',')
3747
+ } : {})
3748
+ }
3749
+ })) {
3750
+ if (batchResult.success) {
3751
+ const artifact = batchResult.data;
3752
+ await addArtifactToAlertsMap(artifact, alertsByPurl, alertsMapOptions);
3753
+ } else if (!opts.nothrow) {
3754
+ spinner?.stop();
3755
+ if (strings.isNonEmptyString(batchResult.error)) {
3756
+ throw new Error(batchResult.error);
3757
+ }
3758
+ const statusCode = batchResult.status ?? 'unknown';
3759
+ throw new Error(`Socket API server error (${statusCode}): No status message`);
3760
+ } else {
3761
+ spinner?.stop();
3762
+ logger.logger.fail(`Received a ${batchResult.status} response from Socket API which we consider a permanent failure:`, batchResult.error, batchResult.cause ? `( ${batchResult.cause} )` : '');
3763
+ require$$9.debugDir('inspect', {
3764
+ batchResult
3765
+ });
3766
+ break;
3767
+ }
3768
+ remaining -= 1;
3769
+ if (remaining > 0) {
3770
+ spinner?.start(getText());
3771
+ }
3772
+ }
3773
+ } catch (e) {
3774
+ spinner?.stop();
3775
+ throw e;
3991
3776
  }
3992
- require$$9.debugFn('notice', 'send: exception to Sentry');
3993
- return Sentry.captureException(exception, hint);
3994
- }
3995
-
3996
- const ALL_ECOSYSTEMS = ['apk', 'bitbucket', 'cargo', 'chrome', 'cocoapods', 'composer', 'conan', 'conda', 'cran', 'deb', 'docker', 'gem', 'generic', 'github', 'golang', 'hackage', 'hex', 'huggingface', 'maven', 'mlflow', 'npm', 'nuget', 'oci', 'pub', 'pypi', 'qpkg', 'rpm', 'swift', 'swid', 'unknown'];
3997
- new Set(ALL_ECOSYSTEMS);
3998
- function getEcosystemChoicesForMeow() {
3999
- return [...ALL_ECOSYSTEMS];
3777
+ spinner?.stop();
3778
+ return alertsByPurl;
4000
3779
  }
4001
3780
 
4002
3781
  exports.AuthError = AuthError;
4003
3782
  exports.COMPLETION_CMD_PREFIX = COMPLETION_CMD_PREFIX;
4004
3783
  exports.InputError = InputError;
4005
3784
  exports.RangeStyles = RangeStyles;
4006
- exports.applyRange = applyRange;
4007
3785
  exports.captureException = captureException;
4008
3786
  exports.checkCommandInput = checkCommandInput;
4009
3787
  exports.cmdFlagValueToArray = cmdFlagValueToArray;
@@ -4013,32 +3791,27 @@ exports.createEnum = createEnum;
4013
3791
  exports.detectAndValidatePackageEnvironment = detectAndValidatePackageEnvironment;
4014
3792
  exports.detectDefaultBranch = detectDefaultBranch;
4015
3793
  exports.determineOrgSlug = determineOrgSlug;
4016
- exports.extractOverridesFromPnpmLockSrc = extractOverridesFromPnpmLockSrc;
4017
3794
  exports.extractTier1ReachabilityScanId = extractTier1ReachabilityScanId;
4018
3795
  exports.failMsgWithBadge = failMsgWithBadge;
4019
3796
  exports.fetchOrganization = fetchOrganization;
4020
- exports.getAlertsMapFromPnpmLockfile = getAlertsMapFromPnpmLockfile;
3797
+ exports.findUp = findUp;
4021
3798
  exports.getAlertsMapFromPurls = getAlertsMapFromPurls;
4022
3799
  exports.getBaseBranch = getBaseBranch;
4023
3800
  exports.getBashrcDetails = getBashrcDetails;
4024
3801
  exports.getConfigValue = getConfigValue;
4025
3802
  exports.getConfigValueOrUndef = getConfigValueOrUndef;
4026
- exports.getCveInfoFromAlertsMap = getCveInfoFromAlertsMap;
4027
3803
  exports.getDefaultOrgSlug = getDefaultOrgSlug;
4028
3804
  exports.getEcosystemChoicesForMeow = getEcosystemChoicesForMeow;
4029
3805
  exports.getEnterpriseOrgs = getEnterpriseOrgs;
4030
3806
  exports.getFlagApiRequirementsOutput = getFlagApiRequirementsOutput;
4031
3807
  exports.getFlagListOutput = getFlagListOutput;
4032
3808
  exports.getMajor = getMajor;
4033
- exports.getMinVersion = getMinVersion;
4034
3809
  exports.getNpmBinPath = getNpmBinPath;
4035
- exports.getNpmConfig = getNpmConfig;
4036
3810
  exports.getNpmRequire = getNpmRequire;
4037
3811
  exports.getNpxBinPath = getNpxBinPath;
4038
3812
  exports.getOrgSlugs = getOrgSlugs;
4039
3813
  exports.getOutputKind = getOutputKind;
4040
3814
  exports.getPackageFilesForScan = getPackageFilesForScan;
4041
- exports.getPkgFullNameFromPurl = getPkgFullNameFromPurl;
4042
3815
  exports.getPublicApiToken = getPublicApiToken;
4043
3816
  exports.getPurlObject = getPurlObject;
4044
3817
  exports.getRepoInfo = getRepoInfo;
@@ -4062,7 +3835,6 @@ exports.handleApiCallNoSpinner = handleApiCallNoSpinner;
4062
3835
  exports.hasDefaultApiToken = hasDefaultApiToken;
4063
3836
  exports.hasEnterpriseOrgPlan = hasEnterpriseOrgPlan;
4064
3837
  exports.idToNpmPurl = idToNpmPurl;
4065
- exports.idToPurl = idToPurl;
4066
3838
  exports.isHelpFlag = isHelpFlag;
4067
3839
  exports.isNpmBinPathShadowed = isNpmBinPathShadowed;
4068
3840
  exports.isNpxBinPathShadowed = isNpxBinPathShadowed;
@@ -4079,14 +3851,10 @@ exports.meowOrExit = meowOrExit;
4079
3851
  exports.meowWithSubcommands = meowWithSubcommands;
4080
3852
  exports.msAtHome = msAtHome;
4081
3853
  exports.npa = npa;
4082
- exports.parsePnpmLockfile = parsePnpmLockfile;
4083
- exports.parsePnpmLockfileVersion = parsePnpmLockfileVersion;
4084
3854
  exports.queryApiSafeJson = queryApiSafeJson;
4085
3855
  exports.queryApiSafeText = queryApiSafeText;
4086
- exports.readLockfile = readLockfile;
4087
3856
  exports.readOrDefaultSocketJson = readOrDefaultSocketJson;
4088
3857
  exports.readSocketJsonSync = readSocketJsonSync;
4089
- exports.removeNodeModules = removeNodeModules;
4090
3858
  exports.runAgentInstall = runAgentInstall;
4091
3859
  exports.sendApiRequest = sendApiRequest;
4092
3860
  exports.serializeResultJson = serializeResultJson;
@@ -4098,5 +3866,5 @@ exports.toFilterConfig = toFilterConfig;
4098
3866
  exports.updateConfigValue = updateConfigValue;
4099
3867
  exports.walkNestedMap = walkNestedMap;
4100
3868
  exports.writeSocketJson = writeSocketJson;
4101
- //# debugId=7bc6e694-34e1-474f-8bfe-df9d9abb3db6
3869
+ //# debugId=6bbbb6b9-ace3-439a-9c19-0674a8ba872d
4102
3870
  //# sourceMappingURL=utils.js.map