@socketsecurity/cli 0.14.60 → 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.60:48319f6:78cf0eae:pub";
1536
+ "0.14.62:681c774:be9a8ff8: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
  });
@@ -3206,17 +3241,9 @@ const {
3206
3241
  YARN_CLASSIC: YARN_CLASSIC$6
3207
3242
  } = constants;
3208
3243
  const AGENTS = [BUN$5, NPM$b, PNPM$8, YARN_BERRY$5, YARN_CLASSIC$6, VLT$5];
3209
- const binByAgent = {
3210
- __proto__: null,
3211
- [BUN$5]: BUN$5,
3212
- [NPM$b]: NPM$b,
3213
- [PNPM$8]: PNPM$8,
3214
- [YARN_BERRY$5]: YARN,
3215
- [YARN_CLASSIC$6]: YARN,
3216
- [VLT$5]: VLT$5
3217
- };
3244
+ 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]]);
3218
3245
  async function getAgentExecPath(agent) {
3219
- const binName = binByAgent[agent];
3246
+ const binName = binByAgent.get(agent);
3220
3247
  return (await which(binName, {
3221
3248
  nothrow: true
3222
3249
  })) ?? binName;
@@ -3224,7 +3251,11 @@ async function getAgentExecPath(agent) {
3224
3251
  async function getAgentVersion(agentExecPath, cwd) {
3225
3252
  let result;
3226
3253
  try {
3227
- result = semver.coerce(
3254
+ result =
3255
+ // Coerce version output into a valid semver version by passing it through
3256
+ // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
3257
+ // and tildes (~).
3258
+ semver.coerce(
3228
3259
  // All package managers support the "--version" flag.
3229
3260
  (await spawn.spawn(agentExecPath, ['--version'], {
3230
3261
  cwd
@@ -3265,32 +3296,25 @@ const readLockFileByAgent = (() => {
3265
3296
  }
3266
3297
  const binaryReader = wrapReader(shadowNpmInject.readFileBinary);
3267
3298
  const defaultReader = wrapReader(async lockPath => await shadowNpmInject.readFileUtf8(lockPath));
3268
- return {
3269
- [BUN$5]: wrapReader(async (lockPath, agentExecPath) => {
3270
- const ext = path.extname(lockPath);
3271
- if (ext === LOCK_EXT$1) {
3272
- return await defaultReader(lockPath);
3273
- }
3274
- if (ext === BINARY_LOCK_EXT) {
3275
- const lockBuffer = await binaryReader(lockPath);
3276
- if (lockBuffer) {
3277
- try {
3278
- return index_cjs.parse(lockBuffer);
3279
- } catch {}
3280
- }
3281
- // To print a Yarn lockfile to your console without writing it to disk
3282
- // use `bun bun.lockb`.
3283
- // https://bun.sh/guides/install/yarnlock
3284
- return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3299
+ return new Map([[BUN$5, wrapReader(async (lockPath, agentExecPath) => {
3300
+ const ext = path.extname(lockPath);
3301
+ if (ext === LOCK_EXT$1) {
3302
+ return await defaultReader(lockPath);
3303
+ }
3304
+ if (ext === BINARY_LOCK_EXT) {
3305
+ const lockBuffer = await binaryReader(lockPath);
3306
+ if (lockBuffer) {
3307
+ try {
3308
+ return index_cjs.parse(lockBuffer);
3309
+ } catch {}
3285
3310
  }
3286
- return undefined;
3287
- }),
3288
- [NPM$b]: defaultReader,
3289
- [PNPM$8]: defaultReader,
3290
- [VLT$5]: defaultReader,
3291
- [YARN_BERRY$5]: defaultReader,
3292
- [YARN_CLASSIC$6]: defaultReader
3293
- };
3311
+ // To print a Yarn lockfile to your console without writing it to disk
3312
+ // use `bun bun.lockb`.
3313
+ // https://bun.sh/guides/install/yarnlock
3314
+ return (await spawn.spawn(agentExecPath, [lockPath])).stdout.trim();
3315
+ }
3316
+ return undefined;
3317
+ })], [NPM$b, defaultReader], [PNPM$8, defaultReader], [VLT$5, defaultReader], [YARN_BERRY$5, defaultReader], [YARN_CLASSIC$6, defaultReader]]);
3294
3318
  })();
3295
3319
  async function detectPackageEnvironment({
3296
3320
  cwd = process$1.cwd(),
@@ -3315,13 +3339,14 @@ async function detectPackageEnvironment({
3315
3339
  let agent;
3316
3340
  let agentVersion;
3317
3341
  if (pkgManager) {
3342
+ // A valid "packageManager" field value is "<package manager name>@<version>".
3343
+ // https://nodejs.org/api/packages.html#packagemanager
3318
3344
  const atSignIndex = pkgManager.lastIndexOf('@');
3319
3345
  if (atSignIndex !== -1) {
3320
3346
  const name = pkgManager.slice(0, atSignIndex);
3321
3347
  const version = pkgManager.slice(atSignIndex + 1);
3322
3348
  if (version && AGENTS.includes(name)) {
3323
3349
  agent = name;
3324
- agentVersion = semver.coerce(version) ?? undefined;
3325
3350
  }
3326
3351
  }
3327
3352
  }
@@ -3340,64 +3365,92 @@ async function detectPackageEnvironment({
3340
3365
  if (agent === YARN_CLASSIC$6 && (agentVersion?.major ?? 0) > 1) {
3341
3366
  agent = YARN_BERRY$5;
3342
3367
  }
3343
- const targets = {
3344
- browser: false,
3345
- node: true
3346
- };
3347
- let lockSrc;
3348
3368
  // Lazily access constants.maintainedNodeVersions.
3349
- let minimumNodeVersion = constants.maintainedNodeVersions.last;
3369
+ const {
3370
+ maintainedNodeVersions
3371
+ } = constants;
3372
+ // Lazily access constants.minimumVersionByAgent.
3373
+ const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent);
3374
+ const minSupportedNodeVersion = maintainedNodeVersions.last;
3375
+ const nodeVersion = semver.coerce(process$1.version);
3376
+ let lockSrc;
3377
+ let pkgAgentRange;
3378
+ let pkgNodeRange;
3379
+ let pkgMinAgentVersion = minSupportedAgentVersion;
3380
+ let pkgMinNodeVersion = minSupportedNodeVersion;
3350
3381
  if (pkgJson) {
3351
- const browserField = pkgJson.browser;
3352
- if (strings.isNonEmptyString(browserField) || objects.isObjectObject(browserField)) {
3353
- targets.browser = true;
3354
- }
3355
- const nodeRange = pkgJson.engines?.['node'];
3356
- if (strings.isNonEmptyString(nodeRange)) {
3357
- const coerced = semver.coerce(nodeRange);
3358
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3359
- minimumNodeVersion = coerced.version;
3382
+ const {
3383
+ engines
3384
+ } = pkgJson;
3385
+ const engineAgentRange = engines?.[agent];
3386
+ const engineNodeRange = engines?.['node'];
3387
+ if (strings.isNonEmptyString(engineAgentRange)) {
3388
+ pkgAgentRange = engineAgentRange;
3389
+ // Roughly check agent range as semver.coerce will strip leading
3390
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3391
+ const coerced = semver.coerce(pkgAgentRange);
3392
+ if (coerced && semver.lt(coerced, pkgMinAgentVersion)) {
3393
+ pkgMinAgentVersion = coerced.version;
3394
+ }
3395
+ }
3396
+ if (strings.isNonEmptyString(engineNodeRange)) {
3397
+ pkgNodeRange = engineNodeRange;
3398
+ // Roughly check Node range as semver.coerce will strip leading
3399
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3400
+ const coerced = semver.coerce(pkgNodeRange);
3401
+ if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {
3402
+ pkgMinNodeVersion = coerced.version;
3360
3403
  }
3361
3404
  }
3362
3405
  const browserslistQuery = pkgJson['browserslist'];
3363
3406
  if (Array.isArray(browserslistQuery)) {
3364
- const browserslistTargets = browserslist(browserslistQuery).map(s => s.toLowerCase()).sort(sorts.naturalCompare);
3365
- const browserslistNodeTargets = browserslistTargets.filter(v => v.startsWith('node ')).map(v => v.slice(5 /*'node '.length*/));
3366
- if (!targets.browser && browserslistTargets.length) {
3367
- targets.browser = browserslistTargets.length !== browserslistNodeTargets.length;
3368
- }
3407
+ // List Node targets in ascending version order.
3408
+ const browserslistNodeTargets = browserslist(browserslistQuery).filter(v => /^node /i.test(v)).map(v => v.slice(5 /*'node '.length*/)).sort(sorts.naturalCompare);
3369
3409
  if (browserslistNodeTargets.length) {
3410
+ // browserslistNodeTargets[0] is the lowest Node target version.
3370
3411
  const coerced = semver.coerce(browserslistNodeTargets[0]);
3371
- if (coerced && semver.lt(coerced, minimumNodeVersion)) {
3372
- minimumNodeVersion = coerced.version;
3412
+ if (coerced && semver.lt(coerced, pkgMinNodeVersion)) {
3413
+ pkgMinNodeVersion = coerced.version;
3373
3414
  }
3374
3415
  }
3375
3416
  }
3376
- // Lazily access constants.maintainedNodeVersions.
3377
- targets.node = constants.maintainedNodeVersions.some(v => semver.satisfies(v, `>=${minimumNodeVersion}`));
3378
- lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent[agent](lockPath, agentExecPath) : undefined;
3417
+ lockSrc = typeof lockPath === 'string' ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath) : undefined;
3379
3418
  } else {
3380
3419
  lockName = undefined;
3381
3420
  lockPath = undefined;
3382
3421
  }
3383
- const pkgSupported = targets.browser || targets.node;
3422
+ // Does the system agent version meet our minimum supported agent version?
3423
+ const agentSupported = !!agentVersion && semver.satisfies(agentVersion, `>=${minSupportedAgentVersion}`);
3424
+
3425
+ // Does the system Node version meet our minimum supported Node version?
3426
+ const nodeSupported = semver.satisfies(nodeVersion, `>=${minSupportedNodeVersion}`);
3384
3427
  const npmBuggyOverrides = agent === NPM$b && !!agentVersion && semver.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION$1);
3385
3428
  return {
3386
3429
  agent,
3387
3430
  agentExecPath,
3431
+ agentSupported,
3388
3432
  agentVersion,
3433
+ features: {
3434
+ npmBuggyOverrides
3435
+ },
3389
3436
  lockName,
3390
3437
  lockPath,
3391
3438
  lockSrc,
3392
- minimumNodeVersion,
3439
+ nodeSupported,
3440
+ nodeVersion,
3393
3441
  npmExecPath,
3394
3442
  pkgJson: editablePkgJson,
3395
3443
  pkgPath,
3396
- pkgSupported,
3397
- features: {
3398
- npmBuggyOverrides
3444
+ pkgRequirements: {
3445
+ agent: pkgAgentRange ?? `>=${pkgMinAgentVersion}`,
3446
+ node: pkgNodeRange ?? `>=${pkgMinNodeVersion}`
3399
3447
  },
3400
- targets
3448
+ pkgSupports: {
3449
+ // Does our minimum supported agent version meet the package's requirements?
3450
+ agent: semver.satisfies(minSupportedAgentVersion, `>=${pkgMinAgentVersion}`),
3451
+ // Does our supported Node versions meet the package's requirements?
3452
+ node: maintainedNodeVersions.some(v => semver.satisfies(v, `>=${pkgMinNodeVersion}`))
3453
+ }
3401
3454
  };
3402
3455
  }
3403
3456
  async function detectAndValidatePackageEnvironment(cwd, options) {
@@ -3415,12 +3468,32 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3415
3468
  logger?.warn(cmdPrefixMessage(cmdName, `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`));
3416
3469
  }
3417
3470
  });
3418
- if (!details.pkgSupported) {
3419
- logger?.fail(cmdPrefixMessage(cmdName, 'No supported Node or browser range detected'));
3471
+ const {
3472
+ agent,
3473
+ nodeVersion,
3474
+ pkgRequirements
3475
+ } = details;
3476
+ const agentVersion = details.agentVersion ?? 'unknown';
3477
+ if (!details.agentSupported) {
3478
+ const minVersion = constants.minimumVersionByAgent.get(agent);
3479
+ logger?.fail(cmdPrefixMessage(cmdName, `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`));
3480
+ return;
3481
+ }
3482
+ if (!details.nodeSupported) {
3483
+ const minVersion = constants.maintainedNodeVersions.last;
3484
+ logger?.fail(cmdPrefixMessage(cmdName, `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`));
3485
+ return;
3486
+ }
3487
+ if (!details.pkgSupports.agent) {
3488
+ logger?.fail(cmdPrefixMessage(cmdName, `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`));
3489
+ return;
3490
+ }
3491
+ if (!details.pkgSupports.node) {
3492
+ logger?.fail(cmdPrefixMessage(cmdName, `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`));
3420
3493
  return;
3421
3494
  }
3422
- if (details.agent === VLT$5) {
3423
- logger?.fail(cmdPrefixMessage(cmdName, `${details.agent} does not support overrides. Soon, though ⚡`));
3495
+ if (agent === VLT$5) {
3496
+ logger?.fail(cmdPrefixMessage(cmdName, `${agent} does not support overrides. Soon, though ⚡`));
3424
3497
  return;
3425
3498
  }
3426
3499
  const lockName = details.lockName ?? 'lock file';
@@ -3436,8 +3509,8 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3436
3509
  logger?.fail(cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`));
3437
3510
  return;
3438
3511
  }
3439
- if (prod && (details.agent === BUN$5 || details.agent === YARN_BERRY$5)) {
3440
- logger?.fail(cmdPrefixMessage(cmdName, `--prod not supported for ${details.agent}${details.agentVersion ? `@${details.agentVersion.version}` : ''}`));
3512
+ if (prod && (agent === BUN$5 || agent === YARN_BERRY$5)) {
3513
+ logger?.fail(cmdPrefixMessage(cmdName, `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`));
3441
3514
  return;
3442
3515
  }
3443
3516
  if (details.lockPath && path.relative(cwd, details.lockPath).startsWith('.')) {
@@ -3482,9 +3555,9 @@ async function runFix() {
3482
3555
  }
3483
3556
 
3484
3557
  const {
3485
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3558
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v
3486
3559
  } = constants;
3487
- const config$t = {
3560
+ const config$w = {
3488
3561
  commandName: 'fix',
3489
3562
  description: 'Fix "fixable" Socket alerts',
3490
3563
  hidden: true,
@@ -3500,21 +3573,21 @@ const config$t = {
3500
3573
  `
3501
3574
  };
3502
3575
  const cmdFix = {
3503
- description: config$t.description,
3504
- hidden: config$t.hidden,
3505
- run: run$t
3576
+ description: config$w.description,
3577
+ hidden: config$w.hidden,
3578
+ run: run$w
3506
3579
  };
3507
- async function run$t(argv, importMeta, {
3580
+ async function run$w(argv, importMeta, {
3508
3581
  parentName
3509
3582
  }) {
3510
3583
  const cli = meowOrExit({
3511
3584
  argv,
3512
- config: config$t,
3585
+ config: config$w,
3513
3586
  importMeta,
3514
3587
  parentName
3515
3588
  });
3516
3589
  if (cli.flags['dryRun']) {
3517
- logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3590
+ logger.logger.log(DRY_RUN_BAIL_TEXT$v);
3518
3591
  return;
3519
3592
  }
3520
3593
  await runFix();
@@ -3671,9 +3744,9 @@ async function getPackageInfo({
3671
3744
  }
3672
3745
 
3673
3746
  const {
3674
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r
3747
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u
3675
3748
  } = constants;
3676
- const config$s = {
3749
+ const config$v = {
3677
3750
  commandName: 'info',
3678
3751
  description: 'Look up info regarding a package',
3679
3752
  hidden: false,
@@ -3695,16 +3768,16 @@ const config$s = {
3695
3768
  `
3696
3769
  };
3697
3770
  const cmdInfo = {
3698
- description: config$s.description,
3699
- hidden: config$s.hidden,
3700
- run: run$s
3771
+ description: config$v.description,
3772
+ hidden: config$v.hidden,
3773
+ run: run$v
3701
3774
  };
3702
- async function run$s(argv, importMeta, {
3775
+ async function run$v(argv, importMeta, {
3703
3776
  parentName
3704
3777
  }) {
3705
3778
  const cli = meowOrExit({
3706
3779
  argv,
3707
- config: config$s,
3780
+ config: config$v,
3708
3781
  importMeta,
3709
3782
  parentName
3710
3783
  });
@@ -3729,11 +3802,11 @@ async function run$s(argv, importMeta, {
3729
3802
  const pkgName = versionSeparator < 1 ? rawPkgName : rawPkgName.slice(0, versionSeparator);
3730
3803
  const pkgVersion = versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1);
3731
3804
  if (cli.flags['dryRun']) {
3732
- logger.logger.log(DRY_RUN_BAIL_TEXT$r);
3805
+ logger.logger.log(DRY_RUN_BAIL_TEXT$u);
3733
3806
  return;
3734
3807
  }
3735
3808
  await getPackageInfo({
3736
- commandName: `${parentName} ${config$s.commandName}`,
3809
+ commandName: `${parentName} ${config$v.commandName}`,
3737
3810
  includeAllIssues: Boolean(all),
3738
3811
  outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
3739
3812
  pkgName,
@@ -3820,9 +3893,9 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3820
3893
  }
3821
3894
 
3822
3895
  const {
3823
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q
3896
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t
3824
3897
  } = constants;
3825
- const config$r = {
3898
+ const config$u = {
3826
3899
  commandName: 'login',
3827
3900
  description: 'Socket API login',
3828
3901
  hidden: false,
@@ -3852,23 +3925,23 @@ const config$r = {
3852
3925
  `
3853
3926
  };
3854
3927
  const cmdLogin = {
3855
- description: config$r.description,
3856
- hidden: config$r.hidden,
3857
- run: run$r
3928
+ description: config$u.description,
3929
+ hidden: config$u.hidden,
3930
+ run: run$u
3858
3931
  };
3859
- async function run$r(argv, importMeta, {
3932
+ async function run$u(argv, importMeta, {
3860
3933
  parentName
3861
3934
  }) {
3862
3935
  const cli = meowOrExit({
3863
3936
  argv,
3864
- config: config$r,
3937
+ config: config$u,
3865
3938
  importMeta,
3866
3939
  parentName
3867
3940
  });
3868
3941
  const apiBaseUrl = cli.flags['apiBaseUrl'];
3869
3942
  const apiProxy = cli.flags['apiProxy'];
3870
3943
  if (cli.flags['dryRun']) {
3871
- logger.logger.log(DRY_RUN_BAIL_TEXT$q);
3944
+ logger.logger.log(DRY_RUN_BAIL_TEXT$t);
3872
3945
  return;
3873
3946
  }
3874
3947
  if (!isInteractive()) {
@@ -3894,9 +3967,9 @@ function attemptLogout() {
3894
3967
  }
3895
3968
 
3896
3969
  const {
3897
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p
3970
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s
3898
3971
  } = constants;
3899
- const config$q = {
3972
+ const config$t = {
3900
3973
  commandName: 'logout',
3901
3974
  description: 'Socket API logout',
3902
3975
  hidden: false,
@@ -3911,21 +3984,21 @@ const config$q = {
3911
3984
  `
3912
3985
  };
3913
3986
  const cmdLogout = {
3914
- description: config$q.description,
3915
- hidden: config$q.hidden,
3916
- run: run$q
3987
+ description: config$t.description,
3988
+ hidden: config$t.hidden,
3989
+ run: run$t
3917
3990
  };
3918
- async function run$q(argv, importMeta, {
3991
+ async function run$t(argv, importMeta, {
3919
3992
  parentName
3920
3993
  }) {
3921
3994
  const cli = meowOrExit({
3922
3995
  argv,
3923
- config: config$q,
3996
+ config: config$t,
3924
3997
  importMeta,
3925
3998
  parentName
3926
3999
  });
3927
4000
  if (cli.flags['dryRun']) {
3928
- logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4001
+ logger.logger.log(DRY_RUN_BAIL_TEXT$s);
3929
4002
  return;
3930
4003
  }
3931
4004
  attemptLogout();
@@ -4030,9 +4103,9 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
4030
4103
  }
4031
4104
 
4032
4105
  const {
4033
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o
4106
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r
4034
4107
  } = constants;
4035
- const config$p = {
4108
+ const config$s = {
4036
4109
  commandName: 'gradle',
4037
4110
  description: '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Gradle/Java/Kotlin/etc project',
4038
4111
  hidden: false,
@@ -4104,22 +4177,22 @@ const config$p = {
4104
4177
  `
4105
4178
  };
4106
4179
  const cmdManifestGradle = {
4107
- description: config$p.description,
4108
- hidden: config$p.hidden,
4109
- run: run$p
4180
+ description: config$s.description,
4181
+ hidden: config$s.hidden,
4182
+ run: run$s
4110
4183
  };
4111
- async function run$p(argv, importMeta, {
4184
+ async function run$s(argv, importMeta, {
4112
4185
  parentName
4113
4186
  }) {
4114
4187
  const cli = meowOrExit({
4115
4188
  argv,
4116
- config: config$p,
4189
+ config: config$s,
4117
4190
  importMeta,
4118
4191
  parentName
4119
4192
  });
4120
4193
  const verbose = Boolean(cli.flags['verbose']);
4121
4194
  if (verbose) {
4122
- logger.logger.group('- ', parentName, config$p.commandName, ':');
4195
+ logger.logger.group('- ', parentName, config$s.commandName, ':');
4123
4196
  logger.logger.group('- flags:', cli.flags);
4124
4197
  logger.logger.groupEnd();
4125
4198
  logger.logger.log('- input:', cli.input);
@@ -4167,7 +4240,7 @@ async function run$p(argv, importMeta, {
4167
4240
  gradleOpts = cli.flags['gradleOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4168
4241
  }
4169
4242
  if (cli.flags['dryRun']) {
4170
- logger.logger.log(DRY_RUN_BAIL_TEXT$o);
4243
+ logger.logger.log(DRY_RUN_BAIL_TEXT$r);
4171
4244
  return;
4172
4245
  }
4173
4246
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts);
@@ -4272,9 +4345,9 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
4272
4345
  }
4273
4346
 
4274
4347
  const {
4275
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n
4348
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q
4276
4349
  } = constants;
4277
- const config$o = {
4350
+ const config$r = {
4278
4351
  commandName: 'scala',
4279
4352
  description: "[beta] Generate a manifest file (`pom.xml`) from Scala's `build.sbt` file",
4280
4353
  hidden: false,
@@ -4347,22 +4420,22 @@ const config$o = {
4347
4420
  `
4348
4421
  };
4349
4422
  const cmdManifestScala = {
4350
- description: config$o.description,
4351
- hidden: config$o.hidden,
4352
- run: run$o
4423
+ description: config$r.description,
4424
+ hidden: config$r.hidden,
4425
+ run: run$r
4353
4426
  };
4354
- async function run$o(argv, importMeta, {
4427
+ async function run$r(argv, importMeta, {
4355
4428
  parentName
4356
4429
  }) {
4357
4430
  const cli = meowOrExit({
4358
4431
  argv,
4359
- config: config$o,
4432
+ config: config$r,
4360
4433
  importMeta,
4361
4434
  parentName
4362
4435
  });
4363
4436
  const verbose = Boolean(cli.flags['verbose']);
4364
4437
  if (verbose) {
4365
- logger.logger.group('- ', parentName, config$o.commandName, ':');
4438
+ logger.logger.group('- ', parentName, config$r.commandName, ':');
4366
4439
  logger.logger.group('- flags:', cli.flags);
4367
4440
  logger.logger.groupEnd();
4368
4441
  logger.logger.log('- input:', cli.input);
@@ -4408,16 +4481,16 @@ async function run$o(argv, importMeta, {
4408
4481
  sbtOpts = cli.flags['sbtOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4409
4482
  }
4410
4483
  if (cli.flags['dryRun']) {
4411
- logger.logger.log(DRY_RUN_BAIL_TEXT$n);
4484
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q);
4412
4485
  return;
4413
4486
  }
4414
4487
  await convertSbtToMaven(target, bin, out, verbose, sbtOpts);
4415
4488
  }
4416
4489
 
4417
4490
  const {
4418
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m
4491
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p
4419
4492
  } = constants;
4420
- const config$n = {
4493
+ const config$q = {
4421
4494
  commandName: 'auto',
4422
4495
  description: 'Auto-detect build and attempt to generate manifest file',
4423
4496
  hidden: false,
@@ -4447,23 +4520,23 @@ const config$n = {
4447
4520
  `
4448
4521
  };
4449
4522
  const cmdManifestAuto = {
4450
- description: config$n.description,
4451
- hidden: config$n.hidden,
4452
- run: run$n
4523
+ description: config$q.description,
4524
+ hidden: config$q.hidden,
4525
+ run: run$q
4453
4526
  };
4454
- async function run$n(argv, importMeta, {
4527
+ async function run$q(argv, importMeta, {
4455
4528
  parentName
4456
4529
  }) {
4457
4530
  const cli = meowOrExit({
4458
4531
  argv,
4459
- config: config$n,
4532
+ config: config$q,
4460
4533
  importMeta,
4461
4534
  parentName
4462
4535
  });
4463
4536
  const verbose = !!cli.flags['verbose'];
4464
4537
  const cwd = cli.flags['cwd'] ?? process.cwd();
4465
4538
  if (verbose) {
4466
- logger.logger.group('- ', parentName, config$n.commandName, ':');
4539
+ logger.logger.group('- ', parentName, config$q.commandName, ':');
4467
4540
  logger.logger.group('- flags:', cli.flags);
4468
4541
  logger.logger.groupEnd();
4469
4542
  logger.logger.log('- input:', cli.input);
@@ -4482,7 +4555,7 @@ async function run$n(argv, importMeta, {
4482
4555
  }
4483
4556
  subArgs.push(dir);
4484
4557
  if (cli.flags['dryRun']) {
4485
- logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4558
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4486
4559
  return;
4487
4560
  }
4488
4561
  await cmdManifestScala.run(subArgs, importMeta, {
@@ -4497,7 +4570,7 @@ async function run$n(argv, importMeta, {
4497
4570
  subArgs.push(cwd);
4498
4571
  }
4499
4572
  if (cli.flags['dryRun']) {
4500
- logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4573
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4501
4574
  return;
4502
4575
  }
4503
4576
  await cmdManifestGradle.run(subArgs, importMeta, {
@@ -4505,10 +4578,14 @@ async function run$n(argv, importMeta, {
4505
4578
  });
4506
4579
  return;
4507
4580
  }
4581
+ if (cli.flags['dryRun']) {
4582
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p);
4583
+ return;
4584
+ }
4508
4585
 
4509
4586
  // Show new help screen and exit.
4510
4587
  vendor.meow(`
4511
- $ ${parentName} ${config$n.commandName}
4588
+ $ ${parentName} ${config$q.commandName}
4512
4589
 
4513
4590
  Unfortunately this script did not discover a supported language in the
4514
4591
  current folder.
@@ -4521,13 +4598,13 @@ async function run$n(argv, importMeta, {
4521
4598
  your target language.
4522
4599
  `, {
4523
4600
  argv: [],
4524
- description: config$n.description,
4601
+ description: config$q.description,
4525
4602
  importMeta
4526
4603
  }).showHelp();
4527
4604
  }
4528
4605
 
4529
4606
  const {
4530
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l
4607
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o
4531
4608
  } = constants;
4532
4609
 
4533
4610
  // TODO: we may want to dedupe some pieces for all gradle languages. I think it
@@ -4535,7 +4612,7 @@ const {
4535
4612
  // sense for the help panels to note the requested language, rather than
4536
4613
  // `socket manifest kotlin` to print help screens with `gradle` as the
4537
4614
  // command. Room for improvement.
4538
- const config$m = {
4615
+ const config$p = {
4539
4616
  commandName: 'kotlin',
4540
4617
  description: '[beta] Use Gradle to generate a manifest file (`pom.xml`) for a Kotlin project',
4541
4618
  hidden: false,
@@ -4607,22 +4684,22 @@ const config$m = {
4607
4684
  `
4608
4685
  };
4609
4686
  const cmdManifestKotlin = {
4610
- description: config$m.description,
4611
- hidden: config$m.hidden,
4612
- run: run$m
4687
+ description: config$p.description,
4688
+ hidden: config$p.hidden,
4689
+ run: run$p
4613
4690
  };
4614
- async function run$m(argv, importMeta, {
4691
+ async function run$p(argv, importMeta, {
4615
4692
  parentName
4616
4693
  }) {
4617
4694
  const cli = meowOrExit({
4618
4695
  argv,
4619
- config: config$m,
4696
+ config: config$p,
4620
4697
  importMeta,
4621
4698
  parentName
4622
4699
  });
4623
4700
  const verbose = Boolean(cli.flags['verbose']);
4624
4701
  if (verbose) {
4625
- logger.logger.group('- ', parentName, config$m.commandName, ':');
4702
+ logger.logger.group('- ', parentName, config$p.commandName, ':');
4626
4703
  logger.logger.group('- flags:', cli.flags);
4627
4704
  logger.logger.groupEnd();
4628
4705
  logger.logger.log('- input:', cli.input);
@@ -4670,13 +4747,13 @@ async function run$m(argv, importMeta, {
4670
4747
  gradleOpts = cli.flags['gradleOpts'].split(' ').map(s => s.trim()).filter(Boolean);
4671
4748
  }
4672
4749
  if (cli.flags['dryRun']) {
4673
- logger.logger.log(DRY_RUN_BAIL_TEXT$l);
4750
+ logger.logger.log(DRY_RUN_BAIL_TEXT$o);
4674
4751
  return;
4675
4752
  }
4676
4753
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts);
4677
4754
  }
4678
4755
 
4679
- const config$l = {
4756
+ const config$o = {
4680
4757
  commandName: 'manifest',
4681
4758
  description: 'Generate a dependency manifest for given file or dir',
4682
4759
  hidden: false,
@@ -4684,11 +4761,11 @@ const config$l = {
4684
4761
  ...commonFlags
4685
4762
  }};
4686
4763
  const cmdManifest = {
4687
- description: config$l.description,
4688
- hidden: config$l.hidden,
4689
- run: run$l
4764
+ description: config$o.description,
4765
+ hidden: config$o.hidden,
4766
+ run: run$o
4690
4767
  };
4691
- async function run$l(argv, importMeta, {
4768
+ async function run$o(argv, importMeta, {
4692
4769
  parentName
4693
4770
  }) {
4694
4771
  await meowWithSubcommands({
@@ -4700,15 +4777,15 @@ async function run$l(argv, importMeta, {
4700
4777
  argv,
4701
4778
  aliases: {
4702
4779
  yolo: {
4703
- description: config$l.description,
4780
+ description: config$o.description,
4704
4781
  hidden: true,
4705
4782
  argv: ['auto']
4706
4783
  }
4707
4784
  },
4708
- description: config$l.description,
4785
+ description: config$o.description,
4709
4786
  importMeta,
4710
- flags: config$l.flags,
4711
- name: `${parentName} ${config$l.commandName}`
4787
+ flags: config$o.flags,
4788
+ name: `${parentName} ${config$o.commandName}`
4712
4789
  });
4713
4790
  }
4714
4791
 
@@ -4722,10 +4799,10 @@ async function wrapNpm(argv) {
4722
4799
  }
4723
4800
 
4724
4801
  const {
4725
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k,
4802
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n,
4726
4803
  NPM: NPM$7
4727
4804
  } = constants;
4728
- const config$k = {
4805
+ const config$n = {
4729
4806
  commandName: 'npm',
4730
4807
  description: `${NPM$7} wrapper functionality`,
4731
4808
  hidden: false,
@@ -4736,22 +4813,22 @@ const config$k = {
4736
4813
  `
4737
4814
  };
4738
4815
  const cmdNpm = {
4739
- description: config$k.description,
4740
- hidden: config$k.hidden,
4741
- run: run$k
4816
+ description: config$n.description,
4817
+ hidden: config$n.hidden,
4818
+ run: run$n
4742
4819
  };
4743
- async function run$k(argv, importMeta, {
4820
+ async function run$n(argv, importMeta, {
4744
4821
  parentName
4745
4822
  }) {
4746
4823
  const cli = meowOrExit({
4747
4824
  allowUnknownFlags: true,
4748
4825
  argv,
4749
- config: config$k,
4826
+ config: config$n,
4750
4827
  importMeta,
4751
4828
  parentName
4752
4829
  });
4753
4830
  if (cli.flags['dryRun']) {
4754
- logger.logger.log(DRY_RUN_BAIL_TEXT$k);
4831
+ logger.logger.log(DRY_RUN_BAIL_TEXT$n);
4755
4832
  return;
4756
4833
  }
4757
4834
  await wrapNpm(argv);
@@ -4767,10 +4844,10 @@ async function wrapNpx(argv) {
4767
4844
  }
4768
4845
 
4769
4846
  const {
4770
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j,
4847
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m,
4771
4848
  NPX: NPX$1
4772
4849
  } = constants;
4773
- const config$j = {
4850
+ const config$m = {
4774
4851
  commandName: 'npx',
4775
4852
  description: `${NPX$1} wrapper functionality`,
4776
4853
  hidden: false,
@@ -4781,31 +4858,31 @@ const config$j = {
4781
4858
  `
4782
4859
  };
4783
4860
  const cmdNpx = {
4784
- description: config$j.description,
4785
- hidden: config$j.hidden,
4786
- run: run$j
4861
+ description: config$m.description,
4862
+ hidden: config$m.hidden,
4863
+ run: run$m
4787
4864
  };
4788
- async function run$j(argv, importMeta, {
4865
+ async function run$m(argv, importMeta, {
4789
4866
  parentName
4790
4867
  }) {
4791
4868
  const cli = meowOrExit({
4792
4869
  allowUnknownFlags: true,
4793
4870
  argv,
4794
- config: config$j,
4871
+ config: config$m,
4795
4872
  importMeta,
4796
4873
  parentName
4797
4874
  });
4798
4875
  if (cli.flags['dryRun']) {
4799
- logger.logger.log(DRY_RUN_BAIL_TEXT$j);
4876
+ logger.logger.log(DRY_RUN_BAIL_TEXT$m);
4800
4877
  return;
4801
4878
  }
4802
4879
  await wrapNpx(argv);
4803
4880
  }
4804
4881
 
4805
4882
  const {
4806
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i
4883
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l
4807
4884
  } = constants;
4808
- const config$i = {
4885
+ const config$l = {
4809
4886
  commandName: 'oops',
4810
4887
  description: 'Trigger an intentional error (for development)',
4811
4888
  hidden: true,
@@ -4820,21 +4897,21 @@ const config$i = {
4820
4897
  `
4821
4898
  };
4822
4899
  const cmdOops = {
4823
- description: config$i.description,
4824
- hidden: config$i.hidden,
4825
- run: run$i
4900
+ description: config$l.description,
4901
+ hidden: config$l.hidden,
4902
+ run: run$l
4826
4903
  };
4827
- async function run$i(argv, importMeta, {
4904
+ async function run$l(argv, importMeta, {
4828
4905
  parentName
4829
4906
  }) {
4830
4907
  const cli = meowOrExit({
4831
4908
  argv,
4832
- config: config$i,
4909
+ config: config$l,
4833
4910
  importMeta,
4834
4911
  parentName
4835
4912
  });
4836
4913
  if (cli.flags['dryRun']) {
4837
- logger.logger.log(DRY_RUN_BAIL_TEXT$i);
4914
+ logger.logger.log(DRY_RUN_BAIL_TEXT$l);
4838
4915
  return;
4839
4916
  }
4840
4917
  throw new Error('This error was intentionally left blank');
@@ -5341,10 +5418,12 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5341
5418
  spinner?.setText(`Adding overrides${workspaceName ? ` to ${workspaceName}` : ''}...`);
5342
5419
  const depAliasMap = new Map();
5343
5420
  const depEntries = getDependencyEntries(pkgJson);
5344
- const nodeRange = `>=${pkgEnvDetails.minimumNodeVersion}`;
5345
5421
  const manifestEntries = manifestNpmOverrides.filter(({
5346
5422
  1: data
5347
- }) => semver.satisfies(semver.coerce(data.engines.node), nodeRange));
5423
+ }) => semver.satisfies(
5424
+ // Roughly check Node range as semver.coerce will strip leading
5425
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
5426
+ semver.coerce(data.engines.node), pkgEnvDetails.pkgRequirements.node));
5348
5427
 
5349
5428
  // Chunk package names to process them in parallel 3 at a time.
5350
5429
  await promises.pEach(manifestEntries, 3, async ({
@@ -5368,9 +5447,15 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5368
5447
  const origSpec = objects.hasOwn(depObj, origPkgName) ? depObj[origPkgName] : undefined;
5369
5448
  if (origSpec) {
5370
5449
  let thisSpec = origSpec;
5371
- // Add package aliases for direct dependencies to avoid npm EOVERRIDE errors.
5450
+ // Add package aliases for direct dependencies to avoid npm EOVERRIDE
5451
+ // errors...
5372
5452
  // https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases
5373
- if (!(thisSpec.startsWith(sockOverridePrefix) && semver.coerce(npa(thisSpec).rawSpec)?.version)) {
5453
+ if (
5454
+ // ...if the spec doesn't start with a valid Socket override.
5455
+ !(thisSpec.startsWith(sockOverridePrefix) &&
5456
+ // Check the validity of the spec by passing it through npa and
5457
+ // seeing if it will coerce to a version.
5458
+ semver.coerce(npa(thisSpec).rawSpec)?.version)) {
5374
5459
  thisSpec = sockOverrideSpec;
5375
5460
  depObj[origPkgName] = thisSpec;
5376
5461
  state.added.add(sockRegPkgName);
@@ -5414,7 +5499,13 @@ async function addOverrides(pkgPath, pkgEnvDetails, options) {
5414
5499
  } else if (typeof oldSpec === 'string') {
5415
5500
  const thisSpec = oldSpec.startsWith('$') ? depAlias || newSpec : oldSpec || newSpec;
5416
5501
  if (thisSpec.startsWith(sockOverridePrefix)) {
5417
- if (pin && semver.major(semver.coerce(npa(thisSpec).rawSpec)?.version ?? version) !== major) {
5502
+ if (pin && semver.major(
5503
+ // Check the validity of the spec by passing it through npa
5504
+ // and seeing if it will coerce to a version. semver.coerce
5505
+ // will strip leading v's, carets (^), comparators (<,<=,>,>=,=),
5506
+ // and tildes (~). If not coerced to a valid version then
5507
+ // default to the manifest entry version.
5508
+ semver.coerce(npa(thisSpec).rawSpec)?.version ?? version) !== major) {
5418
5509
  const otherVersion = (await packages.fetchPackageManifest(thisSpec))?.version;
5419
5510
  if (otherVersion && otherVersion !== version) {
5420
5511
  newSpec = `${sockOverridePrefix}${pin ? otherVersion : `^${semver.major(otherVersion)}`}`;
@@ -5551,9 +5642,9 @@ async function applyOptimization(cwd, pin, prod) {
5551
5642
  }
5552
5643
 
5553
5644
  const {
5554
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
5645
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k
5555
5646
  } = constants;
5556
- const config$h = {
5647
+ const config$k = {
5557
5648
  commandName: 'optimize',
5558
5649
  description: 'Optimize dependencies with @socketregistry overrides',
5559
5650
  hidden: false,
@@ -5583,22 +5674,22 @@ const config$h = {
5583
5674
  `
5584
5675
  };
5585
5676
  const cmdOptimize = {
5586
- description: config$h.description,
5587
- hidden: config$h.hidden,
5588
- run: run$h
5677
+ description: config$k.description,
5678
+ hidden: config$k.hidden,
5679
+ run: run$k
5589
5680
  };
5590
- async function run$h(argv, importMeta, {
5681
+ async function run$k(argv, importMeta, {
5591
5682
  parentName
5592
5683
  }) {
5593
5684
  const cli = meowOrExit({
5594
5685
  argv,
5595
- config: config$h,
5686
+ config: config$k,
5596
5687
  importMeta,
5597
5688
  parentName
5598
5689
  });
5599
5690
  const cwd = process$1.cwd();
5600
5691
  if (cli.flags['dryRun']) {
5601
- logger.logger.log(DRY_RUN_BAIL_TEXT$h);
5692
+ logger.logger.log(DRY_RUN_BAIL_TEXT$k);
5602
5693
  return;
5603
5694
  }
5604
5695
  await applyOptimization(cwd, Boolean(cli.flags['pin']), Boolean(cli.flags['prod']));
@@ -5672,10 +5763,10 @@ async function printOrganizationsFromToken(apiToken, format = 'text') {
5672
5763
  }
5673
5764
 
5674
5765
  const {
5675
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g
5766
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j
5676
5767
  } = constants;
5677
- const config$g = {
5678
- commandName: 'organizations',
5768
+ const config$j = {
5769
+ commandName: 'list',
5679
5770
  description: 'List organizations associated with the API key used',
5680
5771
  hidden: false,
5681
5772
  flags: {
@@ -5687,20 +5778,20 @@ const config$g = {
5687
5778
  $ ${command}
5688
5779
 
5689
5780
  Options
5690
- ${getFlagListOutput(config$g.flags, 6)}
5781
+ ${getFlagListOutput(config$j.flags, 6)}
5691
5782
  `
5692
5783
  };
5693
- const cmdOrganization = {
5694
- description: config$g.description,
5695
- hidden: config$g.hidden,
5696
- run: run$g
5784
+ const cmdOrganizationList = {
5785
+ description: config$j.description,
5786
+ hidden: config$j.hidden,
5787
+ run: run$j
5697
5788
  };
5698
- async function run$g(argv, importMeta, {
5789
+ async function run$j(argv, importMeta, {
5699
5790
  parentName
5700
5791
  }) {
5701
5792
  const cli = meowOrExit({
5702
5793
  argv,
5703
- config: config$g,
5794
+ config: config$j,
5704
5795
  importMeta,
5705
5796
  parentName
5706
5797
  });
@@ -5719,115 +5810,366 @@ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields
5719
5810
  return;
5720
5811
  }
5721
5812
  if (cli.flags['dryRun']) {
5722
- logger.logger.log(DRY_RUN_BAIL_TEXT$g);
5813
+ logger.logger.log(DRY_RUN_BAIL_TEXT$j);
5723
5814
  return;
5724
5815
  }
5725
5816
  await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text');
5726
5817
  }
5727
5818
 
5728
- async function runRawNpm(argv) {
5729
- const spawnPromise = spawn.spawn(shadowNpmPaths.getNpmBinPath(), argv, {
5730
- stdio: 'inherit'
5731
- });
5732
- // See https://nodejs.org/api/all.html#all_child_process_event-exit.
5733
- spawnPromise.process.on('exit', (code, signalName) => {
5734
- if (signalName) {
5735
- process$1.kill(process$1.pid, signalName);
5736
- } else if (code !== null) {
5737
- process$1.exit(code);
5738
- }
5739
- });
5740
- await spawnPromise;
5819
+ async function getSecurityPolicy(orgSlug, format) {
5820
+ const apiToken = shadowNpmInject.getDefaultToken();
5821
+ if (!apiToken) {
5822
+ 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.');
5823
+ }
5824
+ await getSecurityPolicyWithToken(apiToken, orgSlug, format);
5825
+ }
5826
+ async function getSecurityPolicyWithToken(apiToken, orgSlug, format) {
5827
+ // Lazily access constants.spinner.
5828
+ const {
5829
+ spinner
5830
+ } = constants;
5831
+ spinner.start('Fetching organization quota...');
5832
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
5833
+ const result = await handleApiCall(socketSdk.getOrgSecurityPolicy(orgSlug), 'looking up organization quota');
5834
+ if (!result.success) {
5835
+ handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result);
5836
+ return;
5837
+ }
5838
+ spinner.stop();
5839
+ switch (format) {
5840
+ case 'json':
5841
+ {
5842
+ logger.logger.log(JSON.stringify(result.data, null, 2));
5843
+ return;
5844
+ }
5845
+ default:
5846
+ {
5847
+ logger.logger.log('# Security policy\n');
5848
+ logger.logger.log(`The default security policy setting is: "${result.data.securityPolicyDefault}"\n`);
5849
+ logger.logger.log('These are the security policies per setting for your organization:\n');
5850
+ const data = result.data;
5851
+ const rules = data.securityPolicyRules;
5852
+ const entries = Object.entries(rules);
5853
+ const mapped = entries.map(([key, value]) => [key, value.action]);
5854
+ mapped.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
5855
+ logger.logger.log(mdTableOfPairs(mapped, ['name', 'action']));
5856
+ }
5857
+ }
5741
5858
  }
5742
5859
 
5743
5860
  const {
5744
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f,
5745
- NPM
5861
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i
5746
5862
  } = constants;
5747
- const config$f = {
5748
- commandName: 'raw-npm',
5749
- description: `Temporarily disable the Socket ${NPM} wrapper`,
5750
- hidden: false,
5751
- flags: {},
5752
- help: command => `
5863
+
5864
+ // TODO: secret toplevel alias `socket security policy`?
5865
+ const config$i = {
5866
+ commandName: 'security',
5867
+ description: 'Retrieve the security policy of an organization.',
5868
+ hidden: true,
5869
+ flags: {
5870
+ ...commonFlags,
5871
+ ...outputFlags
5872
+ },
5873
+ help: (command, _config) => `
5753
5874
  Usage
5754
- $ ${command} <command>
5875
+ $ ${command} <org slug>
5876
+
5877
+ Options
5878
+ ${getFlagListOutput(config$i.flags, 6)}
5879
+
5880
+ Your API token will need the \`security-policy:read\` permission otherwise
5881
+ the request will fail with an authentication error.
5755
5882
 
5756
5883
  Examples
5757
- $ ${command} install
5884
+ $ ${command} mycorp
5885
+ $ ${command} mycorp --json
5758
5886
  `
5759
5887
  };
5760
- const cmdRawNpm = {
5761
- description: config$f.description,
5762
- hidden: config$f.hidden,
5763
- run: run$f
5888
+ const cmdOrganizationPolicyPolicy = {
5889
+ description: config$i.description,
5890
+ hidden: config$i.hidden,
5891
+ run: run$i
5764
5892
  };
5765
- async function run$f(argv, importMeta, {
5893
+ async function run$i(argv, importMeta, {
5766
5894
  parentName
5767
5895
  }) {
5768
5896
  const cli = meowOrExit({
5769
- allowUnknownFlags: true,
5770
5897
  argv,
5771
- config: config$f,
5898
+ config: config$i,
5772
5899
  importMeta,
5773
5900
  parentName
5774
5901
  });
5902
+ const json = Boolean(cli.flags['json']);
5903
+ const markdown = Boolean(cli.flags['markdown']);
5904
+ const [orgSlug = ''] = cli.input;
5905
+ if (!orgSlug || json && markdown) {
5906
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
5907
+ // options or missing arguments.
5908
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5909
+ process.exitCode = 2;
5910
+ logger.logger.fail(commonTags.stripIndents`
5911
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
5912
+
5913
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
5914
+ - The json and markdown flags cannot be both set ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
5915
+ `);
5916
+ return;
5917
+ }
5775
5918
  if (cli.flags['dryRun']) {
5776
- logger.logger.log(DRY_RUN_BAIL_TEXT$f);
5919
+ logger.logger.log(DRY_RUN_BAIL_TEXT$i);
5777
5920
  return;
5778
5921
  }
5779
- await runRawNpm(argv);
5922
+ await getSecurityPolicy(orgSlug, json ? 'json' : markdown ? 'markdown' : 'text');
5780
5923
  }
5781
5924
 
5782
- async function runRawNpx(argv) {
5783
- const spawnPromise = spawn.spawn(shadowNpmPaths.getNpxBinPath(), argv, {
5784
- stdio: 'inherit'
5785
- });
5786
- // See https://nodejs.org/api/all.html#all_child_process_event-exit.
5787
- spawnPromise.process.on('exit', (code, signalName) => {
5788
- if (signalName) {
5789
- process$1.kill(process$1.pid, signalName);
5790
- } else if (code !== null) {
5791
- process$1.exit(code);
5792
- }
5793
- });
5794
- await spawnPromise;
5925
+ const description$4 = 'Organization policy details';
5926
+ const cmdOrganizationPolicy = {
5927
+ description: description$4,
5928
+ // Hidden because it was broken all this time (nobody could be using it)
5929
+ // and we're not sure if it's useful to anyone in its current state.
5930
+ // Until we do, we'll hide this to keep the help tidier.
5931
+ // And later, we may simply move this under `scan`, anyways.
5932
+ hidden: true,
5933
+ async run(argv, importMeta, {
5934
+ parentName
5935
+ }) {
5936
+ await meowWithSubcommands({
5937
+ security: cmdOrganizationPolicyPolicy
5938
+ }, {
5939
+ argv,
5940
+ description: description$4,
5941
+ defaultSub: 'list',
5942
+ // Backwards compat
5943
+ importMeta,
5944
+ name: parentName + ' policy'
5945
+ });
5946
+ }
5947
+ };
5948
+
5949
+ async function getQuota(format = 'text') {
5950
+ const apiToken = shadowNpmInject.getDefaultToken();
5951
+ if (!apiToken) {
5952
+ 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.');
5953
+ }
5954
+ await getQuotaWithToken(apiToken, format);
5955
+ }
5956
+ async function getQuotaWithToken(apiToken, format = 'text') {
5957
+ // Lazily access constants.spinner.
5958
+ const {
5959
+ spinner
5960
+ } = constants;
5961
+ spinner.start('Fetching organization quota...');
5962
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
5963
+ const result = await handleApiCall(socketSdk.getQuota(), 'looking up organization quota');
5964
+ if (!result.success) {
5965
+ handleUnsuccessfulApiResponse('getQuota', result);
5966
+ return;
5967
+ }
5968
+ spinner.stop();
5969
+ switch (format) {
5970
+ case 'json':
5971
+ {
5972
+ logger.logger.log(JSON.stringify({
5973
+ quota: result.data.quota
5974
+ }, null, 2));
5975
+ return;
5976
+ }
5977
+ case 'markdown':
5978
+ {
5979
+ logger.logger.log('# Quota\n');
5980
+ logger.logger.log(`Quota left on the current API token: ${result.data.quota}\n`);
5981
+ return;
5982
+ }
5983
+ default:
5984
+ {
5985
+ logger.logger.log(`Quota left on the current API token: ${result.data.quota}\n`);
5986
+ }
5987
+ }
5795
5988
  }
5796
5989
 
5797
5990
  const {
5798
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e,
5799
- NPX
5991
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h
5800
5992
  } = constants;
5801
- const config$e = {
5802
- commandName: 'raw-npx',
5803
- description: `Temporarily disable the Socket ${NPX} wrapper`,
5804
- hidden: false,
5805
- flags: {},
5806
- help: command => `
5993
+ const config$h = {
5994
+ commandName: 'quota',
5995
+ description: 'List organizations associated with the API key used',
5996
+ hidden: true,
5997
+ flags: {
5998
+ ...commonFlags,
5999
+ ...outputFlags
6000
+ },
6001
+ help: (command, _config) => `
5807
6002
  Usage
5808
- $ ${command} <command>
6003
+ $ ${command}
5809
6004
 
5810
- Examples
5811
- $ ${command} install
6005
+ Options
6006
+ ${getFlagListOutput(config$h.flags, 6)}
5812
6007
  `
5813
6008
  };
5814
- const cmdRawNpx = {
5815
- description: config$e.description,
5816
- hidden: config$e.hidden,
5817
- run: run$e
6009
+ const cmdOrganizationQuota = {
6010
+ description: config$h.description,
6011
+ hidden: config$h.hidden,
6012
+ run: run$h
5818
6013
  };
5819
- async function run$e(argv, importMeta, {
6014
+ async function run$h(argv, importMeta, {
6015
+ parentName
6016
+ }) {
6017
+ const cli = meowOrExit({
6018
+ argv,
6019
+ config: config$h,
6020
+ importMeta,
6021
+ parentName
6022
+ });
6023
+ const json = Boolean(cli.flags['json']);
6024
+ const markdown = Boolean(cli.flags['markdown']);
6025
+ if (json && markdown) {
6026
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
6027
+ // options or missing arguments.
6028
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6029
+ process.exitCode = 2;
6030
+ logger.logger.fail(commonTags.stripIndents`
6031
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6032
+
6033
+ - The json and markdown flags cannot be both set, pick one
6034
+ `);
6035
+ return;
6036
+ }
6037
+ if (cli.flags['dryRun']) {
6038
+ logger.logger.log(DRY_RUN_BAIL_TEXT$h);
6039
+ return;
6040
+ }
6041
+ await getQuota(json ? 'json' : markdown ? 'markdown' : 'text');
6042
+ }
6043
+
6044
+ const description$3 = 'Account details';
6045
+ const cmdOrganization = {
6046
+ description: description$3,
6047
+ // Hidden because it was broken all this time (nobody could be using it)
6048
+ // and we're not sure if it's useful to anyone in its current state.
6049
+ // Until we do, we'll hide this to keep the help tidier.
6050
+ // And later, we may simply move this under `scan`, anyways.
6051
+ hidden: true,
6052
+ async run(argv, importMeta, {
6053
+ parentName
6054
+ }) {
6055
+ await meowWithSubcommands({
6056
+ list: cmdOrganizationList,
6057
+ quota: cmdOrganizationQuota,
6058
+ policy: cmdOrganizationPolicy
6059
+ }, {
6060
+ argv,
6061
+ description: description$3,
6062
+ defaultSub: 'list',
6063
+ // Backwards compat
6064
+ importMeta,
6065
+ name: parentName + ' organization'
6066
+ });
6067
+ }
6068
+ };
6069
+
6070
+ async function runRawNpm(argv) {
6071
+ const spawnPromise = spawn.spawn(shadowNpmPaths.getNpmBinPath(), argv, {
6072
+ stdio: 'inherit'
6073
+ });
6074
+ // See https://nodejs.org/api/all.html#all_child_process_event-exit.
6075
+ spawnPromise.process.on('exit', (code, signalName) => {
6076
+ if (signalName) {
6077
+ process$1.kill(process$1.pid, signalName);
6078
+ } else if (code !== null) {
6079
+ process$1.exit(code);
6080
+ }
6081
+ });
6082
+ await spawnPromise;
6083
+ }
6084
+
6085
+ const {
6086
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g,
6087
+ NPM
6088
+ } = constants;
6089
+ const config$g = {
6090
+ commandName: 'raw-npm',
6091
+ description: `Temporarily disable the Socket ${NPM} wrapper`,
6092
+ hidden: false,
6093
+ flags: {},
6094
+ help: command => `
6095
+ Usage
6096
+ $ ${command} <command>
6097
+
6098
+ Examples
6099
+ $ ${command} install
6100
+ `
6101
+ };
6102
+ const cmdRawNpm = {
6103
+ description: config$g.description,
6104
+ hidden: config$g.hidden,
6105
+ run: run$g
6106
+ };
6107
+ async function run$g(argv, importMeta, {
5820
6108
  parentName
5821
6109
  }) {
5822
6110
  const cli = meowOrExit({
5823
6111
  allowUnknownFlags: true,
5824
6112
  argv,
5825
- config: config$e,
6113
+ config: config$g,
5826
6114
  importMeta,
5827
6115
  parentName
5828
6116
  });
5829
6117
  if (cli.flags['dryRun']) {
5830
- logger.logger.log(DRY_RUN_BAIL_TEXT$e);
6118
+ logger.logger.log(DRY_RUN_BAIL_TEXT$g);
6119
+ return;
6120
+ }
6121
+ await runRawNpm(argv);
6122
+ }
6123
+
6124
+ async function runRawNpx(argv) {
6125
+ const spawnPromise = spawn.spawn(shadowNpmPaths.getNpxBinPath(), argv, {
6126
+ stdio: 'inherit'
6127
+ });
6128
+ // See https://nodejs.org/api/all.html#all_child_process_event-exit.
6129
+ spawnPromise.process.on('exit', (code, signalName) => {
6130
+ if (signalName) {
6131
+ process$1.kill(process$1.pid, signalName);
6132
+ } else if (code !== null) {
6133
+ process$1.exit(code);
6134
+ }
6135
+ });
6136
+ await spawnPromise;
6137
+ }
6138
+
6139
+ const {
6140
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f,
6141
+ NPX
6142
+ } = constants;
6143
+ const config$f = {
6144
+ commandName: 'raw-npx',
6145
+ description: `Temporarily disable the Socket ${NPX} wrapper`,
6146
+ hidden: false,
6147
+ flags: {},
6148
+ help: command => `
6149
+ Usage
6150
+ $ ${command} <command>
6151
+
6152
+ Examples
6153
+ $ ${command} install
6154
+ `
6155
+ };
6156
+ const cmdRawNpx = {
6157
+ description: config$f.description,
6158
+ hidden: config$f.hidden,
6159
+ run: run$f
6160
+ };
6161
+ async function run$f(argv, importMeta, {
6162
+ parentName
6163
+ }) {
6164
+ const cli = meowOrExit({
6165
+ allowUnknownFlags: true,
6166
+ argv,
6167
+ config: config$f,
6168
+ importMeta,
6169
+ parentName
6170
+ });
6171
+ if (cli.flags['dryRun']) {
6172
+ logger.logger.log(DRY_RUN_BAIL_TEXT$f);
5831
6173
  return;
5832
6174
  }
5833
6175
  await runRawNpx(argv);
@@ -5876,8 +6218,8 @@ async function createReport(socketConfig, inputPaths, {
5876
6218
  }
5877
6219
 
5878
6220
  async function getSocketConfig(absoluteConfigPath) {
5879
- const socketConfig = await config$A.readSocketConfig(absoluteConfigPath).catch(cause => {
5880
- if (cause && typeof cause === 'object' && cause instanceof config$A.SocketValidationError) {
6221
+ const socketConfig = await config$D.readSocketConfig(absoluteConfigPath).catch(cause => {
6222
+ if (cause && typeof cause === 'object' && cause instanceof config$D.SocketValidationError) {
5881
6223
  // Inspired by workbox-build:
5882
6224
  // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71
5883
6225
  const betterErrors = betterAjvErrors.betterAjvErrors({
@@ -5898,12 +6240,13 @@ async function getSocketConfig(absoluteConfigPath) {
5898
6240
 
5899
6241
  const MAX_TIMEOUT_RETRY = 5;
5900
6242
  const HTTP_CODE_TIMEOUT = 524;
5901
- async function fetchReportData(reportId, includeAllIssues, strict) {
6243
+ async function fetchReportData$1(reportId, includeAllIssues, strict) {
5902
6244
  // Lazily access constants.spinner.
5903
6245
  const {
5904
6246
  spinner
5905
6247
  } = constants;
5906
- spinner.start(`Fetching report with ID ${reportId} (this could take a while)`);
6248
+ spinner.log('Fetching report with ID ${reportId} (this could take a while)');
6249
+ spinner.start(`Fetch started... (this could take a while)`);
5907
6250
  const socketSdk = await shadowNpmInject.setupSdk();
5908
6251
  let result;
5909
6252
  for (let retry = 1; !result; ++retry) {
@@ -5912,9 +6255,10 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5912
6255
  result = await handleApiCall(socketSdk.getReport(reportId), 'fetching report');
5913
6256
  } catch (err) {
5914
6257
  if (retry >= MAX_TIMEOUT_RETRY || !(err instanceof Error) || err.cause?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT) {
5915
- spinner.stop();
6258
+ spinner.stop(`Failed to fetch report`);
5916
6259
  throw err;
5917
6260
  }
6261
+ spinner?.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`);
5918
6262
  }
5919
6263
  }
5920
6264
  if (!result.success) {
@@ -5939,17 +6283,20 @@ async function fetchReportData(reportId, includeAllIssues, strict) {
5939
6283
  return result.data;
5940
6284
  }
5941
6285
 
5942
- function formatReportDataOutput(reportId, data, commandName, outputJson, outputMarkdown, strict) {
5943
- if (outputJson) {
6286
+ function formatReportDataOutput(reportId, data, commandName, outputKind, strict, artifacts) {
6287
+ if (outputKind === 'json') {
5944
6288
  logger.logger.log(JSON.stringify(data, undefined, 2));
5945
6289
  } else {
5946
- const format = new shadowNpmInject.ColorOrMarkdown(outputMarkdown);
6290
+ const format = new shadowNpmInject.ColorOrMarkdown(outputKind === 'markdown');
5947
6291
  logger.logger.log(commonTags.stripIndents`
5948
6292
  Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {
5949
6293
  fallbackToUrl: true
5950
6294
  })}`);
5951
- if (!outputMarkdown) {
6295
+ if (outputKind === 'print') {
6296
+ logger.logger.log(data);
5952
6297
  logger.logger.log(colors.dim(`Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`));
6298
+ logger.logger.log('The scan:');
6299
+ logger.logger.log(artifacts);
5953
6300
  }
5954
6301
  }
5955
6302
  if (strict && !data.healthy) {
@@ -5957,23 +6304,55 @@ function formatReportDataOutput(reportId, data, commandName, outputJson, outputM
5957
6304
  }
5958
6305
  }
5959
6306
 
6307
+ async function getFullScan(orgSlug, fullScanId) {
6308
+ // Lazily access constants.spinner.
6309
+ const {
6310
+ spinner
6311
+ } = constants;
6312
+ const apiToken = shadowNpmInject.getDefaultToken();
6313
+ if (!apiToken) {
6314
+ 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.');
6315
+ }
6316
+ spinner.start('Fetching full-scan...');
6317
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
6318
+ spinner.stop('Fetch complete.');
6319
+ if (!response.ok) {
6320
+ const err = await handleAPIError(response.status);
6321
+ logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
6322
+ return;
6323
+ }
6324
+
6325
+ // This is nd-json; each line is a json object
6326
+ const jsons = await response.text();
6327
+ const lines = jsons.split('\n').filter(Boolean);
6328
+ const data = lines.map(line => {
6329
+ try {
6330
+ return JSON.parse(line);
6331
+ } catch {
6332
+ console.error('At least one line item was returned that could not be parsed as JSON...');
6333
+ return {};
6334
+ }
6335
+ });
6336
+ return data;
6337
+ }
6338
+
5960
6339
  async function viewReport(reportId, {
5961
6340
  all,
5962
6341
  commandName,
5963
- json,
5964
- markdown,
6342
+ outputKind,
5965
6343
  strict
5966
6344
  }) {
5967
- const result = await fetchReportData(reportId, all, strict);
6345
+ const result = await fetchReportData$1(reportId, all, strict);
6346
+ const artifacts = await getFullScan('socketdev', reportId);
5968
6347
  if (result) {
5969
- formatReportDataOutput(reportId, result, commandName, json, markdown, strict);
6348
+ formatReportDataOutput(reportId, result, commandName, outputKind, strict, artifacts);
5970
6349
  }
5971
6350
  }
5972
6351
 
5973
6352
  const {
5974
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d
6353
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e
5975
6354
  } = constants;
5976
- const config$d = {
6355
+ const config$e = {
5977
6356
  commandName: 'create',
5978
6357
  description: '[Deprecated] Create a project report',
5979
6358
  hidden: false,
@@ -5994,21 +6373,21 @@ const config$d = {
5994
6373
  }
5995
6374
  },
5996
6375
  help: () => `
5997
- This command is deprecated in favor of \`socket scan create\`.
6376
+ This command is deprecated in favor of \`socket scan view\`.
5998
6377
  It will be removed in the next major release of the CLI.
5999
6378
  `
6000
6379
  };
6001
6380
  const cmdReportCreate = {
6002
- description: config$d.description,
6003
- hidden: config$d.hidden,
6004
- run: run$d
6381
+ description: config$e.description,
6382
+ hidden: config$e.hidden,
6383
+ run: run$e
6005
6384
  };
6006
- async function run$d(argv, importMeta, {
6385
+ async function run$e(argv, importMeta, {
6007
6386
  parentName
6008
6387
  }) {
6009
6388
  const cli = meowOrExit({
6010
6389
  argv,
6011
- config: config$d,
6390
+ config: config$e,
6012
6391
  importMeta,
6013
6392
  parentName
6014
6393
  });
@@ -6025,7 +6404,7 @@ async function run$d(argv, importMeta, {
6025
6404
 
6026
6405
  // Note exiting earlier to skirt a hidden auth requirement
6027
6406
  if (cli.flags['dryRun']) {
6028
- logger.logger.log(DRY_RUN_BAIL_TEXT$d);
6407
+ logger.logger.log(DRY_RUN_BAIL_TEXT$e);
6029
6408
  return;
6030
6409
  }
6031
6410
  const socketConfig = await getSocketConfig(absoluteConfigPath);
@@ -6033,15 +6412,14 @@ async function run$d(argv, importMeta, {
6033
6412
  cwd,
6034
6413
  dryRun
6035
6414
  });
6036
- const commandName = `${parentName} ${config$d.commandName}`;
6415
+ const commandName = `${parentName} ${config$e.commandName}`;
6037
6416
  if (result?.success) {
6038
6417
  if (view) {
6039
6418
  const reportId = result.data.id;
6040
6419
  await viewReport(reportId, {
6041
6420
  all: includeAllIssues,
6042
6421
  commandName,
6043
- json,
6044
- markdown,
6422
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
6045
6423
  strict
6046
6424
  });
6047
6425
  } else if (json) {
@@ -6056,9 +6434,9 @@ async function run$d(argv, importMeta, {
6056
6434
  }
6057
6435
 
6058
6436
  const {
6059
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$c
6437
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d
6060
6438
  } = constants;
6061
- const config$c = {
6439
+ const config$d = {
6062
6440
  commandName: 'view',
6063
6441
  description: '[Deprecated] View a project report',
6064
6442
  hidden: false,
@@ -6073,16 +6451,16 @@ const config$c = {
6073
6451
  `
6074
6452
  };
6075
6453
  const cmdReportView = {
6076
- description: config$c.description,
6077
- hidden: config$c.hidden,
6078
- run: run$c
6454
+ description: config$d.description,
6455
+ hidden: config$d.hidden,
6456
+ run: run$d
6079
6457
  };
6080
- async function run$c(argv, importMeta, {
6458
+ async function run$d(argv, importMeta, {
6081
6459
  parentName
6082
6460
  }) {
6083
6461
  const cli = meowOrExit({
6084
6462
  argv,
6085
- config: config$c,
6463
+ config: config$d,
6086
6464
  importMeta,
6087
6465
  parentName
6088
6466
  });
@@ -6102,14 +6480,13 @@ async function run$c(argv, importMeta, {
6102
6480
  return;
6103
6481
  }
6104
6482
  if (cli.flags['dryRun']) {
6105
- logger.logger.log(DRY_RUN_BAIL_TEXT$c);
6483
+ logger.logger.log(DRY_RUN_BAIL_TEXT$d);
6106
6484
  return;
6107
6485
  }
6108
6486
  await viewReport(reportId, {
6109
6487
  all: Boolean(cli.flags['all']),
6110
- commandName: `${parentName} ${config$c.commandName}`,
6111
- json: Boolean(cli.flags['json']),
6112
- markdown: Boolean(cli.flags['markdown']),
6488
+ commandName: `${parentName} ${config$d.commandName}`,
6489
+ outputKind: cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print',
6113
6490
  strict: Boolean(cli.flags['strict'])
6114
6491
  });
6115
6492
  }
@@ -6186,9 +6563,9 @@ async function createRepoWithToken({
6186
6563
  }
6187
6564
 
6188
6565
  const {
6189
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$b
6566
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$c
6190
6567
  } = constants;
6191
- const config$b = {
6568
+ const config$c = {
6192
6569
  commandName: 'create',
6193
6570
  description: 'Create a repository in an organization',
6194
6571
  hidden: false,
@@ -6237,16 +6614,16 @@ const config$b = {
6237
6614
  `
6238
6615
  };
6239
6616
  const cmdReposCreate = {
6240
- description: config$b.description,
6241
- hidden: config$b.hidden,
6242
- run: run$b
6617
+ description: config$c.description,
6618
+ hidden: config$c.hidden,
6619
+ run: run$c
6243
6620
  };
6244
- async function run$b(argv, importMeta, {
6621
+ async function run$c(argv, importMeta, {
6245
6622
  parentName
6246
6623
  }) {
6247
6624
  const cli = meowOrExit({
6248
6625
  argv,
6249
- config: config$b,
6626
+ config: config$c,
6250
6627
  importMeta,
6251
6628
  parentName
6252
6629
  });
@@ -6265,7 +6642,7 @@ async function run$b(argv, importMeta, {
6265
6642
  return;
6266
6643
  }
6267
6644
  if (cli.flags['dryRun']) {
6268
- logger.logger.log(DRY_RUN_BAIL_TEXT$b);
6645
+ logger.logger.log(DRY_RUN_BAIL_TEXT$c);
6269
6646
  return;
6270
6647
  }
6271
6648
  await createRepo({
@@ -6301,9 +6678,9 @@ async function deleteRepoWithToken(orgSlug, repoName, apiToken) {
6301
6678
  }
6302
6679
 
6303
6680
  const {
6304
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$a
6681
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$b
6305
6682
  } = constants;
6306
- const config$a = {
6683
+ const config$b = {
6307
6684
  commandName: 'del',
6308
6685
  description: 'Delete a repository in an organization',
6309
6686
  hidden: false,
@@ -6322,16 +6699,16 @@ const config$a = {
6322
6699
  `
6323
6700
  };
6324
6701
  const cmdReposDel = {
6325
- description: config$a.description,
6326
- hidden: config$a.hidden,
6327
- run: run$a
6702
+ description: config$b.description,
6703
+ hidden: config$b.hidden,
6704
+ run: run$b
6328
6705
  };
6329
- async function run$a(argv, importMeta, {
6706
+ async function run$b(argv, importMeta, {
6330
6707
  parentName
6331
6708
  }) {
6332
6709
  const cli = meowOrExit({
6333
6710
  argv,
6334
- config: config$a,
6711
+ config: config$b,
6335
6712
  importMeta,
6336
6713
  parentName
6337
6714
  });
@@ -6351,7 +6728,7 @@ async function run$a(argv, importMeta, {
6351
6728
  return;
6352
6729
  }
6353
6730
  if (cli.flags['dryRun']) {
6354
- logger.logger.log(DRY_RUN_BAIL_TEXT$a);
6731
+ logger.logger.log(DRY_RUN_BAIL_TEXT$b);
6355
6732
  return;
6356
6733
  }
6357
6734
  await deleteRepo(orgSlug, repoName);
@@ -6439,9 +6816,9 @@ async function listReposWithToken({
6439
6816
  }
6440
6817
 
6441
6818
  const {
6442
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$9
6819
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$a
6443
6820
  } = constants;
6444
- const config$9 = {
6821
+ const config$a = {
6445
6822
  commandName: 'list',
6446
6823
  description: 'List repositories in an organization',
6447
6824
  hidden: false,
@@ -6484,16 +6861,16 @@ const config$9 = {
6484
6861
  `
6485
6862
  };
6486
6863
  const cmdReposList = {
6487
- description: config$9.description,
6488
- hidden: config$9.hidden,
6489
- run: run$9
6864
+ description: config$a.description,
6865
+ hidden: config$a.hidden,
6866
+ run: run$a
6490
6867
  };
6491
- async function run$9(argv, importMeta, {
6868
+ async function run$a(argv, importMeta, {
6492
6869
  parentName
6493
6870
  }) {
6494
6871
  const cli = meowOrExit({
6495
6872
  argv,
6496
- config: config$9,
6873
+ config: config$a,
6497
6874
  importMeta,
6498
6875
  parentName
6499
6876
  });
@@ -6511,7 +6888,7 @@ async function run$9(argv, importMeta, {
6511
6888
  return;
6512
6889
  }
6513
6890
  if (cli.flags['dryRun']) {
6514
- logger.logger.log(DRY_RUN_BAIL_TEXT$9);
6891
+ logger.logger.log(DRY_RUN_BAIL_TEXT$a);
6515
6892
  return;
6516
6893
  }
6517
6894
  await listRepos({
@@ -6577,9 +6954,9 @@ async function updateRepoWithToken({
6577
6954
  }
6578
6955
 
6579
6956
  const {
6580
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$8
6957
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$9
6581
6958
  } = constants;
6582
- const config$8 = {
6959
+ const config$9 = {
6583
6960
  commandName: 'update',
6584
6961
  description: 'Update a repository in an organization',
6585
6962
  hidden: false,
@@ -6628,16 +7005,16 @@ const config$8 = {
6628
7005
  `
6629
7006
  };
6630
7007
  const cmdReposUpdate = {
6631
- description: config$8.description,
6632
- hidden: config$8.hidden,
6633
- run: run$8
7008
+ description: config$9.description,
7009
+ hidden: config$9.hidden,
7010
+ run: run$9
6634
7011
  };
6635
- async function run$8(argv, importMeta, {
7012
+ async function run$9(argv, importMeta, {
6636
7013
  parentName
6637
7014
  }) {
6638
7015
  const cli = meowOrExit({
6639
7016
  argv,
6640
- config: config$8,
7017
+ config: config$9,
6641
7018
  importMeta,
6642
7019
  parentName
6643
7020
  });
@@ -6658,7 +7035,7 @@ async function run$8(argv, importMeta, {
6658
7035
  return;
6659
7036
  }
6660
7037
  if (cli.flags['dryRun']) {
6661
- logger.logger.log(DRY_RUN_BAIL_TEXT$8);
7038
+ logger.logger.log(DRY_RUN_BAIL_TEXT$9);
6662
7039
  return;
6663
7040
  }
6664
7041
  await updateRepo({
@@ -6741,9 +7118,9 @@ async function viewRepoWithToken(orgSlug, repoName, apiToken, outputKind) {
6741
7118
  }
6742
7119
 
6743
7120
  const {
6744
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$7
7121
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$8
6745
7122
  } = constants;
6746
- const config$7 = {
7123
+ const config$8 = {
6747
7124
  commandName: 'view',
6748
7125
  description: 'View repositories in an organization',
6749
7126
  hidden: false,
@@ -6768,16 +7145,16 @@ const config$7 = {
6768
7145
  `
6769
7146
  };
6770
7147
  const cmdReposView = {
6771
- description: config$7.description,
6772
- hidden: config$7.hidden,
6773
- run: run$7
7148
+ description: config$8.description,
7149
+ hidden: config$8.hidden,
7150
+ run: run$8
6774
7151
  };
6775
- async function run$7(argv, importMeta, {
7152
+ async function run$8(argv, importMeta, {
6776
7153
  parentName
6777
7154
  }) {
6778
7155
  const cli = meowOrExit({
6779
7156
  argv,
6780
- config: config$7,
7157
+ config: config$8,
6781
7158
  importMeta,
6782
7159
  parentName
6783
7160
  });
@@ -6798,7 +7175,7 @@ async function run$7(argv, importMeta, {
6798
7175
  return;
6799
7176
  }
6800
7177
  if (cli.flags['dryRun']) {
6801
- logger.logger.log(DRY_RUN_BAIL_TEXT$7);
7178
+ logger.logger.log(DRY_RUN_BAIL_TEXT$8);
6802
7179
  return;
6803
7180
  }
6804
7181
  await viewRepo(orgSlug, repoName, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
@@ -7109,9 +7486,9 @@ async function createFullScan({
7109
7486
  }
7110
7487
 
7111
7488
  const {
7112
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$6
7489
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$7
7113
7490
  } = constants;
7114
- const config$6 = {
7491
+ const config$7 = {
7115
7492
  commandName: 'create',
7116
7493
  description: 'Create a scan',
7117
7494
  hidden: false,
@@ -7216,16 +7593,16 @@ const config$6 = {
7216
7593
  `
7217
7594
  };
7218
7595
  const cmdScanCreate = {
7219
- description: config$6.description,
7220
- hidden: config$6.hidden,
7221
- run: run$6
7596
+ description: config$7.description,
7597
+ hidden: config$7.hidden,
7598
+ run: run$7
7222
7599
  };
7223
- async function run$6(argv, importMeta, {
7600
+ async function run$7(argv, importMeta, {
7224
7601
  parentName
7225
7602
  }) {
7226
7603
  const cli = meowOrExit({
7227
7604
  argv,
7228
- config: config$6,
7605
+ config: config$7,
7229
7606
  importMeta,
7230
7607
  parentName
7231
7608
  });
@@ -7263,7 +7640,7 @@ async function run$6(argv, importMeta, {
7263
7640
 
7264
7641
  // Note exiting earlier to skirt a hidden auth requirement
7265
7642
  if (cli.flags['dryRun']) {
7266
- logger.logger.log(DRY_RUN_BAIL_TEXT$6);
7643
+ logger.logger.log(DRY_RUN_BAIL_TEXT$7);
7267
7644
  return;
7268
7645
  }
7269
7646
  await createFullScan({
@@ -7306,9 +7683,9 @@ async function deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken) {
7306
7683
  }
7307
7684
 
7308
7685
  const {
7309
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$5
7686
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$6
7310
7687
  } = constants;
7311
- const config$5 = {
7688
+ const config$6 = {
7312
7689
  commandName: 'del',
7313
7690
  description: 'Delete a scan',
7314
7691
  hidden: false,
@@ -7328,16 +7705,16 @@ const config$5 = {
7328
7705
  `
7329
7706
  };
7330
7707
  const cmdScanDel = {
7331
- description: config$5.description,
7332
- hidden: config$5.hidden,
7333
- run: run$5
7708
+ description: config$6.description,
7709
+ hidden: config$6.hidden,
7710
+ run: run$6
7334
7711
  };
7335
- async function run$5(argv, importMeta, {
7712
+ async function run$6(argv, importMeta, {
7336
7713
  parentName
7337
7714
  }) {
7338
7715
  const cli = meowOrExit({
7339
7716
  argv,
7340
- config: config$5,
7717
+ config: config$6,
7341
7718
  importMeta,
7342
7719
  parentName
7343
7720
  });
@@ -7355,7 +7732,7 @@ async function run$5(argv, importMeta, {
7355
7732
  return;
7356
7733
  }
7357
7734
  if (cli.flags['dryRun']) {
7358
- logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7735
+ logger.logger.log(DRY_RUN_BAIL_TEXT$6);
7359
7736
  return;
7360
7737
  }
7361
7738
  await deleteOrgFullScan(orgSlug, fullScanId);
@@ -7449,11 +7826,11 @@ async function listFullScansWithToken({
7449
7826
  }
7450
7827
 
7451
7828
  const {
7452
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$4
7829
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$5
7453
7830
  } = constants;
7454
- const config$4 = {
7831
+ const config$5 = {
7455
7832
  commandName: 'list',
7456
- description: 'List the full scans for an organization',
7833
+ description: 'List the scans for an organization',
7457
7834
  hidden: false,
7458
7835
  flags: {
7459
7836
  ...commonFlags,
@@ -7507,16 +7884,16 @@ const config$4 = {
7507
7884
  `
7508
7885
  };
7509
7886
  const cmdScanList = {
7510
- description: config$4.description,
7511
- hidden: config$4.hidden,
7512
- run: run$4
7887
+ description: config$5.description,
7888
+ hidden: config$5.hidden,
7889
+ run: run$5
7513
7890
  };
7514
- async function run$4(argv, importMeta, {
7891
+ async function run$5(argv, importMeta, {
7515
7892
  parentName
7516
7893
  }) {
7517
7894
  const cli = meowOrExit({
7518
7895
  argv,
7519
- config: config$4,
7896
+ config: config$5,
7520
7897
  importMeta,
7521
7898
  parentName
7522
7899
  });
@@ -7532,7 +7909,7 @@ async function run$4(argv, importMeta, {
7532
7909
  return;
7533
7910
  }
7534
7911
  if (cli.flags['dryRun']) {
7535
- logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7912
+ logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7536
7913
  return;
7537
7914
  }
7538
7915
  await listFullScans({
@@ -7587,11 +7964,11 @@ async function getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind
7587
7964
  }
7588
7965
 
7589
7966
  const {
7590
- DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$3
7967
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$4
7591
7968
  } = constants;
7592
- const config$3 = {
7969
+ const config$4 = {
7593
7970
  commandName: 'metadata',
7594
- description: "Get a full scan's metadata",
7971
+ description: "Get a scan's metadata",
7595
7972
  hidden: false,
7596
7973
  flags: {
7597
7974
  ...commonFlags,
@@ -7609,16 +7986,16 @@ const config$3 = {
7609
7986
  `
7610
7987
  };
7611
7988
  const cmdScanMetadata = {
7612
- description: config$3.description,
7613
- hidden: config$3.hidden,
7614
- run: run$3
7989
+ description: config$4.description,
7990
+ hidden: config$4.hidden,
7991
+ run: run$4
7615
7992
  };
7616
- async function run$3(argv, importMeta, {
7993
+ async function run$4(argv, importMeta, {
7617
7994
  parentName
7618
7995
  }) {
7619
7996
  const cli = meowOrExit({
7620
7997
  argv,
7621
- config: config$3,
7998
+ config: config$4,
7622
7999
  importMeta,
7623
8000
  parentName
7624
8001
  });
@@ -7636,33 +8013,588 @@ async function run$3(argv, importMeta, {
7636
8013
  return;
7637
8014
  }
7638
8015
  if (cli.flags['dryRun']) {
7639
- logger.logger.log(DRY_RUN_BAIL_TEXT$3);
8016
+ logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7640
8017
  return;
7641
8018
  }
7642
8019
  await getOrgScanMetadata(orgSlug, fullScanId, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
7643
8020
  }
7644
8021
 
7645
- async function streamFullScan(orgSlug, fullScanId, file) {
8022
+ /**
8023
+ * This fetches all the relevant pieces of data to generate a report, given a
8024
+ * full scan ID.
8025
+ * It can optionally only fetch the security or license side of things.
8026
+ */
8027
+ async function fetchReportData(orgSlug, fullScanId,
8028
+ // includeLicensePolicy: boolean,
8029
+ includeSecurityPolicy) {
8030
+ let haveScan = false;
8031
+ // let haveLicensePolicy = false
8032
+ let haveSecurityPolicy = false;
8033
+
7646
8034
  // Lazily access constants.spinner.
7647
8035
  const {
7648
8036
  spinner
7649
8037
  } = constants;
8038
+ function updateProgress() {
8039
+ const needs = [!haveScan ? 'scan' : undefined,
8040
+ // includeLicensePolicy && !haveLicensePolicy ? 'license policy' : undefined,
8041
+ includeSecurityPolicy && !haveSecurityPolicy ? 'security policy' : undefined].filter(Boolean);
8042
+ if (needs.length > 2) {
8043
+ // .toOxford()
8044
+ needs[needs.length - 1] = `and ${needs[needs.length - 1]}`;
8045
+ }
8046
+ const haves = [haveScan ? 'scan' : undefined,
8047
+ // includeLicensePolicy && haveLicensePolicy ? 'license policy' : undefined,
8048
+ includeSecurityPolicy && haveSecurityPolicy ? 'security policy' : undefined].filter(Boolean);
8049
+ if (haves.length > 2) {
8050
+ // .toOxford()
8051
+ haves[haves.length - 1] = `and ${haves[haves.length - 1]}`;
8052
+ }
8053
+ if (needs.length) {
8054
+ spinner.start(`Fetching ${needs.join(needs.length > 2 ? ', ' : ' and ')}...${haves.length ? ` Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` : ''}`);
8055
+ } else {
8056
+ spinner?.successAndStop(`Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.`);
8057
+ }
8058
+ }
7650
8059
  const apiToken = shadowNpmInject.getDefaultToken();
7651
8060
  if (!apiToken) {
7652
8061
  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.');
7653
8062
  }
7654
- spinner.start('Fetching scan...');
8063
+ updateProgress();
7655
8064
  const socketSdk = await shadowNpmInject.setupSdk(apiToken);
7656
- const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
7657
- if (!data?.success) {
7658
- handleUnsuccessfulApiResponse('getOrgFullScan', data);
8065
+
8066
+ // @ts-ignore
8067
+ const [scan,
8068
+ // licensePolicyMaybe,
8069
+ securityPolicyMaybe] = await Promise.all([(async () => {
8070
+ try {
8071
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
8072
+ haveScan = true;
8073
+ updateProgress();
8074
+ if (!response.ok) {
8075
+ const err = await handleAPIError(response.status);
8076
+ logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
8077
+ return undefined;
8078
+ }
8079
+ const jsons = await response.text();
8080
+ const lines = jsons.split('\n').filter(Boolean);
8081
+ const data = lines.map(line => {
8082
+ try {
8083
+ return JSON.parse(line);
8084
+ } catch {
8085
+ console.error('At least one line item was returned that could not be parsed as JSON...');
8086
+ return;
8087
+ }
8088
+ });
8089
+ return data;
8090
+ } catch (e) {
8091
+ spinner.errorAndStop('There was an issue while fetching full scan data.');
8092
+ throw e;
8093
+ }
8094
+ })(),
8095
+ // includeLicensePolicy &&
8096
+ // (async () => {
8097
+ // const r = await socketSdk.getOrgSecurityPolicy(orgSlug)
8098
+ // haveLicensePolicy = true
8099
+ // updateProgress()
8100
+ // return await handleApiCall(
8101
+ // r,
8102
+ // "looking up organization's license policy"
8103
+ // )
8104
+ // })(),
8105
+ includeSecurityPolicy && (async () => {
8106
+ const r = await socketSdk.getOrgSecurityPolicy(orgSlug);
8107
+ haveSecurityPolicy = true;
8108
+ updateProgress();
8109
+ return await handleApiCall(r, "looking up organization's security policy");
8110
+ })()]).finally(() => spinner.stop());
8111
+ if (!Array.isArray(scan)) {
8112
+ logger.logger.error('Was unable to fetch scan, bailing');
8113
+ process.exitCode = 1;
8114
+ return {
8115
+ ok: false,
8116
+ scan: undefined,
8117
+ // licensePolicy: undefined,
8118
+ securityPolicy: undefined
8119
+ };
8120
+ }
8121
+
8122
+ // // Note: security->license once the api ships in the sdk
8123
+ // let licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'> =
8124
+ // undefined
8125
+ // if (includeLicensePolicy) {
8126
+ // if (licensePolicyMaybe && licensePolicyMaybe.success) {
8127
+ // licensePolicy = licensePolicyMaybe
8128
+ // } else {
8129
+ // logger.error('Was unable to fetch license policy, bailing')
8130
+ // process.exitCode = 1
8131
+ // return {
8132
+ // ok: false,
8133
+ // scan: undefined,
8134
+ // licensePolicy: undefined,
8135
+ // securityPolicy: undefined
8136
+ // }
8137
+ // }
8138
+ // }
8139
+
8140
+ let securityPolicy = undefined;
8141
+ if (includeSecurityPolicy) {
8142
+ if (securityPolicyMaybe && securityPolicyMaybe.success) {
8143
+ securityPolicy = securityPolicyMaybe;
8144
+ } else {
8145
+ logger.logger.error('Was unable to fetch security policy, bailing');
8146
+ process.exitCode = 1;
8147
+ return {
8148
+ ok: false,
8149
+ scan: undefined,
8150
+ // licensePolicy: undefined,
8151
+ securityPolicy: undefined
8152
+ };
8153
+ }
8154
+ }
8155
+ return {
8156
+ ok: true,
8157
+ scan,
8158
+ // licensePolicy,
8159
+ securityPolicy
8160
+ };
8161
+ }
8162
+
8163
+ function generateReport(scan, _licensePolicy, securityPolicy, {
8164
+ fold,
8165
+ orgSlug,
8166
+ reportLevel,
8167
+ scanId,
8168
+ short
8169
+ }) {
8170
+ const now = Date.now();
8171
+
8172
+ // Lazily access constants.spinner.
8173
+ const {
8174
+ spinner
8175
+ } = constants;
8176
+ spinner.start('Generating report...');
8177
+
8178
+ // Create an object that includes:
8179
+ // healthy: boolean
8180
+ // worst violation level;
8181
+ // per eco
8182
+ // per package
8183
+ // per version
8184
+ // per offending file
8185
+ // reported issue -> policy action
8186
+
8187
+ // In the context of a report;
8188
+ // - the alert.severity is irrelevant
8189
+ // - the securityPolicyDefault is irrelevant
8190
+ // - the report defaults to healthy:true with no alerts
8191
+ // - the appearance of an alert will trigger the policy action;
8192
+ // - error: healthy will end up as false, add alerts to report
8193
+ // - warn: healthy unchanged, add alerts to report
8194
+ // - monitor/ignore: no action
8195
+ // - defer: unknown (no action)
8196
+
8197
+ const violations = new Map();
8198
+ let healthy = true;
8199
+ const securityRules = securityPolicy?.data.securityPolicyRules;
8200
+ if (securityPolicy && securityRules) {
8201
+ // Note: reportLevel: error > warn > monitor > ignore > defer
8202
+ scan.forEach(artifact => {
8203
+ const {
8204
+ alerts,
8205
+ name: pkgName = '<unknown>',
8206
+ type: ecosystem,
8207
+ version = '<unknown>'
8208
+ } = artifact;
8209
+ alerts?.forEach(alert => {
8210
+ const alertName = alert.type; // => policy[type]
8211
+ const action = securityRules[alertName]?.action || '';
8212
+ switch (action) {
8213
+ case 'error':
8214
+ {
8215
+ healthy = false;
8216
+ if (!short) {
8217
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8218
+ }
8219
+ break;
8220
+ }
8221
+ case 'warn':
8222
+ {
8223
+ if (!short && reportLevel !== 'error') {
8224
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8225
+ }
8226
+ break;
8227
+ }
8228
+ case 'monitor':
8229
+ {
8230
+ if (!short && reportLevel !== 'warn' && reportLevel !== 'error') {
8231
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8232
+ }
8233
+ break;
8234
+ }
8235
+ case 'ignore':
8236
+ {
8237
+ if (!short && reportLevel !== 'warn' && reportLevel !== 'error' && reportLevel !== 'monitor') {
8238
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8239
+ }
8240
+ break;
8241
+ }
8242
+ case 'defer':
8243
+ {
8244
+ // Not sure but ignore for now. Defer to later ;)
8245
+ if (!short && reportLevel === 'defer') {
8246
+ addAlert(artifact, violations, fold, ecosystem, pkgName, version, alert, action);
8247
+ }
8248
+ break;
8249
+ }
8250
+ }
8251
+ });
8252
+ });
8253
+ }
8254
+ spinner.successAndStop(`Generated reported in ${Date.now() - now} ms`);
8255
+ const report = short ? {
8256
+ healthy
8257
+ } : {
8258
+ healthy,
8259
+ orgSlug,
8260
+ scanId,
8261
+ options: {
8262
+ fold,
8263
+ reportLevel
8264
+ },
8265
+ alerts: violations
8266
+ };
8267
+ return report;
8268
+ }
8269
+ function createLeaf(art, alert, policyAction) {
8270
+ const leaf = {
8271
+ type: alert.type,
8272
+ policy: policyAction,
8273
+ url: `https://socket.dev/${art.type}/package/${art.name}/${art.version}`,
8274
+ manifest: art.manifestFiles?.map(obj => obj.file) ?? []
8275
+ };
8276
+ return leaf;
8277
+ }
8278
+ function addAlert(art, violations, foldSetting, ecosystem, pkgName, version, alert, policyAction) {
8279
+ if (!violations.has(ecosystem)) violations.set(ecosystem, new Map());
8280
+ const ecomap = violations.get(ecosystem);
8281
+ if (foldSetting === 'pkg') {
8282
+ const existing = ecomap.get(pkgName);
8283
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8284
+ ecomap.set(pkgName, createLeaf(art, alert, policyAction));
8285
+ }
8286
+ } else {
8287
+ if (!ecomap.has(pkgName)) ecomap.set(pkgName, new Map());
8288
+ const pkgmap = ecomap.get(pkgName);
8289
+ if (foldSetting === 'version') {
8290
+ const existing = pkgmap.get(version);
8291
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8292
+ pkgmap.set(version, createLeaf(art, alert, policyAction));
8293
+ }
8294
+ } else {
8295
+ if (!pkgmap.has(version)) pkgmap.set(version, new Map());
8296
+ const file = alert.file || '<unknown>';
8297
+ const vermap = pkgmap.get(version);
8298
+ if (foldSetting === 'file') {
8299
+ const existing = vermap.get(file);
8300
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8301
+ vermap.set(file, createLeaf(art, alert, policyAction));
8302
+ }
8303
+ } else {
8304
+ if (!vermap.has(file)) vermap.set(file, new Map());
8305
+ const key = `${alert.type} at ${alert.start}:${alert.end}`;
8306
+ const filemap = vermap.get(file);
8307
+ const existing = filemap.get(key);
8308
+ if (!existing || isStricterPolicy(existing.policy, policyAction)) {
8309
+ filemap.set(key, createLeaf(art, alert, policyAction));
8310
+ }
8311
+ }
8312
+ }
8313
+ }
8314
+ }
8315
+ function isStricterPolicy(was, is) {
8316
+ // error > warn > monitor > ignore > defer > {unknown}
8317
+ if (was === 'error') return false;
8318
+ if (is === 'error') return true;
8319
+ if (was === 'warn') return false;
8320
+ if (is === 'warn') return false;
8321
+ if (was === 'monitor') return false;
8322
+ if (is === 'monitor') return false;
8323
+ if (was === 'ignore') return false;
8324
+ if (is === 'ignore') return false;
8325
+ if (was === 'defer') return false;
8326
+ if (is === 'defer') return false;
8327
+ // unreachable?
8328
+ return false;
8329
+ }
8330
+
8331
+ /**
8332
+ * Convert a Map<string, Map|string> to a nested object of similar shape.
8333
+ * The goal is to serialize it with JSON.stringify, which Map can't do.
8334
+ */
8335
+ function mapToObject(map) {
8336
+ return Object.fromEntries(Array.from(map.entries()).map(([k, v]) => [k, v instanceof Map ? mapToObject(v) : v]));
8337
+ }
8338
+
8339
+ function* walkNestedMap(map, keys = []) {
8340
+ for (const [key, value] of map.entries()) {
8341
+ if (value instanceof Map) {
8342
+ yield* walkNestedMap(value, keys.concat(key));
8343
+ } else {
8344
+ yield {
8345
+ keys: keys.concat(key),
8346
+ value: value
8347
+ };
8348
+ }
8349
+ }
8350
+ }
8351
+
8352
+ async function reportFullScan({
8353
+ filePath,
8354
+ fold,
8355
+ fullScanId,
8356
+ includeLicensePolicy,
8357
+ includeSecurityPolicy,
8358
+ orgSlug,
8359
+ outputKind,
8360
+ reportLevel,
8361
+ short
8362
+ }) {
8363
+ logger.logger.error('output:', outputKind, ', file:', filePath, ', fold:', fold, ', reportLevel:', reportLevel);
8364
+ if (!includeSecurityPolicy) {
8365
+ return; // caller should assert
8366
+ }
8367
+ const {
8368
+ // licensePolicy,
8369
+ ok,
8370
+ scan,
8371
+ securityPolicy
8372
+ } = await fetchReportData(orgSlug, fullScanId,
8373
+ // includeLicensePolicy
8374
+ includeSecurityPolicy);
8375
+ if (!ok) {
7659
8376
  return;
7660
8377
  }
7661
- spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7662
- return data;
8378
+ const scanReport = generateReport(scan, undefined,
8379
+ // licensePolicy,
8380
+ securityPolicy, {
8381
+ orgSlug,
8382
+ scanId: fullScanId,
8383
+ fold,
8384
+ short,
8385
+ reportLevel
8386
+ });
8387
+ if (!scanReport.healthy) {
8388
+ process.exitCode = 1;
8389
+ }
8390
+ if (outputKind === 'json' || outputKind === 'text' && filePath && filePath.endsWith('.json')) {
8391
+ const json = short ? JSON.stringify(scanReport) : toJsonReport(scanReport);
8392
+ if (filePath && filePath !== '-') {
8393
+ logger.logger.log('Writing json report to', filePath);
8394
+ return await fs$1.writeFile(filePath, json);
8395
+ }
8396
+ logger.logger.log(json);
8397
+ return;
8398
+ }
8399
+ if (outputKind === 'markdown' || filePath && filePath.endsWith('.md')) {
8400
+ const md = short ? `healthy = ${scanReport.healthy}` : toMarkdownReport(scanReport);
8401
+ if (filePath && filePath !== '-') {
8402
+ logger.logger.log('Writing markdown report to', filePath);
8403
+ return await fs$1.writeFile(filePath, md);
8404
+ }
8405
+ logger.logger.log(md);
8406
+ return;
8407
+ }
8408
+ if (short) {
8409
+ logger.logger.log(scanReport.healthy ? 'OK' : 'ERR');
8410
+ } else {
8411
+ logger.logger.dir(scanReport, {
8412
+ depth: null
8413
+ });
8414
+ }
7663
8415
  }
8416
+ function toJsonReport(report) {
8417
+ const obj = mapToObject(report.alerts);
8418
+ const json = JSON.stringify({
8419
+ ...report,
8420
+ alerts: obj
8421
+ }, null, 2);
8422
+ return json;
8423
+ }
8424
+ function toMarkdownReport(report) {
8425
+ const flatData = Array.from(walkNestedMap(report.alerts)).map(({
8426
+ keys,
8427
+ value
8428
+ }) => {
8429
+ const {
8430
+ manifest,
8431
+ policy,
8432
+ type,
8433
+ url
8434
+ } = value;
8435
+ return {
8436
+ 'Alert Type': type,
8437
+ Package: keys[1] || '<unknown>',
8438
+ 'Introduced by': keys[2] || '<unknown>',
8439
+ url,
8440
+ 'Manifest file': manifest.join(', '),
8441
+ Policy: policy
8442
+ };
8443
+ });
8444
+ const md = `
8445
+ # Scan Policy Report
7664
8446
 
7665
- async function getFullScan(orgSlug, fullScanId) {
8447
+ This report tells you whether the results of a Socket scan results violate the
8448
+ security or license policy set by your organization.
8449
+
8450
+ ## Health status
8451
+
8452
+ ${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.'}
8453
+
8454
+ ## Settings
8455
+
8456
+ Configuration used to generate this report:
8457
+
8458
+ - Organization: ${report.orgSlug}
8459
+ - Scan ID: ${report.scanId}
8460
+ - Alert folding: ${report.options.fold === 'none' ? 'none' : `up to ${report.options.fold}`}
8461
+ - Minimal policy level for alert to be included in report: ${report.options.reportLevel === 'defer' ? 'everything' : report.options.reportLevel}
8462
+
8463
+ ## Alerts
8464
+
8465
+ ${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}".`}
8466
+
8467
+ ${!report.alerts.size ? '' : mdTable(flatData, ['Policy', 'Alert Type', 'Package', 'Introduced by', 'url', 'Manifest file'])}
8468
+ `.trim() + '\n';
8469
+ return md;
8470
+ }
8471
+
8472
+ const {
8473
+ DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$3
8474
+ } = constants;
8475
+ const config$3 = {
8476
+ commandName: 'report',
8477
+ description: 'Check whether a scan result passes the organizational policies (security, license)',
8478
+ hidden: true,
8479
+ // [beta]
8480
+ flags: {
8481
+ ...commonFlags,
8482
+ ...outputFlags,
8483
+ fold: {
8484
+ type: 'string',
8485
+ default: 'none',
8486
+ description: 'Fold reported alerts to some degree'
8487
+ },
8488
+ reportLevel: {
8489
+ type: 'string',
8490
+ default: 'warn',
8491
+ description: 'Which policy level alerts should be reported'
8492
+ },
8493
+ short: {
8494
+ type: 'boolean',
8495
+ default: false,
8496
+ description: 'Report only the healthy status'
8497
+ },
8498
+ // license: {
8499
+ // type: 'boolean',
8500
+ // default: true,
8501
+ // description: 'Report the license policy status. Default: true'
8502
+ // },
8503
+ security: {
8504
+ type: 'boolean',
8505
+ default: true,
8506
+ description: 'Report the security policy status. Default: true'
8507
+ }
8508
+ },
8509
+ help: (command, config) => `
8510
+ Usage
8511
+ $ ${command} <org slug> <scan ID> [path to output file]
8512
+
8513
+ Options
8514
+ ${getFlagListOutput(config.flags, 6)}
8515
+
8516
+ This consumes 1 quota unit plus 1 for each of the requested policy types.
8517
+
8518
+ Note: By default it reports both so by default it consumes 3 quota units.
8519
+
8520
+ Your API token will need the \`full-scans:list\` scope regardless. Additionally
8521
+ it needs \`security-policy:read\` to report on the security policy.
8522
+
8523
+ By default the result is a nested object that looks like this:
8524
+ \`{[ecosystem]: {[pkgName]: {[version]: {[file]: {[type:loc]: policy}}}}\`
8525
+ You can fold this up to given level: 'pkg', 'version', 'file', and 'none'.
8526
+
8527
+ By default only the warn and error policy level alerts are reported. You can
8528
+ override this and request more ('defer' < 'ignore' < 'monitor' < 'warn' < 'error')
8529
+
8530
+ Short responses: JSON: \`{healthy:bool}\`, markdown: \`healthy = bool\`, text: \`OK/ERR\`
8531
+
8532
+ Examples
8533
+ $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version
8534
+ `
8535
+ };
8536
+ const cmdScanReport = {
8537
+ description: config$3.description,
8538
+ hidden: config$3.hidden,
8539
+ run: run$3
8540
+ };
8541
+ async function run$3(argv, importMeta, {
8542
+ parentName
8543
+ }) {
8544
+ const cli = meowOrExit({
8545
+ argv,
8546
+ config: config$3,
8547
+ importMeta,
8548
+ parentName
8549
+ });
8550
+ const {
8551
+ fold = 'none',
8552
+ json,
8553
+ // license,
8554
+ markdown,
8555
+ reportLevel = 'warn',
8556
+ security
8557
+ } = cli.flags;
8558
+ const [orgSlug = '', fullScanId = '', file = '-'] = cli.input;
8559
+ if (!orgSlug || !fullScanId ||
8560
+ // (!license && !security) ||
8561
+ json && markdown) {
8562
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
8563
+ // options or missing arguments.
8564
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
8565
+ process.exitCode = 2;
8566
+ logger.logger.fail(commonTags.stripIndents`
8567
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
8568
+
8569
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
8570
+
8571
+ - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}
8572
+
8573
+ - Not both the --json and --markdown flags ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
8574
+ `
8575
+ // - At least one policy to report ${!license && !security ? colors.red('(do not omit both!)') : colors.green('(ok)')}
8576
+ );
8577
+ return;
8578
+ }
8579
+ if (cli.flags['dryRun']) {
8580
+ logger.logger.log(DRY_RUN_BAIL_TEXT$3);
8581
+ return;
8582
+ }
8583
+ await reportFullScan({
8584
+ orgSlug,
8585
+ fullScanId,
8586
+ includeLicensePolicy: false,
8587
+ // !!license,
8588
+ includeSecurityPolicy: typeof security === 'boolean' ? security : true,
8589
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
8590
+ filePath: file,
8591
+ fold: fold,
8592
+ short: !!cli.flags['short'],
8593
+ reportLevel: reportLevel
8594
+ });
8595
+ }
8596
+
8597
+ async function streamFullScan(orgSlug, fullScanId, file) {
7666
8598
  // Lazily access constants.spinner.
7667
8599
  const {
7668
8600
  spinner
@@ -7671,26 +8603,14 @@ async function getFullScan(orgSlug, fullScanId) {
7671
8603
  if (!apiToken) {
7672
8604
  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.');
7673
8605
  }
7674
- spinner.start('Fetching full-scan...');
7675
- const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
7676
- spinner.stop('Fetch complete.');
7677
- if (!response.ok) {
7678
- const err = await handleAPIError(response.status);
7679
- logger.logger.fail(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
8606
+ spinner.start('Fetching scan...');
8607
+ const socketSdk = await shadowNpmInject.setupSdk(apiToken);
8608
+ const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
8609
+ if (!data?.success) {
8610
+ handleUnsuccessfulApiResponse('getOrgFullScan', data);
7680
8611
  return;
7681
8612
  }
7682
-
7683
- // This is nd-json; each line is a json object
7684
- const jsons = await response.text();
7685
- const lines = jsons.split('\n').filter(Boolean);
7686
- const data = lines.map(line => {
7687
- try {
7688
- return JSON.parse(line);
7689
- } catch {
7690
- console.error('At least one line item was returned that could not be parsed as JSON...');
7691
- return {};
7692
- }
7693
- });
8613
+ spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7694
8614
  return data;
7695
8615
  }
7696
8616
 
@@ -7808,6 +8728,7 @@ const cmdScan = {
7808
8728
  list: cmdScanList,
7809
8729
  del: cmdScanDel,
7810
8730
  metadata: cmdScanMetadata,
8731
+ report: cmdScanReport,
7811
8732
  view: cmdScanView
7812
8733
  }, {
7813
8734
  aliases: {
@@ -8288,7 +9209,7 @@ void (async () => {
8288
9209
  await vendor.updater({
8289
9210
  name: SOCKET_CLI_BIN_NAME,
8290
9211
  // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION']".
8291
- version: "0.14.60",
9212
+ version: "0.14.62",
8292
9213
  ttl: 86_400_000 /* 24 hours in milliseconds */
8293
9214
  });
8294
9215
  try {
@@ -8355,5 +9276,5 @@ void (async () => {
8355
9276
  await shadowNpmInject.captureException(e);
8356
9277
  }
8357
9278
  })();
8358
- //# debugId=a4fe81ae-a54c-4a9c-bd36-803984c36419
9279
+ //# debugId=a794b9bc-963d-4504-a1af-e5c87018417b
8359
9280
  //# sourceMappingURL=cli.js.map