@socketsecurity/cli-with-sentry 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:723e3f67:pub";
1539
+ "0.14.62:681c774:2b72b86b: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
  });
@@ -3211,17 +3246,9 @@ const {
3211
3246
  YARN_CLASSIC: YARN_CLASSIC$6
3212
3247
  } = constants;
3213
3248
  const AGENTS = [BUN$5, NPM$b, PNPM$8, YARN_BERRY$5, YARN_CLASSIC$6, VLT$5];
3214
- const binByAgent = {
3215
- __proto__: null,
3216
- [BUN$5]: BUN$5,
3217
- [NPM$b]: NPM$b,
3218
- [PNPM$8]: PNPM$8,
3219
- [YARN_BERRY$5]: YARN,
3220
- [YARN_CLASSIC$6]: YARN,
3221
- [VLT$5]: VLT$5
3222
- };
3249
+ 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]]);
3223
3250
  async function getAgentExecPath(agent) {
3224
- const binName = binByAgent[agent];
3251
+ const binName = binByAgent.get(agent);
3225
3252
  return (await which(binName, {
3226
3253
  nothrow: true
3227
3254
  })) ?? binName;
@@ -3229,7 +3256,11 @@ async function getAgentExecPath(agent) {
3229
3256
  async function getAgentVersion(agentExecPath, cwd) {
3230
3257
  let result;
3231
3258
  try {
3232
- result = semver.coerce(
3259
+ result =
3260
+ // Coerce version output into a valid semver version by passing it through
3261
+ // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
3262
+ // and tildes (~).
3263
+ semver.coerce(
3233
3264
  // All package managers support the "--version" flag.
3234
3265
  (await spawn.spawn(agentExecPath, ['--version'], {
3235
3266
  cwd
@@ -3270,32 +3301,25 @@ const readLockFileByAgent = (() => {
3270
3301
  }
3271
3302
  const binaryReader = wrapReader(shadowNpmInject.readFileBinary);
3272
3303
  const defaultReader = wrapReader(async lockPath => await shadowNpmInject.readFileUtf8(lockPath));
3273
- return {
3274
- [BUN$5]: wrapReader(async (lockPath, agentExecPath) => {
3275
- const ext = path.extname(lockPath);
3276
- if (ext === LOCK_EXT$1) {
3277
- return await defaultReader(lockPath);
3278
- }
3279
- if (ext === BINARY_LOCK_EXT) {
3280
- const lockBuffer = await binaryReader(lockPath);
3281
- if (lockBuffer) {
3282
- try {
3283
- return index_cjs.parse(lockBuffer);
3284
- } catch {}
3285
- }
3286
- // To print a Yarn lockfile to your console without writing it to disk
3287
- // use `bun bun.lockb`.
3288
- // https://bun.sh/guides/install/yarnlock
3289
- return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3304
+ return new Map([[BUN$5, wrapReader(async (lockPath, agentExecPath) => {
3305
+ const ext = path.extname(lockPath);
3306
+ if (ext === LOCK_EXT$1) {
3307
+ return await defaultReader(lockPath);
3308
+ }
3309
+ if (ext === BINARY_LOCK_EXT) {
3310
+ const lockBuffer = await binaryReader(lockPath);
3311
+ if (lockBuffer) {
3312
+ try {
3313
+ return index_cjs.parse(lockBuffer);
3314
+ } catch {}
3290
3315
  }
3291
- return undefined;
3292
- }),
3293
- [NPM$b]: defaultReader,
3294
- [PNPM$8]: defaultReader,
3295
- [VLT$5]: defaultReader,
3296
- [YARN_BERRY$5]: defaultReader,
3297
- [YARN_CLASSIC$6]: defaultReader
3298
- };
3316
+ // To print a Yarn lockfile to your console without writing it to disk
3317
+ // use `bun bun.lockb`.
3318
+ // https://bun.sh/guides/install/yarnlock
3319
+ return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3320
+ }
3321
+ return undefined;
3322
+ })], [NPM$b, defaultReader], [PNPM$8, defaultReader], [VLT$5, defaultReader], [YARN_BERRY$5, defaultReader], [YARN_CLASSIC$6, defaultReader]]);
3299
3323
  })();
3300
3324
  async function detectPackageEnvironment({
3301
3325
  cwd = process$1.cwd(),
@@ -3320,13 +3344,14 @@ async function detectPackageEnvironment({
3320
3344
  let agent;
3321
3345
  let agentVersion;
3322
3346
  if (pkgManager) {
3347
+ // A valid "packageManager" field value is "<package manager name>@<version>".
3348
+ // https://nodejs.org/api/packages.html#packagemanager
3323
3349
  const atSignIndex = pkgManager.lastIndexOf('@');
3324
3350
  if (atSignIndex !== -1) {
3325
3351
  const name = pkgManager.slice(0, atSignIndex);
3326
3352
  const version = pkgManager.slice(atSignIndex + 1);
3327
3353
  if (version && AGENTS.includes(name)) {
3328
3354
  agent = name;
3329
- agentVersion = semver.coerce(version) ?? undefined;
3330
3355
  }
3331
3356
  }
3332
3357
  }
@@ -3345,64 +3370,92 @@ async function detectPackageEnvironment({
3345
3370
  if (agent === YARN_CLASSIC$6 && (agentVersion?.major ?? 0) > 1) {
3346
3371
  agent = YARN_BERRY$5;
3347
3372
  }
3348
- const targets = {
3349
- browser: false,
3350
- node: true
3351
- };
3352
- let lockSrc;
3353
3373
  // Lazily access constants.maintainedNodeVersions.
3354
- let minimumNodeVersion = constants.maintainedNodeVersions.last;
3374
+ const {
3375
+ maintainedNodeVersions
3376
+ } = constants;
3377
+ // Lazily access constants.minimumVersionByAgent.
3378
+ const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent);
3379
+ const minSupportedNodeVersion = maintainedNodeVersions.last;
3380
+ const nodeVersion = semver.coerce(process$1.version);
3381
+ let lockSrc;
3382
+ let pkgAgentRange;
3383
+ let pkgNodeRange;
3384
+ let pkgMinAgentVersion = minSupportedAgentVersion;
3385
+ let pkgMinNodeVersion = minSupportedNodeVersion;
3355
3386
  if (pkgJson) {
3356
- const browserField = pkgJson.browser;
3357
- if (strings.isNonEmptyString(browserField) || objects.isObjectObject(browserField)) {
3358
- targets.browser = true;
3359
- }
3360
- const nodeRange = pkgJson.engines?.['node'];
3361
- if (strings.isNonEmptyString(nodeRange)) {
3362
- const coerced = semver.coerce(nodeRange);
3363
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3364
- minimumNodeVersion = coerced.version;
3387
+ const {
3388
+ engines
3389
+ } = pkgJson;
3390
+ const engineAgentRange = engines?.[agent];
3391
+ const engineNodeRange = engines?.['node'];
3392
+ if (strings.isNonEmptyString(engineAgentRange)) {
3393
+ pkgAgentRange = engineAgentRange;
3394
+ // Roughly check agent range as semver.coerce will strip leading
3395
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3396
+ const coerced = semver.coerce(pkgAgentRange);
3397
+ if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {
3398
+ pkgMinAgentVersion = coerced.version;
3399
+ }
3400
+ }
3401
+ if (strings.isNonEmptyString(engineNodeRange)) {
3402
+ pkgNodeRange = engineNodeRange;
3403
+ // Roughly check Node range as semver.coerce will strip leading
3404
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3405
+ const coerced = semver.coerce(pkgNodeRange);
3406
+ if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {
3407
+ pkgMinNodeVersion = coerced.version;
3365
3408
  }
3366
3409
  }
3367
3410
  const browserslistQuery = pkgJson['browserslist'];
3368
3411
  if (Array.isArray(browserslistQuery)) {
3369
- const browserslistTargets = browserslist(browserslistQuery).map(s => s.toLowerCase()).sort(sorts.naturalCompare);
3370
- const browserslistNodeTargets = browserslistTargets.filter(v => v.startsWith('node ')).map(v => v.slice(5 /*'node '.length*/));
3371
- if (!targets.browser && browserslistTargets.length) {
3372
- targets.browser = browserslistTargets.length !== browserslistNodeTargets.length;
3373
- }
3412
+ // List Node targets in ascending version order.
3413
+ const browserslistNodeTargets = browserslist(browserslistQuery).filter(v => /^node /i.test(v)).map(v => v.slice(5 /*'node '.length*/)).sort(sorts.naturalCompare);
3374
3414
  if (browserslistNodeTargets.length) {
3415
+ // browserslistNodeTargets[0] is the lowest Node target version.
3375
3416
  const coerced = semver.coerce(browserslistNodeTargets[0]);
3376
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3377
- minimumNodeVersion = coerced.version;
3417
+ if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {
3418
+ pkgMinNodeVersion = coerced.version;
3378
3419
  }
3379
3420
  }
3380
3421
  }
3381
- // Lazily access constants.maintainedNodeVersions.
3382
- targets.node = constants.maintainedNodeVersions.some(v => semver.satisfies(v, `>=${minimumNodeVersion}`));
3383
- lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent[agent](lockPath, agentExecPath) : undefined;
3422
+ lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath) : undefined;
3384
3423
  } else {
3385
3424
  lockName = undefined;
3386
3425
  lockPath = undefined;
3387
3426
  }
3388
- const pkgSupported = targets.browser || targets.node;
3427
+ // Does the system agent version meet our minimum supported agent version?
3428
+ const agentSupported = !!agentVersion && semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`);
3429
+
3430
+ // Does the system Node version meet our minimum supported Node version?
3431
+ const nodeSupported = semver.satisfies(nodeVersion, `>=${minSupportedNodeVersion}`);
3389
3432
  const npmBuggyOverrides = agent === NPM$b && !!agentVersion && semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION$1);
3390
3433
  return {
3391
3434
  agent,
3392
3435
  agentExecPath,
3436
+ agentSupported,
3393
3437
  agentVersion,
3438
+ features: {
3439
+ npmBuggyOverrides
3440
+ },
3394
3441
  lockName,
3395
3442
  lockPath,
3396
3443
  lockSrc,
3397
- minimumNodeVersion,
3444
+ nodeSupported,
3445
+ nodeVersion,
3398
3446
  npmExecPath,
3399
3447
  pkgJson: editablePkgJson,
3400
3448
  pkgPath,
3401
- pkgSupported,
3402
- features: {
3403
- npmBuggyOverrides
3449
+ pkgRequirements: {
3450
+ agent: pkgAgentRange ?? `>=${pkgMinAgentVersion}`,
3451
+ node: pkgNodeRange ?? `>=${pkgMinNodeVersion}`
3404
3452
  },
3405
- targets
3453
+ pkgSupports: {
3454
+ // Does our minimum supported agent version meet the package's requirements?
3455
+ agent: semver.satisfies(minSupportedAgentVersion, `>=${pkgMinAgentVersion}`),
3456
+ // Does our supported Node versions meet the package's requirements?
3457
+ node: maintainedNodeVersions.some(v => semver.satisfies(v, `>=${pkgMinNodeVersion}`))
3458
+ }
3406
3459
  };
3407
3460
  }
3408
3461
  async function detectAndValidatePackageEnvironment(cwd, options) {
@@ -3420,12 +3473,32 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3420
3473
  logger?.warn(cmdPrefixMessage(cmdName, `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`));
3421
3474
  }
3422
3475
  });
3423
- if (!details.pkgSupported) {
3424
- logger?.fail(cmdPrefixMessage(cmdName, 'No supported Node or browser range detected'));
3476
+ const {
3477
+ agent,
3478
+ nodeVersion,
3479
+ pkgRequirements
3480
+ } = details;
3481
+ const agentVersion = details.agentVersion ?? 'unknown';
3482
+ if (!details.agentSupported) {
3483
+ const minVersion = constants.minimumVersionByAgent.get(agent);
3484
+ logger?.fail(cmdPrefixMessage(cmdName, `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`));
3485
+ return;
3486
+ }
3487
+ if (!details.nodeSupported) {
3488
+ const minVersion = constants.maintainedNodeVersions.last;
3489
+ logger?.fail(cmdPrefixMessage(cmdName, `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`));
3490
+ return;
3491
+ }
3492
+ if (!details.pkgSupports.agent) {
3493
+ logger?.fail(cmdPrefixMessage(cmdName, `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`));
3494
+ return;
3495
+ }
3496
+ if (!details.pkgSupports.node) {
3497
+ logger?.fail(cmdPrefixMessage(cmdName, `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`));
3425
3498
  return;
3426
3499
  }
3427
- if (details.agent === VLT$5) {
3428
- logger?.fail(cmdPrefixMessage(cmdName, `${details.agent} does not support overrides. Soon, though ⚡`));
3500
+ if (agent === VLT$5) {
3501
+ logger?.fail(cmdPrefixMessage(cmdName, `${agent} does not support overrides. Soon, though ⚡`));
3429
3502
  return;
3430
3503
  }
3431
3504
  const lockName = details.lockName ?? 'lock file';
@@ -3441,8 +3514,8 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3441
3514
  logger?.fail(cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`));
3442
3515
  return;
3443
3516
  }
3444
- if (prod && (details.agent === BUN$5 || details.agent === YARN_BERRY$5)) {
3445
- logger?.fail(cmdPrefixMessage(cmdName, `--prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.version}` : ''}`));
3517
+ if (prod && (agent === BUN$5 || agent === YARN_BERRY$5)) {
3518
+ logger?.fail(cmdPrefixMessage(cmdName, `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`));
3446
3519
  return;
3447
3520
  }
3448
3521
  if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
@@ -3487,9 +3560,9 @@ async function runFix() {
3487
3560
  }
3488
3561
 
3489
3562
  const {
3490
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3563
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v
3491
3564
  } = constants;
3492
- const config$t = {
3565
+ const config$w = {
3493
3566
  commandName: 'fix',
3494
3567
  description: 'Fix "fixable" Socket alerts',
3495
3568
  hidden: true,
@@ -3505,21 +3578,21 @@ const config$t = {
3505
3578
  `
3506
3579
  };
3507
3580
  const cmdFix = {
3508
- description: config$t.description,
3509
- hidden: config$t.hidden,
3510
- run: run$t
3581
+ description: config$w.description,
3582
+ hidden: config$w.hidden,
3583
+ run: run$w
3511
3584
  };
3512
- async function run$t(argv, importMeta, {
3585
+ async function run$w(argv, importMeta, {
3513
3586
  parentName
3514
3587
  }) {
3515
3588
  const cli = meowOrExit({
3516
3589
  argv,
3517
- config: config$t,
3590
+ config: config$w,
3518
3591
  importMeta,
3519
3592
  parentName
3520
3593
  });
3521
3594
  if (cli.flags['dryRun']) {
3522
- logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3595
+ logger.logger.log(DRY_RUN_BAIL_TEXT$v);
3523
3596
  return;
3524
3597
  }
3525
3598
  await runFix();
@@ -3676,9 +3749,9 @@ async function getPackageInfo({
3676
3749
  }
3677
3750
 
3678
3751
  const {
3679
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r
3752
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u
3680
3753
  } = constants;
3681
- const config$s = {
3754
+ const config$v = {
3682
3755
  commandName: 'info',
3683
3756
  description: 'Look up info regarding a package',
3684
3757
  hidden: false,
@@ -3700,16 +3773,16 @@ const config$s = {
3700
3773
  `
3701
3774
  };
3702
3775
  const cmdInfo = {
3703
- description: config$s.description,
3704
- hidden: config$s.hidden,
3705
- run: run$s
3776
+ description: config$v.description,
3777
+ hidden: config$v.hidden,
3778
+ run: run$v
3706
3779
  };
3707
- async function run$s(argv, importMeta, {
3780
+ async function run$v(argv, importMeta, {
3708
3781
  parentName
3709
3782
  }) {
3710
3783
  const cli = meowOrExit({
3711
3784
  argv,
3712
- config: config$s,
3785
+ config: config$v,
3713
3786
  importMeta,
3714
3787
  parentName
3715
3788
  });
@@ -3734,11 +3807,11 @@ async function run$s(argv, importMeta, {
3734
3807
  const pkgName = versionSeparator < 1 ? rawPkgName : rawPkgName.slice(0, versionSeparator);
3735
3808
  const pkgVersion = versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1);
3736
3809
  if (cli.flags['dryRun']) {
3737
- logger.logger.log(DRY_RUN_BAIL_TEXT$r);
3810
+ logger.logger.log(DRY_RUN_BAIL_TEXT$u);
3738
3811
  return;
3739
3812
  }
3740
3813
  await getPackageInfo({
3741
- commandName: `${parentName} ${config$s.commandName}`,
3814
+ commandName: `${parentName} ${config$v.commandName}`,
3742
3815
  includeAllIssues: Boolean(all),
3743
3816
  outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
3744
3817
  pkgName,
@@ -3825,9 +3898,9 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3825
3898
  }
3826
3899
 
3827
3900
  const {
3828
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q
3901
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t
3829
3902
  } = constants;
3830
- const config$r = {
3903
+ const config$u = {
3831
3904
  commandName: 'login',
3832
3905
  description: 'Socket API login',
3833
3906
  hidden: false,
@@ -3857,23 +3930,23 @@ const config$r = {
3857
3930
  `
3858
3931
  };
3859
3932
  const cmdLogin = {
3860
- description: config$r.description,
3861
- hidden: config$r.hidden,
3862
- run: run$r
3933
+ description: config$u.description,
3934
+ hidden: config$u.hidden,
3935
+ run: run$u
3863
3936
  };
3864
- async function run$r(argv, importMeta, {
3937
+ async function run$u(argv, importMeta, {
3865
3938
  parentName
3866
3939
  }) {
3867
3940
  const cli = meowOrExit({
3868
3941
  argv,
3869
- config: config$r,
3942
+ config: config$u,
3870
3943
  importMeta,
3871
3944
  parentName
3872
3945
  });
3873
3946
  const apiBaseUrl = cli.flags['apiBaseUrl'];
3874
3947
  const apiProxy = cli.flags['apiProxy'];
3875
3948
  if (cli.flags['dryRun']) {
3876
- logger.logger.log(DRY_RUN_BAIL_TEXT$q);
3949
+ logger.logger.log(DRY_RUN_BAIL_TEXT$t);
3877
3950
  return;
3878
3951
  }
3879
3952
  if (!isInteractive()) {
@@ -3899,9 +3972,9 @@ function attemptLogout() {
3899
3972
  }
3900
3973
 
3901
3974
  const {
3902
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p
3975
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3903
3976
  } = constants;
3904
- const config$q = {
3977
+ const config$t = {
3905
3978
  commandName: 'logout',
3906
3979
  description: 'Socket API logout',
3907
3980
  hidden: false,
@@ -3916,21 +3989,21 @@ const config$q = {
3916
3989
  `
3917
3990
  };
3918
3991
  const cmdLogout = {
3919
- description: config$q.description,
3920
- hidden: config$q.hidden,
3921
- run: run$q
3992
+ description: config$t.description,
3993
+ hidden: config$t.hidden,
3994
+ run: run$t
3922
3995
  };
3923
- async function run$q(argv, importMeta, {
3996
+ async function run$t(argv, importMeta, {
3924
3997
  parentName
3925
3998
  }) {
3926
3999
  const cli = meowOrExit({
3927
4000
  argv,
3928
- config: config$q,
4001
+ config: config$t,
3929
4002
  importMeta,
3930
4003
  parentName
3931
4004
  });
3932
4005
  if (cli.flags['dryRun']) {
3933
- logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4006
+ logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3934
4007
  return;
3935
4008
  }
3936
4009
  attemptLogout();
@@ -4035,9 +4108,9 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
4035
4108
  }
4036
4109
 
4037
4110
  const {
4038
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o
4111
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r
4039
4112
  } = constants;
4040
- const config$p = {
4113
+ const config$s = {
4041
4114
  commandName: 'gradle',
4042
4115
  description: '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Gradle/Java/Kotlin/etc project',
4043
4116
  hidden: false,
@@ -4109,22 +4182,22 @@ const config$p = {
4109
4182
  `
4110
4183
  };
4111
4184
  const cmdManifestGradle = {
4112
- description: config$p.description,
4113
- hidden: config$p.hidden,
4114
- run: run$p
4185
+ description: config$s.description,
4186
+ hidden: config$s.hidden,
4187
+ run: run$s
4115
4188
  };
4116
- async function run$p(argv, importMeta, {
4189
+ async function run$s(argv, importMeta, {
4117
4190
  parentName
4118
4191
  }) {
4119
4192
  const cli = meowOrExit({
4120
4193
  argv,
4121
- config: config$p,
4194
+ config: config$s,
4122
4195
  importMeta,
4123
4196
  parentName
4124
4197
  });
4125
4198
  const verbose = Boolean(cli.flags['verbose']);
4126
4199
  if (verbose) {
4127
- logger.logger.group('- ', parentName, config$p.commandName, ':');
4200
+ logger.logger.group('- ', parentName, config$s.commandName, ':');
4128
4201
  logger.logger.group('- flags:', cli.flags);
4129
4202
  logger.logger.groupEnd();
4130
4203
  logger.logger.log('- input:', cli.input);
@@ -4172,7 +4245,7 @@ async function run$p(argv, importMeta, {
4172
4245
  gradleOpts = cli.flags['gradleOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4173
4246
  }
4174
4247
  if (cli.flags['dryRun']) {
4175
- logger.logger.log(DRY_RUN_BAIL_TEXT$o);
4248
+ logger.logger.log(DRY_RUN_BAIL_TEXT$r);
4176
4249
  return;
4177
4250
  }
4178
4251
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts);
@@ -4277,9 +4350,9 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
4277
4350
  }
4278
4351
 
4279
4352
  const {
4280
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n
4353
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q
4281
4354
  } = constants;
4282
- const config$o = {
4355
+ const config$r = {
4283
4356
  commandName: 'scala',
4284
4357
  description: "[beta] Generate a manifest file (`pom.xml`) from Scala's `build.sbt` file",
4285
4358
  hidden: false,
@@ -4352,22 +4425,22 @@ const config$o = {
4352
4425
  `
4353
4426
  };
4354
4427
  const cmdManifestScala = {
4355
- description: config$o.description,
4356
- hidden: config$o.hidden,
4357
- run: run$o
4428
+ description: config$r.description,
4429
+ hidden: config$r.hidden,
4430
+ run: run$r
4358
4431
  };
4359
- async function run$o(argv, importMeta, {
4432
+ async function run$r(argv, importMeta, {
4360
4433
  parentName
4361
4434
  }) {
4362
4435
  const cli = meowOrExit({
4363
4436
  argv,
4364
- config: config$o,
4437
+ config: config$r,
4365
4438
  importMeta,
4366
4439
  parentName
4367
4440
  });
4368
4441
  const verbose = Boolean(cli.flags['verbose']);
4369
4442
  if (verbose) {
4370
- logger.logger.group('- ', parentName, config$o.commandName, ':');
4443
+ logger.logger.group('- ', parentName, config$r.commandName, ':');
4371
4444
  logger.logger.group('- flags:', cli.flags);
4372
4445
  logger.logger.groupEnd();
4373
4446
  logger.logger.log('- input:', cli.input);
@@ -4413,16 +4486,16 @@ async function run$o(argv, importMeta, {
4413
4486
  sbtOpts = cli.flags['sbtOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4414
4487
  }
4415
4488
  if (cli.flags['dryRun']) {
4416
- logger.logger.log(DRY_RUN_BAIL_TEXT$n);
4489
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q);
4417
4490
  return;
4418
4491
  }
4419
4492
  await convertSbtToMaven(target, bin, out, verbose, sbtOpts);
4420
4493
  }
4421
4494
 
4422
4495
  const {
4423
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m
4496
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p
4424
4497
  } = constants;
4425
- const config$n = {
4498
+ const config$q = {
4426
4499
  commandName: 'auto',
4427
4500
  description: 'Auto-detect build and attempt to generate manifest file',
4428
4501
  hidden: false,
@@ -4452,23 +4525,23 @@ const config$n = {
4452
4525
  `
4453
4526
  };
4454
4527
  const cmdManifestAuto = {
4455
- description: config$n.description,
4456
- hidden: config$n.hidden,
4457
- run: run$n
4528
+ description: config$q.description,
4529
+ hidden: config$q.hidden,
4530
+ run: run$q
4458
4531
  };
4459
- async function run$n(argv, importMeta, {
4532
+ async function run$q(argv, importMeta, {
4460
4533
  parentName
4461
4534
  }) {
4462
4535
  const cli = meowOrExit({
4463
4536
  argv,
4464
- config: config$n,
4537
+ config: config$q,
4465
4538
  importMeta,
4466
4539
  parentName
4467
4540
  });
4468
4541
  const verbose = !!cli.flags['verbose'];
4469
4542
  const cwd = cli.flags['cwd'] ?? process.cwd();
4470
4543
  if (verbose) {
4471
- logger.logger.group('- ', parentName, config$n.commandName, ':');
4544
+ logger.logger.group('- ', parentName, config$q.commandName, ':');
4472
4545
  logger.logger.group('- flags:', cli.flags);
4473
4546
  logger.logger.groupEnd();
4474
4547
  logger.logger.log('- input:', cli.input);
@@ -4487,7 +4560,7 @@ async function run$n(argv, importMeta, {
4487
4560
  }
4488
4561
  subArgs.push(dir);
4489
4562
  if (cli.flags['dryRun']) {
4490
- logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4563
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4491
4564
  return;
4492
4565
  }
4493
4566
  await cmdManifestScala.run(subArgs, importMeta, {
@@ -4502,7 +4575,7 @@ async function run$n(argv, importMeta, {
4502
4575
  subArgs.push(cwd);
4503
4576
  }
4504
4577
  if (cli.flags['dryRun']) {
4505
- logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4578
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4506
4579
  return;
4507
4580
  }
4508
4581
  await cmdManifestGradle.run(subArgs, importMeta, {
@@ -4510,10 +4583,14 @@ async function run$n(argv, importMeta, {
4510
4583
  });
4511
4584
  return;
4512
4585
  }
4586
+ if (cli.flags['dryRun']) {
4587
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4588
+ return;
4589
+ }
4513
4590
 
4514
4591
  // Show new help screen and exit.
4515
4592
  meow(`
4516
- $ ${parentName} ${config$n.commandName}
4593
+ $ ${parentName} ${config$q.commandName}
4517
4594
 
4518
4595
  Unfortunately this script did not discover a supported language in the
4519
4596
  current folder.
@@ -4526,13 +4603,13 @@ async function run$n(argv, importMeta, {
4526
4603
  your target language.
4527
4604
  `, {
4528
4605
  argv: [],
4529
- description: config$n.description,
4606
+ description: config$q.description,
4530
4607
  importMeta
4531
4608
  }).showHelp();
4532
4609
  }
4533
4610
 
4534
4611
  const {
4535
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l
4612
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o
4536
4613
  } = constants;
4537
4614
 
4538
4615
  // TODO: we may want to dedupe some pieces for all gradle languages. I think it
@@ -4540,7 +4617,7 @@ const {
4540
4617
  // sense for the help panels to note the requested language, rather than
4541
4618
  // `socket manifest kotlin` to print help screens with `gradle` as the
4542
4619
  // command. Room for improvement.
4543
- const config$m = {
4620
+ const config$p = {
4544
4621
  commandName: 'kotlin',
4545
4622
  description: '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Kotlin project',
4546
4623
  hidden: false,
@@ -4612,22 +4689,22 @@ const config$m = {
4612
4689
  `
4613
4690
  };
4614
4691
  const cmdManifestKotlin = {
4615
- description: config$m.description,
4616
- hidden: config$m.hidden,
4617
- run: run$m
4692
+ description: config$p.description,
4693
+ hidden: config$p.hidden,
4694
+ run: run$p
4618
4695
  };
4619
- async function run$m(argv, importMeta, {
4696
+ async function run$p(argv, importMeta, {
4620
4697
  parentName
4621
4698
  }) {
4622
4699
  const cli = meowOrExit({
4623
4700
  argv,
4624
- config: config$m,
4701
+ config: config$p,
4625
4702
  importMeta,
4626
4703
  parentName
4627
4704
  });
4628
4705
  const verbose = Boolean(cli.flags['verbose']);
4629
4706
  if (verbose) {
4630
- logger.logger.group('- ', parentName, config$m.commandName, ':');
4707
+ logger.logger.group('- ', parentName, config$p.commandName, ':');
4631
4708
  logger.logger.group('- flags:', cli.flags);
4632
4709
  logger.logger.groupEnd();
4633
4710
  logger.logger.log('- input:', cli.input);
@@ -4675,13 +4752,13 @@ async function run$m(argv, importMeta, {
4675
4752
  gradleOpts = cli.flags['gradleOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4676
4753
  }
4677
4754
  if (cli.flags['dryRun']) {
4678
- logger.logger.log(DRY_RUN_BAIL_TEXT$l);
4755
+ logger.logger.log(DRY_RUN_BAIL_TEXT$o);
4679
4756
  return;
4680
4757
  }
4681
4758
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts);
4682
4759
  }
4683
4760
 
4684
- const config$l = {
4761
+ const config$o = {
4685
4762
  commandName: 'manifest',
4686
4763
  description: 'Generate a dependency manifest for given file or dir',
4687
4764
  hidden: false,
@@ -4689,11 +4766,11 @@ const config$l = {
4689
4766
  ...commonFlags
4690
4767
  }};
4691
4768
  const cmdManifest = {
4692
- description: config$l.description,
4693
- hidden: config$l.hidden,
4694
- run: run$l
4769
+ description: config$o.description,
4770
+ hidden: config$o.hidden,
4771
+ run: run$o
4695
4772
  };
4696
- async function run$l(argv, importMeta, {
4773
+ async function run$o(argv, importMeta, {
4697
4774
  parentName
4698
4775
  }) {
4699
4776
  await meowWithSubcommands({
@@ -4705,15 +4782,15 @@ async function run$l(argv, importMeta, {
4705
4782
  argv,
4706
4783
  aliases: {
4707
4784
  yolo: {
4708
- description: config$l.description,
4785
+ description: config$o.description,
4709
4786
  hidden: true,
4710
4787
  argv: ['auto']
4711
4788
  }
4712
4789
  },
4713
- description: config$l.description,
4790
+ description: config$o.description,
4714
4791
  importMeta,
4715
- flags: config$l.flags,
4716
- name: `${parentName} ${config$l.commandName}`
4792
+ flags: config$o.flags,
4793
+ name: `${parentName} ${config$o.commandName}`
4717
4794
  });
4718
4795
  }
4719
4796
 
@@ -4727,10 +4804,10 @@ async function wrapNpm(argv) {
4727
4804
  }
4728
4805
 
4729
4806
  const {
4730
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k,
4807
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n,
4731
4808
  NPM: NPM$7
4732
4809
  } = constants;
4733
- const config$k = {
4810
+ const config$n = {
4734
4811
  commandName: 'npm',
4735
4812
  description: `${NPM$7} wrapper functionality`,
4736
4813
  hidden: false,
@@ -4741,22 +4818,22 @@ const config$k = {
4741
4818
  `
4742
4819
  };
4743
4820
  const cmdNpm = {
4744
- description: config$k.description,
4745
- hidden: config$k.hidden,
4746
- run: run$k
4821
+ description: config$n.description,
4822
+ hidden: config$n.hidden,
4823
+ run: run$n
4747
4824
  };
4748
- async function run$k(argv, importMeta, {
4825
+ async function run$n(argv, importMeta, {
4749
4826
  parentName
4750
4827
  }) {
4751
4828
  const cli = meowOrExit({
4752
4829
  allowUnknownFlags: true,
4753
4830
  argv,
4754
- config: config$k,
4831
+ config: config$n,
4755
4832
  importMeta,
4756
4833
  parentName
4757
4834
  });
4758
4835
  if (cli.flags['dryRun']) {
4759
- logger.logger.log(DRY_RUN_BAIL_TEXT$k);
4836
+ logger.logger.log(DRY_RUN_BAIL_TEXT$n);
4760
4837
  return;
4761
4838
  }
4762
4839
  await wrapNpm(argv);
@@ -4772,10 +4849,10 @@ async function wrapNpx(argv) {
4772
4849
  }
4773
4850
 
4774
4851
  const {
4775
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j,
4852
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m,
4776
4853
  NPX: NPX$1
4777
4854
  } = constants;
4778
- const config$j = {
4855
+ const config$m = {
4779
4856
  commandName: 'npx',
4780
4857
  description: `${NPX$1} wrapper functionality`,
4781
4858
  hidden: false,
@@ -4786,31 +4863,31 @@ const config$j = {
4786
4863
  `
4787
4864
  };
4788
4865
  const cmdNpx = {
4789
- description: config$j.description,
4790
- hidden: config$j.hidden,
4791
- run: run$j
4866
+ description: config$m.description,
4867
+ hidden: config$m.hidden,
4868
+ run: run$m
4792
4869
  };
4793
- async function run$j(argv, importMeta, {
4870
+ async function run$m(argv, importMeta, {
4794
4871
  parentName
4795
4872
  }) {
4796
4873
  const cli = meowOrExit({
4797
4874
  allowUnknownFlags: true,
4798
4875
  argv,
4799
- config: config$j,
4876
+ config: config$m,
4800
4877
  importMeta,
4801
4878
  parentName
4802
4879
  });
4803
4880
  if (cli.flags['dryRun']) {
4804
- logger.logger.log(DRY_RUN_BAIL_TEXT$j);
4881
+ logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4805
4882
  return;
4806
4883
  }
4807
4884
  await wrapNpx(argv);
4808
4885
  }
4809
4886
 
4810
4887
  const {
4811
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i
4888
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l
4812
4889
  } = constants;
4813
- const config$i = {
4890
+ const config$l = {
4814
4891
  commandName: 'oops',
4815
4892
  description: 'Trigger an intentional error (for development)',
4816
4893
  hidden: true,
@@ -4825,21 +4902,21 @@ const config$i = {
4825
4902
  `
4826
4903
  };
4827
4904
  const cmdOops = {
4828
- description: config$i.description,
4829
- hidden: config$i.hidden,
4830
- run: run$i
4905
+ description: config$l.description,
4906
+ hidden: config$l.hidden,
4907
+ run: run$l
4831
4908
  };
4832
- async function run$i(argv, importMeta, {
4909
+ async function run$l(argv, importMeta, {
4833
4910
  parentName
4834
4911
  }) {
4835
4912
  const cli = meowOrExit({
4836
4913
  argv,
4837
- config: config$i,
4914
+ config: config$l,
4838
4915
  importMeta,
4839
4916
  parentName
4840
4917
  });
4841
4918
  if (cli.flags['dryRun']) {
4842
- logger.logger.log(DRY_RUN_BAIL_TEXT$i);
4919
+ logger.logger.log(DRY_RUN_BAIL_TEXT$l);
4843
4920
  return;
4844
4921
  }
4845
4922
  throw new Error('This error was intentionally left blank');
@@ -5346,10 +5423,12 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5346
5423
  spinner?.setText(`Adding overrides${workspaceName ? ` to ${workspaceName}` : ''}...`);
5347
5424
  const depAliasMap = new Map();
5348
5425
  const depEntries = getDependencyEntries(pkgJson);
5349
- const nodeRange = `>=${pkgEnvDetails.minimumNodeVersion}`;
5350
5426
  const manifestEntries = manifestNpmOverrides.filter(({
5351
5427
  1: data
5352
- }) => semver.satisfies(semver.coerce(data.engines.node), nodeRange));
5428
+ }) => semver.satisfies(
5429
+ // Roughly check Node range as semver.coerce will strip leading
5430
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
5431
+ semver.coerce(data.engines.node), pkgEnvDetails.pkgRequirements.node));
5353
5432
 
5354
5433
  // Chunk package names to process them in parallel 3 at a time.
5355
5434
  await promises.pEach(manifestEntries, 3, async ({
@@ -5373,9 +5452,15 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5373
5452
  const origSpec = objects.hasOwn(depObj, origPkgName) ? depObj[origPkgName] : undefined;
5374
5453
  if (origSpec) {
5375
5454
  let thisSpec = origSpec;
5376
- // Add package aliases for direct dependencies to avoid npm EOVERRIDE errors.
5455
+ // Add package aliases for direct dependencies to avoid npm EOVERRIDE
5456
+ // errors...
5377
5457
  // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases
5378
- if (!(thisSpec.startsWith(sockOverridePrefix) && semver.coerce(npa(thisSpec).rawSpec)?.version)) {
5458
+ if (
5459
+ // ...if the spec doesn't start with a valid Socket override.
5460
+ !(thisSpec.startsWith(sockOverridePrefix) &&
5461
+ // Check the validity of the spec by passing it through npa and
5462
+ // seeing if it will coerce to a version.
5463
+ semver.coerce(npa(thisSpec).rawSpec)?.version)) {
5379
5464
  thisSpec = sockOverrideSpec;
5380
5465
  depObj[origPkgName] = thisSpec;
5381
5466
  state.added.add(sockRegPkgName);
@@ -5419,7 +5504,13 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5419
5504
  } else if (typeof oldSpec === 'string') {
5420
5505
  const thisSpec = oldSpec.startsWith('$') ? depAlias || newSpec : oldSpec || newSpec;
5421
5506
  if (thisSpec.startsWith(sockOverridePrefix)) {
5422
- if (pin && semver.major(semver.coerce(npa(thisSpec).rawSpec)?.version ?? version) !== major) {
5507
+ if (pin && semver.major(
5508
+ // Check the validity of the spec by passing it through npa
5509
+ // and seeing if it will coerce to a version. semver.coerce
5510
+ // will strip leading v's, carets (^), comparators (<,<=,>,>=,=),
5511
+ // and tildes (~). If not coerced to a valid version then
5512
+ // default to the manifest entry version.
5513
+ semver.coerce(npa(thisSpec).rawSpec)?.version ?? version) !== major) {
5423
5514
  const otherVersion = (await packages.fetchPackageManifest(thisSpec))?.version;
5424
5515
  if (otherVersion && otherVersion !== version) {
5425
5516
  newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${semver.major(otherVersion)}`}`;
@@ -5556,9 +5647,9 @@ async function applyOptimization(cwd, pin, prod) {
5556
5647
  }
5557
5648
 
5558
5649
  const {
5559
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
5650
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k
5560
5651
  } = constants;
5561
- const config$h = {
5652
+ const config$k = {
5562
5653
  commandName: 'optimize',
5563
5654
  description: 'Optimize dependencies with @socketregistry overrides',
5564
5655
  hidden: false,
@@ -5588,22 +5679,22 @@ const config$h = {
5588
5679
  `
5589
5680
  };
5590
5681
  const cmdOptimize = {
5591
- description: config$h.description,
5592
- hidden: config$h.hidden,
5593
- run: run$h
5682
+ description: config$k.description,
5683
+ hidden: config$k.hidden,
5684
+ run: run$k
5594
5685
  };
5595
- async function run$h(argv, importMeta, {
5686
+ async function run$k(argv, importMeta, {
5596
5687
  parentName
5597
5688
  }) {
5598
5689
  const cli = meowOrExit({
5599
5690
  argv,
5600
- config: config$h,
5691
+ config: config$k,
5601
5692
  importMeta,
5602
5693
  parentName
5603
5694
  });
5604
5695
  const cwd = process$1.cwd();
5605
5696
  if (cli.flags['dryRun']) {
5606
- logger.logger.log(DRY_RUN_BAIL_TEXT$h);
5697
+ logger.logger.log(DRY_RUN_BAIL_TEXT$k);
5607
5698
  return;
5608
5699
  }
5609
5700
  await applyOptimization(cwd, Boolean(cli.flags['pin']), Boolean(cli.flags['prod']));
@@ -5677,10 +5768,10 @@ async function printOrganizationsFromToken(apiToken, format = 'text') {
5677
5768
  }
5678
5769
 
5679
5770
  const {
5680
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g
5771
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j
5681
5772
  } = constants;
5682
- const config$g = {
5683
- commandName: 'organizations',
5773
+ const config$j = {
5774
+ commandName: 'list',
5684
5775
  description: 'List organizations associated with the API key used',
5685
5776
  hidden: false,
5686
5777
  flags: {
@@ -5692,20 +5783,20 @@ const config$g = {
5692
5783
  $ ${command}
5693
5784
 
5694
5785
  Options
5695
- ${getFlagListOutput(config$g.flags, 6)}
5786
+ ${getFlagListOutput(config$j.flags, 6)}
5696
5787
  `
5697
5788
  };
5698
- const cmdOrganization = {
5699
- description: config$g.description,
5700
- hidden: config$g.hidden,
5701
- run: run$g
5789
+ const cmdOrganizationList = {
5790
+ description: config$j.description,
5791
+ hidden: config$j.hidden,
5792
+ run: run$j
5702
5793
  };
5703
- async function run$g(argv, importMeta, {
5794
+ async function run$j(argv, importMeta, {
5704
5795
  parentName
5705
5796
  }) {
5706
5797
  const cli = meowOrExit({
5707
5798
  argv,
5708
- config: config$g,
5799
+ config: config$j,
5709
5800
  importMeta,
5710
5801
  parentName
5711
5802
  });
@@ -5724,115 +5815,366 @@ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields
5724
5815
  return;
5725
5816
  }
5726
5817
  if (cli.flags['dryRun']) {
5727
- logger.logger.log(DRY_RUN_BAIL_TEXT$g);
5818
+ logger.logger.log(DRY_RUN_BAIL_TEXT$j);
5728
5819
  return;
5729
5820
  }
5730
5821
  await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text');
5731
5822
  }
5732
5823
 
5733
- async function runRawNpm(argv) {
5734
- const spawnPromise = spawn.spawn(shadowNpmPaths.getNpmBinPath(), argv, {
5735
- stdio: 'inherit'
5736
- });
5737
- // See https://nodejs.org/api/all.html#all_child_process_event-exit.
5738
- spawnPromise.process.on('exit', (code, signalName) => {
5739
- if (signalName) {
5740
- process$1.kill(process$1.pid, signalName);
5741
- } else if (code !== null) {
5742
- process$1.exit(code);
5743
- }
5744
- });
5745
- await spawnPromise;
5824
+ async function getSecurityPolicy(orgSlug, format) {
5825
+ const apiToken = shadowNpmInject.getDefaultToken();
5826
+ if (!apiToken) {
5827
+ 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.');
5828
+ }
5829
+ await getSecurityPolicyWithToken(apiToken, orgSlug, format);
5830
+ }
5831
+ async function getSecurityPolicyWithToken(apiToken, orgSlug, format) {
5832
+ // Lazily access constants.spinner.
5833
+ const {
5834
+ spinner
5835
+ } = constants;
5836
+ spinner.start('Fetching organization quota...');
5837
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
5838
+ const result = await handleApiCall(socketSdk.getOrgSecurityPolicy(orgSlug), 'looking up organization quota');
5839
+ if (!result.success) {
5840
+ handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result);
5841
+ return;
5842
+ }
5843
+ spinner.stop();
5844
+ switch (format) {
5845
+ case 'json':
5846
+ {
5847
+ logger.logger.log(JSON.stringify(result.data, null, 2));
5848
+ return;
5849
+ }
5850
+ default:
5851
+ {
5852
+ logger.logger.log('# Security policy\n');
5853
+ logger.logger.log(`The default security policy setting is: "${result.data.securityPolicyDefault}"\n`);
5854
+ logger.logger.log('These are the security policies per setting for your organization:\n');
5855
+ const data = result.data;
5856
+ const rules = data.securityPolicyRules;
5857
+ const entries = Object.entries(rules);
5858
+ const mapped = entries.map(([key, value]) => [key, value.action]);
5859
+ mapped.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
5860
+ logger.logger.log(mdTableOfPairs(mapped, ['name', 'action']));
5861
+ }
5862
+ }
5746
5863
  }
5747
5864
 
5748
5865
  const {
5749
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f,
5750
- NPM
5866
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i
5751
5867
  } = constants;
5752
- const config$f = {
5753
- commandName: 'raw-npm',
5754
- description: `Temporarily disable the Socket ${NPM} wrapper`,
5755
- hidden: false,
5756
- flags: {},
5757
- help: command => `
5868
+
5869
+ // TODO: secret toplevel alias `socket security policy`?
5870
+ const config$i = {
5871
+ commandName: 'security',
5872
+ description: 'Retrieve the security policy of an organization.',
5873
+ hidden: true,
5874
+ flags: {
5875
+ ...commonFlags,
5876
+ ...outputFlags
5877
+ },
5878
+ help: (command, _config) => `
5758
5879
  Usage
5759
- $ ${command} <command>
5880
+ $ ${command} <org slug>
5881
+
5882
+ Options
5883
+ ${getFlagListOutput(config$i.flags, 6)}
5884
+
5885
+ Your API token will need the \`security-policy:read\` permission otherwise
5886
+ the request will fail with an authentication error.
5760
5887
 
5761
5888
  Examples
5762
- $ ${command} install
5889
+ $ ${command} mycorp
5890
+ $ ${command} mycorp --json
5763
5891
  `
5764
5892
  };
5765
- const cmdRawNpm = {
5766
- description: config$f.description,
5767
- hidden: config$f.hidden,
5768
- run: run$f
5893
+ const cmdOrganizationPolicyPolicy = {
5894
+ description: config$i.description,
5895
+ hidden: config$i.hidden,
5896
+ run: run$i
5769
5897
  };
5770
- async function run$f(argv, importMeta, {
5898
+ async function run$i(argv, importMeta, {
5771
5899
  parentName
5772
5900
  }) {
5773
5901
  const cli = meowOrExit({
5774
- allowUnknownFlags: true,
5775
5902
  argv,
5776
- config: config$f,
5903
+ config: config$i,
5777
5904
  importMeta,
5778
5905
  parentName
5779
5906
  });
5907
+ const json = Boolean(cli.flags['json']);
5908
+ const markdown = Boolean(cli.flags['markdown']);
5909
+ const [orgSlug = ''] = cli.input;
5910
+ if (!orgSlug || json && markdown) {
5911
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
5912
+ // options or missing arguments.
5913
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5914
+ process.exitCode = 2;
5915
+ logger.logger.fail(commonTags.stripIndents`
5916
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
5917
+
5918
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
5919
+ - The json and markdown flags cannot be both set ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
5920
+ `);
5921
+ return;
5922
+ }
5780
5923
  if (cli.flags['dryRun']) {
5781
- logger.logger.log(DRY_RUN_BAIL_TEXT$f);
5924
+ logger.logger.log(DRY_RUN_BAIL_TEXT$i);
5782
5925
  return;
5783
5926
  }
5784
- await runRawNpm(argv);
5927
+ await getSecurityPolicy(orgSlug, json ? 'json' : markdown ? 'markdown' : 'text');
5785
5928
  }
5786
5929
 
5787
- async function runRawNpx(argv) {
5788
- const spawnPromise = spawn.spawn(shadowNpmPaths.getNpxBinPath(), argv, {
5789
- stdio: 'inherit'
5790
- });
5791
- // See https://nodejs.org/api/all.html#all_child_process_event-exit.
5792
- spawnPromise.process.on('exit', (code, signalName) => {
5793
- if (signalName) {
5794
- process$1.kill(process$1.pid, signalName);
5795
- } else if (code !== null) {
5796
- process$1.exit(code);
5797
- }
5798
- });
5799
- await spawnPromise;
5930
+ const description$4 = 'Organization policy details';
5931
+ const cmdOrganizationPolicy = {
5932
+ description: description$4,
5933
+ // Hidden because it was broken all this time (nobody could be using it)
5934
+ // and we're not sure if it's useful to anyone in its current state.
5935
+ // Until we do, we'll hide this to keep the help tidier.
5936
+ // And later, we may simply move this under `scan`, anyways.
5937
+ hidden: true,
5938
+ async run(argv, importMeta, {
5939
+ parentName
5940
+ }) {
5941
+ await meowWithSubcommands({
5942
+ security: cmdOrganizationPolicyPolicy
5943
+ }, {
5944
+ argv,
5945
+ description: description$4,
5946
+ defaultSub: 'list',
5947
+ // Backwards compat
5948
+ importMeta,
5949
+ name: parentName + ' policy'
5950
+ });
5951
+ }
5952
+ };
5953
+
5954
+ async function getQuota(format = 'text') {
5955
+ const apiToken = shadowNpmInject.getDefaultToken();
5956
+ if (!apiToken) {
5957
+ 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.');
5958
+ }
5959
+ await getQuotaWithToken(apiToken, format);
5960
+ }
5961
+ async function getQuotaWithToken(apiToken, format = 'text') {
5962
+ // Lazily access constants.spinner.
5963
+ const {
5964
+ spinner
5965
+ } = constants;
5966
+ spinner.start('Fetching organization quota...');
5967
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
5968
+ const result = await handleApiCall(socketSdk.getQuota(), 'looking up organization quota');
5969
+ if (!result.success) {
5970
+ handleUnsuccessfulApiResponse('getQuota', result);
5971
+ return;
5972
+ }
5973
+ spinner.stop();
5974
+ switch (format) {
5975
+ case 'json':
5976
+ {
5977
+ logger.logger.log(JSON.stringify({
5978
+ quota: result.data.quota
5979
+ }, null, 2));
5980
+ return;
5981
+ }
5982
+ case 'markdown':
5983
+ {
5984
+ logger.logger.log('# Quota\n');
5985
+ logger.logger.log(`Quota left on the current API token: ${result.data.quota}\n`);
5986
+ return;
5987
+ }
5988
+ default:
5989
+ {
5990
+ logger.logger.log(`Quota left on the current API token: ${result.data.quota}\n`);
5991
+ }
5992
+ }
5800
5993
  }
5801
5994
 
5802
5995
  const {
5803
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e,
5804
- NPX
5996
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
5805
5997
  } = constants;
5806
- const config$e = {
5807
- commandName: 'raw-npx',
5808
- description: `Temporarily disable the Socket ${NPX} wrapper`,
5809
- hidden: false,
5810
- flags: {},
5811
- help: command => `
5998
+ const config$h = {
5999
+ commandName: 'quota',
6000
+ description: 'List organizations associated with the API key used',
6001
+ hidden: true,
6002
+ flags: {
6003
+ ...commonFlags,
6004
+ ...outputFlags
6005
+ },
6006
+ help: (command, _config) => `
5812
6007
  Usage
5813
- $ ${command} <command>
6008
+ $ ${command}
5814
6009
 
5815
- Examples
5816
- $ ${command} install
6010
+ Options
6011
+ ${getFlagListOutput(config$h.flags, 6)}
5817
6012
  `
5818
6013
  };
5819
- const cmdRawNpx = {
5820
- description: config$e.description,
5821
- hidden: config$e.hidden,
5822
- run: run$e
6014
+ const cmdOrganizationQuota = {
6015
+ description: config$h.description,
6016
+ hidden: config$h.hidden,
6017
+ run: run$h
5823
6018
  };
5824
- async function run$e(argv, importMeta, {
6019
+ async function run$h(argv, importMeta, {
6020
+ parentName
6021
+ }) {
6022
+ const cli = meowOrExit({
6023
+ argv,
6024
+ config: config$h,
6025
+ importMeta,
6026
+ parentName
6027
+ });
6028
+ const json = Boolean(cli.flags['json']);
6029
+ const markdown = Boolean(cli.flags['markdown']);
6030
+ if (json && markdown) {
6031
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
6032
+ // options or missing arguments.
6033
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6034
+ process.exitCode = 2;
6035
+ logger.logger.fail(commonTags.stripIndents`
6036
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6037
+
6038
+ - The json and markdown flags cannot be both set, pick one
6039
+ `);
6040
+ return;
6041
+ }
6042
+ if (cli.flags['dryRun']) {
6043
+ logger.logger.log(DRY_RUN_BAIL_TEXT$h);
6044
+ return;
6045
+ }
6046
+ await getQuota(json ? 'json' : markdown ? 'markdown' : 'text');
6047
+ }
6048
+
6049
+ const description$3 = 'Account details';
6050
+ const cmdOrganization = {
6051
+ description: description$3,
6052
+ // Hidden because it was broken all this time (nobody could be using it)
6053
+ // and we're not sure if it's useful to anyone in its current state.
6054
+ // Until we do, we'll hide this to keep the help tidier.
6055
+ // And later, we may simply move this under `scan`, anyways.
6056
+ hidden: true,
6057
+ async run(argv, importMeta, {
6058
+ parentName
6059
+ }) {
6060
+ await meowWithSubcommands({
6061
+ list: cmdOrganizationList,
6062
+ quota: cmdOrganizationQuota,
6063
+ policy: cmdOrganizationPolicy
6064
+ }, {
6065
+ argv,
6066
+ description: description$3,
6067
+ defaultSub: 'list',
6068
+ // Backwards compat
6069
+ importMeta,
6070
+ name: parentName + ' organization'
6071
+ });
6072
+ }
6073
+ };
6074
+
6075
+ async function runRawNpm(argv) {
6076
+ const spawnPromise = spawn.spawn(shadowNpmPaths.getNpmBinPath(), argv, {
6077
+ stdio: 'inherit'
6078
+ });
6079
+ // See https://nodejs.org/api/all.html#all_child_process_event-exit.
6080
+ spawnPromise.process.on('exit', (code, signalName) => {
6081
+ if (signalName) {
6082
+ process$1.kill(process$1.pid, signalName);
6083
+ } else if (code !== null) {
6084
+ process$1.exit(code);
6085
+ }
6086
+ });
6087
+ await spawnPromise;
6088
+ }
6089
+
6090
+ const {
6091
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g,
6092
+ NPM
6093
+ } = constants;
6094
+ const config$g = {
6095
+ commandName: 'raw-npm',
6096
+ description: `Temporarily disable the Socket ${NPM} wrapper`,
6097
+ hidden: false,
6098
+ flags: {},
6099
+ help: command => `
6100
+ Usage
6101
+ $ ${command} <command>
6102
+
6103
+ Examples
6104
+ $ ${command} install
6105
+ `
6106
+ };
6107
+ const cmdRawNpm = {
6108
+ description: config$g.description,
6109
+ hidden: config$g.hidden,
6110
+ run: run$g
6111
+ };
6112
+ async function run$g(argv, importMeta, {
5825
6113
  parentName
5826
6114
  }) {
5827
6115
  const cli = meowOrExit({
5828
6116
  allowUnknownFlags: true,
5829
6117
  argv,
5830
- config: config$e,
6118
+ config: config$g,
5831
6119
  importMeta,
5832
6120
  parentName
5833
6121
  });
5834
6122
  if (cli.flags['dryRun']) {
5835
- logger.logger.log(DRY_RUN_BAIL_TEXT$e);
6123
+ logger.logger.log(DRY_RUN_BAIL_TEXT$g);
6124
+ return;
6125
+ }
6126
+ await runRawNpm(argv);
6127
+ }
6128
+
6129
+ async function runRawNpx(argv) {
6130
+ const spawnPromise = spawn.spawn(shadowNpmPaths.getNpxBinPath(), argv, {
6131
+ stdio: 'inherit'
6132
+ });
6133
+ // See https://nodejs.org/api/all.html#all_child_process_event-exit.
6134
+ spawnPromise.process.on('exit', (code, signalName) => {
6135
+ if (signalName) {
6136
+ process$1.kill(process$1.pid, signalName);
6137
+ } else if (code !== null) {
6138
+ process$1.exit(code);
6139
+ }
6140
+ });
6141
+ await spawnPromise;
6142
+ }
6143
+
6144
+ const {
6145
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f,
6146
+ NPX
6147
+ } = constants;
6148
+ const config$f = {
6149
+ commandName: 'raw-npx',
6150
+ description: `Temporarily disable the Socket ${NPX} wrapper`,
6151
+ hidden: false,
6152
+ flags: {},
6153
+ help: command => `
6154
+ Usage
6155
+ $ ${command} <command>
6156
+
6157
+ Examples
6158
+ $ ${command} install
6159
+ `
6160
+ };
6161
+ const cmdRawNpx = {
6162
+ description: config$f.description,
6163
+ hidden: config$f.hidden,
6164
+ run: run$f
6165
+ };
6166
+ async function run$f(argv, importMeta, {
6167
+ parentName
6168
+ }) {
6169
+ const cli = meowOrExit({
6170
+ allowUnknownFlags: true,
6171
+ argv,
6172
+ config: config$f,
6173
+ importMeta,
6174
+ parentName
6175
+ });
6176
+ if (cli.flags['dryRun']) {
6177
+ logger.logger.log(DRY_RUN_BAIL_TEXT$f);
5836
6178
  return;
5837
6179
  }
5838
6180
  await runRawNpx(argv);
@@ -5881,8 +6223,8 @@ async function createReport(socketConfig, inputPaths, {
5881
6223
  }
5882
6224
 
5883
6225
  async function getSocketConfig(absoluteConfigPath) {
5884
- const socketConfig = await config$A.readSocketConfig(absoluteConfigPath).catch(cause => {
5885
- if (cause && typeof cause === 'object' && cause instanceof config$A.SocketValidationError) {
6226
+ const socketConfig = await config$D.readSocketConfig(absoluteConfigPath).catch(cause => {
6227
+ if (cause && typeof cause === 'object' && cause instanceof config$D.SocketValidationError) {
5886
6228
  // Inspired by workbox-build:
5887
6229
  // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71
5888
6230
  const betterErrors = betterAjvErrors.betterAjvErrors({
@@ -5903,12 +6245,13 @@ async function getSocketConfig(absoluteConfigPath) {
5903
6245
 
5904
6246
  const MAX_TIMEOUT_RETRY = 5;
5905
6247
  const HTTP_CODE_TIMEOUT = 524;
5906
- async function fetchReportData(reportId, includeAllIssues, strict) {
6248
+ async function fetchReportData$1(reportId, includeAllIssues, strict) {
5907
6249
  // Lazily access constants.spinner.
5908
6250
  const {
5909
6251
  spinner
5910
6252
  } = constants;
5911
- spinner.start(`Fetching report with ID ${reportId} (this could take a while)`);
6253
+ spinner.log('Fetching report with ID ${reportId} (this could take a while)');
6254
+ spinner.start(`Fetch started... (this could take a while)`);
5912
6255
  const socketSdk = await shadowNpmInject.setupSdk();
5913
6256
  let result;
5914
6257
  for (let retry = 1; !result; ++retry) {
@@ -5917,9 +6260,10 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5917
6260
  result = await handleApiCall(socketSdk.getReport(reportId), 'fetching report');
5918
6261
  } catch (err) {
5919
6262
  if (retry >= MAX_TIMEOUT_RETRY || !(err instanceof Error) || err.cause?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT) {
5920
- spinner.stop();
6263
+ spinner.stop(`Failed to fetch report`);
5921
6264
  throw err;
5922
6265
  }
6266
+ spinner?.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`);
5923
6267
  }
5924
6268
  }
5925
6269
  if (!result.success) {
@@ -5944,17 +6288,20 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5944
6288
  return result.data;
5945
6289
  }
5946
6290
 
5947
- function formatReportDataOutput(reportId, data, commandName, outputJson, outputMarkdown, strict) {
5948
- if (outputJson) {
6291
+ function formatReportDataOutput(reportId, data, commandName, outputKind, strict, artifacts) {
6292
+ if (outputKind === 'json') {
5949
6293
  logger.logger.log(JSON.stringify(data, undefined, 2));
5950
6294
  } else {
5951
- const format = new shadowNpmInject.ColorOrMarkdown(outputMarkdown);
6295
+ const format = new shadowNpmInject.ColorOrMarkdown(outputKind === 'markdown');
5952
6296
  logger.logger.log(commonTags.stripIndents`
5953
6297
  Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {
5954
6298
  fallbackToUrl: true
5955
6299
  })}`);
5956
- if (!outputMarkdown) {
6300
+ if (outputKind === 'print') {
6301
+ logger.logger.log(data);
5957
6302
  logger.logger.log(colors.dim(`Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`));
6303
+ logger.logger.log('The scan:');
6304
+ logger.logger.log(artifacts);
5958
6305
  }
5959
6306
  }
5960
6307
  if (strict && !data.healthy) {
@@ -5962,23 +6309,55 @@ function formatReportDataOutput(reportId, data, commandName, outputJson, outputM
5962
6309
  }
5963
6310
  }
5964
6311
 
6312
+ async function getFullScan(orgSlug, fullScanId) {
6313
+ // Lazily access constants.spinner.
6314
+ const {
6315
+ spinner
6316
+ } = constants;
6317
+ const apiToken = shadowNpmInject.getDefaultToken();
6318
+ if (!apiToken) {
6319
+ 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.');
6320
+ }
6321
+ spinner.start('Fetching full-scan...');
6322
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
6323
+ spinner.stop('Fetch complete.');
6324
+ if (!response.ok) {
6325
+ const err = await handleAPIError(response.status);
6326
+ logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
6327
+ return;
6328
+ }
6329
+
6330
+ // This is nd-json; each line is a json object
6331
+ const jsons = await response.text();
6332
+ const lines = jsons.split('\n').filter(Boolean);
6333
+ const data = lines.map(line => {
6334
+ try {
6335
+ return JSON.parse(line);
6336
+ } catch {
6337
+ console.error('At least one line item was returned that could not be parsed as JSON...');
6338
+ return {};
6339
+ }
6340
+ });
6341
+ return data;
6342
+ }
6343
+
5965
6344
  async function viewReport(reportId, {
5966
6345
  all,
5967
6346
  commandName,
5968
- json,
5969
- markdown,
6347
+ outputKind,
5970
6348
  strict
5971
6349
  }) {
5972
- const result = await fetchReportData(reportId, all, strict);
6350
+ const result = await fetchReportData$1(reportId, all, strict);
6351
+ const artifacts = await getFullScan('socketdev', reportId);
5973
6352
  if (result) {
5974
- formatReportDataOutput(reportId, result, commandName, json, markdown, strict);
6353
+ formatReportDataOutput(reportId, result, commandName, outputKind, strict, artifacts);
5975
6354
  }
5976
6355
  }
5977
6356
 
5978
6357
  const {
5979
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d
6358
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e
5980
6359
  } = constants;
5981
- const config$d = {
6360
+ const config$e = {
5982
6361
  commandName: 'create',
5983
6362
  description: '[Deprecated] Create a project report',
5984
6363
  hidden: false,
@@ -5999,21 +6378,21 @@ const config$d = {
5999
6378
  }
6000
6379
  },
6001
6380
  help: () => `
6002
- This command is deprecated in favor of \`socket scan create\`.
6381
+ This command is deprecated in favor of \`socket scan view\`.
6003
6382
  It will be removed in the next major release of the CLI.
6004
6383
  `
6005
6384
  };
6006
6385
  const cmdReportCreate = {
6007
- description: config$d.description,
6008
- hidden: config$d.hidden,
6009
- run: run$d
6386
+ description: config$e.description,
6387
+ hidden: config$e.hidden,
6388
+ run: run$e
6010
6389
  };
6011
- async function run$d(argv, importMeta, {
6390
+ async function run$e(argv, importMeta, {
6012
6391
  parentName
6013
6392
  }) {
6014
6393
  const cli = meowOrExit({
6015
6394
  argv,
6016
- config: config$d,
6395
+ config: config$e,
6017
6396
  importMeta,
6018
6397
  parentName
6019
6398
  });
@@ -6030,7 +6409,7 @@ async function run$d(argv, importMeta, {
6030
6409
 
6031
6410
  // Note exiting earlier to skirt a hidden auth requirement
6032
6411
  if (cli.flags['dryRun']) {
6033
- logger.logger.log(DRY_RUN_BAIL_TEXT$d);
6412
+ logger.logger.log(DRY_RUN_BAIL_TEXT$e);
6034
6413
  return;
6035
6414
  }
6036
6415
  const socketConfig = await getSocketConfig(absoluteConfigPath);
@@ -6038,15 +6417,14 @@ async function run$d(argv, importMeta, {
6038
6417
  cwd,
6039
6418
  dryRun
6040
6419
  });
6041
- const commandName = `${parentName} ${config$d.commandName}`;
6420
+ const commandName = `${parentName} ${config$e.commandName}`;
6042
6421
  if (result?.success) {
6043
6422
  if (view) {
6044
6423
  const reportId = result.data.id;
6045
6424
  await viewReport(reportId, {
6046
6425
  all: includeAllIssues,
6047
6426
  commandName,
6048
- json,
6049
- markdown,
6427
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
6050
6428
  strict
6051
6429
  });
6052
6430
  } else if (json) {
@@ -6061,9 +6439,9 @@ async function run$d(argv, importMeta, {
6061
6439
  }
6062
6440
 
6063
6441
  const {
6064
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$c
6442
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d
6065
6443
  } = constants;
6066
- const config$c = {
6444
+ const config$d = {
6067
6445
  commandName: 'view',
6068
6446
  description: '[Deprecated] View a project report',
6069
6447
  hidden: false,
@@ -6078,16 +6456,16 @@ const config$c = {
6078
6456
  `
6079
6457
  };
6080
6458
  const cmdReportView = {
6081
- description: config$c.description,
6082
- hidden: config$c.hidden,
6083
- run: run$c
6459
+ description: config$d.description,
6460
+ hidden: config$d.hidden,
6461
+ run: run$d
6084
6462
  };
6085
- async function run$c(argv, importMeta, {
6463
+ async function run$d(argv, importMeta, {
6086
6464
  parentName
6087
6465
  }) {
6088
6466
  const cli = meowOrExit({
6089
6467
  argv,
6090
- config: config$c,
6468
+ config: config$d,
6091
6469
  importMeta,
6092
6470
  parentName
6093
6471
  });
@@ -6107,14 +6485,13 @@ async function run$c(argv, importMeta, {
6107
6485
  return;
6108
6486
  }
6109
6487
  if (cli.flags['dryRun']) {
6110
- logger.logger.log(DRY_RUN_BAIL_TEXT$c);
6488
+ logger.logger.log(DRY_RUN_BAIL_TEXT$d);
6111
6489
  return;
6112
6490
  }
6113
6491
  await viewReport(reportId, {
6114
6492
  all: Boolean(cli.flags['all']),
6115
- commandName: `${parentName} ${config$c.commandName}`,
6116
- json: Boolean(cli.flags['json']),
6117
- markdown: Boolean(cli.flags['markdown']),
6493
+ commandName: `${parentName} ${config$d.commandName}`,
6494
+ outputKind: cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print',
6118
6495
  strict: Boolean(cli.flags['strict'])
6119
6496
  });
6120
6497
  }
@@ -6191,9 +6568,9 @@ async function createRepoWithToken({
6191
6568
  }
6192
6569
 
6193
6570
  const {
6194
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$b
6571
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$c
6195
6572
  } = constants;
6196
- const config$b = {
6573
+ const config$c = {
6197
6574
  commandName: 'create',
6198
6575
  description: 'Create a repository in an organization',
6199
6576
  hidden: false,
@@ -6242,16 +6619,16 @@ const config$b = {
6242
6619
  `
6243
6620
  };
6244
6621
  const cmdReposCreate = {
6245
- description: config$b.description,
6246
- hidden: config$b.hidden,
6247
- run: run$b
6622
+ description: config$c.description,
6623
+ hidden: config$c.hidden,
6624
+ run: run$c
6248
6625
  };
6249
- async function run$b(argv, importMeta, {
6626
+ async function run$c(argv, importMeta, {
6250
6627
  parentName
6251
6628
  }) {
6252
6629
  const cli = meowOrExit({
6253
6630
  argv,
6254
- config: config$b,
6631
+ config: config$c,
6255
6632
  importMeta,
6256
6633
  parentName
6257
6634
  });
@@ -6270,7 +6647,7 @@ async function run$b(argv, importMeta, {
6270
6647
  return;
6271
6648
  }
6272
6649
  if (cli.flags['dryRun']) {
6273
- logger.logger.log(DRY_RUN_BAIL_TEXT$b);
6650
+ logger.logger.log(DRY_RUN_BAIL_TEXT$c);
6274
6651
  return;
6275
6652
  }
6276
6653
  await createRepo({
@@ -6306,9 +6683,9 @@ async function deleteRepoWithToken(orgSlug, repoName, apiToken) {
6306
6683
  }
6307
6684
 
6308
6685
  const {
6309
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$a
6686
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$b
6310
6687
  } = constants;
6311
- const config$a = {
6688
+ const config$b = {
6312
6689
  commandName: 'del',
6313
6690
  description: 'Delete a repository in an organization',
6314
6691
  hidden: false,
@@ -6327,16 +6704,16 @@ const config$a = {
6327
6704
  `
6328
6705
  };
6329
6706
  const cmdReposDel = {
6330
- description: config$a.description,
6331
- hidden: config$a.hidden,
6332
- run: run$a
6707
+ description: config$b.description,
6708
+ hidden: config$b.hidden,
6709
+ run: run$b
6333
6710
  };
6334
- async function run$a(argv, importMeta, {
6711
+ async function run$b(argv, importMeta, {
6335
6712
  parentName
6336
6713
  }) {
6337
6714
  const cli = meowOrExit({
6338
6715
  argv,
6339
- config: config$a,
6716
+ config: config$b,
6340
6717
  importMeta,
6341
6718
  parentName
6342
6719
  });
@@ -6356,7 +6733,7 @@ async function run$a(argv, importMeta, {
6356
6733
  return;
6357
6734
  }
6358
6735
  if (cli.flags['dryRun']) {
6359
- logger.logger.log(DRY_RUN_BAIL_TEXT$a);
6736
+ logger.logger.log(DRY_RUN_BAIL_TEXT$b);
6360
6737
  return;
6361
6738
  }
6362
6739
  await deleteRepo(orgSlug, repoName);
@@ -6444,9 +6821,9 @@ async function listReposWithToken({
6444
6821
  }
6445
6822
 
6446
6823
  const {
6447
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$9
6824
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$a
6448
6825
  } = constants;
6449
- const config$9 = {
6826
+ const config$a = {
6450
6827
  commandName: 'list',
6451
6828
  description: 'List repositories in an organization',
6452
6829
  hidden: false,
@@ -6489,16 +6866,16 @@ const config$9 = {
6489
6866
  `
6490
6867
  };
6491
6868
  const cmdReposList = {
6492
- description: config$9.description,
6493
- hidden: config$9.hidden,
6494
- run: run$9
6869
+ description: config$a.description,
6870
+ hidden: config$a.hidden,
6871
+ run: run$a
6495
6872
  };
6496
- async function run$9(argv, importMeta, {
6873
+ async function run$a(argv, importMeta, {
6497
6874
  parentName
6498
6875
  }) {
6499
6876
  const cli = meowOrExit({
6500
6877
  argv,
6501
- config: config$9,
6878
+ config: config$a,
6502
6879
  importMeta,
6503
6880
  parentName
6504
6881
  });
@@ -6516,7 +6893,7 @@ async function run$9(argv, importMeta, {
6516
6893
  return;
6517
6894
  }
6518
6895
  if (cli.flags['dryRun']) {
6519
- logger.logger.log(DRY_RUN_BAIL_TEXT$9);
6896
+ logger.logger.log(DRY_RUN_BAIL_TEXT$a);
6520
6897
  return;
6521
6898
  }
6522
6899
  await listRepos({
@@ -6582,9 +6959,9 @@ async function updateRepoWithToken({
6582
6959
  }
6583
6960
 
6584
6961
  const {
6585
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$8
6962
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$9
6586
6963
  } = constants;
6587
- const config$8 = {
6964
+ const config$9 = {
6588
6965
  commandName: 'update',
6589
6966
  description: 'Update a repository in an organization',
6590
6967
  hidden: false,
@@ -6633,16 +7010,16 @@ const config$8 = {
6633
7010
  `
6634
7011
  };
6635
7012
  const cmdReposUpdate = {
6636
- description: config$8.description,
6637
- hidden: config$8.hidden,
6638
- run: run$8
7013
+ description: config$9.description,
7014
+ hidden: config$9.hidden,
7015
+ run: run$9
6639
7016
  };
6640
- async function run$8(argv, importMeta, {
7017
+ async function run$9(argv, importMeta, {
6641
7018
  parentName
6642
7019
  }) {
6643
7020
  const cli = meowOrExit({
6644
7021
  argv,
6645
- config: config$8,
7022
+ config: config$9,
6646
7023
  importMeta,
6647
7024
  parentName
6648
7025
  });
@@ -6663,7 +7040,7 @@ async function run$8(argv, importMeta, {
6663
7040
  return;
6664
7041
  }
6665
7042
  if (cli.flags['dryRun']) {
6666
- logger.logger.log(DRY_RUN_BAIL_TEXT$8);
7043
+ logger.logger.log(DRY_RUN_BAIL_TEXT$9);
6667
7044
  return;
6668
7045
  }
6669
7046
  await updateRepo({
@@ -6746,9 +7123,9 @@ async function viewRepoWithToken(orgSlug, repoName, apiToken, outputKind) {
6746
7123
  }
6747
7124
 
6748
7125
  const {
6749
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$7
7126
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$8
6750
7127
  } = constants;
6751
- const config$7 = {
7128
+ const config$8 = {
6752
7129
  commandName: 'view',
6753
7130
  description: 'View repositories in an organization',
6754
7131
  hidden: false,
@@ -6773,16 +7150,16 @@ const config$7 = {
6773
7150
  `
6774
7151
  };
6775
7152
  const cmdReposView = {
6776
- description: config$7.description,
6777
- hidden: config$7.hidden,
6778
- run: run$7
7153
+ description: config$8.description,
7154
+ hidden: config$8.hidden,
7155
+ run: run$8
6779
7156
  };
6780
- async function run$7(argv, importMeta, {
7157
+ async function run$8(argv, importMeta, {
6781
7158
  parentName
6782
7159
  }) {
6783
7160
  const cli = meowOrExit({
6784
7161
  argv,
6785
- config: config$7,
7162
+ config: config$8,
6786
7163
  importMeta,
6787
7164
  parentName
6788
7165
  });
@@ -6803,7 +7180,7 @@ async function run$7(argv, importMeta, {
6803
7180
  return;
6804
7181
  }
6805
7182
  if (cli.flags['dryRun']) {
6806
- logger.logger.log(DRY_RUN_BAIL_TEXT$7);
7183
+ logger.logger.log(DRY_RUN_BAIL_TEXT$8);
6807
7184
  return;
6808
7185
  }
6809
7186
  await viewRepo(orgSlug, repoName, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
@@ -7114,9 +7491,9 @@ async function createFullScan({
7114
7491
  }
7115
7492
 
7116
7493
  const {
7117
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$6
7494
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$7
7118
7495
  } = constants;
7119
- const config$6 = {
7496
+ const config$7 = {
7120
7497
  commandName: 'create',
7121
7498
  description: 'Create a scan',
7122
7499
  hidden: false,
@@ -7221,16 +7598,16 @@ const config$6 = {
7221
7598
  `
7222
7599
  };
7223
7600
  const cmdScanCreate = {
7224
- description: config$6.description,
7225
- hidden: config$6.hidden,
7226
- run: run$6
7601
+ description: config$7.description,
7602
+ hidden: config$7.hidden,
7603
+ run: run$7
7227
7604
  };
7228
- async function run$6(argv, importMeta, {
7605
+ async function run$7(argv, importMeta, {
7229
7606
  parentName
7230
7607
  }) {
7231
7608
  const cli = meowOrExit({
7232
7609
  argv,
7233
- config: config$6,
7610
+ config: config$7,
7234
7611
  importMeta,
7235
7612
  parentName
7236
7613
  });
@@ -7268,7 +7645,7 @@ async function run$6(argv, importMeta, {
7268
7645
 
7269
7646
  // Note exiting earlier to skirt a hidden auth requirement
7270
7647
  if (cli.flags['dryRun']) {
7271
- logger.logger.log(DRY_RUN_BAIL_TEXT$6);
7648
+ logger.logger.log(DRY_RUN_BAIL_TEXT$7);
7272
7649
  return;
7273
7650
  }
7274
7651
  await createFullScan({
@@ -7311,9 +7688,9 @@ async function deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken) {
7311
7688
  }
7312
7689
 
7313
7690
  const {
7314
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$5
7691
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$6
7315
7692
  } = constants;
7316
- const config$5 = {
7693
+ const config$6 = {
7317
7694
  commandName: 'del',
7318
7695
  description: 'Delete a scan',
7319
7696
  hidden: false,
@@ -7333,16 +7710,16 @@ const config$5 = {
7333
7710
  `
7334
7711
  };
7335
7712
  const cmdScanDel = {
7336
- description: config$5.description,
7337
- hidden: config$5.hidden,
7338
- run: run$5
7713
+ description: config$6.description,
7714
+ hidden: config$6.hidden,
7715
+ run: run$6
7339
7716
  };
7340
- async function run$5(argv, importMeta, {
7717
+ async function run$6(argv, importMeta, {
7341
7718
  parentName
7342
7719
  }) {
7343
7720
  const cli = meowOrExit({
7344
7721
  argv,
7345
- config: config$5,
7722
+ config: config$6,
7346
7723
  importMeta,
7347
7724
  parentName
7348
7725
  });
@@ -7360,7 +7737,7 @@ async function run$5(argv, importMeta, {
7360
7737
  return;
7361
7738
  }
7362
7739
  if (cli.flags['dryRun']) {
7363
- logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7740
+ logger.logger.log(DRY_RUN_BAIL_TEXT$6);
7364
7741
  return;
7365
7742
  }
7366
7743
  await deleteOrgFullScan(orgSlug, fullScanId);
@@ -7454,11 +7831,11 @@ async function listFullScansWithToken({
7454
7831
  }
7455
7832
 
7456
7833
  const {
7457
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$4
7834
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$5
7458
7835
  } = constants;
7459
- const config$4 = {
7836
+ const config$5 = {
7460
7837
  commandName: 'list',
7461
- description: 'List the full scans for an organization',
7838
+ description: 'List the scans for an organization',
7462
7839
  hidden: false,
7463
7840
  flags: {
7464
7841
  ...commonFlags,
@@ -7512,16 +7889,16 @@ const config$4 = {
7512
7889
  `
7513
7890
  };
7514
7891
  const cmdScanList = {
7515
- description: config$4.description,
7516
- hidden: config$4.hidden,
7517
- run: run$4
7892
+ description: config$5.description,
7893
+ hidden: config$5.hidden,
7894
+ run: run$5
7518
7895
  };
7519
- async function run$4(argv, importMeta, {
7896
+ async function run$5(argv, importMeta, {
7520
7897
  parentName
7521
7898
  }) {
7522
7899
  const cli = meowOrExit({
7523
7900
  argv,
7524
- config: config$4,
7901
+ config: config$5,
7525
7902
  importMeta,
7526
7903
  parentName
7527
7904
  });
@@ -7537,7 +7914,7 @@ async function run$4(argv, importMeta, {
7537
7914
  return;
7538
7915
  }
7539
7916
  if (cli.flags['dryRun']) {
7540
- logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7917
+ logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7541
7918
  return;
7542
7919
  }
7543
7920
  await listFullScans({
@@ -7592,11 +7969,11 @@ async function getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind
7592
7969
  }
7593
7970
 
7594
7971
  const {
7595
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$3
7972
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$4
7596
7973
  } = constants;
7597
- const config$3 = {
7974
+ const config$4 = {
7598
7975
  commandName: 'metadata',
7599
- description: "Get a full scan's metadata",
7976
+ description: "Get a scan's metadata",
7600
7977
  hidden: false,
7601
7978
  flags: {
7602
7979
  ...commonFlags,
@@ -7614,16 +7991,16 @@ const config$3 = {
7614
7991
  `
7615
7992
  };
7616
7993
  const cmdScanMetadata = {
7617
- description: config$3.description,
7618
- hidden: config$3.hidden,
7619
- run: run$3
7994
+ description: config$4.description,
7995
+ hidden: config$4.hidden,
7996
+ run: run$4
7620
7997
  };
7621
- async function run$3(argv, importMeta, {
7998
+ async function run$4(argv, importMeta, {
7622
7999
  parentName
7623
8000
  }) {
7624
8001
  const cli = meowOrExit({
7625
8002
  argv,
7626
- config: config$3,
8003
+ config: config$4,
7627
8004
  importMeta,
7628
8005
  parentName
7629
8006
  });
@@ -7641,33 +8018,588 @@ async function run$3(argv, importMeta, {
7641
8018
  return;
7642
8019
  }
7643
8020
  if (cli.flags['dryRun']) {
7644
- logger.logger.log(DRY_RUN_BAIL_TEXT$3);
8021
+ logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7645
8022
  return;
7646
8023
  }
7647
8024
  await getOrgScanMetadata(orgSlug, fullScanId, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
7648
8025
  }
7649
8026
 
7650
- async function streamFullScan(orgSlug, fullScanId, file) {
8027
+ /**
8028
+ * This fetches all the relevant pieces of data to generate a report, given a
8029
+ * full scan ID.
8030
+ * It can optionally only fetch the security or license side of things.
8031
+ */
8032
+ async function fetchReportData(orgSlug, fullScanId,
8033
+ // includeLicensePolicy: boolean,
8034
+ includeSecurityPolicy) {
8035
+ let haveScan = false;
8036
+ // let haveLicensePolicy = false
8037
+ let haveSecurityPolicy = false;
8038
+
7651
8039
  // Lazily access constants.spinner.
7652
8040
  const {
7653
8041
  spinner
7654
8042
  } = constants;
8043
+ function updateProgress() {
8044
+ const needs = [!haveScan ? 'scan' : undefined,
8045
+ // includeLicensePolicy && !haveLicensePolicy ? 'license policy' : undefined,
8046
+ includeSecurityPolicy && !haveSecurityPolicy ? 'security policy' : undefined].filter(Boolean);
8047
+ if (needs.length > 2) {
8048
+ // .toOxford()
8049
+ needs[needs.length - 1] = `and ${needs[needs.length - 1]}`;
8050
+ }
8051
+ const haves = [haveScan ? 'scan' : undefined,
8052
+ // includeLicensePolicy && haveLicensePolicy ? 'license policy' : undefined,
8053
+ includeSecurityPolicy && haveSecurityPolicy ? 'security policy' : undefined].filter(Boolean);
8054
+ if (haves.length > 2) {
8055
+ // .toOxford()
8056
+ haves[haves.length - 1] = `and ${haves[haves.length - 1]}`;
8057
+ }
8058
+ if (needs.length) {
8059
+ spinner.start(`Fetching ${needs.join(needs.length > 2 ? ', ' : ' and ')}...${haves.length ? ` Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` : ''}`);
8060
+ } else {
8061
+ spinner?.successAndStop(`Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.`);
8062
+ }
8063
+ }
7655
8064
  const apiToken = shadowNpmInject.getDefaultToken();
7656
8065
  if (!apiToken) {
7657
8066
  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.');
7658
8067
  }
7659
- spinner.start('Fetching scan...');
8068
+ updateProgress();
7660
8069
  const socketSdk = await shadowNpmInject.setupSdk(apiToken);
7661
- const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
7662
- if (!data?.success) {
7663
- handleUnsuccessfulApiResponse('getOrgFullScan', data);
8070
+
8071
+ // @ts-ignore
8072
+ const [scan,
8073
+ // licensePolicyMaybe,
8074
+ securityPolicyMaybe] = await Promise.all([(async () => {
8075
+ try {
8076
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
8077
+ haveScan = true;
8078
+ updateProgress();
8079
+ if (!response.ok) {
8080
+ const err = await handleAPIError(response.status);
8081
+ logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
8082
+ return undefined;
8083
+ }
8084
+ const jsons = await response.text();
8085
+ const lines = jsons.split('\n').filter(Boolean);
8086
+ const data = lines.map(line => {
8087
+ try {
8088
+ return JSON.parse(line);
8089
+ } catch {
8090
+ console.error('At least one line item was returned that could not be parsed as JSON...');
8091
+ return;
8092
+ }
8093
+ });
8094
+ return data;
8095
+ } catch (e) {
8096
+ spinner.errorAndStop('There was an issue while fetching full scan data.');
8097
+ throw e;
8098
+ }
8099
+ })(),
8100
+ // includeLicensePolicy &&
8101
+ // (async () => {
8102
+ // const r = await socketSdk.getOrgSecurityPolicy(orgSlug)
8103
+ // haveLicensePolicy = true
8104
+ // updateProgress()
8105
+ // return await handleApiCall(
8106
+ // r,
8107
+ // "looking up organization's license policy"
8108
+ // )
8109
+ // })(),
8110
+ includeSecurityPolicy && (async () => {
8111
+ const r = await socketSdk.getOrgSecurityPolicy(orgSlug);
8112
+ haveSecurityPolicy = true;
8113
+ updateProgress();
8114
+ return await handleApiCall(r, "looking up organization's security policy");
8115
+ })()]).finally(() => spinner.stop());
8116
+ if (!Array.isArray(scan)) {
8117
+ logger.logger.error('Was unable to fetch scan, bailing');
8118
+ process.exitCode = 1;
8119
+ return {
8120
+ ok: false,
8121
+ scan: undefined,
8122
+ // licensePolicy: undefined,
8123
+ securityPolicy: undefined
8124
+ };
8125
+ }
8126
+
8127
+ // // Note: security->license once the api ships in the sdk
8128
+ // let licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'> =
8129
+ // undefined
8130
+ // if (includeLicensePolicy) {
8131
+ // if (licensePolicyMaybe && licensePolicyMaybe.success) {
8132
+ // licensePolicy = licensePolicyMaybe
8133
+ // } else {
8134
+ // logger.error('Was unable to fetch license policy, bailing')
8135
+ // process.exitCode = 1
8136
+ // return {
8137
+ // ok: false,
8138
+ // scan: undefined,
8139
+ // licensePolicy: undefined,
8140
+ // securityPolicy: undefined
8141
+ // }
8142
+ // }
8143
+ // }
8144
+
8145
+ let securityPolicy = undefined;
8146
+ if (includeSecurityPolicy) {
8147
+ if (securityPolicyMaybe && securityPolicyMaybe.success) {
8148
+ securityPolicy = securityPolicyMaybe;
8149
+ } else {
8150
+ logger.logger.error('Was unable to fetch security policy, bailing');
8151
+ process.exitCode = 1;
8152
+ return {
8153
+ ok: false,
8154
+ scan: undefined,
8155
+ // licensePolicy: undefined,
8156
+ securityPolicy: undefined
8157
+ };
8158
+ }
8159
+ }
8160
+ return {
8161
+ ok: true,
8162
+ scan,
8163
+ // licensePolicy,
8164
+ securityPolicy
8165
+ };
8166
+ }
8167
+
8168
+ function generateReport(scan, _licensePolicy, securityPolicy, {
8169
+ fold,
8170
+ orgSlug,
8171
+ reportLevel,
8172
+ scanId,
8173
+ short
8174
+ }) {
8175
+ const now = Date.now();
8176
+
8177
+ // Lazily access constants.spinner.
8178
+ const {
8179
+ spinner
8180
+ } = constants;
8181
+ spinner.start('Generating report...');
8182
+
8183
+ // Create an object that includes:
8184
+ // healthy: boolean
8185
+ // worst violation level;
8186
+ // per eco
8187
+ // per package
8188
+ // per version
8189
+ // per offending file
8190
+ // reported issue -> policy action
8191
+
8192
+ // In the context of a report;
8193
+ // - the alert.severity is irrelevant
8194
+ // - the securityPolicyDefault is irrelevant
8195
+ // - the report defaults to healthy:true with no alerts
8196
+ // - the appearance of an alert will trigger the policy action;
8197
+ // - error: healthy will end up as false, add alerts to report
8198
+ // - warn: healthy unchanged, add alerts to report
8199
+ // - monitor/ignore: no action
8200
+ // - defer: unknown (no action)
8201
+
8202
+ const violations = new Map();
8203
+ let healthy = true;
8204
+ const securityRules = securityPolicy?.data.securityPolicyRules;
8205
+ if (securityPolicy && securityRules) {
8206
+ // Note: reportLevel: error > warn > monitor > ignore > defer
8207
+ scan.forEach(artifact => {
8208
+ const {
8209
+ alerts,
8210
+ name: pkgName = '<unknown>',
8211
+ type: ecosystem,
8212
+ version = '<unknown>'
8213
+ } = artifact;
8214
+ alerts?.forEach(alert => {
8215
+ const alertName = alert.type; // => policy[type]
8216
+ const action = securityRules[alertName]?.action || '';
8217
+ switch (action) {
8218
+ case 'error':
8219
+ {
8220
+ healthy = false;
8221
+ if (!short) {
8222
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8223
+ }
8224
+ break;
8225
+ }
8226
+ case 'warn':
8227
+ {
8228
+ if (!short && reportLevel !== 'error') {
8229
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8230
+ }
8231
+ break;
8232
+ }
8233
+ case 'monitor':
8234
+ {
8235
+ if (!short && reportLevel !== 'warn' && reportLevel !== 'error') {
8236
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8237
+ }
8238
+ break;
8239
+ }
8240
+ case 'ignore':
8241
+ {
8242
+ if (!short && reportLevel !== 'warn' && reportLevel !== 'error' && reportLevel !== 'monitor') {
8243
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8244
+ }
8245
+ break;
8246
+ }
8247
+ case 'defer':
8248
+ {
8249
+ // Not sure but ignore for now. Defer to later ;)
8250
+ if (!short && reportLevel === 'defer') {
8251
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8252
+ }
8253
+ break;
8254
+ }
8255
+ }
8256
+ });
8257
+ });
8258
+ }
8259
+ spinner.successAndStop(`Generated reported in ${Date.now() - now} ms`);
8260
+ const report = short ? {
8261
+ healthy
8262
+ } : {
8263
+ healthy,
8264
+ orgSlug,
8265
+ scanId,
8266
+ options: {
8267
+ fold,
8268
+ reportLevel
8269
+ },
8270
+ alerts: violations
8271
+ };
8272
+ return report;
8273
+ }
8274
+ function createLeaf(art, alert, policyAction) {
8275
+ const leaf = {
8276
+ type: alert.type,
8277
+ policy: policyAction,
8278
+ url: `https://socket.dev/${art.type}/package/${art.name}/${art.version}`,
8279
+ manifest: art.manifestFiles?.map(obj => obj.file) ?? []
8280
+ };
8281
+ return leaf;
8282
+ }
8283
+ function addAlert(art, violations, foldSetting, ecosystem, pkgName, version, alert, policyAction) {
8284
+ if (!violations.has(ecosystem)) violations.set(ecosystem, new Map());
8285
+ const ecomap = violations.get(ecosystem);
8286
+ if (foldSetting === 'pkg') {
8287
+ const existing = ecomap.get(pkgName);
8288
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8289
+ ecomap.set(pkgName, createLeaf(art, alert, policyAction));
8290
+ }
8291
+ } else {
8292
+ if (!ecomap.has(pkgName)) ecomap.set(pkgName, new Map());
8293
+ const pkgmap = ecomap.get(pkgName);
8294
+ if (foldSetting === 'version') {
8295
+ const existing = pkgmap.get(version);
8296
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8297
+ pkgmap.set(version, createLeaf(art, alert, policyAction));
8298
+ }
8299
+ } else {
8300
+ if (!pkgmap.has(version)) pkgmap.set(version, new Map());
8301
+ const file = alert.file || '<unknown>';
8302
+ const vermap = pkgmap.get(version);
8303
+ if (foldSetting === 'file') {
8304
+ const existing = vermap.get(file);
8305
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8306
+ vermap.set(file, createLeaf(art, alert, policyAction));
8307
+ }
8308
+ } else {
8309
+ if (!vermap.has(file)) vermap.set(file, new Map());
8310
+ const key = `${alert.type} at ${alert.start}:${alert.end}`;
8311
+ const filemap = vermap.get(file);
8312
+ const existing = filemap.get(key);
8313
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8314
+ filemap.set(key, createLeaf(art, alert, policyAction));
8315
+ }
8316
+ }
8317
+ }
8318
+ }
8319
+ }
8320
+ function isStricterPolicy(was, is) {
8321
+ // error > warn > monitor > ignore > defer > {unknown}
8322
+ if (was === 'error') return false;
8323
+ if (is === 'error') return true;
8324
+ if (was === 'warn') return false;
8325
+ if (is === 'warn') return false;
8326
+ if (was === 'monitor') return false;
8327
+ if (is === 'monitor') return false;
8328
+ if (was === 'ignore') return false;
8329
+ if (is === 'ignore') return false;
8330
+ if (was === 'defer') return false;
8331
+ if (is === 'defer') return false;
8332
+ // unreachable?
8333
+ return false;
8334
+ }
8335
+
8336
+ /**
8337
+ * Convert a Map<string, Map|string> to a nested object of similar shape.
8338
+ * The goal is to serialize it with JSON.stringify, which Map can't do.
8339
+ */
8340
+ function mapToObject(map) {
8341
+ return Object.fromEntries(Array.from(map.entries()).map(([k, v]) => [k, v instanceof Map ? mapToObject(v) : v]));
8342
+ }
8343
+
8344
+ function* walkNestedMap(map, keys = []) {
8345
+ for (const [key, value] of map.entries()) {
8346
+ if (value instanceof Map) {
8347
+ yield* walkNestedMap(value, keys.concat(key));
8348
+ } else {
8349
+ yield {
8350
+ keys: keys.concat(key),
8351
+ value: value
8352
+ };
8353
+ }
8354
+ }
8355
+ }
8356
+
8357
+ async function reportFullScan({
8358
+ filePath,
8359
+ fold,
8360
+ fullScanId,
8361
+ includeLicensePolicy,
8362
+ includeSecurityPolicy,
8363
+ orgSlug,
8364
+ outputKind,
8365
+ reportLevel,
8366
+ short
8367
+ }) {
8368
+ logger.logger.error('output:', outputKind, ', file:', filePath, ', fold:', fold, ', reportLevel:', reportLevel);
8369
+ if (!includeSecurityPolicy) {
8370
+ return; // caller should assert
8371
+ }
8372
+ const {
8373
+ // licensePolicy,
8374
+ ok,
8375
+ scan,
8376
+ securityPolicy
8377
+ } = await fetchReportData(orgSlug, fullScanId,
8378
+ // includeLicensePolicy
8379
+ includeSecurityPolicy);
8380
+ if (!ok) {
7664
8381
  return;
7665
8382
  }
7666
- spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7667
- return data;
8383
+ const scanReport = generateReport(scan, undefined,
8384
+ // licensePolicy,
8385
+ securityPolicy, {
8386
+ orgSlug,
8387
+ scanId: fullScanId,
8388
+ fold,
8389
+ short,
8390
+ reportLevel
8391
+ });
8392
+ if (!scanReport.healthy) {
8393
+ process.exitCode = 1;
8394
+ }
8395
+ if (outputKind === 'json' || outputKind === 'text' && filePath && filePath.endsWith('.json')) {
8396
+ const json = short ? JSON.stringify(scanReport) : toJsonReport(scanReport);
8397
+ if (filePath && filePath !== '-') {
8398
+ logger.logger.log('Writing json report to', filePath);
8399
+ return await fs$1.writeFile(filePath, json);
8400
+ }
8401
+ logger.logger.log(json);
8402
+ return;
8403
+ }
8404
+ if (outputKind === 'markdown' || filePath && filePath.endsWith('.md')) {
8405
+ const md = short ? `healthy = ${scanReport.healthy}` : toMarkdownReport(scanReport);
8406
+ if (filePath && filePath !== '-') {
8407
+ logger.logger.log('Writing markdown report to', filePath);
8408
+ return await fs$1.writeFile(filePath, md);
8409
+ }
8410
+ logger.logger.log(md);
8411
+ return;
8412
+ }
8413
+ if (short) {
8414
+ logger.logger.log(scanReport.healthy ? 'OK' : 'ERR');
8415
+ } else {
8416
+ logger.logger.dir(scanReport, {
8417
+ depth: null
8418
+ });
8419
+ }
7668
8420
  }
8421
+ function toJsonReport(report) {
8422
+ const obj = mapToObject(report.alerts);
8423
+ const json = JSON.stringify({
8424
+ ...report,
8425
+ alerts: obj
8426
+ }, null, 2);
8427
+ return json;
8428
+ }
8429
+ function toMarkdownReport(report) {
8430
+ const flatData = Array.from(walkNestedMap(report.alerts)).map(({
8431
+ keys,
8432
+ value
8433
+ }) => {
8434
+ const {
8435
+ manifest,
8436
+ policy,
8437
+ type,
8438
+ url
8439
+ } = value;
8440
+ return {
8441
+ 'Alert Type': type,
8442
+ Package: keys[1] || '<unknown>',
8443
+ 'Introduced by': keys[2] || '<unknown>',
8444
+ url,
8445
+ 'Manifest file': manifest.join(', '),
8446
+ Policy: policy
8447
+ };
8448
+ });
8449
+ const md = `
8450
+ # Scan Policy Report
7669
8451
 
7670
- async function getFullScan(orgSlug, fullScanId) {
8452
+ This report tells you whether the results of a Socket scan results violate the
8453
+ security or license policy set by your organization.
8454
+
8455
+ ## Health status
8456
+
8457
+ ${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.'}
8458
+
8459
+ ## Settings
8460
+
8461
+ Configuration used to generate this report:
8462
+
8463
+ - Organization: ${report.orgSlug}
8464
+ - Scan ID: ${report.scanId}
8465
+ - Alert folding: ${report.options.fold === 'none' ? 'none' : `up to ${report.options.fold}`}
8466
+ - Minimal policy level for alert to be included in report: ${report.options.reportLevel === 'defer' ? 'everything' : report.options.reportLevel}
8467
+
8468
+ ## Alerts
8469
+
8470
+ ${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}".`}
8471
+
8472
+ ${!report.alerts.size ? '' : mdTable(flatData, ['Policy', 'Alert Type', 'Package', 'Introduced by', 'url', 'Manifest file'])}
8473
+ `.trim() + '\n';
8474
+ return md;
8475
+ }
8476
+
8477
+ const {
8478
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$3
8479
+ } = constants;
8480
+ const config$3 = {
8481
+ commandName: 'report',
8482
+ description: 'Check whether a scan result passes the organizational policies (security, license)',
8483
+ hidden: true,
8484
+ // [beta]
8485
+ flags: {
8486
+ ...commonFlags,
8487
+ ...outputFlags,
8488
+ fold: {
8489
+ type: 'string',
8490
+ default: 'none',
8491
+ description: 'Fold reported alerts to some degree'
8492
+ },
8493
+ reportLevel: {
8494
+ type: 'string',
8495
+ default: 'warn',
8496
+ description: 'Which policy level alerts should be reported'
8497
+ },
8498
+ short: {
8499
+ type: 'boolean',
8500
+ default: false,
8501
+ description: 'Report only the healthy status'
8502
+ },
8503
+ // license: {
8504
+ // type: 'boolean',
8505
+ // default: true,
8506
+ // description: 'Report the license policy status. Default: true'
8507
+ // },
8508
+ security: {
8509
+ type: 'boolean',
8510
+ default: true,
8511
+ description: 'Report the security policy status. Default: true'
8512
+ }
8513
+ },
8514
+ help: (command, config) => `
8515
+ Usage
8516
+ $ ${command} <org slug> <scan ID> [path to output file]
8517
+
8518
+ Options
8519
+ ${getFlagListOutput(config.flags, 6)}
8520
+
8521
+ This consumes 1 quota unit plus 1 for each of the requested policy types.
8522
+
8523
+ Note: By default it reports both so by default it consumes 3 quota units.
8524
+
8525
+ Your API token will need the \`full-scans:list\` scope regardless. Additionally
8526
+ it needs \`security-policy:read\` to report on the security policy.
8527
+
8528
+ By default the result is a nested object that looks like this:
8529
+ \`{[ecosystem]: {[pkgName]: {[version]: {[file]: {[type:loc]: policy}}}}\`
8530
+ You can fold this up to given level: 'pkg', 'version', 'file', and 'none'.
8531
+
8532
+ By default only the warn and error policy level alerts are reported. You can
8533
+ override this and request more ('defer' < 'ignore' < 'monitor' < 'warn' < 'error')
8534
+
8535
+ Short responses: JSON: \`{healthy:bool}\`, markdown: \`healthy = bool\`, text: \`OK/ERR\`
8536
+
8537
+ Examples
8538
+ $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version
8539
+ `
8540
+ };
8541
+ const cmdScanReport = {
8542
+ description: config$3.description,
8543
+ hidden: config$3.hidden,
8544
+ run: run$3
8545
+ };
8546
+ async function run$3(argv, importMeta, {
8547
+ parentName
8548
+ }) {
8549
+ const cli = meowOrExit({
8550
+ argv,
8551
+ config: config$3,
8552
+ importMeta,
8553
+ parentName
8554
+ });
8555
+ const {
8556
+ fold = 'none',
8557
+ json,
8558
+ // license,
8559
+ markdown,
8560
+ reportLevel = 'warn',
8561
+ security
8562
+ } = cli.flags;
8563
+ const [orgSlug = '', fullScanId = '', file = '-'] = cli.input;
8564
+ if (!orgSlug || !fullScanId ||
8565
+ // (!license && !security) ||
8566
+ json && markdown) {
8567
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
8568
+ // options or missing arguments.
8569
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
8570
+ process.exitCode = 2;
8571
+ logger.logger.fail(commonTags.stripIndents`
8572
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
8573
+
8574
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
8575
+
8576
+ - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}
8577
+
8578
+ - Not both the --json and --markdown flags ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
8579
+ `
8580
+ // - At least one policy to report ${!license && !security ? colors.red('(do not omit both!)') : colors.green('(ok)')}
8581
+ );
8582
+ return;
8583
+ }
8584
+ if (cli.flags['dryRun']) {
8585
+ logger.logger.log(DRY_RUN_BAIL_TEXT$3);
8586
+ return;
8587
+ }
8588
+ await reportFullScan({
8589
+ orgSlug,
8590
+ fullScanId,
8591
+ includeLicensePolicy: false,
8592
+ // !!license,
8593
+ includeSecurityPolicy: typeof security === 'boolean' ? security : true,
8594
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
8595
+ filePath: file,
8596
+ fold: fold,
8597
+ short: !!cli.flags['short'],
8598
+ reportLevel: reportLevel
8599
+ });
8600
+ }
8601
+
8602
+ async function streamFullScan(orgSlug, fullScanId, file) {
7671
8603
  // Lazily access constants.spinner.
7672
8604
  const {
7673
8605
  spinner
@@ -7676,26 +8608,14 @@ async function getFullScan(orgSlug, fullScanId) {
7676
8608
  if (!apiToken) {
7677
8609
  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.');
7678
8610
  }
7679
- spinner.start('Fetching full-scan...');
7680
- const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
7681
- spinner.stop('Fetch complete.');
7682
- if (!response.ok) {
7683
- const err = await handleAPIError(response.status);
7684
- logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
8611
+ spinner.start('Fetching scan...');
8612
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
8613
+ const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
8614
+ if (!data?.success) {
8615
+ handleUnsuccessfulApiResponse('getOrgFullScan', data);
7685
8616
  return;
7686
8617
  }
7687
-
7688
- // This is nd-json; each line is a json object
7689
- const jsons = await response.text();
7690
- const lines = jsons.split('\n').filter(Boolean);
7691
- const data = lines.map(line => {
7692
- try {
7693
- return JSON.parse(line);
7694
- } catch {
7695
- console.error('At least one line item was returned that could not be parsed as JSON...');
7696
- return {};
7697
- }
7698
- });
8618
+ spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7699
8619
  return data;
7700
8620
  }
7701
8621
 
@@ -7813,6 +8733,7 @@ const cmdScan = {
7813
8733
  list: cmdScanList,
7814
8734
  del: cmdScanDel,
7815
8735
  metadata: cmdScanMetadata,
8736
+ report: cmdScanReport,
7816
8737
  view: cmdScanView
7817
8738
  }, {
7818
8739
  aliases: {
@@ -8293,7 +9214,7 @@ void (async () => {
8293
9214
  await updateNotifier({
8294
9215
  name: SOCKET_CLI_BIN_NAME,
8295
9216
  // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION']".
8296
- version: "0.14.61",
9217
+ version: "0.14.62",
8297
9218
  ttl: 86_400_000 /* 24 hours in milliseconds */
8298
9219
  });
8299
9220
  try {
@@ -8360,5 +9281,5 @@ void (async () => {
8360
9281
  await shadowNpmInject.captureException(e);
8361
9282
  }
8362
9283
  })();
8363
- //# debugId=89426f9d-4cbc-4ef9-8acb-dd541c5afe36
9284
+ //# debugId=c7ce2da4-7a0b-4866-b8a7-cab140e6a2af
8364
9285
  //# sourceMappingURL=cli.js.map