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