@socketsecurity/cli-with-sentry 0.14.55 → 0.14.56

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.
@@ -36,10 +36,11 @@ var contrib = _socketInterop(require('blessed-contrib'));
36
36
  var prompts = require('@socketsecurity/registry/lib/prompts');
37
37
  var yargsParse = _socketInterop(require('yargs-parser'));
38
38
  var words = require('@socketsecurity/registry/lib/words');
39
- var npm = require('@socketsecurity/registry/lib/npm');
39
+ var shadowBin = require('./shadow-bin.js');
40
40
  var chalkTable = _socketInterop(require('chalk-table'));
41
41
  var util = require('node:util');
42
42
  var registry = require('@socketsecurity/registry');
43
+ var npm = require('@socketsecurity/registry/lib/npm');
43
44
  var packages = require('@socketsecurity/registry/lib/packages');
44
45
  var registryConstants = require('@socketsecurity/registry/lib/constants');
45
46
  var isInteractive = require('@socketregistry/is-interactive/index.cjs');
@@ -1519,7 +1520,7 @@ function meowOrExit({
1519
1520
  }
1520
1521
  function getAsciiHeader(command) {
1521
1522
  const cliVersion = // The '@rollup/plugin-replace' will replace "process.env['SOCKET_CLI_VERSION_HASH']".
1522
- "0.14.55:51de259:b691b88f:pub";
1523
+ "0.14.56:5a261bf:186ce7ee:pub";
1523
1524
  const nodeVersion = process.version;
1524
1525
  const apiToken = index.getSetting('apiToken');
1525
1526
  const shownToken = apiToken ? getLastFiveOfApiToken(apiToken) : 'no';
@@ -1643,6 +1644,35 @@ function mdTableStringNumber(title1, title2, obj) {
1643
1644
  lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`);
1644
1645
  return lines.join('\n');
1645
1646
  }
1647
+ function mdTable(logs,
1648
+ // This is saying "an array of strings and the strings are a valid key of elements of T"
1649
+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
1650
+ cols) {
1651
+ // Max col width required to fit all data in that column
1652
+ const cws = cols.map(col => col.length);
1653
+ for (const log of logs) {
1654
+ for (let i = 0; i < cols.length; ++i) {
1655
+ // @ts-ignore
1656
+ const val = log[cols[i] ?? ''] ?? '';
1657
+ cws[i] = Math.max(cws[i] ?? 0, String(val).length);
1658
+ }
1659
+ }
1660
+ let div = '|';
1661
+ for (const cw of cws) div += ' ' + '-'.repeat(cw) + ' |';
1662
+ let header = '|';
1663
+ for (let i = 0; i < cols.length; ++i) header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |';
1664
+ let body = '';
1665
+ for (const log of logs) {
1666
+ body += '|';
1667
+ for (let i = 0; i < cols.length; ++i) {
1668
+ // @ts-ignore
1669
+ const val = log[cols[i] ?? ''] ?? '';
1670
+ body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |';
1671
+ }
1672
+ body += '\n';
1673
+ }
1674
+ return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n');
1675
+ }
1646
1676
 
1647
1677
  // Note: Widgets does not seem to actually work as code :'(
1648
1678
 
@@ -2075,35 +2105,6 @@ ${table}
2075
2105
  return;
2076
2106
  }
2077
2107
  }
2078
- function mdTable(logs,
2079
- // This is saying "an array of strings and the strings are a valid key of elements of T"
2080
- // In turn, T is defined above as the audit log event type from our OpenAPI docs.
2081
- cols) {
2082
- // Max col width required to fit all data in that column
2083
- const cws = cols.map(col => col.length);
2084
- for (const log of logs) {
2085
- for (let i = 0; i < cols.length; ++i) {
2086
- // @ts-ignore
2087
- const val = log[cols[i] ?? ''] ?? '';
2088
- cws[i] = Math.max(cws[i] ?? 0, String(val).length);
2089
- }
2090
- }
2091
- let div = '|';
2092
- for (const cw of cws) div += ' ' + '-'.repeat(cw) + ' |';
2093
- let header = '|';
2094
- for (let i = 0; i < cols.length; ++i) header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |';
2095
- let body = '';
2096
- for (const log of logs) {
2097
- body += '|';
2098
- for (let i = 0; i < cols.length; ++i) {
2099
- // @ts-ignore
2100
- const val = log[cols[i] ?? ''] ?? '';
2101
- body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |';
2102
- }
2103
- body += '\n';
2104
- }
2105
- return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n');
2106
- }
2107
2108
  async function outputAsPrint(auditLogs, orgSlug, logType) {
2108
2109
  const data = [];
2109
2110
  const logDetails = {};
@@ -2248,18 +2249,10 @@ async function run$x(argv, importMeta, {
2248
2249
  });
2249
2250
  }
2250
2251
 
2251
- const {
2252
- SBOM_SIGN_ALGORITHM,
2253
- // Algorithm. Example: RS512
2254
- SBOM_SIGN_PRIVATE_KEY,
2255
- // Location to the RSA private key
2256
- SBOM_SIGN_PUBLIC_KEY // Optional. Location to the RSA public key
2257
- } = process$1.env;
2258
2252
  const {
2259
2253
  NPM: NPM$e,
2260
- PNPM: PNPM$8,
2261
- cdxgenBinPath,
2262
- synpBinPath
2254
+ NPX: NPX$3,
2255
+ PNPM: PNPM$8
2263
2256
  } = constants;
2264
2257
  const nodejsPlatformTypes = new Set(['javascript', 'js', 'nodejs', NPM$e, PNPM$8, 'ts', 'tsx', 'typescript']);
2265
2258
  async function runCycloneDX(yargv) {
@@ -2271,21 +2264,13 @@ async function runCycloneDX(yargv) {
2271
2264
  // Use synp to create a package-lock.json from the yarn.lock,
2272
2265
  // based on the node_modules folder, for a more accurate SBOM.
2273
2266
  try {
2274
- await npm.runBin(await fs.promises.realpath(synpBinPath), ['--source-file', './yarn.lock']);
2267
+ await shadowBin(NPX$3, ['synp@1.9.14', '--', '--source-file', './yarn.lock'], 2);
2275
2268
  yargv.type = NPM$e;
2276
2269
  cleanupPackageLock = true;
2277
2270
  } catch {}
2278
2271
  }
2279
2272
  }
2280
- await npm.runBin(await fs.promises.realpath(cdxgenBinPath), argvToArray(yargv), {
2281
- env: {
2282
- NODE_ENV: '',
2283
- SBOM_SIGN_ALGORITHM,
2284
- SBOM_SIGN_PRIVATE_KEY,
2285
- SBOM_SIGN_PUBLIC_KEY
2286
- },
2287
- stdio: 'inherit'
2288
- });
2273
+ await shadowBin(NPX$3, ['@cyclonedx/cdxgen@11.2.0', '--', ...argvToArray(yargv)], 2);
2289
2274
  if (cleanupPackageLock) {
2290
2275
  try {
2291
2276
  await fs.promises.rm('./package-lock.json');
@@ -2497,6 +2482,7 @@ async function findDependencies({
2497
2482
  logger.logger.log(result.data);
2498
2483
  return;
2499
2484
  }
2485
+ logger.logger.log('Request details: Offset:', offset, ', limit:', limit, ', is there more data after this?', result.data.end ? 'no' : 'yes');
2500
2486
  const options = {
2501
2487
  columns: [{
2502
2488
  field: 'namespace',
@@ -2588,43 +2574,95 @@ async function run$v(argv, importMeta, {
2588
2574
  async function getDiffScan({
2589
2575
  after,
2590
2576
  before,
2577
+ depth,
2591
2578
  file,
2592
2579
  orgSlug,
2593
2580
  outputJson
2594
- }, apiToken) {
2581
+ }) {
2582
+ const apiToken = index.getDefaultToken();
2583
+ if (!apiToken) {
2584
+ throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
2585
+ }
2586
+ await getDiffScanWithToken({
2587
+ after,
2588
+ before,
2589
+ depth,
2590
+ file,
2591
+ orgSlug,
2592
+ outputJson,
2593
+ apiToken
2594
+ });
2595
+ }
2596
+ async function getDiffScanWithToken({
2597
+ after,
2598
+ apiToken,
2599
+ before,
2600
+ depth,
2601
+ file,
2602
+ orgSlug,
2603
+ outputJson
2604
+ }) {
2595
2605
  // Lazily access constants.spinner.
2596
2606
  const {
2597
2607
  spinner
2598
2608
  } = constants;
2599
2609
  spinner.start('Getting diff scan...');
2600
- const response = await queryAPI(`${orgSlug}/full-scans/diff?before=${before}&after=${after}&preview`, apiToken);
2601
- const data = await response.json();
2610
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`, apiToken);
2602
2611
  if (!response.ok) {
2603
2612
  const err = await handleAPIError(response.status);
2604
2613
  spinner.errorAndStop(`${colors.bgRed(colors.white(response.statusText))}: ${err}`);
2605
2614
  return;
2606
2615
  }
2616
+ const result = await handleApiCall(await response.json(), 'Deserializing json');
2607
2617
  spinner.stop();
2608
- if (file && !outputJson) {
2609
- fs.writeFile(file, JSON.stringify(data), err => {
2610
- err ? logger.logger.error(err) : logger.logger.log(`Data successfully written to ${file}`);
2611
- });
2612
- return;
2613
- }
2614
- if (outputJson) {
2615
- logger.logger.log(`\n Diff scan result: \n`);
2616
- logger.logger.log(util.inspect(data, {
2617
- showHidden: false,
2618
- depth: null,
2619
- colors: true
2620
- }));
2621
- logger.logger.log(`\n View this diff scan in the Socket dashboard: ${colors.cyan(data?.['diff_report_url'])}`);
2618
+ const dashboardUrl = result?.['diff_report_url'];
2619
+ const dashboardMessage = dashboardUrl ? `\n View this diff scan in the Socket dashboard: ${colors.cyan(dashboardUrl)}` : '';
2620
+
2621
+ // When forcing json, or dumping to file, serialize to string such that it
2622
+ // won't get truncated. The only way to dump the full raw JSON to stdout is
2623
+ // to use `--json --file -` (the dash is a standard notation for stdout)
2624
+ if (outputJson || file) {
2625
+ let json;
2626
+ try {
2627
+ json = JSON.stringify(result, null, 2);
2628
+ } catch (e) {
2629
+ // Most likely caused by a circular reference (or OOM)
2630
+ logger.logger.error('There was a problem converting the data to JSON');
2631
+ process.exitCode = 1;
2632
+ return;
2633
+ }
2634
+ if (file && file !== '-') {
2635
+ logger.logger.log(`Writing json to \`${file}\``);
2636
+ fs.writeFile(file, JSON.stringify(result, null, 2), err => {
2637
+ if (err) {
2638
+ logger.logger.error(`Writing to \`${file}\` failed...`);
2639
+ logger.logger.error(err);
2640
+ } else {
2641
+ logger.logger.log(`Data successfully written to \`${file}\``);
2642
+ }
2643
+ logger.logger.error(dashboardMessage);
2644
+ });
2645
+ } else {
2646
+ // TODO: expose different method for writing to stderr when simply dodging stdout
2647
+ logger.logger.error(`\n Diff scan result: \n`);
2648
+ logger.logger.log(json);
2649
+ logger.logger.error(dashboardMessage);
2650
+ }
2622
2651
  return;
2623
2652
  }
2653
+
2654
+ // In this case neither the --json nor the --file flag was passed
2655
+ // Dump the JSON to CLI and let NodeJS deal with truncation
2656
+
2624
2657
  logger.logger.log('Diff scan result:');
2625
- logger.logger.log(data);
2658
+ logger.logger.log(util.inspect(result, {
2659
+ showHidden: false,
2660
+ depth: depth > 0 ? depth : null,
2661
+ colors: true,
2662
+ maxArrayLength: null
2663
+ }));
2626
2664
  logger.logger.log(`\n 📝 To display the detailed report in the terminal, use the --json flag \n`);
2627
- logger.logger.log(`\n View this diff scan in the Socket dashboard: ${colors.cyan(data?.['diff_report_url'])}`);
2665
+ logger.logger.log(dashboardMessage);
2628
2666
  }
2629
2667
 
2630
2668
  const {
@@ -2636,36 +2674,44 @@ const config$u = {
2636
2674
  hidden: false,
2637
2675
  flags: {
2638
2676
  ...commonFlags,
2677
+ after: {
2678
+ type: 'string',
2679
+ shortFlag: 'a',
2680
+ default: '',
2681
+ description: 'The full scan ID of the head scan'
2682
+ },
2639
2683
  before: {
2640
2684
  type: 'string',
2641
2685
  shortFlag: 'b',
2642
2686
  default: '',
2643
2687
  description: 'The full scan ID of the base scan'
2644
2688
  },
2645
- after: {
2646
- type: 'string',
2647
- shortFlag: 'a',
2648
- default: '',
2649
- description: 'The full scan ID of the head scan'
2689
+ depth: {
2690
+ type: 'number',
2691
+ default: 2,
2692
+ description: 'Max depth of JSON to display before truncating, use zero for no limit (without --json/--file)'
2650
2693
  },
2651
- preview: {
2694
+ json: {
2652
2695
  type: 'boolean',
2653
- shortFlag: 'p',
2654
- default: true,
2655
- description: 'A boolean flag to persist or not the diff scan result'
2696
+ shortFlag: 'j',
2697
+ default: false,
2698
+ description: 'Output result as json. This can be big. Use --file to store it to disk without truncation.'
2656
2699
  },
2657
2700
  file: {
2658
2701
  type: 'string',
2659
2702
  shortFlag: 'f',
2660
2703
  default: '',
2661
- description: 'Path to a local file where the output should be saved'
2662
- },
2663
- ...outputFlags
2704
+ description: 'Path to a local file where the output should be saved. Use `-` to force stdout.'
2705
+ }
2664
2706
  },
2665
2707
  help: (command, config) => `
2666
2708
  Usage
2667
2709
  $ ${command} <org slug> --before=<before> --after=<after>
2668
2710
 
2711
+ This command displays the package changes between two scans. The full output
2712
+ can be pretty large depending on the size of your repo and time range. It is
2713
+ best stored to disk to be further analyzed by other tools.
2714
+
2669
2715
  Options
2670
2716
  ${getFlagListOutput(config.flags, 6)}
2671
2717
 
@@ -2698,6 +2744,7 @@ async function run$u(argv, importMeta, {
2698
2744
  logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
2699
2745
  - Specify a before and after full scan ID ${!before && !after ? colors.red('(missing before and after!)') : !before ? colors.red('(missing before!)') : !after ? colors.red('(missing after!)') : colors.green('(ok)')}\n
2700
2746
  - To get full scans IDs, you can run the command "socket scan list <your org slug>".
2747
+ The args are expecting a full \`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\` ID.\n
2701
2748
  - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
2702
2749
  return;
2703
2750
  }
@@ -2705,24 +2752,24 @@ async function run$u(argv, importMeta, {
2705
2752
  logger.logger.log(DRY_RUN_BAIL_TEXT$t);
2706
2753
  return;
2707
2754
  }
2708
- const apiToken = index.getDefaultToken();
2709
- if (!apiToken) {
2710
- throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
2711
- }
2712
2755
  await getDiffScan({
2713
2756
  outputJson: Boolean(cli.flags['json']),
2714
- outputMarkdown: Boolean(cli.flags['markdown']),
2715
2757
  before,
2716
2758
  after,
2717
- preview: Boolean(cli.flags['preview']),
2759
+ depth: Number(cli.flags['depth']),
2718
2760
  orgSlug,
2719
2761
  file: String(cli.flags['file'] || '')
2720
- }, apiToken);
2762
+ });
2721
2763
  }
2722
2764
 
2723
2765
  const description$3 = 'Diff scans related commands';
2724
2766
  const cmdDiffScan = {
2725
2767
  description: description$3,
2768
+ // Hidden because it was broken all this time (nobody could be using it)
2769
+ // and we're not sure if it's useful to anyone in its current state.
2770
+ // Until we do, we'll hide this to keep the help tidier.
2771
+ // And later, we may simply move this under `scan`, anyways.
2772
+ hidden: true,
2726
2773
  async run(argv, importMeta, {
2727
2774
  parentName
2728
2775
  }) {
@@ -2954,7 +3001,12 @@ function getSeverityCount(issues, lowestToInclude) {
2954
3001
  return severityCount;
2955
3002
  }
2956
3003
 
2957
- async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues, spinner) {
3004
+ async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
3005
+ // Lazily access constants.spinner.
3006
+ const {
3007
+ spinner
3008
+ } = constants;
3009
+ spinner.start(pkgVersion === 'latest' ? `Looking up data for the latest version of ${pkgName}` : `Looking up data for version ${pkgVersion} of ${pkgName}`);
2958
3010
  const socketSdk = await index.setupSdk(index.getPublicToken());
2959
3011
  const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package');
2960
3012
  const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score');
@@ -2965,6 +3017,7 @@ async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues, spinner)
2965
3017
  return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult, spinner);
2966
3018
  }
2967
3019
  const severityCount = getSeverityCount(result.data, includeAllIssues ? undefined : 'high');
3020
+ spinner?.successAndStop('Data fetched');
2968
3021
  return {
2969
3022
  data: result.data,
2970
3023
  severityCount,
@@ -2981,49 +3034,54 @@ function formatPackageInfo({
2981
3034
  severityCount
2982
3035
  }, {
2983
3036
  name,
2984
- outputJson,
2985
- outputMarkdown,
3037
+ outputKind,
2986
3038
  pkgName,
2987
- pkgVersion,
2988
- strict
2989
- }, spinner) {
2990
- if (outputJson) {
3039
+ pkgVersion
3040
+ }) {
3041
+ if (outputKind === 'json') {
2991
3042
  logger.logger.log(JSON.stringify(data, undefined, 2));
3043
+ return;
3044
+ }
3045
+ if (outputKind === 'markdown') {
3046
+ logger.logger.log(`\n# Package report for ${pkgName}\n`);
3047
+ logger.logger.log('Package report card:\n');
2992
3048
  } else {
2993
- logger.logger.log('\nPackage report card:');
2994
- const scoreResult = {
2995
- 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
2996
- Maintenance: Math.floor(score.maintenance.score * 100),
2997
- Quality: Math.floor(score.quality.score * 100),
2998
- Vulnerabilities: Math.floor(score.vulnerability.score * 100),
2999
- License: Math.floor(score.license.score * 100)
3000
- };
3001
- Object.entries(scoreResult).map(score => logger.logger.log(`- ${score[0]}: ${formatScore(score[1])}`));
3002
- logger.logger.log('\n');
3003
- if (objectSome(severityCount)) {
3004
- spinner[strict ? 'error' : 'success'](`Package has these issues: ${formatSeverityCount(severityCount)}`);
3005
- formatPackageIssuesDetails(data, outputMarkdown);
3006
- } else {
3007
- spinner.successAndStop('Package has no issues');
3008
- }
3009
- const format = new index.ColorOrMarkdown(!!outputMarkdown);
3010
- const url = index.getSocketDevPackageOverviewUrl(NPM$c, pkgName, pkgVersion);
3011
- logger.logger.log('\n');
3012
- if (pkgVersion === 'latest') {
3013
- logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, {
3014
- fallbackToUrl: true
3015
- })}`);
3016
- } else {
3017
- logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, {
3018
- fallbackToUrl: true
3019
- })}`);
3020
- }
3021
- if (!outputMarkdown) {
3022
- logger.logger.log(colors.dim(`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`));
3049
+ logger.logger.log(`\nPackage report card for ${pkgName}:\n`);
3050
+ }
3051
+ const scoreResult = {
3052
+ 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
3053
+ Maintenance: Math.floor(score.maintenance.score * 100),
3054
+ Quality: Math.floor(score.quality.score * 100),
3055
+ Vulnerabilities: Math.floor(score.vulnerability.score * 100),
3056
+ License: Math.floor(score.license.score * 100)
3057
+ };
3058
+ Object.entries(scoreResult).map(score => logger.logger.log(`- ${score[0]}: ${formatScore(score[1])}`));
3059
+ logger.logger.log('\n');
3060
+ if (objectSome(severityCount)) {
3061
+ if (outputKind === 'markdown') {
3062
+ logger.logger.log('# Issues\n');
3023
3063
  }
3064
+ logger.logger.log(`Package has these issues: ${formatSeverityCount(severityCount)}\n`);
3065
+ formatPackageIssuesDetails(data, outputKind === 'markdown');
3066
+ } else {
3067
+ logger.logger.log('Package has no issues');
3024
3068
  }
3025
- if (strict && objectSome(severityCount)) {
3026
- process$1.exit(1);
3069
+ const format = new index.ColorOrMarkdown(outputKind === 'markdown');
3070
+ const url = index.getSocketDevPackageOverviewUrl(NPM$c, pkgName, pkgVersion);
3071
+ logger.logger.log('\n');
3072
+ if (pkgVersion === 'latest') {
3073
+ logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, {
3074
+ fallbackToUrl: true
3075
+ })}`);
3076
+ } else {
3077
+ logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, {
3078
+ fallbackToUrl: true
3079
+ })}`);
3080
+ }
3081
+ if (outputKind !== 'markdown') {
3082
+ logger.logger.log(colors.dim(`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`));
3083
+ } else {
3084
+ logger.logger.log('');
3027
3085
  }
3028
3086
  }
3029
3087
  function formatPackageIssuesDetails(packageData, outputMarkdown) {
@@ -3044,7 +3102,7 @@ function formatPackageIssuesDetails(packageData, outputMarkdown) {
3044
3102
  }
3045
3103
  return acc;
3046
3104
  }, {});
3047
- const format = new index.ColorOrMarkdown(!!outputMarkdown);
3105
+ const format = new index.ColorOrMarkdown(outputMarkdown);
3048
3106
  for (const issue of Object.keys(uniqueIssues)) {
3049
3107
  const issueWithLink = format.hyperlink(`${uniqueIssues[issue]?.label}`, index.getSocketDevAlertUrl(issue), {
3050
3108
  fallbackToUrl: true
@@ -3068,27 +3126,23 @@ function formatScore(score) {
3068
3126
  async function getPackageInfo({
3069
3127
  commandName,
3070
3128
  includeAllIssues,
3071
- outputJson,
3072
- outputMarkdown,
3129
+ outputKind,
3073
3130
  pkgName,
3074
3131
  pkgVersion,
3075
3132
  strict
3076
3133
  }) {
3077
- // Lazily access constants.spinner.
3078
- const {
3079
- spinner
3080
- } = constants;
3081
- spinner.start(pkgVersion === 'latest' ? `Looking up data for the latest version of ${pkgName}` : `Looking up data for version ${pkgVersion} of ${pkgName}`);
3082
- const packageData = await fetchPackageInfo(pkgName, pkgVersion, includeAllIssues, spinner);
3134
+ const packageData = await fetchPackageInfo(pkgName, pkgVersion, includeAllIssues);
3083
3135
  if (packageData) {
3084
3136
  formatPackageInfo(packageData, {
3085
3137
  name: commandName,
3086
- outputJson,
3087
- outputMarkdown,
3138
+ outputKind,
3088
3139
  pkgName,
3089
- pkgVersion,
3090
- strict
3091
- }, spinner);
3140
+ pkgVersion
3141
+ });
3142
+ if (strict && objectSome(packageData.severityCount)) {
3143
+ // Let NodeJS exit gracefully but with exit(1)
3144
+ process$1.exitCode = 1;
3145
+ }
3092
3146
  }
3093
3147
  }
3094
3148
 
@@ -3130,6 +3184,12 @@ async function run$s(argv, importMeta, {
3130
3184
  importMeta,
3131
3185
  parentName
3132
3186
  });
3187
+ const {
3188
+ all,
3189
+ json,
3190
+ markdown,
3191
+ strict
3192
+ } = cli.flags;
3133
3193
  const [rawPkgName = ''] = cli.input;
3134
3194
  if (!rawPkgName || cli.input.length > 1) {
3135
3195
  // Use exit status of 2 to indicate incorrect usage, generally invalid
@@ -3150,12 +3210,11 @@ async function run$s(argv, importMeta, {
3150
3210
  }
3151
3211
  await getPackageInfo({
3152
3212
  commandName: `${parentName} ${config$s.commandName}`,
3153
- includeAllIssues: Boolean(cli.flags['all']),
3154
- outputJson: Boolean(cli.flags['json']),
3155
- outputMarkdown: Boolean(cli.flags['markdown']),
3213
+ includeAllIssues: Boolean(all),
3214
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
3156
3215
  pkgName,
3157
3216
  pkgVersion,
3158
- strict: Boolean(cli.flags['strict'])
3217
+ strict: Boolean(strict)
3159
3218
  });
3160
3219
  }
3161
3220
 
@@ -3188,7 +3247,7 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3188
3247
  throw new index.AuthError();
3189
3248
  }
3190
3249
  orgs = result.data;
3191
- spinner.success('API key verified');
3250
+ spinner.successAndStop('API key verified');
3192
3251
  } catch {
3193
3252
  spinner.errorAndStop('Invalid API key');
3194
3253
  return;
@@ -3229,8 +3288,10 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3229
3288
  const oldToken = index.getSetting('apiToken');
3230
3289
  try {
3231
3290
  applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy);
3291
+ spinner.start();
3232
3292
  spinner.successAndStop(`API credentials ${oldToken ? 'updated' : 'set'}`);
3233
3293
  } catch {
3294
+ spinner.start();
3234
3295
  spinner.errorAndStop(`API login failed`);
3235
3296
  }
3236
3297
  }
@@ -3365,7 +3426,6 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3365
3426
  logger.logger.log(`- src dir: \`${target}\``);
3366
3427
  logger.logger.groupEnd();
3367
3428
  }
3368
- spinner.start(`Converting gradle to maven from \`${bin}\` on \`${target}\`...`);
3369
3429
  try {
3370
3430
  // Run sbt with the init script we provide which should yield zero or more pom files.
3371
3431
  // We have to figure out where to store those pom files such that we can upload them and predict them through the GitHub API.
@@ -3375,8 +3435,9 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3375
3435
  const initLocation = path.join(constants.rootDistPath, 'init.gradle');
3376
3436
  const commandArgs = ['--init-script', initLocation, ...gradleOpts, 'pom'];
3377
3437
  if (verbose) {
3378
- spinner.log('[VERBOSE] Executing:', bin, commandArgs);
3438
+ logger.logger.log('[VERBOSE] Executing:', bin, commandArgs);
3379
3439
  }
3440
+ spinner.start(`Converting gradle to maven from \`${bin}\` on \`${target}\`...`);
3380
3441
  const output = await spawn.spawn(bin, commandArgs, {
3381
3442
  cwd: target || '.'
3382
3443
  });
@@ -3394,7 +3455,8 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3394
3455
  logger.logger.error(output.stderr);
3395
3456
  logger.logger.groupEnd();
3396
3457
  }
3397
- process.exit(1);
3458
+ process.exitCode = 1;
3459
+ return;
3398
3460
  }
3399
3461
  logger.logger.success('Executed gradle successfully');
3400
3462
  logger.logger.log('Reported exports:');
@@ -3431,14 +3493,15 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3431
3493
  // spinner.successAndStop(`OK. File should be available in \`${out}\``)
3432
3494
  // }
3433
3495
  } catch (e) {
3434
- spinner.stop();
3435
- logger.logger.error('There was an unexpected error while running this' + (verbose ? '' : ' (use --verbose for details)'));
3496
+ spinner.errorAndStop('There was an unexpected error while running this' + (verbose ? '' : ' (use --verbose for details)'));
3436
3497
  if (verbose) {
3437
3498
  logger.logger.group('[VERBOSE] error:');
3438
3499
  logger.logger.log(e);
3439
3500
  logger.logger.groupEnd();
3440
3501
  }
3441
- process.exit(1);
3502
+ process.exitCode = 1;
3503
+ } finally {
3504
+ spinner.stop();
3442
3505
  }
3443
3506
  }
3444
3507
 
@@ -3606,8 +3669,9 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3606
3669
  // logger.log(`- dst dir: \`${out}\``)
3607
3670
  logger.logger.groupEnd();
3608
3671
  }
3609
- spinner.start(`Converting sbt to maven from \`${bin}\` on \`${target}\`...`);
3610
3672
  try {
3673
+ spinner.start(`Converting sbt to maven from \`${bin}\` on \`${target}\`...`);
3674
+
3611
3675
  // Run sbt with the init script we provide which should yield zero or more
3612
3676
  // pom files. We have to figure out where to store those pom files such that
3613
3677
  // we can upload them and predict them through the GitHub API. We could do a
@@ -3630,7 +3694,8 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3630
3694
  logger.logger.error(output.stderr);
3631
3695
  logger.logger.groupEnd();
3632
3696
  }
3633
- process.exit(1);
3697
+ process.exitCode = 1;
3698
+ return;
3634
3699
  }
3635
3700
  const poms = [];
3636
3701
  output.stdout.replace(/Wrote (.*?.pom)\n/g, (_all, fn) => {
@@ -3639,7 +3704,8 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3639
3704
  });
3640
3705
  if (!poms.length) {
3641
3706
  logger.logger.error('There were no errors from sbt but it seems to not have generated any poms either');
3642
- process.exit(1);
3707
+ process.exitCode = 1;
3708
+ return;
3643
3709
  }
3644
3710
  // Move the pom file to ...? initial cwd? loc will be an absolute path, or dump to stdout
3645
3711
  // TODO: what to do with multiple output files? Do we want to dump them to stdout? Raw or with separators or ?
@@ -3653,7 +3719,8 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3653
3719
  logger.logger.error('Requested out target was stdout but there are multiple generated files');
3654
3720
  poms.forEach(fn => logger.logger.error('-', fn));
3655
3721
  logger.logger.error('Exiting now...');
3656
- process.exit(1);
3722
+ process.exitCode = 1;
3723
+ return;
3657
3724
  } else {
3658
3725
  // if (verbose) {
3659
3726
  // logger.log(
@@ -3669,14 +3736,15 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3669
3736
  logger.logger.success(`OK`);
3670
3737
  }
3671
3738
  } catch (e) {
3672
- spinner.stop();
3673
- logger.logger.error('There was an unexpected error while running this' + (verbose ? '' : ' (use --verbose for details)'));
3739
+ spinner?.errorAndStop('There was an unexpected error while running this' + (verbose ? '' : ' (use --verbose for details)'));
3674
3740
  if (verbose) {
3675
3741
  logger.logger.group('[VERBOSE] error:');
3676
3742
  logger.logger.log(e);
3677
3743
  logger.logger.groupEnd();
3678
3744
  }
3679
- process.exit(1);
3745
+ process.exitCode = 1;
3746
+ } finally {
3747
+ spinner.stop();
3680
3748
  }
3681
3749
  }
3682
3750
 
@@ -5484,13 +5552,10 @@ const config$f = {
5484
5552
  description: `Temporarily disable the Socket ${NPM} wrapper`,
5485
5553
  hidden: false,
5486
5554
  flags: {},
5487
- help: (command, config) => `
5555
+ help: command => `
5488
5556
  Usage
5489
5557
  $ ${command} <command>
5490
5558
 
5491
- Options
5492
- ${getFlagListOutput(config.flags, 6)}
5493
-
5494
5559
  Examples
5495
5560
  $ ${command} install
5496
5561
  `
@@ -5541,13 +5606,10 @@ const config$e = {
5541
5606
  description: `Temporarily disable the Socket ${NPX} wrapper`,
5542
5607
  hidden: false,
5543
5608
  flags: {},
5544
- help: (command, config) => `
5609
+ help: command => `
5545
5610
  Usage
5546
5611
  $ ${command} <command>
5547
5612
 
5548
- Options
5549
- ${getFlagListOutput(config.flags, 6)}
5550
-
5551
5613
  Examples
5552
5614
  $ ${command} install
5553
5615
  `
@@ -5594,10 +5656,8 @@ async function createReport(socketConfig, inputPaths, {
5594
5656
  cause
5595
5657
  });
5596
5658
  });
5597
- const packagePaths = await npmPaths.getPackageFiles(cwd, inputPaths, socketConfig, supportedFiles);
5598
- const {
5599
- length: packagePathsCount
5600
- } = packagePaths;
5659
+ const packagePaths = await npmPaths.getPackageFilesFullScans(cwd, inputPaths, supportedFiles, socketConfig);
5660
+ const packagePathsCount = packagePaths.length;
5601
5661
  if (packagePathsCount && debug.isDebug()) {
5602
5662
  for (const pkgPath of packagePaths) {
5603
5663
  debug.debugLog(`Uploading: ${pkgPath}`);
@@ -5718,7 +5778,7 @@ const {
5718
5778
  } = constants;
5719
5779
  const config$d = {
5720
5780
  commandName: 'create',
5721
- description: 'Create a project report',
5781
+ description: '[Deprecated] Create a project report',
5722
5782
  hidden: false,
5723
5783
  flags: {
5724
5784
  ...commonFlags,
@@ -5736,27 +5796,9 @@ const config$d = {
5736
5796
  description: 'Will wait for and return the created report'
5737
5797
  }
5738
5798
  },
5739
- help: (command, config) => `
5740
- Usage
5741
- $ ${command} <paths-to-package-folders-and-files>
5742
-
5743
- Uploads the specified "package.json" and lock files for JavaScript, Python, and Go dependency manifests.
5744
- If any folder is specified, the ones found in there recursively are uploaded.
5745
-
5746
- Supports globbing such as "**/package.json", "**/requirements.txt", "**/pyproject.toml", and "**/go.mod".
5747
-
5748
- Ignores any file specified in your project's ".gitignore", your project's
5749
- "socket.yml" file's "projectIgnorePaths" and also has a sensible set of
5750
- default ignores from the "ignore-by-default" module.
5751
-
5752
- Options
5753
- ${getFlagListOutput(config.flags, 6)}
5754
-
5755
- Examples
5756
- $ ${command} .
5757
- $ ${command} '**/package.json'
5758
- $ ${command} /path/to/a/package.json /path/to/another/package.json
5759
- $ ${command} . --view --json
5799
+ help: () => `
5800
+ This command is deprecated in favor of \`socket scan create\`.
5801
+ It will be removed in the next major release of the CLI.
5760
5802
  `
5761
5803
  };
5762
5804
  const cmdReportCreate = {
@@ -5821,22 +5863,16 @@ const {
5821
5863
  } = constants;
5822
5864
  const config$c = {
5823
5865
  commandName: 'view',
5824
- description: 'View a project report',
5866
+ description: '[Deprecated] View a project report',
5825
5867
  hidden: false,
5826
5868
  flags: {
5827
5869
  ...commonFlags,
5828
5870
  ...outputFlags,
5829
5871
  ...validationFlags
5830
5872
  },
5831
- help: (command, config) => `
5832
- Usage
5833
- $ ${command} <report-identifier>
5834
-
5835
- Options
5836
- ${getFlagListOutput(config.flags, 6)}
5837
-
5838
- Examples
5839
- $ ${command} QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
5873
+ help: () => `
5874
+ This command is deprecated in favor of \`socket scan view\`.
5875
+ It will be removed in the next major release of the CLI.
5840
5876
  `
5841
5877
  };
5842
5878
  const cmdReportView = {
@@ -5884,6 +5920,8 @@ async function run$c(argv, importMeta, {
5884
5920
  const description$2 = '[Deprecated] Project report related commands';
5885
5921
  const cmdReport = {
5886
5922
  description: description$2,
5923
+ hidden: true,
5924
+ // Deprecated in favor of `scan`
5887
5925
  async run(argv, importMeta, {
5888
5926
  parentName
5889
5927
  }) {
@@ -6708,39 +6746,46 @@ async function createFullScan({
6708
6746
  targets = received ?? [];
6709
6747
  updatedInput = true;
6710
6748
  }
6711
- const packagePaths = await npmPaths.getPackageFilesFullScans(cwd, targets, supportedFiles);
6749
+
6750
+ // // TODO: we'll probably use socket.json or something else soon...
6751
+ // const absoluteConfigPath = path.join(cwd, 'socket.yml')
6752
+ // const socketConfig = await getSocketConfig(absoluteConfigPath)
6753
+
6754
+ const packagePaths = await npmPaths.getPackageFilesFullScans(cwd, targets, supportedFiles
6755
+ // socketConfig
6756
+ );
6712
6757
 
6713
6758
  // We're going to need an api token to suggest data because those suggestions
6714
6759
  // must come from data we already know. Don't error on missing api token yet.
6715
6760
  // If the api-token is not set, ignore it for the sake of suggestions.
6716
6761
  const apiToken = index.getDefaultToken();
6717
- if (apiToken && !orgSlug) {
6718
- const suggestion = await suggestOrgSlug(socketSdk);
6719
- if (suggestion) orgSlug = suggestion;
6720
- updatedInput = true;
6721
- }
6722
6762
 
6723
6763
  // If the current cwd is unknown and is used as a repo slug anyways, we will
6724
6764
  // first need to register the slug before we can use it.
6725
6765
  let repoDefaultBranch = '';
6726
-
6727
- // (Don't bother asking for the rest if we didn't get an org slug above)
6728
- if (apiToken && orgSlug && !repoName) {
6729
- const suggestion = await suggestRepoSlug(socketSdk, orgSlug);
6730
- if (suggestion) {
6731
- ({
6732
- defaultBranch: repoDefaultBranch,
6733
- slug: repoName
6734
- } = suggestion);
6766
+ if (apiToken) {
6767
+ if (!orgSlug) {
6768
+ const suggestion = await suggestOrgSlug(socketSdk);
6769
+ if (suggestion) orgSlug = suggestion;
6770
+ updatedInput = true;
6771
+ }
6772
+
6773
+ // (Don't bother asking for the rest if we didn't get an org slug above)
6774
+ if (orgSlug && !repoName) {
6775
+ const suggestion = await suggestRepoSlug(socketSdk, orgSlug);
6776
+ if (suggestion) {
6777
+ repoDefaultBranch = suggestion.defaultBranch;
6778
+ repoName = suggestion.slug;
6779
+ }
6780
+ updatedInput = true;
6735
6781
  }
6736
- updatedInput = true;
6737
- }
6738
6782
 
6739
- // (Don't bother asking for the rest if we didn't get an org/repo above)
6740
- if (apiToken && orgSlug && repoName && !branchName) {
6741
- const suggestion = await suggestBranchSlug(repoDefaultBranch);
6742
- if (suggestion) branchName = suggestion;
6743
- updatedInput = true;
6783
+ // (Don't bother asking for the rest if we didn't get an org/repo above)
6784
+ if (orgSlug && repoName && !branchName) {
6785
+ const suggestion = await suggestBranchSlug(repoDefaultBranch);
6786
+ if (suggestion) branchName = suggestion;
6787
+ updatedInput = true;
6788
+ }
6744
6789
  }
6745
6790
  if (!orgSlug || !repoName || !branchName || !packagePaths.length) {
6746
6791
  // Use exit status of 2 to indicate incorrect usage, generally invalid
@@ -6775,7 +6820,7 @@ async function createFullScan({
6775
6820
  logger.logger.log('[ReadOnly] Bailing now');
6776
6821
  return;
6777
6822
  }
6778
- spinner.start('Creating a scan...');
6823
+ spinner.start(`Creating a scan with ${packagePaths.length} packages...`);
6779
6824
  const result = await handleApiCall(socketSdk.createOrgFullScan(orgSlug, {
6780
6825
  repo: repoName,
6781
6826
  branch: branchName,
@@ -6875,13 +6920,29 @@ const config$6 = {
6875
6920
  shortFlag: 't',
6876
6921
  default: false,
6877
6922
  description: 'Set the visibility (true/false) of the scan in your dashboard'
6923
+ },
6924
+ view: {
6925
+ type: 'boolean',
6926
+ shortFlag: 'v',
6927
+ default: true,
6928
+ description: 'Will wait for and return the created report. Use --no-view to disable.'
6878
6929
  }
6879
6930
  },
6931
+ // TODO: your project's "socket.yml" file's "projectIgnorePaths"
6880
6932
  help: (command, config) => `
6881
6933
  Usage
6882
6934
  $ ${command} [...options] <org> <TARGET> [TARGET...]
6883
6935
 
6884
- Where TARGET is a FILE or DIR that _must_ be inside the CWD.
6936
+ Uploads the specified "package.json" and lock files for JavaScript, Python,
6937
+ Go, Scala, Gradle, and Kotlin dependency manifests.
6938
+ If any folder is specified, the ones found in there recursively are uploaded.
6939
+
6940
+ Supports globbing such as "**/package.json", "**/requirements.txt", etc.
6941
+
6942
+ Ignores any file specified in your project's ".gitignore" and also has a
6943
+ sensible set of default ignores from the "ignore-by-default" module.
6944
+
6945
+ TARGET should be a FILE or DIR that _must_ be inside the CWD.
6885
6946
 
6886
6947
  When a FILE is given only that FILE is targeted. Otherwise any eligible
6887
6948
  files in the given DIR will be considered.
@@ -6913,7 +6974,8 @@ async function run$6(argv, importMeta, {
6913
6974
  branch: branchName,
6914
6975
  repo: repoName
6915
6976
  } = cli.flags;
6916
- const apiToken = index.getDefaultToken();
6977
+ const apiToken = index.getDefaultToken(); // This checks if we _can_ suggest anything
6978
+
6917
6979
  if (!apiToken && (!orgSlug || !repoName || !branchName || !targets.length)) {
6918
6980
  // Without api token we cannot recover because we can't request more info
6919
6981
  // from the server, to match and help with the current cwd/git status.
@@ -6959,7 +7021,14 @@ async function run$6(argv, importMeta, {
6959
7021
  });
6960
7022
  }
6961
7023
 
6962
- async function deleteOrgFullScan(orgSlug, fullScanId, apiToken) {
7024
+ async function deleteOrgFullScan(orgSlug, fullScanId) {
7025
+ const apiToken = index.getDefaultToken();
7026
+ if (!apiToken) {
7027
+ throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7028
+ }
7029
+ await deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken);
7030
+ }
7031
+ async function deleteOrgFullScanWithToken(orgSlug, fullScanId, apiToken) {
6963
7032
  // Lazily access constants.spinner.
6964
7033
  const {
6965
7034
  spinner
@@ -7027,26 +7096,66 @@ async function run$5(argv, importMeta, {
7027
7096
  logger.logger.log(DRY_RUN_BAIL_TEXT$5);
7028
7097
  return;
7029
7098
  }
7099
+ await deleteOrgFullScan(orgSlug, fullScanId);
7100
+ }
7101
+
7102
+ // @ts-ignore
7103
+ async function listFullScans({
7104
+ direction,
7105
+ from_time,
7106
+ orgSlug,
7107
+ outputKind,
7108
+ page,
7109
+ per_page,
7110
+ sort
7111
+ }) {
7030
7112
  const apiToken = index.getDefaultToken();
7031
7113
  if (!apiToken) {
7032
7114
  throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7033
7115
  }
7034
- await deleteOrgFullScan(orgSlug, fullScanId, apiToken);
7116
+ await listFullScansWithToken({
7117
+ apiToken,
7118
+ direction,
7119
+ from_time,
7120
+ orgSlug,
7121
+ outputKind,
7122
+ page,
7123
+ per_page,
7124
+ sort
7125
+ });
7035
7126
  }
7036
-
7037
- // @ts-ignore
7038
- async function listFullScans(orgSlug, input, apiToken) {
7127
+ async function listFullScansWithToken({
7128
+ apiToken,
7129
+ direction,
7130
+ from_time,
7131
+ orgSlug,
7132
+ outputKind,
7133
+ page,
7134
+ per_page,
7135
+ sort
7136
+ }) {
7039
7137
  // Lazily access constants.spinner.
7040
7138
  const {
7041
7139
  spinner
7042
7140
  } = constants;
7043
- spinner.start('Listing scans...');
7141
+ spinner.start('Fetching list of scans...');
7044
7142
  const socketSdk = await index.setupSdk(apiToken);
7045
- const result = await handleApiCall(socketSdk.getOrgFullScanList(orgSlug, input), 'Listing scans');
7143
+ const result = await handleApiCall(socketSdk.getOrgFullScanList(orgSlug, {
7144
+ sort,
7145
+ direction,
7146
+ per_page,
7147
+ page,
7148
+ from: from_time
7149
+ }), 'Listing scans');
7046
7150
  if (!result.success) {
7047
7151
  handleUnsuccessfulApiResponse('getOrgFullScanList', result, spinner);
7048
7152
  return;
7049
7153
  }
7154
+ spinner.stop(`Fetch complete`);
7155
+ if (outputKind === 'json') {
7156
+ logger.logger.log(result.data);
7157
+ return;
7158
+ }
7050
7159
  const options = {
7051
7160
  columns: [{
7052
7161
  field: 'id',
@@ -7074,7 +7183,6 @@ async function listFullScans(orgSlug, input, apiToken) {
7074
7183
  branch: d.branch
7075
7184
  };
7076
7185
  });
7077
- spinner.stop(`Listing scans for: ${orgSlug}`);
7078
7186
  logger.logger.log(chalkTable(options, formattedResults));
7079
7187
  }
7080
7188
 
@@ -7165,39 +7273,55 @@ async function run$4(argv, importMeta, {
7165
7273
  logger.logger.log(DRY_RUN_BAIL_TEXT$4);
7166
7274
  return;
7167
7275
  }
7276
+ await listFullScans({
7277
+ direction: String(cli.flags['direction'] || ''),
7278
+ from_time: String(cli.flags['fromTime'] || ''),
7279
+ orgSlug,
7280
+ outputKind: cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print',
7281
+ page: Number(cli.flags['page'] || 1),
7282
+ per_page: Number(cli.flags['perPage'] || 30),
7283
+ sort: String(cli.flags['sort'] || '')
7284
+ });
7285
+ }
7286
+
7287
+ async function getOrgScanMetadata(orgSlug, scanId, outputKind) {
7168
7288
  const apiToken = index.getDefaultToken();
7169
7289
  if (!apiToken) {
7170
7290
  throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7171
7291
  }
7172
- await listFullScans(orgSlug,
7173
- // TODO: refine this object to what we need
7174
- {
7175
- outputJson: cli.flags['json'],
7176
- outputMarkdown: cli.flags['markdown'],
7177
- orgSlug,
7178
- sort: cli.flags['sort'],
7179
- direction: cli.flags['direction'],
7180
- per_page: cli.flags['perPage'],
7181
- page: cli.flags['page'],
7182
- from_time: cli.flags['fromTime'],
7183
- until_time: cli.flags['untilTime']
7184
- }, apiToken);
7292
+ await getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind);
7185
7293
  }
7186
-
7187
- async function getOrgScanMetadata(orgSlug, scanId, apiToken) {
7294
+ async function getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind) {
7188
7295
  // Lazily access constants.spinner.
7189
7296
  const {
7190
7297
  spinner
7191
7298
  } = constants;
7192
- spinner.start("Getting scan's metadata...");
7299
+ spinner.start('Fetching meta data for a full scan...');
7193
7300
  const socketSdk = await index.setupSdk(apiToken);
7194
7301
  const result = await handleApiCall(socketSdk.getOrgFullScanMetadata(orgSlug, scanId), 'Listing scans');
7195
7302
  if (!result.success) {
7196
7303
  handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result, spinner);
7197
7304
  return;
7198
7305
  }
7199
- spinner.stop('Scan metadata:');
7200
- logger.logger.log(result.data);
7306
+ spinner?.successAndStop('Fetched the meta data\n');
7307
+ if (outputKind === 'json') {
7308
+ logger.logger.log(result.data);
7309
+ } else {
7310
+ // Markdown = print
7311
+ if (outputKind === 'markdown') {
7312
+ logger.logger.log('# Scan meta data\n');
7313
+ }
7314
+ logger.logger.log(`Scan ID: ${scanId}\n`);
7315
+ for (const [key, value] of Object.entries(result.data)) {
7316
+ if (['id', 'updated_at', 'organization_id', 'repository_id', 'commit_hash', 'html_report_url'].includes(key)) continue;
7317
+ logger.logger.log(`- ${key}:`, value);
7318
+ }
7319
+ if (outputKind === 'markdown') {
7320
+ logger.logger.log(`\nYou can view this report at: [${result.data.html_report_url}](${result.data.html_report_url})\n`);
7321
+ } else {
7322
+ logger.logger.log(`\nYou can view this report at: ${result.data.html_report_url}]\n`);
7323
+ }
7324
+ }
7201
7325
  }
7202
7326
 
7203
7327
  const {
@@ -7253,35 +7377,106 @@ async function run$3(argv, importMeta, {
7253
7377
  logger.logger.log(DRY_RUN_BAIL_TEXT$3);
7254
7378
  return;
7255
7379
  }
7380
+ await getOrgScanMetadata(orgSlug, fullScanId, cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print');
7381
+ }
7382
+
7383
+ async function streamFullScan(orgSlug, fullScanId, file) {
7384
+ // Lazily access constants.spinner.
7385
+ const {
7386
+ spinner
7387
+ } = constants;
7256
7388
  const apiToken = index.getDefaultToken();
7257
7389
  if (!apiToken) {
7258
7390
  throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7259
7391
  }
7260
- await getOrgScanMetadata(orgSlug, fullScanId, apiToken);
7392
+ spinner.start('Fetching scan...');
7393
+ const socketSdk = await index.setupSdk(apiToken);
7394
+ const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Fetching a scan');
7395
+ if (!data?.success) {
7396
+ handleUnsuccessfulApiResponse('getOrgFullScan', data, spinner);
7397
+ return;
7398
+ }
7399
+ spinner?.successAndStop(file ? `Full scan details written to ${file}` : 'stdout');
7400
+ return data;
7261
7401
  }
7262
7402
 
7263
- async function getFullScan(orgSlug, fullScanId, file, apiToken) {
7403
+ async function getFullScan(orgSlug, fullScanId) {
7264
7404
  // Lazily access constants.spinner.
7265
7405
  const {
7266
7406
  spinner
7267
7407
  } = constants;
7268
- spinner.start('Streaming scan...');
7269
- const socketSdk = await index.setupSdk(apiToken);
7270
- const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Streaming a scan');
7271
- if (data?.success) {
7272
- spinner.stop(file ? `Full scan details written to ${file}` : '');
7273
- } else {
7274
- handleUnsuccessfulApiResponse('getOrgFullScan', data, spinner);
7408
+ const apiToken = index.getDefaultToken();
7409
+ if (!apiToken) {
7410
+ throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7411
+ }
7412
+ spinner.start('Fetching full-scan...');
7413
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/${encodeURIComponent(fullScanId)}`, apiToken);
7414
+ spinner.stop('Fetch complete.');
7415
+ if (!response.ok) {
7416
+ const err = await handleAPIError(response.status);
7417
+ logger.logger.error(`${colors.bgRed(colors.white(response.statusText))}: Fetch error: ${err}`);
7418
+ return;
7275
7419
  }
7420
+
7421
+ // This is nd-json; each line is a json object
7422
+ const jsons = await response.text();
7423
+ const lines = jsons.split('\n').filter(Boolean);
7424
+ const data = lines.map(line => {
7425
+ try {
7426
+ return JSON.parse(line);
7427
+ } catch {
7428
+ console.error('At least one line item was returned that could not be parsed as JSON...');
7429
+ return {};
7430
+ }
7431
+ });
7276
7432
  return data;
7277
7433
  }
7278
7434
 
7435
+ async function viewFullScan(orgSlug, fullScanId, filePath) {
7436
+ const artifacts = await getFullScan(orgSlug, fullScanId);
7437
+ if (!artifacts) return;
7438
+ const display = artifacts.map(art => {
7439
+ const author = Array.isArray(art.author) ? `${art.author[0]}${art.author.length > 1 ? ' et.al.' : ''}` : art.author;
7440
+ return {
7441
+ type: art.type,
7442
+ name: art.name,
7443
+ version: art.version,
7444
+ author,
7445
+ score: JSON.stringify(art.score)
7446
+ };
7447
+ });
7448
+ const md = mdTable(display, ['type', 'version', 'name', 'author', 'score']);
7449
+ const report = `
7450
+ # Scan Details
7451
+
7452
+ These are the artifacts and their scores found.
7453
+
7454
+ Sscan ID: ${fullScanId}
7455
+
7456
+ ${md}
7457
+
7458
+ View this report at: https://socket.dev/dashboard/org/${orgSlug}/sbom/${fullScanId}
7459
+ `.trim() + '\n';
7460
+ if (filePath && filePath !== '-') {
7461
+ try {
7462
+ await fs$1.writeFile(filePath, report, 'utf8');
7463
+ logger.logger.log(`Data successfully written to ${filePath}`);
7464
+ } catch (e) {
7465
+ logger.logger.error('There was an error trying to write the json to disk');
7466
+ logger.logger.error(e);
7467
+ process.exitCode = 1;
7468
+ }
7469
+ } else {
7470
+ logger.logger.log(report);
7471
+ }
7472
+ }
7473
+
7279
7474
  const {
7280
7475
  DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$2
7281
7476
  } = constants;
7282
7477
  const config$2 = {
7283
- commandName: 'stream',
7284
- description: 'Stream the output of a scan',
7478
+ commandName: 'view',
7479
+ description: 'View the raw results of a scan',
7285
7480
  hidden: false,
7286
7481
  flags: {
7287
7482
  ...commonFlags,
@@ -7300,7 +7495,7 @@ const config$2 = {
7300
7495
  $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt
7301
7496
  `
7302
7497
  };
7303
- const cmdScanStream = {
7498
+ const cmdScanView = {
7304
7499
  description: config$2.description,
7305
7500
  hidden: config$2.hidden,
7306
7501
  run: run$2
@@ -7333,14 +7528,14 @@ async function run$2(argv, importMeta, {
7333
7528
  logger.logger.log(DRY_RUN_BAIL_TEXT$2);
7334
7529
  return;
7335
7530
  }
7336
- const apiToken = index.getDefaultToken();
7337
- if (!apiToken) {
7338
- throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.');
7531
+ if (cli.flags['json']) {
7532
+ await streamFullScan(orgSlug, fullScanId, file);
7533
+ } else {
7534
+ await viewFullScan(orgSlug, fullScanId, file);
7339
7535
  }
7340
- await getFullScan(orgSlug, fullScanId, file, apiToken);
7341
7536
  }
7342
7537
 
7343
- const description = 'Scans related commands';
7538
+ const description = 'Full Scan related commands';
7344
7539
  const cmdScan = {
7345
7540
  description,
7346
7541
  async run(argv, importMeta, {
@@ -7348,11 +7543,19 @@ const cmdScan = {
7348
7543
  }) {
7349
7544
  await meowWithSubcommands({
7350
7545
  create: cmdScanCreate,
7351
- stream: cmdScanStream,
7352
7546
  list: cmdScanList,
7353
7547
  del: cmdScanDel,
7354
- metadata: cmdScanMetadata
7548
+ metadata: cmdScanMetadata,
7549
+ view: cmdScanView
7355
7550
  }, {
7551
+ aliases: {
7552
+ // Backwards compat. TODO: Drop next major bump
7553
+ stream: {
7554
+ description: cmdScanView.description,
7555
+ hidden: true,
7556
+ argv: ['view'] // Original args will be appended (!)
7557
+ }
7558
+ },
7356
7559
  argv,
7357
7560
  description,
7358
7561
  importMeta,
@@ -7791,5 +7994,5 @@ void (async () => {
7791
7994
  await index.captureException(e);
7792
7995
  }
7793
7996
  })();
7794
- //# debugId=e6a98767-74ff-4fd4-a15e-82ce65af55f8
7997
+ //# debugId=4fe0e5e5-54cb-444b-88dc-36bf76ff766a
7795
7998
  //# sourceMappingURL=cli.js.map