@socketsecurity/cli 0.14.61 → 0.14.62

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.
@@ -62,7 +62,7 @@ var tinyglobby = _socketInterop(require('tinyglobby'));
62
62
  var promises = require('@socketsecurity/registry/lib/promises');
63
63
  var yaml = _socketInterop(require('yaml'));
64
64
  var betterAjvErrors = _socketInterop(require('@apideck/better-ajv-errors'));
65
- var config$A = require('@socketsecurity/config');
65
+ var config$D = require('@socketsecurity/config');
66
66
  var assert = require('node:assert');
67
67
  var readline = require('node:readline/promises');
68
68
  var open = _socketInterop(require('open'));
@@ -1301,6 +1301,11 @@ function handleUnsuccessfulApiResponse(_name, result) {
1301
1301
  const resultErrorMessage = result.error?.message;
1302
1302
  const message = typeof resultErrorMessage === 'string' ? resultErrorMessage : 'No error message returned';
1303
1303
  if (result.status === 401 || result.status === 403) {
1304
+ // Lazily access constants.spinner.
1305
+ const {
1306
+ spinner
1307
+ } = constants;
1308
+ spinner.stop();
1304
1309
  throw new shadowNpmInject.AuthError(message);
1305
1310
  }
1306
1311
  logger.logger.fail(`${colors.bgRed(colors.white('API returned an error:'))} ${message}`);
@@ -1414,6 +1419,7 @@ async function meowWithSubcommands(subcommands, options) {
1414
1419
  const {
1415
1420
  aliases = {},
1416
1421
  argv,
1422
+ defaultSub,
1417
1423
  importMeta,
1418
1424
  name,
1419
1425
  ...additionalOptions
@@ -1421,7 +1427,11 @@ async function meowWithSubcommands(subcommands, options) {
1421
1427
  __proto__: null,
1422
1428
  ...options
1423
1429
  };
1424
- const [commandOrAliasName, ...rawCommandArgv] = argv;
1430
+ const [commandOrAliasNamex, ...rawCommandArgv] = argv;
1431
+ let commandOrAliasName = commandOrAliasNamex;
1432
+ if (!commandOrAliasName && defaultSub) {
1433
+ commandOrAliasName = defaultSub;
1434
+ }
1425
1435
  // If we got at least some args, then lets find out if we can find a command.
1426
1436
  if (commandOrAliasName) {
1427
1437
  const alias = aliases[commandOrAliasName];
@@ -1526,7 +1536,7 @@ function emitBanner(name) {
1526
1536
  }
1527
1537
  function getAsciiHeader(command) {
1528
1538
  const cliVersion = // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION_HASH']".
1529
- "0.14.61:d32a295:1f3c9bfb:pub";
1539
+ "0.14.62:681c774:be9a8ff8:pub";
1530
1540
  const nodeVersion = process.version;
1531
1541
  const apiToken = shadowNpmInject.getSetting('apiToken');
1532
1542
  const shownToken = apiToken ? getLastFiveOfApiToken(apiToken) : 'no';
@@ -1542,9 +1552,9 @@ function getAsciiHeader(command) {
1542
1552
  // https://github.com/SocketDev/socket-python-cli/blob/6d4fc56faee68d3a4764f1f80f84710635bdaf05/socketsecurity/socketcli.py
1543
1553
 
1544
1554
  const {
1545
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$y
1555
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$B
1546
1556
  } = constants;
1547
- const config$z = {
1557
+ const config$C = {
1548
1558
  commandName: 'action',
1549
1559
  description: 'Socket action command',
1550
1560
  // GitHub Action ?
@@ -1578,23 +1588,23 @@ const config$z = {
1578
1588
  `
1579
1589
  };
1580
1590
  const cmdAction = {
1581
- description: config$z.description,
1582
- hidden: config$z.hidden,
1583
- run: run$z
1591
+ description: config$C.description,
1592
+ hidden: config$C.hidden,
1593
+ run: run$C
1584
1594
  };
1585
- async function run$z(argv, importMeta, {
1595
+ async function run$C(argv, importMeta, {
1586
1596
  parentName
1587
1597
  }) {
1588
1598
  const cli = meowOrExit({
1589
1599
  argv,
1590
- config: config$z,
1600
+ config: config$C,
1591
1601
  importMeta,
1592
1602
  parentName
1593
1603
  });
1594
1604
  const githubEventBefore = String(cli.flags['githubEventBefore'] || '');
1595
1605
  const githubEventAfter = String(cli.flags['githubEventAfter'] || '');
1596
1606
  if (cli.flags['dryRun']) {
1597
- logger.logger.log(DRY_RUN_BAIL_TEXT$y);
1607
+ logger.logger.log(DRY_RUN_BAIL_TEXT$B);
1598
1608
  return;
1599
1609
  }
1600
1610
  await runAction(githubEventBefore, githubEventAfter);
@@ -1679,6 +1689,31 @@ cols) {
1679
1689
  }
1680
1690
  return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n');
1681
1691
  }
1692
+ function mdTableOfPairs(arr,
1693
+ // This is saying "an array of strings and the strings are a valid key of elements of T"
1694
+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
1695
+ cols) {
1696
+ // Max col width required to fit all data in that column
1697
+ const cws = cols.map(col => col.length);
1698
+ for (const [key, val] of arr) {
1699
+ cws[0] = Math.max(cws[0] ?? 0, String(key).length);
1700
+ cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length);
1701
+ }
1702
+ let div = '|';
1703
+ for (const cw of cws) div += ' ' + '-'.repeat(cw) + ' |';
1704
+ let header = '|';
1705
+ for (let i = 0; i < cols.length; ++i) {
1706
+ header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |';
1707
+ }
1708
+ let body = '';
1709
+ for (const [key, val] of arr) {
1710
+ body += '|';
1711
+ body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |';
1712
+ body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |';
1713
+ body += '\n';
1714
+ }
1715
+ return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n');
1716
+ }
1682
1717
 
1683
1718
  // Note: Widgets does not seem to actually work as code :'(
1684
1719
 
@@ -1919,9 +1954,9 @@ function renderLineCharts(grid, screen, title, coords, data) {
1919
1954
  }
1920
1955
 
1921
1956
  const {
1922
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$x
1957
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$A
1923
1958
  } = constants;
1924
- const config$y = {
1959
+ const config$B = {
1925
1960
  commandName: 'analytics',
1926
1961
  description: `Look up analytics data`,
1927
1962
  hidden: false,
@@ -1972,16 +2007,16 @@ const config$y = {
1972
2007
  `
1973
2008
  };
1974
2009
  const cmdAnalytics = {
1975
- description: config$y.description,
1976
- hidden: config$y.hidden,
1977
- run: run$y
2010
+ description: config$B.description,
2011
+ hidden: config$B.hidden,
2012
+ run: run$B
1978
2013
  };
1979
- async function run$y(argv, importMeta, {
2014
+ async function run$B(argv, importMeta, {
1980
2015
  parentName
1981
2016
  }) {
1982
2017
  const cli = meowOrExit({
1983
2018
  argv,
1984
- config: config$y,
2019
+ config: config$B,
1985
2020
  importMeta,
1986
2021
  parentName
1987
2022
  });
@@ -2018,7 +2053,7 @@ async function run$y(argv, importMeta, {
2018
2053
  return;
2019
2054
  }
2020
2055
  if (cli.flags['dryRun']) {
2021
- logger.logger.log(DRY_RUN_BAIL_TEXT$x);
2056
+ logger.logger.log(DRY_RUN_BAIL_TEXT$A);
2022
2057
  return;
2023
2058
  }
2024
2059
  return await displayAnalytics({
@@ -2169,9 +2204,9 @@ async function getAuditLogWithToken({
2169
2204
  }
2170
2205
 
2171
2206
  const {
2172
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w
2207
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$z
2173
2208
  } = constants;
2174
- const config$x = {
2209
+ const config$A = {
2175
2210
  commandName: 'audit-log',
2176
2211
  description: 'Look up the audit log for an organization',
2177
2212
  hidden: false,
@@ -2212,16 +2247,16 @@ const config$x = {
2212
2247
  `
2213
2248
  };
2214
2249
  const cmdAuditLog = {
2215
- description: config$x.description,
2216
- hidden: config$x.hidden,
2217
- run: run$x
2250
+ description: config$A.description,
2251
+ hidden: config$A.hidden,
2252
+ run: run$A
2218
2253
  };
2219
- async function run$x(argv, importMeta, {
2254
+ async function run$A(argv, importMeta, {
2220
2255
  parentName
2221
2256
  }) {
2222
2257
  const cli = meowOrExit({
2223
2258
  argv,
2224
- config: config$x,
2259
+ config: config$A,
2225
2260
  importMeta,
2226
2261
  parentName
2227
2262
  });
@@ -2246,7 +2281,7 @@ async function run$x(argv, importMeta, {
2246
2281
  return;
2247
2282
  }
2248
2283
  if (cli.flags['dryRun']) {
2249
- logger.logger.log(DRY_RUN_BAIL_TEXT$w);
2284
+ logger.logger.log(DRY_RUN_BAIL_TEXT$z);
2250
2285
  return;
2251
2286
  }
2252
2287
  await getAuditLog({
@@ -2363,7 +2398,7 @@ function isHelpFlag(cmdArg) {
2363
2398
 
2364
2399
  // import { meowOrExit } from '../../utils/meow-with-subcommands'
2365
2400
  const {
2366
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v
2401
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$y
2367
2402
  } = constants;
2368
2403
 
2369
2404
  // TODO: convert yargs to meow. Or convert all the other things to yargs.
@@ -2440,7 +2475,7 @@ const yargsConfig = {
2440
2475
  'yes'],
2441
2476
  string: ['api-key', 'lifecycle', 'output', 'parent-project-id', 'profile', 'project-group', 'project-name', 'project-version', 'project-id', 'server-host', 'server-port', 'server-url', 'spec-version']
2442
2477
  };
2443
- const config$w = {
2478
+ const config$z = {
2444
2479
  commandName: 'cdxgen',
2445
2480
  description: 'Create an SBOM with CycloneDX generator (cdxgen)',
2446
2481
  hidden: false,
@@ -2456,18 +2491,18 @@ const config$w = {
2456
2491
  `
2457
2492
  };
2458
2493
  const cmdCdxgen = {
2459
- description: config$w.description,
2460
- hidden: config$w.hidden,
2461
- run: run$w
2494
+ description: config$z.description,
2495
+ hidden: config$z.hidden,
2496
+ run: run$z
2462
2497
  };
2463
- async function run$w(argv, importMeta, {
2498
+ async function run$z(argv, importMeta, {
2464
2499
  parentName
2465
2500
  }) {
2466
2501
  const cli = meowOrExit({
2467
2502
  allowUnknownFlags: true,
2468
2503
  // Don't let meow take over --help.
2469
2504
  argv: argv.filter(a => !isHelpFlag(a)),
2470
- config: config$w,
2505
+ config: config$z,
2471
2506
  importMeta,
2472
2507
  parentName
2473
2508
  });
@@ -2499,7 +2534,7 @@ async function run$w(argv, importMeta, {
2499
2534
  return;
2500
2535
  }
2501
2536
  if (cli.flags['dryRun']) {
2502
- logger.logger.log(DRY_RUN_BAIL_TEXT$v);
2537
+ logger.logger.log(DRY_RUN_BAIL_TEXT$y);
2503
2538
  return;
2504
2539
  }
2505
2540
  if (yargv.output === undefined) {
@@ -2566,9 +2601,9 @@ async function findDependencies({
2566
2601
  }
2567
2602
 
2568
2603
  const {
2569
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u
2604
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$x
2570
2605
  } = constants;
2571
- const config$v = {
2606
+ const config$y = {
2572
2607
  commandName: 'dependencies',
2573
2608
  description: 'Search for any dependency that is being used in your organization',
2574
2609
  hidden: false,
@@ -2600,21 +2635,21 @@ const config$v = {
2600
2635
  `
2601
2636
  };
2602
2637
  const cmdScanCreate$1 = {
2603
- description: config$v.description,
2604
- hidden: config$v.hidden,
2605
- run: run$v
2638
+ description: config$y.description,
2639
+ hidden: config$y.hidden,
2640
+ run: run$y
2606
2641
  };
2607
- async function run$v(argv, importMeta, {
2642
+ async function run$y(argv, importMeta, {
2608
2643
  parentName
2609
2644
  }) {
2610
2645
  const cli = meowOrExit({
2611
2646
  argv,
2612
- config: config$v,
2647
+ config: config$y,
2613
2648
  importMeta,
2614
2649
  parentName
2615
2650
  });
2616
2651
  if (cli.flags['dryRun']) {
2617
- logger.logger.log(DRY_RUN_BAIL_TEXT$u);
2652
+ logger.logger.log(DRY_RUN_BAIL_TEXT$x);
2618
2653
  return;
2619
2654
  }
2620
2655
 
@@ -2722,9 +2757,9 @@ async function getDiffScanWithToken({
2722
2757
  }
2723
2758
 
2724
2759
  const {
2725
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t
2760
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w
2726
2761
  } = constants;
2727
- const config$u = {
2762
+ const config$x = {
2728
2763
  commandName: 'get',
2729
2764
  description: 'Get a diff scan for an organization',
2730
2765
  hidden: false,
@@ -2776,16 +2811,16 @@ const config$u = {
2776
2811
  `
2777
2812
  };
2778
2813
  const cmdDiffScanGet = {
2779
- description: config$u.description,
2780
- hidden: config$u.hidden,
2781
- run: run$u
2814
+ description: config$x.description,
2815
+ hidden: config$x.hidden,
2816
+ run: run$x
2782
2817
  };
2783
- async function run$u(argv, importMeta, {
2818
+ async function run$x(argv, importMeta, {
2784
2819
  parentName
2785
2820
  }) {
2786
2821
  const cli = meowOrExit({
2787
2822
  argv,
2788
- config: config$u,
2823
+ config: config$x,
2789
2824
  importMeta,
2790
2825
  parentName
2791
2826
  });
@@ -2805,7 +2840,7 @@ async function run$u(argv, importMeta, {
2805
2840
  return;
2806
2841
  }
2807
2842
  if (cli.flags['dryRun']) {
2808
- logger.logger.log(DRY_RUN_BAIL_TEXT$t);
2843
+ logger.logger.log(DRY_RUN_BAIL_TEXT$w);
2809
2844
  return;
2810
2845
  }
2811
2846
  await getDiffScan({
@@ -2818,9 +2853,9 @@ async function run$u(argv, importMeta, {
2818
2853
  });
2819
2854
  }
2820
2855
 
2821
- const description$3 = 'Diff scans related commands';
2856
+ const description$5 = 'Diff scans related commands';
2822
2857
  const cmdDiffScan = {
2823
- description: description$3,
2858
+ description: description$5,
2824
2859
  // Hidden because it was broken all this time (nobody could be using it)
2825
2860
  // and we're not sure if it's useful to anyone in its current state.
2826
2861
  // Until we do, we'll hide this to keep the help tidier.
@@ -2833,7 +2868,7 @@ const cmdDiffScan = {
2833
2868
  get: cmdDiffScanGet
2834
2869
  }, {
2835
2870
  argv,
2836
- description: description$3,
2871
+ description: description$5,
2837
2872
  importMeta,
2838
2873
  name: parentName + ' diff-scan'
2839
2874
  });
@@ -3209,17 +3244,9 @@ const {
3209
3244
  YARN_CLASSIC: YARN_CLASSIC$6
3210
3245
  } = constants;
3211
3246
  const AGENTS = [BUN$5, NPM$b, PNPM$8, YARN_BERRY$5, YARN_CLASSIC$6, VLT$5];
3212
- const binByAgent = {
3213
- __proto__: null,
3214
- [BUN$5]: BUN$5,
3215
- [NPM$b]: NPM$b,
3216
- [PNPM$8]: PNPM$8,
3217
- [YARN_BERRY$5]: YARN,
3218
- [YARN_CLASSIC$6]: YARN,
3219
- [VLT$5]: VLT$5
3220
- };
3247
+ const binByAgent = new Map([[BUN$5, BUN$5], [NPM$b, NPM$b], [PNPM$8, PNPM$8], [YARN_BERRY$5, YARN], [YARN_CLASSIC$6, YARN], [VLT$5, VLT$5]]);
3221
3248
  async function getAgentExecPath(agent) {
3222
- const binName = binByAgent[agent];
3249
+ const binName = binByAgent.get(agent);
3223
3250
  return (await which(binName, {
3224
3251
  nothrow: true
3225
3252
  })) ?? binName;
@@ -3227,7 +3254,11 @@ async function getAgentExecPath(agent) {
3227
3254
  async function getAgentVersion(agentExecPath, cwd) {
3228
3255
  let result;
3229
3256
  try {
3230
- result = semver.coerce(
3257
+ result =
3258
+ // Coerce version output into a valid semver version by passing it through
3259
+ // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
3260
+ // and tildes (~).
3261
+ semver.coerce(
3231
3262
  // All package managers support the "--version" flag.
3232
3263
  (await spawn.spawn(agentExecPath, ['--version'], {
3233
3264
  cwd
@@ -3268,32 +3299,25 @@ const readLockFileByAgent = (() => {
3268
3299
  }
3269
3300
  const binaryReader = wrapReader(shadowNpmInject.readFileBinary);
3270
3301
  const defaultReader = wrapReader(async lockPath => await shadowNpmInject.readFileUtf8(lockPath));
3271
- return {
3272
- [BUN$5]: wrapReader(async (lockPath, agentExecPath) => {
3273
- const ext = path.extname(lockPath);
3274
- if (ext === LOCK_EXT$1) {
3275
- return await defaultReader(lockPath);
3276
- }
3277
- if (ext === BINARY_LOCK_EXT) {
3278
- const lockBuffer = await binaryReader(lockPath);
3279
- if (lockBuffer) {
3280
- try {
3281
- return index_cjs.parse(lockBuffer);
3282
- } catch {}
3283
- }
3284
- // To print a Yarn lockfile to your console without writing it to disk
3285
- // use `bun bun.lockb`.
3286
- // https://bun.sh/guides/install/yarnlock
3287
- return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3302
+ return new Map([[BUN$5, wrapReader(async (lockPath, agentExecPath) => {
3303
+ const ext = path.extname(lockPath);
3304
+ if (ext === LOCK_EXT$1) {
3305
+ return await defaultReader(lockPath);
3306
+ }
3307
+ if (ext === BINARY_LOCK_EXT) {
3308
+ const lockBuffer = await binaryReader(lockPath);
3309
+ if (lockBuffer) {
3310
+ try {
3311
+ return index_cjs.parse(lockBuffer);
3312
+ } catch {}
3288
3313
  }
3289
- return undefined;
3290
- }),
3291
- [NPM$b]: defaultReader,
3292
- [PNPM$8]: defaultReader,
3293
- [VLT$5]: defaultReader,
3294
- [YARN_BERRY$5]: defaultReader,
3295
- [YARN_CLASSIC$6]: defaultReader
3296
- };
3314
+ // To print a Yarn lockfile to your console without writing it to disk
3315
+ // use `bun bun.lockb`.
3316
+ // https://bun.sh/guides/install/yarnlock
3317
+ return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3318
+ }
3319
+ return undefined;
3320
+ })], [NPM$b, defaultReader], [PNPM$8, defaultReader], [VLT$5, defaultReader], [YARN_BERRY$5, defaultReader], [YARN_CLASSIC$6, defaultReader]]);
3297
3321
  })();
3298
3322
  async function detectPackageEnvironment({
3299
3323
  cwd = process$1.cwd(),
@@ -3318,13 +3342,14 @@ async function detectPackageEnvironment({
3318
3342
  let agent;
3319
3343
  let agentVersion;
3320
3344
  if (pkgManager) {
3345
+ // A valid "packageManager" field value is "<package manager name>@<version>".
3346
+ // https://nodejs.org/api/packages.html#packagemanager
3321
3347
  const atSignIndex = pkgManager.lastIndexOf('@');
3322
3348
  if (atSignIndex !== -1) {
3323
3349
  const name = pkgManager.slice(0, atSignIndex);
3324
3350
  const version = pkgManager.slice(atSignIndex + 1);
3325
3351
  if (version && AGENTS.includes(name)) {
3326
3352
  agent = name;
3327
- agentVersion = semver.coerce(version) ?? undefined;
3328
3353
  }
3329
3354
  }
3330
3355
  }
@@ -3343,64 +3368,92 @@ async function detectPackageEnvironment({
3343
3368
  if (agent === YARN_CLASSIC$6 && (agentVersion?.major ?? 0) > 1) {
3344
3369
  agent = YARN_BERRY$5;
3345
3370
  }
3346
- const targets = {
3347
- browser: false,
3348
- node: true
3349
- };
3350
- let lockSrc;
3351
3371
  // Lazily access constants.maintainedNodeVersions.
3352
- let minimumNodeVersion = constants.maintainedNodeVersions.last;
3372
+ const {
3373
+ maintainedNodeVersions
3374
+ } = constants;
3375
+ // Lazily access constants.minimumVersionByAgent.
3376
+ const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent);
3377
+ const minSupportedNodeVersion = maintainedNodeVersions.last;
3378
+ const nodeVersion = semver.coerce(process$1.version);
3379
+ let lockSrc;
3380
+ let pkgAgentRange;
3381
+ let pkgNodeRange;
3382
+ let pkgMinAgentVersion = minSupportedAgentVersion;
3383
+ let pkgMinNodeVersion = minSupportedNodeVersion;
3353
3384
  if (pkgJson) {
3354
- const browserField = pkgJson.browser;
3355
- if (strings.isNonEmptyString(browserField) || objects.isObjectObject(browserField)) {
3356
- targets.browser = true;
3357
- }
3358
- const nodeRange = pkgJson.engines?.['node'];
3359
- if (strings.isNonEmptyString(nodeRange)) {
3360
- const coerced = semver.coerce(nodeRange);
3361
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3362
- minimumNodeVersion = coerced.version;
3385
+ const {
3386
+ engines
3387
+ } = pkgJson;
3388
+ const engineAgentRange = engines?.[agent];
3389
+ const engineNodeRange = engines?.['node'];
3390
+ if (strings.isNonEmptyString(engineAgentRange)) {
3391
+ pkgAgentRange = engineAgentRange;
3392
+ // Roughly check agent range as semver.coerce will strip leading
3393
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3394
+ const coerced = semver.coerce(pkgAgentRange);
3395
+ if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {
3396
+ pkgMinAgentVersion = coerced.version;
3397
+ }
3398
+ }
3399
+ if (strings.isNonEmptyString(engineNodeRange)) {
3400
+ pkgNodeRange = engineNodeRange;
3401
+ // Roughly check Node range as semver.coerce will strip leading
3402
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3403
+ const coerced = semver.coerce(pkgNodeRange);
3404
+ if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {
3405
+ pkgMinNodeVersion = coerced.version;
3363
3406
  }
3364
3407
  }
3365
3408
  const browserslistQuery = pkgJson['browserslist'];
3366
3409
  if (Array.isArray(browserslistQuery)) {
3367
- const browserslistTargets = browserslist(browserslistQuery).map(s => s.toLowerCase()).sort(sorts.naturalCompare);
3368
- const browserslistNodeTargets = browserslistTargets.filter(v => v.startsWith('node ')).map(v => v.slice(5 /*'node '.length*/));
3369
- if (!targets.browser && browserslistTargets.length) {
3370
- targets.browser = browserslistTargets.length !== browserslistNodeTargets.length;
3371
- }
3410
+ // List Node targets in ascending version order.
3411
+ const browserslistNodeTargets = browserslist(browserslistQuery).filter(v => /^node /i.test(v)).map(v => v.slice(5 /*'node '.length*/)).sort(sorts.naturalCompare);
3372
3412
  if (browserslistNodeTargets.length) {
3413
+ // browserslistNodeTargets[0] is the lowest Node target version.
3373
3414
  const coerced = semver.coerce(browserslistNodeTargets[0]);
3374
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3375
- minimumNodeVersion = coerced.version;
3415
+ if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {
3416
+ pkgMinNodeVersion = coerced.version;
3376
3417
  }
3377
3418
  }
3378
3419
  }
3379
- // Lazily access constants.maintainedNodeVersions.
3380
- targets.node = constants.maintainedNodeVersions.some(v => semver.satisfies(v, `>=${minimumNodeVersion}`));
3381
- lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent[agent](lockPath, agentExecPath) : undefined;
3420
+ lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath) : undefined;
3382
3421
  } else {
3383
3422
  lockName = undefined;
3384
3423
  lockPath = undefined;
3385
3424
  }
3386
- const pkgSupported = targets.browser || targets.node;
3425
+ // Does the system agent version meet our minimum supported agent version?
3426
+ const agentSupported = !!agentVersion && semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`);
3427
+
3428
+ // Does the system Node version meet our minimum supported Node version?
3429
+ const nodeSupported = semver.satisfies(nodeVersion, `>=${minSupportedNodeVersion}`);
3387
3430
  const npmBuggyOverrides = agent === NPM$b && !!agentVersion && semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION$1);
3388
3431
  return {
3389
3432
  agent,
3390
3433
  agentExecPath,
3434
+ agentSupported,
3391
3435
  agentVersion,
3436
+ features: {
3437
+ npmBuggyOverrides
3438
+ },
3392
3439
  lockName,
3393
3440
  lockPath,
3394
3441
  lockSrc,
3395
- minimumNodeVersion,
3442
+ nodeSupported,
3443
+ nodeVersion,
3396
3444
  npmExecPath,
3397
3445
  pkgJson: editablePkgJson,
3398
3446
  pkgPath,
3399
- pkgSupported,
3400
- features: {
3401
- npmBuggyOverrides
3447
+ pkgRequirements: {
3448
+ agent: pkgAgentRange ?? `>=${pkgMinAgentVersion}`,
3449
+ node: pkgNodeRange ?? `>=${pkgMinNodeVersion}`
3402
3450
  },
3403
- targets
3451
+ pkgSupports: {
3452
+ // Does our minimum supported agent version meet the package's requirements?
3453
+ agent: semver.satisfies(minSupportedAgentVersion, `>=${pkgMinAgentVersion}`),
3454
+ // Does our supported Node versions meet the package's requirements?
3455
+ node: maintainedNodeVersions.some(v => semver.satisfies(v, `>=${pkgMinNodeVersion}`))
3456
+ }
3404
3457
  };
3405
3458
  }
3406
3459
  async function detectAndValidatePackageEnvironment(cwd, options) {
@@ -3418,12 +3471,32 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3418
3471
  logger?.warn(cmdPrefixMessage(cmdName, `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`));
3419
3472
  }
3420
3473
  });
3421
- if (!details.pkgSupported) {
3422
- logger?.fail(cmdPrefixMessage(cmdName, 'No supported Node or browser range detected'));
3474
+ const {
3475
+ agent,
3476
+ nodeVersion,
3477
+ pkgRequirements
3478
+ } = details;
3479
+ const agentVersion = details.agentVersion ?? 'unknown';
3480
+ if (!details.agentSupported) {
3481
+ const minVersion = constants.minimumVersionByAgent.get(agent);
3482
+ logger?.fail(cmdPrefixMessage(cmdName, `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`));
3483
+ return;
3484
+ }
3485
+ if (!details.nodeSupported) {
3486
+ const minVersion = constants.maintainedNodeVersions.last;
3487
+ logger?.fail(cmdPrefixMessage(cmdName, `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`));
3488
+ return;
3489
+ }
3490
+ if (!details.pkgSupports.agent) {
3491
+ logger?.fail(cmdPrefixMessage(cmdName, `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`));
3492
+ return;
3493
+ }
3494
+ if (!details.pkgSupports.node) {
3495
+ logger?.fail(cmdPrefixMessage(cmdName, `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`));
3423
3496
  return;
3424
3497
  }
3425
- if (details.agent === VLT$5) {
3426
- logger?.fail(cmdPrefixMessage(cmdName, `${details.agent} does not support overrides. Soon, though ⚡`));
3498
+ if (agent === VLT$5) {
3499
+ logger?.fail(cmdPrefixMessage(cmdName, `${agent} does not support overrides. Soon, though ⚡`));
3427
3500
  return;
3428
3501
  }
3429
3502
  const lockName = details.lockName ?? 'lock file';
@@ -3439,8 +3512,8 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3439
3512
  logger?.fail(cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`));
3440
3513
  return;
3441
3514
  }
3442
- if (prod && (details.agent === BUN$5 || details.agent === YARN_BERRY$5)) {
3443
- logger?.fail(cmdPrefixMessage(cmdName, `--prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.version}` : ''}`));
3515
+ if (prod && (agent === BUN$5 || agent === YARN_BERRY$5)) {
3516
+ logger?.fail(cmdPrefixMessage(cmdName, `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`));
3444
3517
  return;
3445
3518
  }
3446
3519
  if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
@@ -3485,9 +3558,9 @@ async function runFix() {
3485
3558
  }
3486
3559
 
3487
3560
  const {
3488
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3561
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v
3489
3562
  } = constants;
3490
- const config$t = {
3563
+ const config$w = {
3491
3564
  commandName: 'fix',
3492
3565
  description: 'Fix "fixable" Socket alerts',
3493
3566
  hidden: true,
@@ -3503,21 +3576,21 @@ const config$t = {
3503
3576
  `
3504
3577
  };
3505
3578
  const cmdFix = {
3506
- description: config$t.description,
3507
- hidden: config$t.hidden,
3508
- run: run$t
3579
+ description: config$w.description,
3580
+ hidden: config$w.hidden,
3581
+ run: run$w
3509
3582
  };
3510
- async function run$t(argv, importMeta, {
3583
+ async function run$w(argv, importMeta, {
3511
3584
  parentName
3512
3585
  }) {
3513
3586
  const cli = meowOrExit({
3514
3587
  argv,
3515
- config: config$t,
3588
+ config: config$w,
3516
3589
  importMeta,
3517
3590
  parentName
3518
3591
  });
3519
3592
  if (cli.flags['dryRun']) {
3520
- logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3593
+ logger.logger.log(DRY_RUN_BAIL_TEXT$v);
3521
3594
  return;
3522
3595
  }
3523
3596
  await runFix();
@@ -3674,9 +3747,9 @@ async function getPackageInfo({
3674
3747
  }
3675
3748
 
3676
3749
  const {
3677
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r
3750
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u
3678
3751
  } = constants;
3679
- const config$s = {
3752
+ const config$v = {
3680
3753
  commandName: 'info',
3681
3754
  description: 'Look up info regarding a package',
3682
3755
  hidden: false,
@@ -3698,16 +3771,16 @@ const config$s = {
3698
3771
  `
3699
3772
  };
3700
3773
  const cmdInfo = {
3701
- description: config$s.description,
3702
- hidden: config$s.hidden,
3703
- run: run$s
3774
+ description: config$v.description,
3775
+ hidden: config$v.hidden,
3776
+ run: run$v
3704
3777
  };
3705
- async function run$s(argv, importMeta, {
3778
+ async function run$v(argv, importMeta, {
3706
3779
  parentName
3707
3780
  }) {
3708
3781
  const cli = meowOrExit({
3709
3782
  argv,
3710
- config: config$s,
3783
+ config: config$v,
3711
3784
  importMeta,
3712
3785
  parentName
3713
3786
  });
@@ -3732,11 +3805,11 @@ async function run$s(argv, importMeta, {
3732
3805
  const pkgName = versionSeparator < 1 ? rawPkgName : rawPkgName.slice(0, versionSeparator);
3733
3806
  const pkgVersion = versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1);
3734
3807
  if (cli.flags['dryRun']) {
3735
- logger.logger.log(DRY_RUN_BAIL_TEXT$r);
3808
+ logger.logger.log(DRY_RUN_BAIL_TEXT$u);
3736
3809
  return;
3737
3810
  }
3738
3811
  await getPackageInfo({
3739
- commandName: `${parentName} ${config$s.commandName}`,
3812
+ commandName: `${parentName} ${config$v.commandName}`,
3740
3813
  includeAllIssues: Boolean(all),
3741
3814
  outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
3742
3815
  pkgName,
@@ -3823,9 +3896,9 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3823
3896
  }
3824
3897
 
3825
3898
  const {
3826
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q
3899
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t
3827
3900
  } = constants;
3828
- const config$r = {
3901
+ const config$u = {
3829
3902
  commandName: 'login',
3830
3903
  description: 'Socket API login',
3831
3904
  hidden: false,
@@ -3855,23 +3928,23 @@ const config$r = {
3855
3928
  `
3856
3929
  };
3857
3930
  const cmdLogin = {
3858
- description: config$r.description,
3859
- hidden: config$r.hidden,
3860
- run: run$r
3931
+ description: config$u.description,
3932
+ hidden: config$u.hidden,
3933
+ run: run$u
3861
3934
  };
3862
- async function run$r(argv, importMeta, {
3935
+ async function run$u(argv, importMeta, {
3863
3936
  parentName
3864
3937
  }) {
3865
3938
  const cli = meowOrExit({
3866
3939
  argv,
3867
- config: config$r,
3940
+ config: config$u,
3868
3941
  importMeta,
3869
3942
  parentName
3870
3943
  });
3871
3944
  const apiBaseUrl = cli.flags['apiBaseUrl'];
3872
3945
  const apiProxy = cli.flags['apiProxy'];
3873
3946
  if (cli.flags['dryRun']) {
3874
- logger.logger.log(DRY_RUN_BAIL_TEXT$q);
3947
+ logger.logger.log(DRY_RUN_BAIL_TEXT$t);
3875
3948
  return;
3876
3949
  }
3877
3950
  if (!isInteractive()) {
@@ -3897,9 +3970,9 @@ function attemptLogout() {
3897
3970
  }
3898
3971
 
3899
3972
  const {
3900
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p
3973
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3901
3974
  } = constants;
3902
- const config$q = {
3975
+ const config$t = {
3903
3976
  commandName: 'logout',
3904
3977
  description: 'Socket API logout',
3905
3978
  hidden: false,
@@ -3914,21 +3987,21 @@ const config$q = {
3914
3987
  `
3915
3988
  };
3916
3989
  const cmdLogout = {
3917
- description: config$q.description,
3918
- hidden: config$q.hidden,
3919
- run: run$q
3990
+ description: config$t.description,
3991
+ hidden: config$t.hidden,
3992
+ run: run$t
3920
3993
  };
3921
- async function run$q(argv, importMeta, {
3994
+ async function run$t(argv, importMeta, {
3922
3995
  parentName
3923
3996
  }) {
3924
3997
  const cli = meowOrExit({
3925
3998
  argv,
3926
- config: config$q,
3999
+ config: config$t,
3927
4000
  importMeta,
3928
4001
  parentName
3929
4002
  });
3930
4003
  if (cli.flags['dryRun']) {
3931
- logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4004
+ logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3932
4005
  return;
3933
4006
  }
3934
4007
  attemptLogout();
@@ -4033,9 +4106,9 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
4033
4106
  }
4034
4107
 
4035
4108
  const {
4036
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o
4109
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r
4037
4110
  } = constants;
4038
- const config$p = {
4111
+ const config$s = {
4039
4112
  commandName: 'gradle',
4040
4113
  description: '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Gradle/Java/Kotlin/etc project',
4041
4114
  hidden: false,
@@ -4107,22 +4180,22 @@ const config$p = {
4107
4180
  `
4108
4181
  };
4109
4182
  const cmdManifestGradle = {
4110
- description: config$p.description,
4111
- hidden: config$p.hidden,
4112
- run: run$p
4183
+ description: config$s.description,
4184
+ hidden: config$s.hidden,
4185
+ run: run$s
4113
4186
  };
4114
- async function run$p(argv, importMeta, {
4187
+ async function run$s(argv, importMeta, {
4115
4188
  parentName
4116
4189
  }) {
4117
4190
  const cli = meowOrExit({
4118
4191
  argv,
4119
- config: config$p,
4192
+ config: config$s,
4120
4193
  importMeta,
4121
4194
  parentName
4122
4195
  });
4123
4196
  const verbose = Boolean(cli.flags['verbose']);
4124
4197
  if (verbose) {
4125
- logger.logger.group('- ', parentName, config$p.commandName, ':');
4198
+ logger.logger.group('- ', parentName, config$s.commandName, ':');
4126
4199
  logger.logger.group('- flags:', cli.flags);
4127
4200
  logger.logger.groupEnd();
4128
4201
  logger.logger.log('- input:', cli.input);
@@ -4170,7 +4243,7 @@ async function run$p(argv, importMeta, {
4170
4243
  gradleOpts = cli.flags['gradleOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4171
4244
  }
4172
4245
  if (cli.flags['dryRun']) {
4173
- logger.logger.log(DRY_RUN_BAIL_TEXT$o);
4246
+ logger.logger.log(DRY_RUN_BAIL_TEXT$r);
4174
4247
  return;
4175
4248
  }
4176
4249
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts);
@@ -4275,9 +4348,9 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
4275
4348
  }
4276
4349
 
4277
4350
  const {
4278
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n
4351
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q
4279
4352
  } = constants;
4280
- const config$o = {
4353
+ const config$r = {
4281
4354
  commandName: 'scala',
4282
4355
  description: "[beta] Generate a manifest file (`pom.xml`) from Scala's `build.sbt` file",
4283
4356
  hidden: false,
@@ -4350,22 +4423,22 @@ const config$o = {
4350
4423
  `
4351
4424
  };
4352
4425
  const cmdManifestScala = {
4353
- description: config$o.description,
4354
- hidden: config$o.hidden,
4355
- run: run$o
4426
+ description: config$r.description,
4427
+ hidden: config$r.hidden,
4428
+ run: run$r
4356
4429
  };
4357
- async function run$o(argv, importMeta, {
4430
+ async function run$r(argv, importMeta, {
4358
4431
  parentName
4359
4432
  }) {
4360
4433
  const cli = meowOrExit({
4361
4434
  argv,
4362
- config: config$o,
4435
+ config: config$r,
4363
4436
  importMeta,
4364
4437
  parentName
4365
4438
  });
4366
4439
  const verbose = Boolean(cli.flags['verbose']);
4367
4440
  if (verbose) {
4368
- logger.logger.group('- ', parentName, config$o.commandName, ':');
4441
+ logger.logger.group('- ', parentName, config$r.commandName, ':');
4369
4442
  logger.logger.group('- flags:', cli.flags);
4370
4443
  logger.logger.groupEnd();
4371
4444
  logger.logger.log('- input:', cli.input);
@@ -4411,16 +4484,16 @@ async function run$o(argv, importMeta, {
4411
4484
  sbtOpts = cli.flags['sbtOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4412
4485
  }
4413
4486
  if (cli.flags['dryRun']) {
4414
- logger.logger.log(DRY_RUN_BAIL_TEXT$n);
4487
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q);
4415
4488
  return;
4416
4489
  }
4417
4490
  await convertSbtToMaven(target, bin, out, verbose, sbtOpts);
4418
4491
  }
4419
4492
 
4420
4493
  const {
4421
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m
4494
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p
4422
4495
  } = constants;
4423
- const config$n = {
4496
+ const config$q = {
4424
4497
  commandName: 'auto',
4425
4498
  description: 'Auto-detect build and attempt to generate manifest file',
4426
4499
  hidden: false,
@@ -4450,23 +4523,23 @@ const config$n = {
4450
4523
  `
4451
4524
  };
4452
4525
  const cmdManifestAuto = {
4453
- description: config$n.description,
4454
- hidden: config$n.hidden,
4455
- run: run$n
4526
+ description: config$q.description,
4527
+ hidden: config$q.hidden,
4528
+ run: run$q
4456
4529
  };
4457
- async function run$n(argv, importMeta, {
4530
+ async function run$q(argv, importMeta, {
4458
4531
  parentName
4459
4532
  }) {
4460
4533
  const cli = meowOrExit({
4461
4534
  argv,
4462
- config: config$n,
4535
+ config: config$q,
4463
4536
  importMeta,
4464
4537
  parentName
4465
4538
  });
4466
4539
  const verbose = !!cli.flags['verbose'];
4467
4540
  const cwd = cli.flags['cwd'] ?? process.cwd();
4468
4541
  if (verbose) {
4469
- logger.logger.group('- ', parentName, config$n.commandName, ':');
4542
+ logger.logger.group('- ', parentName, config$q.commandName, ':');
4470
4543
  logger.logger.group('- flags:', cli.flags);
4471
4544
  logger.logger.groupEnd();
4472
4545
  logger.logger.log('- input:', cli.input);
@@ -4485,7 +4558,7 @@ async function run$n(argv, importMeta, {
4485
4558
  }
4486
4559
  subArgs.push(dir);
4487
4560
  if (cli.flags['dryRun']) {
4488
- logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4561
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4489
4562
  return;
4490
4563
  }
4491
4564
  await cmdManifestScala.run(subArgs, importMeta, {
@@ -4500,7 +4573,7 @@ async function run$n(argv, importMeta, {
4500
4573
  subArgs.push(cwd);
4501
4574
  }
4502
4575
  if (cli.flags['dryRun']) {
4503
- logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4576
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4504
4577
  return;
4505
4578
  }
4506
4579
  await cmdManifestGradle.run(subArgs, importMeta, {
@@ -4508,10 +4581,14 @@ async function run$n(argv, importMeta, {
4508
4581
  });
4509
4582
  return;
4510
4583
  }
4584
+ if (cli.flags['dryRun']) {
4585
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4586
+ return;
4587
+ }
4511
4588
 
4512
4589
  // Show new help screen and exit.
4513
4590
  meow(`
4514
- $ ${parentName} ${config$n.commandName}
4591
+ $ ${parentName} ${config$q.commandName}
4515
4592
 
4516
4593
  Unfortunately this script did not discover a supported language in the
4517
4594
  current folder.
@@ -4524,13 +4601,13 @@ async function run$n(argv, importMeta, {
4524
4601
  your target language.
4525
4602
  `, {
4526
4603
  argv: [],
4527
- description: config$n.description,
4604
+ description: config$q.description,
4528
4605
  importMeta
4529
4606
  }).showHelp();
4530
4607
  }
4531
4608
 
4532
4609
  const {
4533
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l
4610
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o
4534
4611
  } = constants;
4535
4612
 
4536
4613
  // TODO: we may want to dedupe some pieces for all gradle languages. I think it
@@ -4538,7 +4615,7 @@ const {
4538
4615
  // sense for the help panels to note the requested language, rather than
4539
4616
  // `socket manifest kotlin` to print help screens with `gradle` as the
4540
4617
  // command. Room for improvement.
4541
- const config$m = {
4618
+ const config$p = {
4542
4619
  commandName: 'kotlin',
4543
4620
  description: '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Kotlin project',
4544
4621
  hidden: false,
@@ -4610,22 +4687,22 @@ const config$m = {
4610
4687
  `
4611
4688
  };
4612
4689
  const cmdManifestKotlin = {
4613
- description: config$m.description,
4614
- hidden: config$m.hidden,
4615
- run: run$m
4690
+ description: config$p.description,
4691
+ hidden: config$p.hidden,
4692
+ run: run$p
4616
4693
  };
4617
- async function run$m(argv, importMeta, {
4694
+ async function run$p(argv, importMeta, {
4618
4695
  parentName
4619
4696
  }) {
4620
4697
  const cli = meowOrExit({
4621
4698
  argv,
4622
- config: config$m,
4699
+ config: config$p,
4623
4700
  importMeta,
4624
4701
  parentName
4625
4702
  });
4626
4703
  const verbose = Boolean(cli.flags['verbose']);
4627
4704
  if (verbose) {
4628
- logger.logger.group('- ', parentName, config$m.commandName, ':');
4705
+ logger.logger.group('- ', parentName, config$p.commandName, ':');
4629
4706
  logger.logger.group('- flags:', cli.flags);
4630
4707
  logger.logger.groupEnd();
4631
4708
  logger.logger.log('- input:', cli.input);
@@ -4673,13 +4750,13 @@ async function run$m(argv, importMeta, {
4673
4750
  gradleOpts = cli.flags['gradleOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4674
4751
  }
4675
4752
  if (cli.flags['dryRun']) {
4676
- logger.logger.log(DRY_RUN_BAIL_TEXT$l);
4753
+ logger.logger.log(DRY_RUN_BAIL_TEXT$o);
4677
4754
  return;
4678
4755
  }
4679
4756
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts);
4680
4757
  }
4681
4758
 
4682
- const config$l = {
4759
+ const config$o = {
4683
4760
  commandName: 'manifest',
4684
4761
  description: 'Generate a dependency manifest for given file or dir',
4685
4762
  hidden: false,
@@ -4687,11 +4764,11 @@ const config$l = {
4687
4764
  ...commonFlags
4688
4765
  }};
4689
4766
  const cmdManifest = {
4690
- description: config$l.description,
4691
- hidden: config$l.hidden,
4692
- run: run$l
4767
+ description: config$o.description,
4768
+ hidden: config$o.hidden,
4769
+ run: run$o
4693
4770
  };
4694
- async function run$l(argv, importMeta, {
4771
+ async function run$o(argv, importMeta, {
4695
4772
  parentName
4696
4773
  }) {
4697
4774
  await meowWithSubcommands({
@@ -4703,15 +4780,15 @@ async function run$l(argv, importMeta, {
4703
4780
  argv,
4704
4781
  aliases: {
4705
4782
  yolo: {
4706
- description: config$l.description,
4783
+ description: config$o.description,
4707
4784
  hidden: true,
4708
4785
  argv: ['auto']
4709
4786
  }
4710
4787
  },
4711
- description: config$l.description,
4788
+ description: config$o.description,
4712
4789
  importMeta,
4713
- flags: config$l.flags,
4714
- name: `${parentName} ${config$l.commandName}`
4790
+ flags: config$o.flags,
4791
+ name: `${parentName} ${config$o.commandName}`
4715
4792
  });
4716
4793
  }
4717
4794
 
@@ -4725,10 +4802,10 @@ async function wrapNpm(argv) {
4725
4802
  }
4726
4803
 
4727
4804
  const {
4728
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k,
4805
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n,
4729
4806
  NPM: NPM$7
4730
4807
  } = constants;
4731
- const config$k = {
4808
+ const config$n = {
4732
4809
  commandName: 'npm',
4733
4810
  description: `${NPM$7} wrapper functionality`,
4734
4811
  hidden: false,
@@ -4739,22 +4816,22 @@ const config$k = {
4739
4816
  `
4740
4817
  };
4741
4818
  const cmdNpm = {
4742
- description: config$k.description,
4743
- hidden: config$k.hidden,
4744
- run: run$k
4819
+ description: config$n.description,
4820
+ hidden: config$n.hidden,
4821
+ run: run$n
4745
4822
  };
4746
- async function run$k(argv, importMeta, {
4823
+ async function run$n(argv, importMeta, {
4747
4824
  parentName
4748
4825
  }) {
4749
4826
  const cli = meowOrExit({
4750
4827
  allowUnknownFlags: true,
4751
4828
  argv,
4752
- config: config$k,
4829
+ config: config$n,
4753
4830
  importMeta,
4754
4831
  parentName
4755
4832
  });
4756
4833
  if (cli.flags['dryRun']) {
4757
- logger.logger.log(DRY_RUN_BAIL_TEXT$k);
4834
+ logger.logger.log(DRY_RUN_BAIL_TEXT$n);
4758
4835
  return;
4759
4836
  }
4760
4837
  await wrapNpm(argv);
@@ -4770,10 +4847,10 @@ async function wrapNpx(argv) {
4770
4847
  }
4771
4848
 
4772
4849
  const {
4773
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j,
4850
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m,
4774
4851
  NPX: NPX$1
4775
4852
  } = constants;
4776
- const config$j = {
4853
+ const config$m = {
4777
4854
  commandName: 'npx',
4778
4855
  description: `${NPX$1} wrapper functionality`,
4779
4856
  hidden: false,
@@ -4784,31 +4861,31 @@ const config$j = {
4784
4861
  `
4785
4862
  };
4786
4863
  const cmdNpx = {
4787
- description: config$j.description,
4788
- hidden: config$j.hidden,
4789
- run: run$j
4864
+ description: config$m.description,
4865
+ hidden: config$m.hidden,
4866
+ run: run$m
4790
4867
  };
4791
- async function run$j(argv, importMeta, {
4868
+ async function run$m(argv, importMeta, {
4792
4869
  parentName
4793
4870
  }) {
4794
4871
  const cli = meowOrExit({
4795
4872
  allowUnknownFlags: true,
4796
4873
  argv,
4797
- config: config$j,
4874
+ config: config$m,
4798
4875
  importMeta,
4799
4876
  parentName
4800
4877
  });
4801
4878
  if (cli.flags['dryRun']) {
4802
- logger.logger.log(DRY_RUN_BAIL_TEXT$j);
4879
+ logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4803
4880
  return;
4804
4881
  }
4805
4882
  await wrapNpx(argv);
4806
4883
  }
4807
4884
 
4808
4885
  const {
4809
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i
4886
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l
4810
4887
  } = constants;
4811
- const config$i = {
4888
+ const config$l = {
4812
4889
  commandName: 'oops',
4813
4890
  description: 'Trigger an intentional error (for development)',
4814
4891
  hidden: true,
@@ -4823,21 +4900,21 @@ const config$i = {
4823
4900
  `
4824
4901
  };
4825
4902
  const cmdOops = {
4826
- description: config$i.description,
4827
- hidden: config$i.hidden,
4828
- run: run$i
4903
+ description: config$l.description,
4904
+ hidden: config$l.hidden,
4905
+ run: run$l
4829
4906
  };
4830
- async function run$i(argv, importMeta, {
4907
+ async function run$l(argv, importMeta, {
4831
4908
  parentName
4832
4909
  }) {
4833
4910
  const cli = meowOrExit({
4834
4911
  argv,
4835
- config: config$i,
4912
+ config: config$l,
4836
4913
  importMeta,
4837
4914
  parentName
4838
4915
  });
4839
4916
  if (cli.flags['dryRun']) {
4840
- logger.logger.log(DRY_RUN_BAIL_TEXT$i);
4917
+ logger.logger.log(DRY_RUN_BAIL_TEXT$l);
4841
4918
  return;
4842
4919
  }
4843
4920
  throw new Error('This error was intentionally left blank');
@@ -5344,10 +5421,12 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5344
5421
  spinner?.setText(`Adding overrides${workspaceName ? ` to ${workspaceName}` : ''}...`);
5345
5422
  const depAliasMap = new Map();
5346
5423
  const depEntries = getDependencyEntries(pkgJson);
5347
- const nodeRange = `>=${pkgEnvDetails.minimumNodeVersion}`;
5348
5424
  const manifestEntries = manifestNpmOverrides.filter(({
5349
5425
  1: data
5350
- }) => semver.satisfies(semver.coerce(data.engines.node), nodeRange));
5426
+ }) => semver.satisfies(
5427
+ // Roughly check Node range as semver.coerce will strip leading
5428
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
5429
+ semver.coerce(data.engines.node), pkgEnvDetails.pkgRequirements.node));
5351
5430
 
5352
5431
  // Chunk package names to process them in parallel 3 at a time.
5353
5432
  await promises.pEach(manifestEntries, 3, async ({
@@ -5371,9 +5450,15 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5371
5450
  const origSpec = objects.hasOwn(depObj, origPkgName) ? depObj[origPkgName] : undefined;
5372
5451
  if (origSpec) {
5373
5452
  let thisSpec = origSpec;
5374
- // Add package aliases for direct dependencies to avoid npm EOVERRIDE errors.
5453
+ // Add package aliases for direct dependencies to avoid npm EOVERRIDE
5454
+ // errors...
5375
5455
  // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases
5376
- if (!(thisSpec.startsWith(sockOverridePrefix) && semver.coerce(npa(thisSpec).rawSpec)?.version)) {
5456
+ if (
5457
+ // ...if the spec doesn't start with a valid Socket override.
5458
+ !(thisSpec.startsWith(sockOverridePrefix) &&
5459
+ // Check the validity of the spec by passing it through npa and
5460
+ // seeing if it will coerce to a version.
5461
+ semver.coerce(npa(thisSpec).rawSpec)?.version)) {
5377
5462
  thisSpec = sockOverrideSpec;
5378
5463
  depObj[origPkgName] = thisSpec;
5379
5464
  state.added.add(sockRegPkgName);
@@ -5417,7 +5502,13 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5417
5502
  } else if (typeof oldSpec === 'string') {
5418
5503
  const thisSpec = oldSpec.startsWith('$') ? depAlias || newSpec : oldSpec || newSpec;
5419
5504
  if (thisSpec.startsWith(sockOverridePrefix)) {
5420
- if (pin && semver.major(semver.coerce(npa(thisSpec).rawSpec)?.version ?? version) !== major) {
5505
+ if (pin && semver.major(
5506
+ // Check the validity of the spec by passing it through npa
5507
+ // and seeing if it will coerce to a version. semver.coerce
5508
+ // will strip leading v's, carets (^), comparators (<,<=,>,>=,=),
5509
+ // and tildes (~). If not coerced to a valid version then
5510
+ // default to the manifest entry version.
5511
+ semver.coerce(npa(thisSpec).rawSpec)?.version ?? version) !== major) {
5421
5512
  const otherVersion = (await packages.fetchPackageManifest(thisSpec))?.version;
5422
5513
  if (otherVersion && otherVersion !== version) {
5423
5514
  newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${semver.major(otherVersion)}`}`;
@@ -5554,9 +5645,9 @@ async function applyOptimization(cwd, pin, prod) {
5554
5645
  }
5555
5646
 
5556
5647
  const {
5557
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
5648
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k
5558
5649
  } = constants;
5559
- const config$h = {
5650
+ const config$k = {
5560
5651
  commandName: 'optimize',
5561
5652
  description: 'Optimize dependencies with @socketregistry overrides',
5562
5653
  hidden: false,
@@ -5586,22 +5677,22 @@ const config$h = {
5586
5677
  `
5587
5678
  };
5588
5679
  const cmdOptimize = {
5589
- description: config$h.description,
5590
- hidden: config$h.hidden,
5591
- run: run$h
5680
+ description: config$k.description,
5681
+ hidden: config$k.hidden,
5682
+ run: run$k
5592
5683
  };
5593
- async function run$h(argv, importMeta, {
5684
+ async function run$k(argv, importMeta, {
5594
5685
  parentName
5595
5686
  }) {
5596
5687
  const cli = meowOrExit({
5597
5688
  argv,
5598
- config: config$h,
5689
+ config: config$k,
5599
5690
  importMeta,
5600
5691
  parentName
5601
5692
  });
5602
5693
  const cwd = process$1.cwd();
5603
5694
  if (cli.flags['dryRun']) {
5604
- logger.logger.log(DRY_RUN_BAIL_TEXT$h);
5695
+ logger.logger.log(DRY_RUN_BAIL_TEXT$k);
5605
5696
  return;
5606
5697
  }
5607
5698
  await applyOptimization(cwd, Boolean(cli.flags['pin']), Boolean(cli.flags['prod']));
@@ -5675,10 +5766,10 @@ async function printOrganizationsFromToken(apiToken, format = 'text') {
5675
5766
  }
5676
5767
 
5677
5768
  const {
5678
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g
5769
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j
5679
5770
  } = constants;
5680
- const config$g = {
5681
- commandName: 'organizations',
5771
+ const config$j = {
5772
+ commandName: 'list',
5682
5773
  description: 'List organizations associated with the API key used',
5683
5774
  hidden: false,
5684
5775
  flags: {
@@ -5690,20 +5781,20 @@ const config$g = {
5690
5781
  $ ${command}
5691
5782
 
5692
5783
  Options
5693
- ${getFlagListOutput(config$g.flags, 6)}
5784
+ ${getFlagListOutput(config$j.flags, 6)}
5694
5785
  `
5695
5786
  };
5696
- const cmdOrganization = {
5697
- description: config$g.description,
5698
- hidden: config$g.hidden,
5699
- run: run$g
5787
+ const cmdOrganizationList = {
5788
+ description: config$j.description,
5789
+ hidden: config$j.hidden,
5790
+ run: run$j
5700
5791
  };
5701
- async function run$g(argv, importMeta, {
5792
+ async function run$j(argv, importMeta, {
5702
5793
  parentName
5703
5794
  }) {
5704
5795
  const cli = meowOrExit({
5705
5796
  argv,
5706
- config: config$g,
5797
+ config: config$j,
5707
5798
  importMeta,
5708
5799
  parentName
5709
5800
  });
@@ -5722,115 +5813,366 @@ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields
5722
5813
  return;
5723
5814
  }
5724
5815
  if (cli.flags['dryRun']) {
5725
- logger.logger.log(DRY_RUN_BAIL_TEXT$g);
5816
+ logger.logger.log(DRY_RUN_BAIL_TEXT$j);
5726
5817
  return;
5727
5818
  }
5728
5819
  await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text');
5729
5820
  }
5730
5821
 
5731
- async function runRawNpm(argv) {
5732
- const spawnPromise = spawn.spawn(shadowNpmPaths.getNpmBinPath(), argv, {
5733
- stdio: 'inherit'
5734
- });
5735
- // See https://nodejs.org/api/all.html#all_child_process_event-exit.
5736
- spawnPromise.process.on('exit', (code, signalName) => {
5737
- if (signalName) {
5738
- process$1.kill(process$1.pid, signalName);
5739
- } else if (code !== null) {
5740
- process$1.exit(code);
5741
- }
5742
- });
5743
- await spawnPromise;
5822
+ async function getSecurityPolicy(orgSlug, format) {
5823
+ const apiToken = shadowNpmInject.getDefaultToken();
5824
+ if (!apiToken) {
5825
+ throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
5826
+ }
5827
+ await getSecurityPolicyWithToken(apiToken, orgSlug, format);
5828
+ }
5829
+ async function getSecurityPolicyWithToken(apiToken, orgSlug, format) {
5830
+ // Lazily access constants.spinner.
5831
+ const {
5832
+ spinner
5833
+ } = constants;
5834
+ spinner.start('Fetching organization quota...');
5835
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
5836
+ const result = await handleApiCall(socketSdk.getOrgSecurityPolicy(orgSlug), 'looking up organization quota');
5837
+ if (!result.success) {
5838
+ handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result);
5839
+ return;
5840
+ }
5841
+ spinner.stop();
5842
+ switch (format) {
5843
+ case 'json':
5844
+ {
5845
+ logger.logger.log(JSON.stringify(result.data, null, 2));
5846
+ return;
5847
+ }
5848
+ default:
5849
+ {
5850
+ logger.logger.log('# Security policy\n');
5851
+ logger.logger.log(`The default security policy setting is: "${result.data.securityPolicyDefault}"\n`);
5852
+ logger.logger.log('These are the security policies per setting for your organization:\n');
5853
+ const data = result.data;
5854
+ const rules = data.securityPolicyRules;
5855
+ const entries = Object.entries(rules);
5856
+ const mapped = entries.map(([key, value]) => [key, value.action]);
5857
+ mapped.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
5858
+ logger.logger.log(mdTableOfPairs(mapped, ['name', 'action']));
5859
+ }
5860
+ }
5744
5861
  }
5745
5862
 
5746
5863
  const {
5747
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f,
5748
- NPM
5864
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i
5749
5865
  } = constants;
5750
- const config$f = {
5751
- commandName: 'raw-npm',
5752
- description: `Temporarily disable the Socket ${NPM} wrapper`,
5753
- hidden: false,
5754
- flags: {},
5755
- help: command => `
5866
+
5867
+ // TODO: secret toplevel alias `socket security policy`?
5868
+ const config$i = {
5869
+ commandName: 'security',
5870
+ description: 'Retrieve the security policy of an organization.',
5871
+ hidden: true,
5872
+ flags: {
5873
+ ...commonFlags,
5874
+ ...outputFlags
5875
+ },
5876
+ help: (command, _config) => `
5756
5877
  Usage
5757
- $ ${command} <command>
5878
+ $ ${command} <org slug>
5879
+
5880
+ Options
5881
+ ${getFlagListOutput(config$i.flags, 6)}
5882
+
5883
+ Your API token will need the \`security-policy:read\` permission otherwise
5884
+ the request will fail with an authentication error.
5758
5885
 
5759
5886
  Examples
5760
- $ ${command} install
5887
+ $ ${command} mycorp
5888
+ $ ${command} mycorp --json
5761
5889
  `
5762
5890
  };
5763
- const cmdRawNpm = {
5764
- description: config$f.description,
5765
- hidden: config$f.hidden,
5766
- run: run$f
5891
+ const cmdOrganizationPolicyPolicy = {
5892
+ description: config$i.description,
5893
+ hidden: config$i.hidden,
5894
+ run: run$i
5767
5895
  };
5768
- async function run$f(argv, importMeta, {
5896
+ async function run$i(argv, importMeta, {
5769
5897
  parentName
5770
5898
  }) {
5771
5899
  const cli = meowOrExit({
5772
- allowUnknownFlags: true,
5773
5900
  argv,
5774
- config: config$f,
5901
+ config: config$i,
5775
5902
  importMeta,
5776
5903
  parentName
5777
5904
  });
5905
+ const json = Boolean(cli.flags['json']);
5906
+ const markdown = Boolean(cli.flags['markdown']);
5907
+ const [orgSlug = ''] = cli.input;
5908
+ if (!orgSlug || json && markdown) {
5909
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
5910
+ // options or missing arguments.
5911
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5912
+ process.exitCode = 2;
5913
+ logger.logger.fail(commonTags.stripIndents`
5914
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
5915
+
5916
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
5917
+ - The json and markdown flags cannot be both set ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
5918
+ `);
5919
+ return;
5920
+ }
5778
5921
  if (cli.flags['dryRun']) {
5779
- logger.logger.log(DRY_RUN_BAIL_TEXT$f);
5922
+ logger.logger.log(DRY_RUN_BAIL_TEXT$i);
5780
5923
  return;
5781
5924
  }
5782
- await runRawNpm(argv);
5925
+ await getSecurityPolicy(orgSlug, json ? 'json' : markdown ? 'markdown' : 'text');
5783
5926
  }
5784
5927
 
5785
- async function runRawNpx(argv) {
5786
- const spawnPromise = spawn.spawn(shadowNpmPaths.getNpxBinPath(), argv, {
5787
- stdio: 'inherit'
5788
- });
5789
- // See https://nodejs.org/api/all.html#all_child_process_event-exit.
5790
- spawnPromise.process.on('exit', (code, signalName) => {
5791
- if (signalName) {
5792
- process$1.kill(process$1.pid, signalName);
5793
- } else if (code !== null) {
5794
- process$1.exit(code);
5795
- }
5796
- });
5797
- await spawnPromise;
5928
+ const description$4 = 'Organization policy details';
5929
+ const cmdOrganizationPolicy = {
5930
+ description: description$4,
5931
+ // Hidden because it was broken all this time (nobody could be using it)
5932
+ // and we're not sure if it's useful to anyone in its current state.
5933
+ // Until we do, we'll hide this to keep the help tidier.
5934
+ // And later, we may simply move this under `scan`, anyways.
5935
+ hidden: true,
5936
+ async run(argv, importMeta, {
5937
+ parentName
5938
+ }) {
5939
+ await meowWithSubcommands({
5940
+ security: cmdOrganizationPolicyPolicy
5941
+ }, {
5942
+ argv,
5943
+ description: description$4,
5944
+ defaultSub: 'list',
5945
+ // Backwards compat
5946
+ importMeta,
5947
+ name: parentName + ' policy'
5948
+ });
5949
+ }
5950
+ };
5951
+
5952
+ async function getQuota(format = 'text') {
5953
+ const apiToken = shadowNpmInject.getDefaultToken();
5954
+ if (!apiToken) {
5955
+ throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
5956
+ }
5957
+ await getQuotaWithToken(apiToken, format);
5958
+ }
5959
+ async function getQuotaWithToken(apiToken, format = 'text') {
5960
+ // Lazily access constants.spinner.
5961
+ const {
5962
+ spinner
5963
+ } = constants;
5964
+ spinner.start('Fetching organization quota...');
5965
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
5966
+ const result = await handleApiCall(socketSdk.getQuota(), 'looking up organization quota');
5967
+ if (!result.success) {
5968
+ handleUnsuccessfulApiResponse('getQuota', result);
5969
+ return;
5970
+ }
5971
+ spinner.stop();
5972
+ switch (format) {
5973
+ case 'json':
5974
+ {
5975
+ logger.logger.log(JSON.stringify({
5976
+ quota: result.data.quota
5977
+ }, null, 2));
5978
+ return;
5979
+ }
5980
+ case 'markdown':
5981
+ {
5982
+ logger.logger.log('# Quota\n');
5983
+ logger.logger.log(`Quota left on the current API token: ${result.data.quota}\n`);
5984
+ return;
5985
+ }
5986
+ default:
5987
+ {
5988
+ logger.logger.log(`Quota left on the current API token: ${result.data.quota}\n`);
5989
+ }
5990
+ }
5798
5991
  }
5799
5992
 
5800
5993
  const {
5801
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e,
5802
- NPX
5994
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
5803
5995
  } = constants;
5804
- const config$e = {
5805
- commandName: 'raw-npx',
5806
- description: `Temporarily disable the Socket ${NPX} wrapper`,
5807
- hidden: false,
5808
- flags: {},
5809
- help: command => `
5996
+ const config$h = {
5997
+ commandName: 'quota',
5998
+ description: 'List organizations associated with the API key used',
5999
+ hidden: true,
6000
+ flags: {
6001
+ ...commonFlags,
6002
+ ...outputFlags
6003
+ },
6004
+ help: (command, _config) => `
5810
6005
  Usage
5811
- $ ${command} <command>
6006
+ $ ${command}
5812
6007
 
5813
- Examples
5814
- $ ${command} install
6008
+ Options
6009
+ ${getFlagListOutput(config$h.flags, 6)}
5815
6010
  `
5816
6011
  };
5817
- const cmdRawNpx = {
5818
- description: config$e.description,
5819
- hidden: config$e.hidden,
5820
- run: run$e
6012
+ const cmdOrganizationQuota = {
6013
+ description: config$h.description,
6014
+ hidden: config$h.hidden,
6015
+ run: run$h
5821
6016
  };
5822
- async function run$e(argv, importMeta, {
6017
+ async function run$h(argv, importMeta, {
6018
+ parentName
6019
+ }) {
6020
+ const cli = meowOrExit({
6021
+ argv,
6022
+ config: config$h,
6023
+ importMeta,
6024
+ parentName
6025
+ });
6026
+ const json = Boolean(cli.flags['json']);
6027
+ const markdown = Boolean(cli.flags['markdown']);
6028
+ if (json && markdown) {
6029
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
6030
+ // options or missing arguments.
6031
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6032
+ process.exitCode = 2;
6033
+ logger.logger.fail(commonTags.stripIndents`
6034
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6035
+
6036
+ - The json and markdown flags cannot be both set, pick one
6037
+ `);
6038
+ return;
6039
+ }
6040
+ if (cli.flags['dryRun']) {
6041
+ logger.logger.log(DRY_RUN_BAIL_TEXT$h);
6042
+ return;
6043
+ }
6044
+ await getQuota(json ? 'json' : markdown ? 'markdown' : 'text');
6045
+ }
6046
+
6047
+ const description$3 = 'Account details';
6048
+ const cmdOrganization = {
6049
+ description: description$3,
6050
+ // Hidden because it was broken all this time (nobody could be using it)
6051
+ // and we're not sure if it's useful to anyone in its current state.
6052
+ // Until we do, we'll hide this to keep the help tidier.
6053
+ // And later, we may simply move this under `scan`, anyways.
6054
+ hidden: true,
6055
+ async run(argv, importMeta, {
6056
+ parentName
6057
+ }) {
6058
+ await meowWithSubcommands({
6059
+ list: cmdOrganizationList,
6060
+ quota: cmdOrganizationQuota,
6061
+ policy: cmdOrganizationPolicy
6062
+ }, {
6063
+ argv,
6064
+ description: description$3,
6065
+ defaultSub: 'list',
6066
+ // Backwards compat
6067
+ importMeta,
6068
+ name: parentName + ' organization'
6069
+ });
6070
+ }
6071
+ };
6072
+
6073
+ async function runRawNpm(argv) {
6074
+ const spawnPromise = spawn.spawn(shadowNpmPaths.getNpmBinPath(), argv, {
6075
+ stdio: 'inherit'
6076
+ });
6077
+ // See https://nodejs.org/api/all.html#all_child_process_event-exit.
6078
+ spawnPromise.process.on('exit', (code, signalName) => {
6079
+ if (signalName) {
6080
+ process$1.kill(process$1.pid, signalName);
6081
+ } else if (code !== null) {
6082
+ process$1.exit(code);
6083
+ }
6084
+ });
6085
+ await spawnPromise;
6086
+ }
6087
+
6088
+ const {
6089
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g,
6090
+ NPM
6091
+ } = constants;
6092
+ const config$g = {
6093
+ commandName: 'raw-npm',
6094
+ description: `Temporarily disable the Socket ${NPM} wrapper`,
6095
+ hidden: false,
6096
+ flags: {},
6097
+ help: command => `
6098
+ Usage
6099
+ $ ${command} <command>
6100
+
6101
+ Examples
6102
+ $ ${command} install
6103
+ `
6104
+ };
6105
+ const cmdRawNpm = {
6106
+ description: config$g.description,
6107
+ hidden: config$g.hidden,
6108
+ run: run$g
6109
+ };
6110
+ async function run$g(argv, importMeta, {
5823
6111
  parentName
5824
6112
  }) {
5825
6113
  const cli = meowOrExit({
5826
6114
  allowUnknownFlags: true,
5827
6115
  argv,
5828
- config: config$e,
6116
+ config: config$g,
5829
6117
  importMeta,
5830
6118
  parentName
5831
6119
  });
5832
6120
  if (cli.flags['dryRun']) {
5833
- logger.logger.log(DRY_RUN_BAIL_TEXT$e);
6121
+ logger.logger.log(DRY_RUN_BAIL_TEXT$g);
6122
+ return;
6123
+ }
6124
+ await runRawNpm(argv);
6125
+ }
6126
+
6127
+ async function runRawNpx(argv) {
6128
+ const spawnPromise = spawn.spawn(shadowNpmPaths.getNpxBinPath(), argv, {
6129
+ stdio: 'inherit'
6130
+ });
6131
+ // See https://nodejs.org/api/all.html#all_child_process_event-exit.
6132
+ spawnPromise.process.on('exit', (code, signalName) => {
6133
+ if (signalName) {
6134
+ process$1.kill(process$1.pid, signalName);
6135
+ } else if (code !== null) {
6136
+ process$1.exit(code);
6137
+ }
6138
+ });
6139
+ await spawnPromise;
6140
+ }
6141
+
6142
+ const {
6143
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f,
6144
+ NPX
6145
+ } = constants;
6146
+ const config$f = {
6147
+ commandName: 'raw-npx',
6148
+ description: `Temporarily disable the Socket ${NPX} wrapper`,
6149
+ hidden: false,
6150
+ flags: {},
6151
+ help: command => `
6152
+ Usage
6153
+ $ ${command} <command>
6154
+
6155
+ Examples
6156
+ $ ${command} install
6157
+ `
6158
+ };
6159
+ const cmdRawNpx = {
6160
+ description: config$f.description,
6161
+ hidden: config$f.hidden,
6162
+ run: run$f
6163
+ };
6164
+ async function run$f(argv, importMeta, {
6165
+ parentName
6166
+ }) {
6167
+ const cli = meowOrExit({
6168
+ allowUnknownFlags: true,
6169
+ argv,
6170
+ config: config$f,
6171
+ importMeta,
6172
+ parentName
6173
+ });
6174
+ if (cli.flags['dryRun']) {
6175
+ logger.logger.log(DRY_RUN_BAIL_TEXT$f);
5834
6176
  return;
5835
6177
  }
5836
6178
  await runRawNpx(argv);
@@ -5879,8 +6221,8 @@ async function createReport(socketConfig, inputPaths, {
5879
6221
  }
5880
6222
 
5881
6223
  async function getSocketConfig(absoluteConfigPath) {
5882
- const socketConfig = await config$A.readSocketConfig(absoluteConfigPath).catch(cause => {
5883
- if (cause && typeof cause === 'object' && cause instanceof config$A.SocketValidationError) {
6224
+ const socketConfig = await config$D.readSocketConfig(absoluteConfigPath).catch(cause => {
6225
+ if (cause && typeof cause === 'object' && cause instanceof config$D.SocketValidationError) {
5884
6226
  // Inspired by workbox-build:
5885
6227
  // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71
5886
6228
  const betterErrors = betterAjvErrors.betterAjvErrors({
@@ -5901,12 +6243,13 @@ async function getSocketConfig(absoluteConfigPath) {
5901
6243
 
5902
6244
  const MAX_TIMEOUT_RETRY = 5;
5903
6245
  const HTTP_CODE_TIMEOUT = 524;
5904
- async function fetchReportData(reportId, includeAllIssues, strict) {
6246
+ async function fetchReportData$1(reportId, includeAllIssues, strict) {
5905
6247
  // Lazily access constants.spinner.
5906
6248
  const {
5907
6249
  spinner
5908
6250
  } = constants;
5909
- spinner.start(`Fetching report with ID ${reportId} (this could take a while)`);
6251
+ spinner.log('Fetching report with ID ${reportId} (this could take a while)');
6252
+ spinner.start(`Fetch started... (this could take a while)`);
5910
6253
  const socketSdk = await shadowNpmInject.setupSdk();
5911
6254
  let result;
5912
6255
  for (let retry = 1; !result; ++retry) {
@@ -5915,9 +6258,10 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5915
6258
  result = await handleApiCall(socketSdk.getReport(reportId), 'fetching report');
5916
6259
  } catch (err) {
5917
6260
  if (retry >= MAX_TIMEOUT_RETRY || !(err instanceof Error) || err.cause?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT) {
5918
- spinner.stop();
6261
+ spinner.stop(`Failed to fetch report`);
5919
6262
  throw err;
5920
6263
  }
6264
+ spinner?.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`);
5921
6265
  }
5922
6266
  }
5923
6267
  if (!result.success) {
@@ -5942,17 +6286,20 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5942
6286
  return result.data;
5943
6287
  }
5944
6288
 
5945
- function formatReportDataOutput(reportId, data, commandName, outputJson, outputMarkdown, strict) {
5946
- if (outputJson) {
6289
+ function formatReportDataOutput(reportId, data, commandName, outputKind, strict, artifacts) {
6290
+ if (outputKind === 'json') {
5947
6291
  logger.logger.log(JSON.stringify(data, undefined, 2));
5948
6292
  } else {
5949
- const format = new shadowNpmInject.ColorOrMarkdown(outputMarkdown);
6293
+ const format = new shadowNpmInject.ColorOrMarkdown(outputKind === 'markdown');
5950
6294
  logger.logger.log(commonTags.stripIndents`
5951
6295
  Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {
5952
6296
  fallbackToUrl: true
5953
6297
  })}`);
5954
- if (!outputMarkdown) {
6298
+ if (outputKind === 'print') {
6299
+ logger.logger.log(data);
5955
6300
  logger.logger.log(colors.dim(`Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`));
6301
+ logger.logger.log('The scan:');
6302
+ logger.logger.log(artifacts);
5956
6303
  }
5957
6304
  }
5958
6305
  if (strict && !data.healthy) {
@@ -5960,23 +6307,55 @@ function formatReportDataOutput(reportId, data, commandName, outputJson, outputM
5960
6307
  }
5961
6308
  }
5962
6309
 
6310
+ async function getFullScan(orgSlug, fullScanId) {
6311
+ // Lazily access constants.spinner.
6312
+ const {
6313
+ spinner
6314
+ } = constants;
6315
+ const apiToken = shadowNpmInject.getDefaultToken();
6316
+ if (!apiToken) {
6317
+ throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
6318
+ }
6319
+ spinner.start('Fetching full-scan...');
6320
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
6321
+ spinner.stop('Fetch complete.');
6322
+ if (!response.ok) {
6323
+ const err = await handleAPIError(response.status);
6324
+ logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
6325
+ return;
6326
+ }
6327
+
6328
+ // This is nd-json; each line is a json object
6329
+ const jsons = await response.text();
6330
+ const lines = jsons.split('\n').filter(Boolean);
6331
+ const data = lines.map(line => {
6332
+ try {
6333
+ return JSON.parse(line);
6334
+ } catch {
6335
+ console.error('At least one line item was returned that could not be parsed as JSON...');
6336
+ return {};
6337
+ }
6338
+ });
6339
+ return data;
6340
+ }
6341
+
5963
6342
  async function viewReport(reportId, {
5964
6343
  all,
5965
6344
  commandName,
5966
- json,
5967
- markdown,
6345
+ outputKind,
5968
6346
  strict
5969
6347
  }) {
5970
- const result = await fetchReportData(reportId, all, strict);
6348
+ const result = await fetchReportData$1(reportId, all, strict);
6349
+ const artifacts = await getFullScan('socketdev', reportId);
5971
6350
  if (result) {
5972
- formatReportDataOutput(reportId, result, commandName, json, markdown, strict);
6351
+ formatReportDataOutput(reportId, result, commandName, outputKind, strict, artifacts);
5973
6352
  }
5974
6353
  }
5975
6354
 
5976
6355
  const {
5977
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d
6356
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e
5978
6357
  } = constants;
5979
- const config$d = {
6358
+ const config$e = {
5980
6359
  commandName: 'create',
5981
6360
  description: '[Deprecated] Create a project report',
5982
6361
  hidden: false,
@@ -5997,21 +6376,21 @@ const config$d = {
5997
6376
  }
5998
6377
  },
5999
6378
  help: () => `
6000
- This command is deprecated in favor of \`socket scan create\`.
6379
+ This command is deprecated in favor of \`socket scan view\`.
6001
6380
  It will be removed in the next major release of the CLI.
6002
6381
  `
6003
6382
  };
6004
6383
  const cmdReportCreate = {
6005
- description: config$d.description,
6006
- hidden: config$d.hidden,
6007
- run: run$d
6384
+ description: config$e.description,
6385
+ hidden: config$e.hidden,
6386
+ run: run$e
6008
6387
  };
6009
- async function run$d(argv, importMeta, {
6388
+ async function run$e(argv, importMeta, {
6010
6389
  parentName
6011
6390
  }) {
6012
6391
  const cli = meowOrExit({
6013
6392
  argv,
6014
- config: config$d,
6393
+ config: config$e,
6015
6394
  importMeta,
6016
6395
  parentName
6017
6396
  });
@@ -6028,7 +6407,7 @@ async function run$d(argv, importMeta, {
6028
6407
 
6029
6408
  // Note exiting earlier to skirt a hidden auth requirement
6030
6409
  if (cli.flags['dryRun']) {
6031
- logger.logger.log(DRY_RUN_BAIL_TEXT$d);
6410
+ logger.logger.log(DRY_RUN_BAIL_TEXT$e);
6032
6411
  return;
6033
6412
  }
6034
6413
  const socketConfig = await getSocketConfig(absoluteConfigPath);
@@ -6036,15 +6415,14 @@ async function run$d(argv, importMeta, {
6036
6415
  cwd,
6037
6416
  dryRun
6038
6417
  });
6039
- const commandName = `${parentName} ${config$d.commandName}`;
6418
+ const commandName = `${parentName} ${config$e.commandName}`;
6040
6419
  if (result?.success) {
6041
6420
  if (view) {
6042
6421
  const reportId = result.data.id;
6043
6422
  await viewReport(reportId, {
6044
6423
  all: includeAllIssues,
6045
6424
  commandName,
6046
- json,
6047
- markdown,
6425
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
6048
6426
  strict
6049
6427
  });
6050
6428
  } else if (json) {
@@ -6059,9 +6437,9 @@ async function run$d(argv, importMeta, {
6059
6437
  }
6060
6438
 
6061
6439
  const {
6062
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$c
6440
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d
6063
6441
  } = constants;
6064
- const config$c = {
6442
+ const config$d = {
6065
6443
  commandName: 'view',
6066
6444
  description: '[Deprecated] View a project report',
6067
6445
  hidden: false,
@@ -6076,16 +6454,16 @@ const config$c = {
6076
6454
  `
6077
6455
  };
6078
6456
  const cmdReportView = {
6079
- description: config$c.description,
6080
- hidden: config$c.hidden,
6081
- run: run$c
6457
+ description: config$d.description,
6458
+ hidden: config$d.hidden,
6459
+ run: run$d
6082
6460
  };
6083
- async function run$c(argv, importMeta, {
6461
+ async function run$d(argv, importMeta, {
6084
6462
  parentName
6085
6463
  }) {
6086
6464
  const cli = meowOrExit({
6087
6465
  argv,
6088
- config: config$c,
6466
+ config: config$d,
6089
6467
  importMeta,
6090
6468
  parentName
6091
6469
  });
@@ -6105,14 +6483,13 @@ async function run$c(argv, importMeta, {
6105
6483
  return;
6106
6484
  }
6107
6485
  if (cli.flags['dryRun']) {
6108
- logger.logger.log(DRY_RUN_BAIL_TEXT$c);
6486
+ logger.logger.log(DRY_RUN_BAIL_TEXT$d);
6109
6487
  return;
6110
6488
  }
6111
6489
  await viewReport(reportId, {
6112
6490
  all: Boolean(cli.flags['all']),
6113
- commandName: `${parentName} ${config$c.commandName}`,
6114
- json: Boolean(cli.flags['json']),
6115
- markdown: Boolean(cli.flags['markdown']),
6491
+ commandName: `${parentName} ${config$d.commandName}`,
6492
+ outputKind: cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print',
6116
6493
  strict: Boolean(cli.flags['strict'])
6117
6494
  });
6118
6495
  }
@@ -6189,9 +6566,9 @@ async function createRepoWithToken({
6189
6566
  }
6190
6567
 
6191
6568
  const {
6192
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$b
6569
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$c
6193
6570
  } = constants;
6194
- const config$b = {
6571
+ const config$c = {
6195
6572
  commandName: 'create',
6196
6573
  description: 'Create a repository in an organization',
6197
6574
  hidden: false,
@@ -6240,16 +6617,16 @@ const config$b = {
6240
6617
  `
6241
6618
  };
6242
6619
  const cmdReposCreate = {
6243
- description: config$b.description,
6244
- hidden: config$b.hidden,
6245
- run: run$b
6620
+ description: config$c.description,
6621
+ hidden: config$c.hidden,
6622
+ run: run$c
6246
6623
  };
6247
- async function run$b(argv, importMeta, {
6624
+ async function run$c(argv, importMeta, {
6248
6625
  parentName
6249
6626
  }) {
6250
6627
  const cli = meowOrExit({
6251
6628
  argv,
6252
- config: config$b,
6629
+ config: config$c,
6253
6630
  importMeta,
6254
6631
  parentName
6255
6632
  });
@@ -6268,7 +6645,7 @@ async function run$b(argv, importMeta, {
6268
6645
  return;
6269
6646
  }
6270
6647
  if (cli.flags['dryRun']) {
6271
- logger.logger.log(DRY_RUN_BAIL_TEXT$b);
6648
+ logger.logger.log(DRY_RUN_BAIL_TEXT$c);
6272
6649
  return;
6273
6650
  }
6274
6651
  await createRepo({
@@ -6304,9 +6681,9 @@ async function deleteRepoWithToken(orgSlug, repoName, apiToken) {
6304
6681
  }
6305
6682
 
6306
6683
  const {
6307
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$a
6684
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$b
6308
6685
  } = constants;
6309
- const config$a = {
6686
+ const config$b = {
6310
6687
  commandName: 'del',
6311
6688
  description: 'Delete a repository in an organization',
6312
6689
  hidden: false,
@@ -6325,16 +6702,16 @@ const config$a = {
6325
6702
  `
6326
6703
  };
6327
6704
  const cmdReposDel = {
6328
- description: config$a.description,
6329
- hidden: config$a.hidden,
6330
- run: run$a
6705
+ description: config$b.description,
6706
+ hidden: config$b.hidden,
6707
+ run: run$b
6331
6708
  };
6332
- async function run$a(argv, importMeta, {
6709
+ async function run$b(argv, importMeta, {
6333
6710
  parentName
6334
6711
  }) {
6335
6712
  const cli = meowOrExit({
6336
6713
  argv,
6337
- config: config$a,
6714
+ config: config$b,
6338
6715
  importMeta,
6339
6716
  parentName
6340
6717
  });
@@ -6354,7 +6731,7 @@ async function run$a(argv, importMeta, {
6354
6731
  return;
6355
6732
  }
6356
6733
  if (cli.flags['dryRun']) {
6357
- logger.logger.log(DRY_RUN_BAIL_TEXT$a);
6734
+ logger.logger.log(DRY_RUN_BAIL_TEXT$b);
6358
6735
  return;
6359
6736
  }
6360
6737
  await deleteRepo(orgSlug, repoName);
@@ -6442,9 +6819,9 @@ async function listReposWithToken({
6442
6819
  }
6443
6820
 
6444
6821
  const {
6445
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$9
6822
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$a
6446
6823
  } = constants;
6447
- const config$9 = {
6824
+ const config$a = {
6448
6825
  commandName: 'list',
6449
6826
  description: 'List repositories in an organization',
6450
6827
  hidden: false,
@@ -6487,16 +6864,16 @@ const config$9 = {
6487
6864
  `
6488
6865
  };
6489
6866
  const cmdReposList = {
6490
- description: config$9.description,
6491
- hidden: config$9.hidden,
6492
- run: run$9
6867
+ description: config$a.description,
6868
+ hidden: config$a.hidden,
6869
+ run: run$a
6493
6870
  };
6494
- async function run$9(argv, importMeta, {
6871
+ async function run$a(argv, importMeta, {
6495
6872
  parentName
6496
6873
  }) {
6497
6874
  const cli = meowOrExit({
6498
6875
  argv,
6499
- config: config$9,
6876
+ config: config$a,
6500
6877
  importMeta,
6501
6878
  parentName
6502
6879
  });
@@ -6514,7 +6891,7 @@ async function run$9(argv, importMeta, {
6514
6891
  return;
6515
6892
  }
6516
6893
  if (cli.flags['dryRun']) {
6517
- logger.logger.log(DRY_RUN_BAIL_TEXT$9);
6894
+ logger.logger.log(DRY_RUN_BAIL_TEXT$a);
6518
6895
  return;
6519
6896
  }
6520
6897
  await listRepos({
@@ -6580,9 +6957,9 @@ async function updateRepoWithToken({
6580
6957
  }
6581
6958
 
6582
6959
  const {
6583
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$8
6960
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$9
6584
6961
  } = constants;
6585
- const config$8 = {
6962
+ const config$9 = {
6586
6963
  commandName: 'update',
6587
6964
  description: 'Update a repository in an organization',
6588
6965
  hidden: false,
@@ -6631,16 +7008,16 @@ const config$8 = {
6631
7008
  `
6632
7009
  };
6633
7010
  const cmdReposUpdate = {
6634
- description: config$8.description,
6635
- hidden: config$8.hidden,
6636
- run: run$8
7011
+ description: config$9.description,
7012
+ hidden: config$9.hidden,
7013
+ run: run$9
6637
7014
  };
6638
- async function run$8(argv, importMeta, {
7015
+ async function run$9(argv, importMeta, {
6639
7016
  parentName
6640
7017
  }) {
6641
7018
  const cli = meowOrExit({
6642
7019
  argv,
6643
- config: config$8,
7020
+ config: config$9,
6644
7021
  importMeta,
6645
7022
  parentName
6646
7023
  });
@@ -6661,7 +7038,7 @@ async function run$8(argv, importMeta, {
6661
7038
  return;
6662
7039
  }
6663
7040
  if (cli.flags['dryRun']) {
6664
- logger.logger.log(DRY_RUN_BAIL_TEXT$8);
7041
+ logger.logger.log(DRY_RUN_BAIL_TEXT$9);
6665
7042
  return;
6666
7043
  }
6667
7044
  await updateRepo({
@@ -6744,9 +7121,9 @@ async function viewRepoWithToken(orgSlug, repoName, apiToken, outputKind) {
6744
7121
  }
6745
7122
 
6746
7123
  const {
6747
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$7
7124
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$8
6748
7125
  } = constants;
6749
- const config$7 = {
7126
+ const config$8 = {
6750
7127
  commandName: 'view',
6751
7128
  description: 'View repositories in an organization',
6752
7129
  hidden: false,
@@ -6771,16 +7148,16 @@ const config$7 = {
6771
7148
  `
6772
7149
  };
6773
7150
  const cmdReposView = {
6774
- description: config$7.description,
6775
- hidden: config$7.hidden,
6776
- run: run$7
7151
+ description: config$8.description,
7152
+ hidden: config$8.hidden,
7153
+ run: run$8
6777
7154
  };
6778
- async function run$7(argv, importMeta, {
7155
+ async function run$8(argv, importMeta, {
6779
7156
  parentName
6780
7157
  }) {
6781
7158
  const cli = meowOrExit({
6782
7159
  argv,
6783
- config: config$7,
7160
+ config: config$8,
6784
7161
  importMeta,
6785
7162
  parentName
6786
7163
  });
@@ -6801,7 +7178,7 @@ async function run$7(argv, importMeta, {
6801
7178
  return;
6802
7179
  }
6803
7180
  if (cli.flags['dryRun']) {
6804
- logger.logger.log(DRY_RUN_BAIL_TEXT$7);
7181
+ logger.logger.log(DRY_RUN_BAIL_TEXT$8);
6805
7182
  return;
6806
7183
  }
6807
7184
  await viewRepo(orgSlug, repoName, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
@@ -7112,9 +7489,9 @@ async function createFullScan({
7112
7489
  }
7113
7490
 
7114
7491
  const {
7115
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$6
7492
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$7
7116
7493
  } = constants;
7117
- const config$6 = {
7494
+ const config$7 = {
7118
7495
  commandName: 'create',
7119
7496
  description: 'Create a scan',
7120
7497
  hidden: false,
@@ -7219,16 +7596,16 @@ const config$6 = {
7219
7596
  `
7220
7597
  };
7221
7598
  const cmdScanCreate = {
7222
- description: config$6.description,
7223
- hidden: config$6.hidden,
7224
- run: run$6
7599
+ description: config$7.description,
7600
+ hidden: config$7.hidden,
7601
+ run: run$7
7225
7602
  };
7226
- async function run$6(argv, importMeta, {
7603
+ async function run$7(argv, importMeta, {
7227
7604
  parentName
7228
7605
  }) {
7229
7606
  const cli = meowOrExit({
7230
7607
  argv,
7231
- config: config$6,
7608
+ config: config$7,
7232
7609
  importMeta,
7233
7610
  parentName
7234
7611
  });
@@ -7266,7 +7643,7 @@ async function run$6(argv, importMeta, {
7266
7643
 
7267
7644
  // Note exiting earlier to skirt a hidden auth requirement
7268
7645
  if (cli.flags['dryRun']) {
7269
- logger.logger.log(DRY_RUN_BAIL_TEXT$6);
7646
+ logger.logger.log(DRY_RUN_BAIL_TEXT$7);
7270
7647
  return;
7271
7648
  }
7272
7649
  await createFullScan({
@@ -7309,9 +7686,9 @@ async function deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken) {
7309
7686
  }
7310
7687
 
7311
7688
  const {
7312
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$5
7689
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$6
7313
7690
  } = constants;
7314
- const config$5 = {
7691
+ const config$6 = {
7315
7692
  commandName: 'del',
7316
7693
  description: 'Delete a scan',
7317
7694
  hidden: false,
@@ -7331,16 +7708,16 @@ const config$5 = {
7331
7708
  `
7332
7709
  };
7333
7710
  const cmdScanDel = {
7334
- description: config$5.description,
7335
- hidden: config$5.hidden,
7336
- run: run$5
7711
+ description: config$6.description,
7712
+ hidden: config$6.hidden,
7713
+ run: run$6
7337
7714
  };
7338
- async function run$5(argv, importMeta, {
7715
+ async function run$6(argv, importMeta, {
7339
7716
  parentName
7340
7717
  }) {
7341
7718
  const cli = meowOrExit({
7342
7719
  argv,
7343
- config: config$5,
7720
+ config: config$6,
7344
7721
  importMeta,
7345
7722
  parentName
7346
7723
  });
@@ -7358,7 +7735,7 @@ async function run$5(argv, importMeta, {
7358
7735
  return;
7359
7736
  }
7360
7737
  if (cli.flags['dryRun']) {
7361
- logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7738
+ logger.logger.log(DRY_RUN_BAIL_TEXT$6);
7362
7739
  return;
7363
7740
  }
7364
7741
  await deleteOrgFullScan(orgSlug, fullScanId);
@@ -7452,11 +7829,11 @@ async function listFullScansWithToken({
7452
7829
  }
7453
7830
 
7454
7831
  const {
7455
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$4
7832
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$5
7456
7833
  } = constants;
7457
- const config$4 = {
7834
+ const config$5 = {
7458
7835
  commandName: 'list',
7459
- description: 'List the full scans for an organization',
7836
+ description: 'List the scans for an organization',
7460
7837
  hidden: false,
7461
7838
  flags: {
7462
7839
  ...commonFlags,
@@ -7510,16 +7887,16 @@ const config$4 = {
7510
7887
  `
7511
7888
  };
7512
7889
  const cmdScanList = {
7513
- description: config$4.description,
7514
- hidden: config$4.hidden,
7515
- run: run$4
7890
+ description: config$5.description,
7891
+ hidden: config$5.hidden,
7892
+ run: run$5
7516
7893
  };
7517
- async function run$4(argv, importMeta, {
7894
+ async function run$5(argv, importMeta, {
7518
7895
  parentName
7519
7896
  }) {
7520
7897
  const cli = meowOrExit({
7521
7898
  argv,
7522
- config: config$4,
7899
+ config: config$5,
7523
7900
  importMeta,
7524
7901
  parentName
7525
7902
  });
@@ -7535,7 +7912,7 @@ async function run$4(argv, importMeta, {
7535
7912
  return;
7536
7913
  }
7537
7914
  if (cli.flags['dryRun']) {
7538
- logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7915
+ logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7539
7916
  return;
7540
7917
  }
7541
7918
  await listFullScans({
@@ -7590,11 +7967,11 @@ async function getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind
7590
7967
  }
7591
7968
 
7592
7969
  const {
7593
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$3
7970
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$4
7594
7971
  } = constants;
7595
- const config$3 = {
7972
+ const config$4 = {
7596
7973
  commandName: 'metadata',
7597
- description: "Get a full scan's metadata",
7974
+ description: "Get a scan's metadata",
7598
7975
  hidden: false,
7599
7976
  flags: {
7600
7977
  ...commonFlags,
@@ -7612,16 +7989,16 @@ const config$3 = {
7612
7989
  `
7613
7990
  };
7614
7991
  const cmdScanMetadata = {
7615
- description: config$3.description,
7616
- hidden: config$3.hidden,
7617
- run: run$3
7992
+ description: config$4.description,
7993
+ hidden: config$4.hidden,
7994
+ run: run$4
7618
7995
  };
7619
- async function run$3(argv, importMeta, {
7996
+ async function run$4(argv, importMeta, {
7620
7997
  parentName
7621
7998
  }) {
7622
7999
  const cli = meowOrExit({
7623
8000
  argv,
7624
- config: config$3,
8001
+ config: config$4,
7625
8002
  importMeta,
7626
8003
  parentName
7627
8004
  });
@@ -7639,33 +8016,588 @@ async function run$3(argv, importMeta, {
7639
8016
  return;
7640
8017
  }
7641
8018
  if (cli.flags['dryRun']) {
7642
- logger.logger.log(DRY_RUN_BAIL_TEXT$3);
8019
+ logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7643
8020
  return;
7644
8021
  }
7645
8022
  await getOrgScanMetadata(orgSlug, fullScanId, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
7646
8023
  }
7647
8024
 
7648
- async function streamFullScan(orgSlug, fullScanId, file) {
8025
+ /**
8026
+ * This fetches all the relevant pieces of data to generate a report, given a
8027
+ * full scan ID.
8028
+ * It can optionally only fetch the security or license side of things.
8029
+ */
8030
+ async function fetchReportData(orgSlug, fullScanId,
8031
+ // includeLicensePolicy: boolean,
8032
+ includeSecurityPolicy) {
8033
+ let haveScan = false;
8034
+ // let haveLicensePolicy = false
8035
+ let haveSecurityPolicy = false;
8036
+
7649
8037
  // Lazily access constants.spinner.
7650
8038
  const {
7651
8039
  spinner
7652
8040
  } = constants;
8041
+ function updateProgress() {
8042
+ const needs = [!haveScan ? 'scan' : undefined,
8043
+ // includeLicensePolicy && !haveLicensePolicy ? 'license policy' : undefined,
8044
+ includeSecurityPolicy && !haveSecurityPolicy ? 'security policy' : undefined].filter(Boolean);
8045
+ if (needs.length > 2) {
8046
+ // .toOxford()
8047
+ needs[needs.length - 1] = `and ${needs[needs.length - 1]}`;
8048
+ }
8049
+ const haves = [haveScan ? 'scan' : undefined,
8050
+ // includeLicensePolicy && haveLicensePolicy ? 'license policy' : undefined,
8051
+ includeSecurityPolicy && haveSecurityPolicy ? 'security policy' : undefined].filter(Boolean);
8052
+ if (haves.length > 2) {
8053
+ // .toOxford()
8054
+ haves[haves.length - 1] = `and ${haves[haves.length - 1]}`;
8055
+ }
8056
+ if (needs.length) {
8057
+ spinner.start(`Fetching ${needs.join(needs.length > 2 ? ', ' : ' and ')}...${haves.length ? ` Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` : ''}`);
8058
+ } else {
8059
+ spinner?.successAndStop(`Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.`);
8060
+ }
8061
+ }
7653
8062
  const apiToken = shadowNpmInject.getDefaultToken();
7654
8063
  if (!apiToken) {
7655
8064
  throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7656
8065
  }
7657
- spinner.start('Fetching scan...');
8066
+ updateProgress();
7658
8067
  const socketSdk = await shadowNpmInject.setupSdk(apiToken);
7659
- const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
7660
- if (!data?.success) {
7661
- handleUnsuccessfulApiResponse('getOrgFullScan', data);
8068
+
8069
+ // @ts-ignore
8070
+ const [scan,
8071
+ // licensePolicyMaybe,
8072
+ securityPolicyMaybe] = await Promise.all([(async () => {
8073
+ try {
8074
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
8075
+ haveScan = true;
8076
+ updateProgress();
8077
+ if (!response.ok) {
8078
+ const err = await handleAPIError(response.status);
8079
+ logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
8080
+ return undefined;
8081
+ }
8082
+ const jsons = await response.text();
8083
+ const lines = jsons.split('\n').filter(Boolean);
8084
+ const data = lines.map(line => {
8085
+ try {
8086
+ return JSON.parse(line);
8087
+ } catch {
8088
+ console.error('At least one line item was returned that could not be parsed as JSON...');
8089
+ return;
8090
+ }
8091
+ });
8092
+ return data;
8093
+ } catch (e) {
8094
+ spinner.errorAndStop('There was an issue while fetching full scan data.');
8095
+ throw e;
8096
+ }
8097
+ })(),
8098
+ // includeLicensePolicy &&
8099
+ // (async () => {
8100
+ // const r = await socketSdk.getOrgSecurityPolicy(orgSlug)
8101
+ // haveLicensePolicy = true
8102
+ // updateProgress()
8103
+ // return await handleApiCall(
8104
+ // r,
8105
+ // "looking up organization's license policy"
8106
+ // )
8107
+ // })(),
8108
+ includeSecurityPolicy && (async () => {
8109
+ const r = await socketSdk.getOrgSecurityPolicy(orgSlug);
8110
+ haveSecurityPolicy = true;
8111
+ updateProgress();
8112
+ return await handleApiCall(r, "looking up organization's security policy");
8113
+ })()]).finally(() => spinner.stop());
8114
+ if (!Array.isArray(scan)) {
8115
+ logger.logger.error('Was unable to fetch scan, bailing');
8116
+ process.exitCode = 1;
8117
+ return {
8118
+ ok: false,
8119
+ scan: undefined,
8120
+ // licensePolicy: undefined,
8121
+ securityPolicy: undefined
8122
+ };
8123
+ }
8124
+
8125
+ // // Note: security->license once the api ships in the sdk
8126
+ // let licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'> =
8127
+ // undefined
8128
+ // if (includeLicensePolicy) {
8129
+ // if (licensePolicyMaybe && licensePolicyMaybe.success) {
8130
+ // licensePolicy = licensePolicyMaybe
8131
+ // } else {
8132
+ // logger.error('Was unable to fetch license policy, bailing')
8133
+ // process.exitCode = 1
8134
+ // return {
8135
+ // ok: false,
8136
+ // scan: undefined,
8137
+ // licensePolicy: undefined,
8138
+ // securityPolicy: undefined
8139
+ // }
8140
+ // }
8141
+ // }
8142
+
8143
+ let securityPolicy = undefined;
8144
+ if (includeSecurityPolicy) {
8145
+ if (securityPolicyMaybe && securityPolicyMaybe.success) {
8146
+ securityPolicy = securityPolicyMaybe;
8147
+ } else {
8148
+ logger.logger.error('Was unable to fetch security policy, bailing');
8149
+ process.exitCode = 1;
8150
+ return {
8151
+ ok: false,
8152
+ scan: undefined,
8153
+ // licensePolicy: undefined,
8154
+ securityPolicy: undefined
8155
+ };
8156
+ }
8157
+ }
8158
+ return {
8159
+ ok: true,
8160
+ scan,
8161
+ // licensePolicy,
8162
+ securityPolicy
8163
+ };
8164
+ }
8165
+
8166
+ function generateReport(scan, _licensePolicy, securityPolicy, {
8167
+ fold,
8168
+ orgSlug,
8169
+ reportLevel,
8170
+ scanId,
8171
+ short
8172
+ }) {
8173
+ const now = Date.now();
8174
+
8175
+ // Lazily access constants.spinner.
8176
+ const {
8177
+ spinner
8178
+ } = constants;
8179
+ spinner.start('Generating report...');
8180
+
8181
+ // Create an object that includes:
8182
+ // healthy: boolean
8183
+ // worst violation level;
8184
+ // per eco
8185
+ // per package
8186
+ // per version
8187
+ // per offending file
8188
+ // reported issue -> policy action
8189
+
8190
+ // In the context of a report;
8191
+ // - the alert.severity is irrelevant
8192
+ // - the securityPolicyDefault is irrelevant
8193
+ // - the report defaults to healthy:true with no alerts
8194
+ // - the appearance of an alert will trigger the policy action;
8195
+ // - error: healthy will end up as false, add alerts to report
8196
+ // - warn: healthy unchanged, add alerts to report
8197
+ // - monitor/ignore: no action
8198
+ // - defer: unknown (no action)
8199
+
8200
+ const violations = new Map();
8201
+ let healthy = true;
8202
+ const securityRules = securityPolicy?.data.securityPolicyRules;
8203
+ if (securityPolicy && securityRules) {
8204
+ // Note: reportLevel: error > warn > monitor > ignore > defer
8205
+ scan.forEach(artifact => {
8206
+ const {
8207
+ alerts,
8208
+ name: pkgName = '<unknown>',
8209
+ type: ecosystem,
8210
+ version = '<unknown>'
8211
+ } = artifact;
8212
+ alerts?.forEach(alert => {
8213
+ const alertName = alert.type; // => policy[type]
8214
+ const action = securityRules[alertName]?.action || '';
8215
+ switch (action) {
8216
+ case 'error':
8217
+ {
8218
+ healthy = false;
8219
+ if (!short) {
8220
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8221
+ }
8222
+ break;
8223
+ }
8224
+ case 'warn':
8225
+ {
8226
+ if (!short && reportLevel !== 'error') {
8227
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8228
+ }
8229
+ break;
8230
+ }
8231
+ case 'monitor':
8232
+ {
8233
+ if (!short && reportLevel !== 'warn' && reportLevel !== 'error') {
8234
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8235
+ }
8236
+ break;
8237
+ }
8238
+ case 'ignore':
8239
+ {
8240
+ if (!short && reportLevel !== 'warn' && reportLevel !== 'error' && reportLevel !== 'monitor') {
8241
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8242
+ }
8243
+ break;
8244
+ }
8245
+ case 'defer':
8246
+ {
8247
+ // Not sure but ignore for now. Defer to later ;)
8248
+ if (!short && reportLevel === 'defer') {
8249
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8250
+ }
8251
+ break;
8252
+ }
8253
+ }
8254
+ });
8255
+ });
8256
+ }
8257
+ spinner.successAndStop(`Generated reported in ${Date.now() - now} ms`);
8258
+ const report = short ? {
8259
+ healthy
8260
+ } : {
8261
+ healthy,
8262
+ orgSlug,
8263
+ scanId,
8264
+ options: {
8265
+ fold,
8266
+ reportLevel
8267
+ },
8268
+ alerts: violations
8269
+ };
8270
+ return report;
8271
+ }
8272
+ function createLeaf(art, alert, policyAction) {
8273
+ const leaf = {
8274
+ type: alert.type,
8275
+ policy: policyAction,
8276
+ url: `https://socket.dev/${art.type}/package/${art.name}/${art.version}`,
8277
+ manifest: art.manifestFiles?.map(obj => obj.file) ?? []
8278
+ };
8279
+ return leaf;
8280
+ }
8281
+ function addAlert(art, violations, foldSetting, ecosystem, pkgName, version, alert, policyAction) {
8282
+ if (!violations.has(ecosystem)) violations.set(ecosystem, new Map());
8283
+ const ecomap = violations.get(ecosystem);
8284
+ if (foldSetting === 'pkg') {
8285
+ const existing = ecomap.get(pkgName);
8286
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8287
+ ecomap.set(pkgName, createLeaf(art, alert, policyAction));
8288
+ }
8289
+ } else {
8290
+ if (!ecomap.has(pkgName)) ecomap.set(pkgName, new Map());
8291
+ const pkgmap = ecomap.get(pkgName);
8292
+ if (foldSetting === 'version') {
8293
+ const existing = pkgmap.get(version);
8294
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8295
+ pkgmap.set(version, createLeaf(art, alert, policyAction));
8296
+ }
8297
+ } else {
8298
+ if (!pkgmap.has(version)) pkgmap.set(version, new Map());
8299
+ const file = alert.file || '<unknown>';
8300
+ const vermap = pkgmap.get(version);
8301
+ if (foldSetting === 'file') {
8302
+ const existing = vermap.get(file);
8303
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8304
+ vermap.set(file, createLeaf(art, alert, policyAction));
8305
+ }
8306
+ } else {
8307
+ if (!vermap.has(file)) vermap.set(file, new Map());
8308
+ const key = `${alert.type} at ${alert.start}:${alert.end}`;
8309
+ const filemap = vermap.get(file);
8310
+ const existing = filemap.get(key);
8311
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8312
+ filemap.set(key, createLeaf(art, alert, policyAction));
8313
+ }
8314
+ }
8315
+ }
8316
+ }
8317
+ }
8318
+ function isStricterPolicy(was, is) {
8319
+ // error > warn > monitor > ignore > defer > {unknown}
8320
+ if (was === 'error') return false;
8321
+ if (is === 'error') return true;
8322
+ if (was === 'warn') return false;
8323
+ if (is === 'warn') return false;
8324
+ if (was === 'monitor') return false;
8325
+ if (is === 'monitor') return false;
8326
+ if (was === 'ignore') return false;
8327
+ if (is === 'ignore') return false;
8328
+ if (was === 'defer') return false;
8329
+ if (is === 'defer') return false;
8330
+ // unreachable?
8331
+ return false;
8332
+ }
8333
+
8334
+ /**
8335
+ * Convert a Map<string, Map|string> to a nested object of similar shape.
8336
+ * The goal is to serialize it with JSON.stringify, which Map can't do.
8337
+ */
8338
+ function mapToObject(map) {
8339
+ return Object.fromEntries(Array.from(map.entries()).map(([k, v]) => [k, v instanceof Map ? mapToObject(v) : v]));
8340
+ }
8341
+
8342
+ function* walkNestedMap(map, keys = []) {
8343
+ for (const [key, value] of map.entries()) {
8344
+ if (value instanceof Map) {
8345
+ yield* walkNestedMap(value, keys.concat(key));
8346
+ } else {
8347
+ yield {
8348
+ keys: keys.concat(key),
8349
+ value: value
8350
+ };
8351
+ }
8352
+ }
8353
+ }
8354
+
8355
+ async function reportFullScan({
8356
+ filePath,
8357
+ fold,
8358
+ fullScanId,
8359
+ includeLicensePolicy,
8360
+ includeSecurityPolicy,
8361
+ orgSlug,
8362
+ outputKind,
8363
+ reportLevel,
8364
+ short
8365
+ }) {
8366
+ logger.logger.error('output:', outputKind, ', file:', filePath, ', fold:', fold, ', reportLevel:', reportLevel);
8367
+ if (!includeSecurityPolicy) {
8368
+ return; // caller should assert
8369
+ }
8370
+ const {
8371
+ // licensePolicy,
8372
+ ok,
8373
+ scan,
8374
+ securityPolicy
8375
+ } = await fetchReportData(orgSlug, fullScanId,
8376
+ // includeLicensePolicy
8377
+ includeSecurityPolicy);
8378
+ if (!ok) {
7662
8379
  return;
7663
8380
  }
7664
- spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7665
- return data;
8381
+ const scanReport = generateReport(scan, undefined,
8382
+ // licensePolicy,
8383
+ securityPolicy, {
8384
+ orgSlug,
8385
+ scanId: fullScanId,
8386
+ fold,
8387
+ short,
8388
+ reportLevel
8389
+ });
8390
+ if (!scanReport.healthy) {
8391
+ process.exitCode = 1;
8392
+ }
8393
+ if (outputKind === 'json' || outputKind === 'text' && filePath && filePath.endsWith('.json')) {
8394
+ const json = short ? JSON.stringify(scanReport) : toJsonReport(scanReport);
8395
+ if (filePath && filePath !== '-') {
8396
+ logger.logger.log('Writing json report to', filePath);
8397
+ return await fs$1.writeFile(filePath, json);
8398
+ }
8399
+ logger.logger.log(json);
8400
+ return;
8401
+ }
8402
+ if (outputKind === 'markdown' || filePath && filePath.endsWith('.md')) {
8403
+ const md = short ? `healthy = ${scanReport.healthy}` : toMarkdownReport(scanReport);
8404
+ if (filePath && filePath !== '-') {
8405
+ logger.logger.log('Writing markdown report to', filePath);
8406
+ return await fs$1.writeFile(filePath, md);
8407
+ }
8408
+ logger.logger.log(md);
8409
+ return;
8410
+ }
8411
+ if (short) {
8412
+ logger.logger.log(scanReport.healthy ? 'OK' : 'ERR');
8413
+ } else {
8414
+ logger.logger.dir(scanReport, {
8415
+ depth: null
8416
+ });
8417
+ }
7666
8418
  }
8419
+ function toJsonReport(report) {
8420
+ const obj = mapToObject(report.alerts);
8421
+ const json = JSON.stringify({
8422
+ ...report,
8423
+ alerts: obj
8424
+ }, null, 2);
8425
+ return json;
8426
+ }
8427
+ function toMarkdownReport(report) {
8428
+ const flatData = Array.from(walkNestedMap(report.alerts)).map(({
8429
+ keys,
8430
+ value
8431
+ }) => {
8432
+ const {
8433
+ manifest,
8434
+ policy,
8435
+ type,
8436
+ url
8437
+ } = value;
8438
+ return {
8439
+ 'Alert Type': type,
8440
+ Package: keys[1] || '<unknown>',
8441
+ 'Introduced by': keys[2] || '<unknown>',
8442
+ url,
8443
+ 'Manifest file': manifest.join(', '),
8444
+ Policy: policy
8445
+ };
8446
+ });
8447
+ const md = `
8448
+ # Scan Policy Report
7667
8449
 
7668
- async function getFullScan(orgSlug, fullScanId) {
8450
+ This report tells you whether the results of a Socket scan results violate the
8451
+ security or license policy set by your organization.
8452
+
8453
+ ## Health status
8454
+
8455
+ ${report.healthy ? 'The scan *PASSES* all requirements set by your security and license policy.' : 'The scan *VIOLATES* one or more policies set to the "error" level.'}
8456
+
8457
+ ## Settings
8458
+
8459
+ Configuration used to generate this report:
8460
+
8461
+ - Organization: ${report.orgSlug}
8462
+ - Scan ID: ${report.scanId}
8463
+ - Alert folding: ${report.options.fold === 'none' ? 'none' : `up to ${report.options.fold}`}
8464
+ - Minimal policy level for alert to be included in report: ${report.options.reportLevel === 'defer' ? 'everything' : report.options.reportLevel}
8465
+
8466
+ ## Alerts
8467
+
8468
+ ${report.alerts.size ? `All the alerts from the scan with a policy set to at least "${report.options.reportLevel}"}.` : `The scan contained no alerts for with a policy set to at least "${report.options.reportLevel}".`}
8469
+
8470
+ ${!report.alerts.size ? '' : mdTable(flatData, ['Policy', 'Alert Type', 'Package', 'Introduced by', 'url', 'Manifest file'])}
8471
+ `.trim() + '\n';
8472
+ return md;
8473
+ }
8474
+
8475
+ const {
8476
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$3
8477
+ } = constants;
8478
+ const config$3 = {
8479
+ commandName: 'report',
8480
+ description: 'Check whether a scan result passes the organizational policies (security, license)',
8481
+ hidden: true,
8482
+ // [beta]
8483
+ flags: {
8484
+ ...commonFlags,
8485
+ ...outputFlags,
8486
+ fold: {
8487
+ type: 'string',
8488
+ default: 'none',
8489
+ description: 'Fold reported alerts to some degree'
8490
+ },
8491
+ reportLevel: {
8492
+ type: 'string',
8493
+ default: 'warn',
8494
+ description: 'Which policy level alerts should be reported'
8495
+ },
8496
+ short: {
8497
+ type: 'boolean',
8498
+ default: false,
8499
+ description: 'Report only the healthy status'
8500
+ },
8501
+ // license: {
8502
+ // type: 'boolean',
8503
+ // default: true,
8504
+ // description: 'Report the license policy status. Default: true'
8505
+ // },
8506
+ security: {
8507
+ type: 'boolean',
8508
+ default: true,
8509
+ description: 'Report the security policy status. Default: true'
8510
+ }
8511
+ },
8512
+ help: (command, config) => `
8513
+ Usage
8514
+ $ ${command} <org slug> <scan ID> [path to output file]
8515
+
8516
+ Options
8517
+ ${getFlagListOutput(config.flags, 6)}
8518
+
8519
+ This consumes 1 quota unit plus 1 for each of the requested policy types.
8520
+
8521
+ Note: By default it reports both so by default it consumes 3 quota units.
8522
+
8523
+ Your API token will need the \`full-scans:list\` scope regardless. Additionally
8524
+ it needs \`security-policy:read\` to report on the security policy.
8525
+
8526
+ By default the result is a nested object that looks like this:
8527
+ \`{[ecosystem]: {[pkgName]: {[version]: {[file]: {[type:loc]: policy}}}}\`
8528
+ You can fold this up to given level: 'pkg', 'version', 'file', and 'none'.
8529
+
8530
+ By default only the warn and error policy level alerts are reported. You can
8531
+ override this and request more ('defer' < 'ignore' < 'monitor' < 'warn' < 'error')
8532
+
8533
+ Short responses: JSON: \`{healthy:bool}\`, markdown: \`healthy = bool\`, text: \`OK/ERR\`
8534
+
8535
+ Examples
8536
+ $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version
8537
+ `
8538
+ };
8539
+ const cmdScanReport = {
8540
+ description: config$3.description,
8541
+ hidden: config$3.hidden,
8542
+ run: run$3
8543
+ };
8544
+ async function run$3(argv, importMeta, {
8545
+ parentName
8546
+ }) {
8547
+ const cli = meowOrExit({
8548
+ argv,
8549
+ config: config$3,
8550
+ importMeta,
8551
+ parentName
8552
+ });
8553
+ const {
8554
+ fold = 'none',
8555
+ json,
8556
+ // license,
8557
+ markdown,
8558
+ reportLevel = 'warn',
8559
+ security
8560
+ } = cli.flags;
8561
+ const [orgSlug = '', fullScanId = '', file = '-'] = cli.input;
8562
+ if (!orgSlug || !fullScanId ||
8563
+ // (!license && !security) ||
8564
+ json && markdown) {
8565
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
8566
+ // options or missing arguments.
8567
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
8568
+ process.exitCode = 2;
8569
+ logger.logger.fail(commonTags.stripIndents`
8570
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
8571
+
8572
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
8573
+
8574
+ - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}
8575
+
8576
+ - Not both the --json and --markdown flags ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
8577
+ `
8578
+ // - At least one policy to report ${!license && !security ? colors.red('(do not omit both!)') : colors.green('(ok)')}
8579
+ );
8580
+ return;
8581
+ }
8582
+ if (cli.flags['dryRun']) {
8583
+ logger.logger.log(DRY_RUN_BAIL_TEXT$3);
8584
+ return;
8585
+ }
8586
+ await reportFullScan({
8587
+ orgSlug,
8588
+ fullScanId,
8589
+ includeLicensePolicy: false,
8590
+ // !!license,
8591
+ includeSecurityPolicy: typeof security === 'boolean' ? security : true,
8592
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
8593
+ filePath: file,
8594
+ fold: fold,
8595
+ short: !!cli.flags['short'],
8596
+ reportLevel: reportLevel
8597
+ });
8598
+ }
8599
+
8600
+ async function streamFullScan(orgSlug, fullScanId, file) {
7669
8601
  // Lazily access constants.spinner.
7670
8602
  const {
7671
8603
  spinner
@@ -7674,26 +8606,14 @@ async function getFullScan(orgSlug, fullScanId) {
7674
8606
  if (!apiToken) {
7675
8607
  throw new shadowNpmInject.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7676
8608
  }
7677
- spinner.start('Fetching full-scan...');
7678
- const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
7679
- spinner.stop('Fetch complete.');
7680
- if (!response.ok) {
7681
- const err = await handleAPIError(response.status);
7682
- logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
8609
+ spinner.start('Fetching scan...');
8610
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
8611
+ const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
8612
+ if (!data?.success) {
8613
+ handleUnsuccessfulApiResponse('getOrgFullScan', data);
7683
8614
  return;
7684
8615
  }
7685
-
7686
- // This is nd-json; each line is a json object
7687
- const jsons = await response.text();
7688
- const lines = jsons.split('\n').filter(Boolean);
7689
- const data = lines.map(line => {
7690
- try {
7691
- return JSON.parse(line);
7692
- } catch {
7693
- console.error('At least one line item was returned that could not be parsed as JSON...');
7694
- return {};
7695
- }
7696
- });
8616
+ spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7697
8617
  return data;
7698
8618
  }
7699
8619
 
@@ -7811,6 +8731,7 @@ const cmdScan = {
7811
8731
  list: cmdScanList,
7812
8732
  del: cmdScanDel,
7813
8733
  metadata: cmdScanMetadata,
8734
+ report: cmdScanReport,
7814
8735
  view: cmdScanView
7815
8736
  }, {
7816
8737
  aliases: {
@@ -8291,7 +9212,7 @@ void (async () => {
8291
9212
  await updateNotifier({
8292
9213
  name: SOCKET_CLI_BIN_NAME,
8293
9214
  // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION']".
8294
- version: "0.14.61",
9215
+ version: "0.14.62",
8295
9216
  ttl: 86_400_000 /* 24 hours in milliseconds */
8296
9217
  });
8297
9218
  try {
@@ -8358,5 +9279,5 @@ void (async () => {
8358
9279
  await shadowNpmInject.captureException(e);
8359
9280
  }
8360
9281
  })();
8361
- //# debugId=cba501c2-a06b-4ae7-83dd-edbedcb772fa
9282
+ //# debugId=2b259356-6f14-46bb-b7ac-145c384c4312
8362
9283
  //# sourceMappingURL=cli.js.map