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