@socketsecurity/cli-with-sentry 0.14.53 → 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.
@@ -27,16 +27,18 @@ var index = require('./index.js');
27
27
  var constants = require('./constants.js');
28
28
  var objects = require('@socketsecurity/registry/lib/objects');
29
29
  var regexps = require('@socketsecurity/registry/lib/regexps');
30
+ var commonTags = _socketInterop(require('common-tags'));
30
31
  var fs$1 = require('node:fs/promises');
31
32
  var ScreenWidget = _socketInterop(require('blessed/lib/widgets/screen'));
32
33
  var contrib = _socketInterop(require('blessed-contrib'));
33
34
  var prompts = require('@socketsecurity/registry/lib/prompts');
34
35
  var yargsParse = _socketInterop(require('yargs-parser'));
35
36
  var words = require('@socketsecurity/registry/lib/words');
36
- var npm = require('@socketsecurity/registry/lib/npm');
37
+ var shadowBin = require('./shadow-bin.js');
37
38
  var chalkTable = _socketInterop(require('chalk-table'));
38
39
  var require$$0$1 = require('node:util');
39
40
  var registry = require('@socketsecurity/registry');
41
+ var npm = require('@socketsecurity/registry/lib/npm');
40
42
  var packages = require('@socketsecurity/registry/lib/packages');
41
43
  var registryConstants = require('@socketsecurity/registry/lib/constants');
42
44
  var isInteractive = require('@socketregistry/is-interactive/index.cjs');
@@ -1515,7 +1517,7 @@ function meowOrExit({
1515
1517
  }
1516
1518
  function getAsciiHeader(command) {
1517
1519
  const cliVersion = // The '@rollup/plugin-replace' will replace "process.env['SOCKET_CLI_VERSION_HASH']".
1518
- "0.14.53:e7fcb39:b41fef49:pub";
1520
+ "0.14.56:5a261bf:186ce7ee:pub";
1519
1521
  const nodeVersion = process.version;
1520
1522
  const apiToken = index.getSetting('apiToken');
1521
1523
  const shownToken = apiToken ? getLastFiveOfApiToken(apiToken) : 'no';
@@ -1589,15 +1591,116 @@ async function run$z(argv, importMeta, {
1589
1591
  await runAction(githubEventBefore, githubEventAfter);
1590
1592
  }
1591
1593
 
1594
+ async function fetchOrgAnalyticsData(time, spinner, apiToken) {
1595
+ const socketSdk = await index.setupSdk(apiToken);
1596
+ const result = await handleApiCall(socketSdk.getOrgAnalytics(time.toString()), 'fetching analytics data');
1597
+ if (result.success === false) {
1598
+ handleUnsuccessfulApiResponse('getOrgAnalytics', result, spinner);
1599
+ return undefined;
1600
+ }
1601
+ spinner.stop();
1602
+ if (!result.data.length) {
1603
+ logger.logger.log('No analytics data is available for this organization yet.');
1604
+ return undefined;
1605
+ }
1606
+ return result.data;
1607
+ }
1608
+
1609
+ async function fetchRepoAnalyticsData(repo, time, spinner, apiToken) {
1610
+ const socketSdk = await index.setupSdk(apiToken);
1611
+ const result = await handleApiCall(socketSdk.getRepoAnalytics(repo, time.toString()), 'fetching analytics data');
1612
+ if (result.success === false) {
1613
+ handleUnsuccessfulApiResponse('getRepoAnalytics', result, spinner);
1614
+ return undefined;
1615
+ }
1616
+ spinner.stop();
1617
+ if (!result.data.length) {
1618
+ logger.logger.log('No analytics data is available for this organization yet.');
1619
+ return undefined;
1620
+ }
1621
+ return result.data;
1622
+ }
1623
+
1624
+ function mdTableStringNumber(title1, title2, obj) {
1625
+ // | Date | Counts |
1626
+ // | ----------- | ------ |
1627
+ // | Header | 201464 |
1628
+ // | Paragraph | 18 |
1629
+ let mw1 = title1.length;
1630
+ let mw2 = title2.length;
1631
+ for (const [key, value] of Object.entries(obj)) {
1632
+ mw1 = Math.max(mw1, key.length);
1633
+ mw2 = Math.max(mw2, String(value ?? '').length);
1634
+ }
1635
+ const lines = [];
1636
+ lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`);
1637
+ lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`);
1638
+ for (const [key, value] of Object.entries(obj)) {
1639
+ lines.push(`| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`);
1640
+ }
1641
+ lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`);
1642
+ return lines.join('\n');
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
+ }
1673
+
1592
1674
  // Note: Widgets does not seem to actually work as code :'(
1593
1675
 
1676
+ const METRICS = ['total_critical_alerts', 'total_high_alerts', 'total_medium_alerts', 'total_low_alerts', 'total_critical_added', 'total_medium_added', 'total_low_added', 'total_high_added', 'total_critical_prevented', 'total_high_prevented', 'total_medium_prevented', 'total_low_prevented'];
1677
+
1594
1678
  // Note: This maps `new Date(date).getMonth()` to English three letters
1595
1679
  const Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
1596
- const METRICS = ['total_critical_alerts', 'total_high_alerts', 'total_medium_alerts', 'total_low_alerts', 'total_critical_added', 'total_medium_added', 'total_low_added', 'total_high_added', 'total_critical_prevented', 'total_high_prevented', 'total_medium_prevented', 'total_low_prevented'];
1597
1680
  async function displayAnalytics({
1681
+ filePath,
1682
+ outputKind,
1683
+ repo,
1684
+ scope,
1685
+ time
1686
+ }) {
1687
+ const apiToken = index.getDefaultToken();
1688
+ if (!apiToken) {
1689
+ throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API token.');
1690
+ }
1691
+ await outputAnalyticsWithToken({
1692
+ apiToken,
1693
+ filePath,
1694
+ outputKind,
1695
+ repo,
1696
+ scope,
1697
+ time
1698
+ });
1699
+ }
1700
+ async function outputAnalyticsWithToken({
1598
1701
  apiToken,
1599
1702
  filePath,
1600
- outputJson,
1703
+ outputKind,
1601
1704
  repo,
1602
1705
  scope,
1603
1706
  time
@@ -1613,22 +1716,70 @@ async function displayAnalytics({
1613
1716
  } else if (repo) {
1614
1717
  data = await fetchRepoAnalyticsData(repo, time, spinner, apiToken);
1615
1718
  }
1616
- if (data) {
1617
- if (outputJson && !filePath) {
1618
- logger.logger.log(data);
1619
- } else if (filePath) {
1719
+
1720
+ // A message should already have been printed if we have no data here
1721
+ if (!data) return;
1722
+ if (outputKind === 'json') {
1723
+ let serialized = renderJson(data);
1724
+ if (!serialized) return;
1725
+ if (filePath && filePath !== '-') {
1620
1726
  try {
1621
- await fs$1.writeFile(filePath, JSON.stringify(data), 'utf8');
1727
+ await fs$1.writeFile(filePath, serialized, 'utf8');
1622
1728
  logger.logger.log(`Data successfully written to ${filePath}`);
1623
1729
  } catch (e) {
1730
+ logger.logger.error('There was an error trying to write the json to disk');
1624
1731
  logger.logger.error(e);
1732
+ process.exitCode = 1;
1733
+ }
1734
+ } else {
1735
+ logger.logger.log(serialized);
1736
+ }
1737
+ } else {
1738
+ const fdata = scope === 'org' ? formatDataOrg(data) : formatDataRepo(data);
1739
+ if (outputKind === 'markdown') {
1740
+ const serialized = renderMarkdown(fdata, time, repo);
1741
+ if (filePath && filePath !== '-') {
1742
+ try {
1743
+ await fs$1.writeFile(filePath, serialized, 'utf8');
1744
+ logger.logger.log(`Data successfully written to ${filePath}`);
1745
+ } catch (e) {
1746
+ logger.logger.error(e);
1747
+ }
1748
+ } else {
1749
+ logger.logger.log(serialized);
1625
1750
  }
1626
1751
  } else {
1627
- const fdata = scope === 'org' ? formatData(data, 'org') : formatData(data, 'repo');
1628
1752
  displayAnalyticsScreen(fdata);
1629
1753
  }
1630
1754
  }
1631
1755
  }
1756
+ function renderJson(data) {
1757
+ try {
1758
+ return JSON.stringify(data, null, 2);
1759
+ } catch (e) {
1760
+ // This could be caused by circular references, which is an "us" problem
1761
+ logger.logger.error('There was a problem converting the data set to JSON. Please try without --json or with --markdown');
1762
+ process.exitCode = 1;
1763
+ return;
1764
+ }
1765
+ }
1766
+ function renderMarkdown(data, days, repoSlug) {
1767
+ return commonTags.stripIndents`
1768
+ # Socket Alert Analytics
1769
+
1770
+ These are the Socket.dev stats are analytics for the ${repoSlug ? `${repoSlug} repo` : 'org'} of the past ${days} days
1771
+
1772
+ ${[['Total critical alerts', mdTableStringNumber('Date', 'Counts', data['total_critical_alerts'])], ['Total high alerts', mdTableStringNumber('Date', 'Counts', data['total_high_alerts'])], ['Total critical alerts added to the main branch', mdTableStringNumber('Date', 'Counts', data['total_critical_added'])], ['Total high alerts added to the main branch', mdTableStringNumber('Date', 'Counts', data['total_high_added'])], ['Total critical alerts prevented from the main branch', mdTableStringNumber('Date', 'Counts', data['total_critical_prevented'])], ['Total high alerts prevented from the main branch', mdTableStringNumber('Date', 'Counts', data['total_high_prevented'])], ['Total medium alerts prevented from the main branch', mdTableStringNumber('Date', 'Counts', data['total_medium_prevented'])], ['Total low alerts prevented from the main branch', mdTableStringNumber('Date', 'Counts', data['total_low_prevented'])]].map(([title, table]) => commonTags.stripIndents`
1773
+ ## ${title}
1774
+
1775
+ ${table}
1776
+ `).join('\n\n')}
1777
+
1778
+ ## Top 5 alert types
1779
+
1780
+ ${mdTableStringNumber('Name', 'Counts', data['top_five_alert_types'])}
1781
+ `;
1782
+ }
1632
1783
  function displayAnalyticsScreen(data) {
1633
1784
  const screen = new ScreenWidget({});
1634
1785
  const grid = new contrib.grid({
@@ -1661,91 +1812,69 @@ function displayAnalyticsScreen(data) {
1661
1812
  screen.render();
1662
1813
  screen.key(['escape', 'q', 'C-c'], () => process.exit(0));
1663
1814
  }
1664
- async function fetchOrgAnalyticsData(time, spinner, apiToken) {
1665
- const socketSdk = await index.setupSdk(apiToken);
1666
- const result = await handleApiCall(socketSdk.getOrgAnalytics(time.toString()), 'fetching analytics data');
1667
- if (result.success === false) {
1668
- handleUnsuccessfulApiResponse('getOrgAnalytics', result, spinner);
1669
- return undefined;
1815
+ function formatDataRepo(data) {
1816
+ const sortedTopFiveAlerts = {};
1817
+ const totalTopAlerts = {};
1818
+ const formattedData = {};
1819
+ for (const metric of METRICS) {
1820
+ formattedData[metric] = {};
1670
1821
  }
1671
- spinner.stop();
1672
- if (!result.data.length) {
1673
- logger.logger.log('No analytics data is available for this organization yet.');
1674
- return undefined;
1822
+ for (const entry of data) {
1823
+ const topFiveAlertTypes = entry['top_five_alert_types'];
1824
+ for (const type of Object.keys(topFiveAlertTypes)) {
1825
+ const count = topFiveAlertTypes[type] ?? 0;
1826
+ if (!totalTopAlerts[type]) {
1827
+ totalTopAlerts[type] = count;
1828
+ } else if (count > (totalTopAlerts[type] ?? 0)) {
1829
+ totalTopAlerts[type] = count;
1830
+ }
1831
+ }
1675
1832
  }
1676
- return result.data;
1677
- }
1678
- async function fetchRepoAnalyticsData(repo, time, spinner, apiToken) {
1679
- const socketSdk = await index.setupSdk(apiToken);
1680
- const result = await handleApiCall(socketSdk.getRepoAnalytics(repo, time.toString()), 'fetching analytics data');
1681
- if (result.success === false) {
1682
- handleUnsuccessfulApiResponse('getRepoAnalytics', result, spinner);
1683
- return undefined;
1833
+ for (const entry of data) {
1834
+ for (const metric of METRICS) {
1835
+ formattedData[metric][formatDate(entry['created_at'])] = entry[metric];
1836
+ }
1684
1837
  }
1685
- spinner.stop();
1686
- if (!result.data.length) {
1687
- logger.logger.log('No analytics data is available for this organization yet.');
1688
- return undefined;
1838
+ const topFiveAlertEntries = Object.entries(totalTopAlerts).sort(([_keya, a], [_keyb, b]) => b - a).slice(0, 5);
1839
+ for (const [key, value] of topFiveAlertEntries) {
1840
+ sortedTopFiveAlerts[key] = value;
1689
1841
  }
1690
- return result.data;
1842
+ return {
1843
+ ...formattedData,
1844
+ top_five_alert_types: sortedTopFiveAlerts
1845
+ };
1691
1846
  }
1692
- function formatData(data, scope) {
1693
- const formattedData = {};
1847
+ function formatDataOrg(data) {
1694
1848
  const sortedTopFiveAlerts = {};
1695
1849
  const totalTopAlerts = {};
1850
+ const formattedData = {};
1696
1851
  for (const metric of METRICS) {
1697
1852
  formattedData[metric] = {};
1698
1853
  }
1699
- if (scope === 'org') {
1700
- for (const entry of data) {
1701
- const topFiveAlertTypes = entry['top_five_alert_types'];
1702
- for (const type of Object.keys(topFiveAlertTypes)) {
1703
- const count = topFiveAlertTypes[type] ?? 0;
1704
- if (!totalTopAlerts[type]) {
1705
- totalTopAlerts[type] = count;
1706
- } else {
1707
- totalTopAlerts[type] += count;
1708
- }
1709
- }
1710
- }
1711
- for (const metric of METRICS) {
1712
- const formatted = formattedData[metric];
1713
- for (const entry of data) {
1714
- const date = formatDate(entry['created_at']);
1715
- if (!formatted[date]) {
1716
- formatted[date] = entry[metric];
1717
- } else {
1718
- formatted[date] += entry[metric];
1719
- }
1720
- }
1721
- }
1722
- } else if (scope === 'repo') {
1723
- for (const entry of data) {
1724
- const topFiveAlertTypes = entry['top_five_alert_types'];
1725
- for (const type of Object.keys(topFiveAlertTypes)) {
1726
- const count = topFiveAlertTypes[type] ?? 0;
1727
- if (!totalTopAlerts[type]) {
1728
- totalTopAlerts[type] = count;
1729
- } else if (count > (totalTopAlerts[type] ?? 0)) {
1730
- totalTopAlerts[type] = count;
1731
- }
1854
+ for (const entry of data) {
1855
+ const topFiveAlertTypes = entry['top_five_alert_types'];
1856
+ for (const type of Object.keys(topFiveAlertTypes)) {
1857
+ const count = topFiveAlertTypes[type] ?? 0;
1858
+ if (!totalTopAlerts[type]) {
1859
+ totalTopAlerts[type] = count;
1860
+ } else {
1861
+ totalTopAlerts[type] += count;
1732
1862
  }
1733
1863
  }
1864
+ }
1865
+ for (const metric of METRICS) {
1866
+ const formatted = formattedData[metric];
1734
1867
  for (const entry of data) {
1735
- for (const metric of METRICS) {
1736
- formattedData[metric][formatDate(entry['created_at'])] = entry[metric];
1868
+ const date = formatDate(entry['created_at']);
1869
+ if (!formatted[date]) {
1870
+ formatted[date] = entry[metric];
1871
+ } else {
1872
+ formatted[date] += entry[metric];
1737
1873
  }
1738
1874
  }
1739
1875
  }
1740
- const topFiveAlertEntries = Object.entries(totalTopAlerts).sort(({
1741
- 1: a
1742
- }, {
1743
- 1: b
1744
- }) => b - a).slice(0, 5);
1745
- for (const {
1746
- 0: key,
1747
- 1: value
1748
- } of topFiveAlertEntries) {
1876
+ const topFiveAlertEntries = Object.entries(totalTopAlerts).sort(([_keya, a], [_keyb, b]) => b - a).slice(0, 5);
1877
+ for (const [key, value] of topFiveAlertEntries) {
1749
1878
  sortedTopFiveAlerts[key] = value;
1750
1879
  }
1751
1880
  return {
@@ -1790,6 +1919,18 @@ const config$y = {
1790
1919
  flags: {
1791
1920
  ...commonFlags,
1792
1921
  ...outputFlags,
1922
+ file: {
1923
+ type: 'string',
1924
+ shortFlag: 'f',
1925
+ default: '-',
1926
+ description: 'Path to a local file to save the output. Only valid with --json/--markdown. Defaults to stdout.'
1927
+ },
1928
+ repo: {
1929
+ type: 'string',
1930
+ shortFlag: 'r',
1931
+ default: '',
1932
+ description: 'Name of the repository. Only valid when scope=repo'
1933
+ },
1793
1934
  scope: {
1794
1935
  type: 'string',
1795
1936
  shortFlag: 's',
@@ -1801,18 +1942,6 @@ const config$y = {
1801
1942
  shortFlag: 't',
1802
1943
  default: 7,
1803
1944
  description: 'Time filter - either 7, 30 or 90, default: 7'
1804
- },
1805
- repo: {
1806
- type: 'string',
1807
- shortFlag: 'r',
1808
- default: '',
1809
- description: 'Name of the repository'
1810
- },
1811
- file: {
1812
- type: 'string',
1813
- shortFlag: 'f',
1814
- default: '',
1815
- description: 'Path to a local file to save the output'
1816
1945
  }
1817
1946
  },
1818
1947
  help: (command, {
@@ -1848,6 +1977,9 @@ async function run$y(argv, importMeta, {
1848
1977
  parentName
1849
1978
  });
1850
1979
  const {
1980
+ file,
1981
+ json,
1982
+ markdown,
1851
1983
  repo,
1852
1984
  scope,
1853
1985
  time
@@ -1855,66 +1987,125 @@ async function run$y(argv, importMeta, {
1855
1987
  const badScope = scope !== 'org' && scope !== 'repo';
1856
1988
  const badTime = time !== 7 && time !== 30 && time !== 90;
1857
1989
  const badRepo = scope === 'repo' && !repo;
1858
- if (badScope || badTime || badRepo) {
1990
+ const badFile = file !== '-' && !json && !markdown;
1991
+ const badFlags = json && markdown;
1992
+ if (badScope || badTime || badRepo || badFile || badFlags) {
1859
1993
  // Use exit status of 2 to indicate incorrect usage, generally invalid
1860
1994
  // options or missing arguments.
1861
1995
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
1862
1996
  process.exitCode = 2;
1863
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
1864
- - Scope must be "repo" or "org" ${badScope ? colors.red('(bad!)') : colors.green('(ok)')}\n
1865
- - The time filter must either be 7, 30 or 90 ${badTime ? colors.red('(bad!)') : colors.green('(ok)')}\n
1866
- - Repository name using --repo when scope is "repo" ${badRepo ? colors.red('(bad!)') : colors.green('(ok)')}\n`);
1997
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
1998
+
1999
+ - Scope must be "repo" or "org" ${badScope ? colors.red('(bad!)') : colors.green('(ok)')}
2000
+
2001
+ - The time filter must either be 7, 30 or 90 ${badTime ? colors.red('(bad!)') : colors.green('(ok)')}
2002
+
2003
+ ${scope === 'repo' ? `- Repository name using --repo when scope is "repo" ${badRepo ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}
2004
+
2005
+ ${badFlags ? `- The \`--json\` and \`--markdown\` flags can not be used at the same time ${badFlags ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}
2006
+
2007
+ ${badFile ? `- The \`--file\` flag is only valid when using \`--json\` or \`--markdown\` ${badFile ? colors.red('(bad!)') : colors.green('(ok)')}` : ''}
2008
+ `.split('\n').filter(s => !!s.trim()).join('\n'));
1867
2009
  return;
1868
2010
  }
1869
2011
  if (cli.flags['dryRun']) {
1870
2012
  logger.logger.log(DRY_RUN_BAIL_TEXT$x);
1871
2013
  return;
1872
2014
  }
1873
- const apiToken = index.getDefaultToken();
1874
- if (!apiToken) {
1875
- throw new index.AuthError('User must be authenticated to run this command. To log in, run the command `socket login` and enter your API token.');
1876
- }
1877
2015
  return await displayAnalytics({
1878
- apiToken,
1879
2016
  scope,
1880
2017
  time,
1881
2018
  repo: String(repo || ''),
1882
- outputJson: Boolean(cli.flags['json']),
1883
- filePath: String(cli.flags['file'] || '')
2019
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
2020
+ filePath: String(file || '')
1884
2021
  });
1885
2022
  }
1886
2023
 
1887
2024
  async function getAuditLog({
1888
- apiToken,
2025
+ logType,
1889
2026
  orgSlug,
1890
- outputJson,
1891
- outputMarkdown,
2027
+ outputKind,
1892
2028
  page,
1893
- perPage,
1894
- type
2029
+ perPage
1895
2030
  }) {
1896
- // Lazily access constants.spinner.
1897
- const {
1898
- spinner
1899
- } = constants;
1900
- spinner.start(`Looking up audit log for ${orgSlug}`);
1901
- const socketSdk = await index.setupSdk(apiToken);
1902
- const result = await handleApiCall(socketSdk.getAuditLogEvents(orgSlug, {
1903
- outputJson,
1904
- outputMarkdown,
2031
+ const apiToken = index.getDefaultToken();
2032
+ if (!apiToken) {
2033
+ 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.');
2034
+ }
2035
+ const auditLogs = await getAuditLogWithToken({
2036
+ apiToken,
1905
2037
  orgSlug,
1906
- type,
2038
+ outputKind,
1907
2039
  page,
1908
- per_page: perPage
1909
- }), `Looking up audit log for ${orgSlug}\n`);
1910
- if (!result.success) {
1911
- handleUnsuccessfulApiResponse('getAuditLogEvents', result, spinner);
2040
+ perPage,
2041
+ logType
2042
+ });
2043
+ if (!auditLogs) return;
2044
+ if (outputKind === 'json') await outputAsJson(auditLogs.results, orgSlug, logType, page, perPage);else if (outputKind === 'markdown') await outputAsMarkdown(auditLogs.results, orgSlug, logType, page, perPage);else await outputAsPrint(auditLogs.results, orgSlug, logType);
2045
+ }
2046
+ async function outputAsJson(auditLogs, orgSlug, logType, page, perPage) {
2047
+ let json;
2048
+ try {
2049
+ json = JSON.stringify({
2050
+ desc: 'Audit logs for given query',
2051
+ generated: new Date().toISOString(),
2052
+ org: orgSlug,
2053
+ logType,
2054
+ page,
2055
+ perPage,
2056
+ logs: auditLogs.map(log => {
2057
+ // Note: The subset is pretty arbitrary
2058
+ const {
2059
+ created_at,
2060
+ event_id,
2061
+ ip_address,
2062
+ type,
2063
+ user_agent,
2064
+ user_email
2065
+ } = log;
2066
+ return {
2067
+ event_id,
2068
+ created_at,
2069
+ ip_address,
2070
+ type,
2071
+ user_agent,
2072
+ user_email
2073
+ };
2074
+ })
2075
+ }, null, 2);
2076
+ } catch (e) {
2077
+ logger.logger.error('There was a problem converting the logs to JSON, please try without the `--json` flag');
2078
+ process.exitCode = 1;
1912
2079
  return;
1913
2080
  }
1914
- spinner.stop();
2081
+ logger.logger.log(json);
2082
+ }
2083
+ async function outputAsMarkdown(auditLogs, orgSlug, logType, page, perPage) {
2084
+ try {
2085
+ const table = mdTable(auditLogs, ['event_id', 'created_at', 'type', 'user_email', 'ip_address', 'user_agent']);
2086
+ logger.logger.log(commonTags.stripIndents`
2087
+ # Socket Audit Logs
2088
+
2089
+ These are the Socket.dev audit logs as per requested query.
2090
+ - org: ${orgSlug}
2091
+ - type filter: ${logType || '(none)'}
2092
+ - page: ${page}
2093
+ - per page: ${perPage}
2094
+ - generated: ${new Date().toISOString()}
2095
+
2096
+ ${table}
2097
+ `);
2098
+ } catch (e) {
2099
+ logger.logger.error('There was a problem converting the logs to JSON, please try without the `--json` flag');
2100
+ logger.logger.error(e);
2101
+ process.exitCode = 1;
2102
+ return;
2103
+ }
2104
+ }
2105
+ async function outputAsPrint(auditLogs, orgSlug, logType) {
1915
2106
  const data = [];
1916
2107
  const logDetails = {};
1917
- for (const d of result.data.results) {
2108
+ for (const d of auditLogs) {
1918
2109
  const {
1919
2110
  created_at
1920
2111
  } = d;
@@ -1931,11 +2122,42 @@ async function getAuditLog({
1931
2122
  }
1932
2123
  }
1933
2124
  logger.logger.log(logDetails[await prompts.select({
1934
- message: type ? `\n Audit log for: ${orgSlug} with type: ${type}\n` : `\n Audit log for: ${orgSlug}\n`,
2125
+ message: logType ? `\n Audit log for: ${orgSlug} with type: ${logType}\n` : `\n Audit log for: ${orgSlug}\n`,
1935
2126
  choices: data,
1936
2127
  pageSize: 30
1937
2128
  })]);
1938
2129
  }
2130
+ async function getAuditLogWithToken({
2131
+ apiToken,
2132
+ logType,
2133
+ orgSlug,
2134
+ outputKind,
2135
+ page,
2136
+ perPage
2137
+ }) {
2138
+ // Lazily access constants.spinner.
2139
+ const {
2140
+ spinner
2141
+ } = constants;
2142
+ spinner.start(`Looking up audit log for ${orgSlug}`);
2143
+ const socketSdk = await index.setupSdk(apiToken);
2144
+ const result = await handleApiCall(socketSdk.getAuditLogEvents(orgSlug, {
2145
+ outputJson: outputKind === 'json',
2146
+ // I'm not sure this is used at all
2147
+ outputMarkdown: outputKind === 'markdown',
2148
+ // I'm not sure this is used at all
2149
+ orgSlug,
2150
+ type: logType,
2151
+ page,
2152
+ per_page: perPage
2153
+ }), `Looking up audit log for ${orgSlug}\n`);
2154
+ if (!result.success) {
2155
+ handleUnsuccessfulApiResponse('getAuditLogEvents', result, spinner);
2156
+ return;
2157
+ }
2158
+ spinner.stop();
2159
+ return result.data;
2160
+ }
1939
2161
 
1940
2162
  const {
1941
2163
  DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w
@@ -1991,48 +2213,43 @@ async function run$x(argv, importMeta, {
1991
2213
  importMeta,
1992
2214
  parentName
1993
2215
  });
1994
- const type = String(cli.flags['type'] || '');
2216
+ const {
2217
+ json,
2218
+ markdown,
2219
+ page,
2220
+ perPage,
2221
+ type
2222
+ } = cli.flags;
2223
+ const logType = String(type || '');
1995
2224
  const [orgSlug = ''] = cli.input;
1996
2225
  if (!orgSlug) {
1997
2226
  // Use exit status of 2 to indicate incorrect usage, generally invalid
1998
2227
  // options or missing arguments.
1999
2228
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
2000
2229
  process.exitCode = 2;
2001
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
2002
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
2230
+ logger.logger.error(commonTags.stripIndents`
2231
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
2232
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
2233
+ `);
2003
2234
  return;
2004
2235
  }
2005
2236
  if (cli.flags['dryRun']) {
2006
2237
  logger.logger.log(DRY_RUN_BAIL_TEXT$w);
2007
2238
  return;
2008
2239
  }
2009
- const apiToken = index.getDefaultToken();
2010
- if (!apiToken) {
2011
- 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.');
2012
- }
2013
2240
  await getAuditLog({
2014
- apiToken,
2015
2241
  orgSlug,
2016
- outputJson: Boolean(cli.flags['json']),
2017
- outputMarkdown: Boolean(cli.flags['markdown']),
2018
- page: Number(cli.flags['page'] || 0),
2019
- perPage: Number(cli.flags['perPage'] || 0),
2020
- type: type.charAt(0).toUpperCase() + type.slice(1)
2242
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
2243
+ page: Number(page || 0),
2244
+ perPage: Number(perPage || 0),
2245
+ logType: logType.charAt(0).toUpperCase() + logType.slice(1)
2021
2246
  });
2022
2247
  }
2023
2248
 
2024
- const {
2025
- SBOM_SIGN_ALGORITHM,
2026
- // Algorithm. Example: RS512
2027
- SBOM_SIGN_PRIVATE_KEY,
2028
- // Location to the RSA private key
2029
- SBOM_SIGN_PUBLIC_KEY // Optional. Location to the RSA public key
2030
- } = process$1.env;
2031
2249
  const {
2032
2250
  NPM: NPM$e,
2033
- PNPM: PNPM$8,
2034
- cdxgenBinPath,
2035
- synpBinPath
2251
+ NPX: NPX$3,
2252
+ PNPM: PNPM$8
2036
2253
  } = constants;
2037
2254
  const nodejsPlatformTypes = new Set(['javascript', 'js', 'nodejs', NPM$e, PNPM$8, 'ts', 'tsx', 'typescript']);
2038
2255
  async function runCycloneDX(yargv) {
@@ -2044,21 +2261,13 @@ async function runCycloneDX(yargv) {
2044
2261
  // Use synp to create a package-lock.json from the yarn.lock,
2045
2262
  // based on the node_modules folder, for a more accurate SBOM.
2046
2263
  try {
2047
- 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);
2048
2265
  yargv.type = NPM$e;
2049
2266
  cleanupPackageLock = true;
2050
2267
  } catch {}
2051
2268
  }
2052
2269
  }
2053
- await npm.runBin(await fs.promises.realpath(cdxgenBinPath), argvToArray(yargv), {
2054
- env: {
2055
- NODE_ENV: '',
2056
- SBOM_SIGN_ALGORITHM,
2057
- SBOM_SIGN_PRIVATE_KEY,
2058
- SBOM_SIGN_PUBLIC_KEY
2059
- },
2060
- stdio: 'inherit'
2061
- });
2270
+ await shadowBin(NPX$3, ['@cyclonedx/cdxgen@11.2.0', '--', ...argvToArray(yargv)], 2);
2062
2271
  if (cleanupPackageLock) {
2063
2272
  try {
2064
2273
  await fs.promises.rm('./package-lock.json');
@@ -2204,8 +2413,11 @@ async function run$w(argv, importMeta, {
2204
2413
  //
2205
2414
  //
2206
2415
  // if (cli.input.length)
2207
- // logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
2208
- // - Unexpected arguments\n
2416
+ // logger.error(
2417
+ // stripIndents`
2418
+ // ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
2419
+ //
2420
+ // - Unexpected arguments
2209
2421
  // `)
2210
2422
  // config.help(parentName, config)
2211
2423
  // return
@@ -2267,6 +2479,7 @@ async function findDependencies({
2267
2479
  logger.logger.log(result.data);
2268
2480
  return;
2269
2481
  }
2482
+ logger.logger.log('Request details: Offset:', offset, ', limit:', limit, ', is there more data after this?', result.data.end ? 'no' : 'yes');
2270
2483
  const options = {
2271
2484
  columns: [{
2272
2485
  field: 'namespace',
@@ -2358,43 +2571,95 @@ async function run$v(argv, importMeta, {
2358
2571
  async function getDiffScan({
2359
2572
  after,
2360
2573
  before,
2574
+ depth,
2361
2575
  file,
2362
2576
  orgSlug,
2363
2577
  outputJson
2364
- }, 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
+ }) {
2365
2602
  // Lazily access constants.spinner.
2366
2603
  const {
2367
2604
  spinner
2368
2605
  } = constants;
2369
2606
  spinner.start('Getting diff scan...');
2370
- const response = await queryAPI(`${orgSlug}/full-scans/diff?before=${before}&after=${after}&preview`, apiToken);
2371
- const data = await response.json();
2607
+ const response = await queryAPI(`orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`, apiToken);
2372
2608
  if (!response.ok) {
2373
2609
  const err = await handleAPIError(response.status);
2374
2610
  spinner.errorAndStop(`${colors.bgRed(colors.white(response.statusText))}: ${err}`);
2375
2611
  return;
2376
2612
  }
2613
+ const result = await handleApiCall(await response.json(), 'Deserializing json');
2377
2614
  spinner.stop();
2378
- if (file && !outputJson) {
2379
- fs.writeFile(file, JSON.stringify(data), err => {
2380
- err ? logger.logger.error(err) : logger.logger.log(`Data successfully written to ${file}`);
2381
- });
2382
- return;
2383
- }
2384
- if (outputJson) {
2385
- logger.logger.log(`\n Diff scan result: \n`);
2386
- logger.logger.log(require$$0$1.inspect(data, {
2387
- showHidden: false,
2388
- depth: null,
2389
- colors: true
2390
- }));
2391
- 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
+ }
2392
2648
  return;
2393
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
+
2394
2654
  logger.logger.log('Diff scan result:');
2395
- 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
+ }));
2396
2661
  logger.logger.log(`\n 📝 To display the detailed report in the terminal, use the --json flag \n`);
2397
- logger.logger.log(`\n View this diff scan in the Socket dashboard: ${colors.cyan(data?.['diff_report_url'])}`);
2662
+ logger.logger.log(dashboardMessage);
2398
2663
  }
2399
2664
 
2400
2665
  const {
@@ -2406,36 +2671,44 @@ const config$u = {
2406
2671
  hidden: false,
2407
2672
  flags: {
2408
2673
  ...commonFlags,
2674
+ after: {
2675
+ type: 'string',
2676
+ shortFlag: 'a',
2677
+ default: '',
2678
+ description: 'The full scan ID of the head scan'
2679
+ },
2409
2680
  before: {
2410
2681
  type: 'string',
2411
2682
  shortFlag: 'b',
2412
2683
  default: '',
2413
2684
  description: 'The full scan ID of the base scan'
2414
2685
  },
2415
- after: {
2416
- type: 'string',
2417
- shortFlag: 'a',
2418
- default: '',
2419
- 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)'
2420
2690
  },
2421
- preview: {
2691
+ json: {
2422
2692
  type: 'boolean',
2423
- shortFlag: 'p',
2424
- default: true,
2425
- 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.'
2426
2696
  },
2427
2697
  file: {
2428
2698
  type: 'string',
2429
2699
  shortFlag: 'f',
2430
2700
  default: '',
2431
- description: 'Path to a local file where the output should be saved'
2432
- },
2433
- ...outputFlags
2701
+ description: 'Path to a local file where the output should be saved. Use `-` to force stdout.'
2702
+ }
2434
2703
  },
2435
2704
  help: (command, config) => `
2436
2705
  Usage
2437
2706
  $ ${command} <org slug> --before=<before> --after=<after>
2438
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
+
2439
2712
  Options
2440
2713
  ${getFlagListOutput(config.flags, 6)}
2441
2714
 
@@ -2468,6 +2741,7 @@ async function run$u(argv, importMeta, {
2468
2741
  logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
2469
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
2470
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
2471
2745
  - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
2472
2746
  return;
2473
2747
  }
@@ -2475,24 +2749,24 @@ async function run$u(argv, importMeta, {
2475
2749
  logger.logger.log(DRY_RUN_BAIL_TEXT$t);
2476
2750
  return;
2477
2751
  }
2478
- const apiToken = index.getDefaultToken();
2479
- if (!apiToken) {
2480
- 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.');
2481
- }
2482
2752
  await getDiffScan({
2483
2753
  outputJson: Boolean(cli.flags['json']),
2484
- outputMarkdown: Boolean(cli.flags['markdown']),
2485
2754
  before,
2486
2755
  after,
2487
- preview: Boolean(cli.flags['preview']),
2756
+ depth: Number(cli.flags['depth']),
2488
2757
  orgSlug,
2489
2758
  file: String(cli.flags['file'] || '')
2490
- }, apiToken);
2759
+ });
2491
2760
  }
2492
2761
 
2493
2762
  const description$3 = 'Diff scans related commands';
2494
2763
  const cmdDiffScan = {
2495
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,
2496
2770
  async run(argv, importMeta, {
2497
2771
  parentName
2498
2772
  }) {
@@ -2724,7 +2998,12 @@ function getSeverityCount(issues, lowestToInclude) {
2724
2998
  return severityCount;
2725
2999
  }
2726
3000
 
2727
- 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}`);
2728
3007
  const socketSdk = await index.setupSdk(index.getPublicToken());
2729
3008
  const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package');
2730
3009
  const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score');
@@ -2735,6 +3014,7 @@ async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues, spinner)
2735
3014
  return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult, spinner);
2736
3015
  }
2737
3016
  const severityCount = getSeverityCount(result.data, includeAllIssues ? undefined : 'high');
3017
+ spinner?.successAndStop('Data fetched');
2738
3018
  return {
2739
3019
  data: result.data,
2740
3020
  severityCount,
@@ -2750,50 +3030,55 @@ function formatPackageInfo({
2750
3030
  score,
2751
3031
  severityCount
2752
3032
  }, {
2753
- name,
2754
- outputJson,
2755
- outputMarkdown,
3033
+ name,
3034
+ outputKind,
2756
3035
  pkgName,
2757
- pkgVersion,
2758
- strict
2759
- }, spinner) {
2760
- if (outputJson) {
3036
+ pkgVersion
3037
+ }) {
3038
+ if (outputKind === 'json') {
2761
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');
2762
3045
  } else {
2763
- logger.logger.log('\nPackage report card:');
2764
- const scoreResult = {
2765
- 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
2766
- Maintenance: Math.floor(score.maintenance.score * 100),
2767
- Quality: Math.floor(score.quality.score * 100),
2768
- Vulnerabilities: Math.floor(score.vulnerability.score * 100),
2769
- License: Math.floor(score.license.score * 100)
2770
- };
2771
- Object.entries(scoreResult).map(score => logger.logger.log(`- ${score[0]}: ${formatScore(score[1])}`));
2772
- logger.logger.log('\n');
2773
- if (objectSome(severityCount)) {
2774
- spinner[strict ? 'error' : 'success'](`Package has these issues: ${formatSeverityCount(severityCount)}`);
2775
- formatPackageIssuesDetails(data, outputMarkdown);
2776
- } else {
2777
- spinner.successAndStop('Package has no issues');
2778
- }
2779
- const format = new index.ColorOrMarkdown(!!outputMarkdown);
2780
- const url = index.getSocketDevPackageOverviewUrl(NPM$c, pkgName, pkgVersion);
2781
- logger.logger.log('\n');
2782
- if (pkgVersion === 'latest') {
2783
- logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, {
2784
- fallbackToUrl: true
2785
- })}`);
2786
- } else {
2787
- logger.logger.log(`Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, {
2788
- fallbackToUrl: true
2789
- })}`);
2790
- }
2791
- if (!outputMarkdown) {
2792
- logger.logger.log(colors.dim(`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`));
2793
- }
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');
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');
2794
3065
  }
2795
- if (strict && objectSome(severityCount)) {
2796
- 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('');
2797
3082
  }
2798
3083
  }
2799
3084
  function formatPackageIssuesDetails(packageData, outputMarkdown) {
@@ -2814,7 +3099,7 @@ function formatPackageIssuesDetails(packageData, outputMarkdown) {
2814
3099
  }
2815
3100
  return acc;
2816
3101
  }, {});
2817
- const format = new index.ColorOrMarkdown(!!outputMarkdown);
3102
+ const format = new index.ColorOrMarkdown(outputMarkdown);
2818
3103
  for (const issue of Object.keys(uniqueIssues)) {
2819
3104
  const issueWithLink = format.hyperlink(`${uniqueIssues[issue]?.label}`, index.getSocketDevAlertUrl(issue), {
2820
3105
  fallbackToUrl: true
@@ -2838,27 +3123,23 @@ function formatScore(score) {
2838
3123
  async function getPackageInfo({
2839
3124
  commandName,
2840
3125
  includeAllIssues,
2841
- outputJson,
2842
- outputMarkdown,
3126
+ outputKind,
2843
3127
  pkgName,
2844
3128
  pkgVersion,
2845
3129
  strict
2846
3130
  }) {
2847
- // Lazily access constants.spinner.
2848
- const {
2849
- spinner
2850
- } = constants;
2851
- spinner.start(pkgVersion === 'latest' ? `Looking up data for the latest version of ${pkgName}` : `Looking up data for version ${pkgVersion} of ${pkgName}`);
2852
- const packageData = await fetchPackageInfo(pkgName, pkgVersion, includeAllIssues, spinner);
3131
+ const packageData = await fetchPackageInfo(pkgName, pkgVersion, includeAllIssues);
2853
3132
  if (packageData) {
2854
3133
  formatPackageInfo(packageData, {
2855
3134
  name: commandName,
2856
- outputJson,
2857
- outputMarkdown,
3135
+ outputKind,
2858
3136
  pkgName,
2859
- pkgVersion,
2860
- strict
2861
- }, 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
+ }
2862
3143
  }
2863
3144
  }
2864
3145
 
@@ -2900,6 +3181,12 @@ async function run$s(argv, importMeta, {
2900
3181
  importMeta,
2901
3182
  parentName
2902
3183
  });
3184
+ const {
3185
+ all,
3186
+ json,
3187
+ markdown,
3188
+ strict
3189
+ } = cli.flags;
2903
3190
  const [rawPkgName = ''] = cli.input;
2904
3191
  if (!rawPkgName || cli.input.length > 1) {
2905
3192
  // Use exit status of 2 to indicate incorrect usage, generally invalid
@@ -2920,12 +3207,11 @@ async function run$s(argv, importMeta, {
2920
3207
  }
2921
3208
  await getPackageInfo({
2922
3209
  commandName: `${parentName} ${config$s.commandName}`,
2923
- includeAllIssues: Boolean(cli.flags['all']),
2924
- outputJson: Boolean(cli.flags['json']),
2925
- outputMarkdown: Boolean(cli.flags['markdown']),
3210
+ includeAllIssues: Boolean(all),
3211
+ outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
2926
3212
  pkgName,
2927
3213
  pkgVersion,
2928
- strict: Boolean(cli.flags['strict'])
3214
+ strict: Boolean(strict)
2929
3215
  });
2930
3216
  }
2931
3217
 
@@ -2958,7 +3244,7 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
2958
3244
  throw new index.AuthError();
2959
3245
  }
2960
3246
  orgs = result.data;
2961
- spinner.success('API key verified');
3247
+ spinner.successAndStop('API key verified');
2962
3248
  } catch {
2963
3249
  spinner.errorAndStop('Invalid API key');
2964
3250
  return;
@@ -2999,8 +3285,10 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
2999
3285
  const oldToken = index.getSetting('apiToken');
3000
3286
  try {
3001
3287
  applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy);
3288
+ spinner.start();
3002
3289
  spinner.successAndStop(`API credentials ${oldToken ? 'updated' : 'set'}`);
3003
3290
  } catch {
3291
+ spinner.start();
3004
3292
  spinner.errorAndStop(`API login failed`);
3005
3293
  }
3006
3294
  }
@@ -3135,7 +3423,6 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3135
3423
  logger.logger.log(`- src dir: \`${target}\``);
3136
3424
  logger.logger.groupEnd();
3137
3425
  }
3138
- spinner.start(`Converting gradle to maven from \`${bin}\` on \`${target}\`...`);
3139
3426
  try {
3140
3427
  // Run sbt with the init script we provide which should yield zero or more pom files.
3141
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.
@@ -3145,8 +3432,9 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3145
3432
  const initLocation = path.join(constants.rootDistPath, 'init.gradle');
3146
3433
  const commandArgs = ['--init-script', initLocation, ...gradleOpts, 'pom'];
3147
3434
  if (verbose) {
3148
- spinner.log('[VERBOSE] Executing:', bin, commandArgs);
3435
+ logger.logger.log('[VERBOSE] Executing:', bin, commandArgs);
3149
3436
  }
3437
+ spinner.start(`Converting gradle to maven from \`${bin}\` on \`${target}\`...`);
3150
3438
  const output = await spawn.spawn(bin, commandArgs, {
3151
3439
  cwd: target || '.'
3152
3440
  });
@@ -3164,7 +3452,8 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3164
3452
  logger.logger.error(output.stderr);
3165
3453
  logger.logger.groupEnd();
3166
3454
  }
3167
- process.exit(1);
3455
+ process.exitCode = 1;
3456
+ return;
3168
3457
  }
3169
3458
  logger.logger.success('Executed gradle successfully');
3170
3459
  logger.logger.log('Reported exports:');
@@ -3201,14 +3490,15 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
3201
3490
  // spinner.successAndStop(`OK. File should be available in \`${out}\``)
3202
3491
  // }
3203
3492
  } catch (e) {
3204
- spinner.stop();
3205
- 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)'));
3206
3494
  if (verbose) {
3207
3495
  logger.logger.group('[VERBOSE] error:');
3208
3496
  logger.logger.log(e);
3209
3497
  logger.logger.groupEnd();
3210
3498
  }
3211
- process.exit(1);
3499
+ process.exitCode = 1;
3500
+ } finally {
3501
+ spinner.stop();
3212
3502
  }
3213
3503
  }
3214
3504
 
@@ -3318,9 +3608,11 @@ async function run$p(argv, importMeta, {
3318
3608
  // options or missing arguments.
3319
3609
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
3320
3610
  process.exitCode = 2;
3321
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
3322
- - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n
3323
- - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}\n`);
3611
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
3612
+
3613
+ - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}
3614
+
3615
+ - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`);
3324
3616
  return;
3325
3617
  }
3326
3618
  let bin;
@@ -3374,8 +3666,9 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3374
3666
  // logger.log(`- dst dir: \`${out}\``)
3375
3667
  logger.logger.groupEnd();
3376
3668
  }
3377
- spinner.start(`Converting sbt to maven from \`${bin}\` on \`${target}\`...`);
3378
3669
  try {
3670
+ spinner.start(`Converting sbt to maven from \`${bin}\` on \`${target}\`...`);
3671
+
3379
3672
  // Run sbt with the init script we provide which should yield zero or more
3380
3673
  // pom files. We have to figure out where to store those pom files such that
3381
3674
  // we can upload them and predict them through the GitHub API. We could do a
@@ -3398,7 +3691,8 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3398
3691
  logger.logger.error(output.stderr);
3399
3692
  logger.logger.groupEnd();
3400
3693
  }
3401
- process.exit(1);
3694
+ process.exitCode = 1;
3695
+ return;
3402
3696
  }
3403
3697
  const poms = [];
3404
3698
  output.stdout.replace(/Wrote (.*?.pom)\n/g, (_all, fn) => {
@@ -3407,7 +3701,8 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3407
3701
  });
3408
3702
  if (!poms.length) {
3409
3703
  logger.logger.error('There were no errors from sbt but it seems to not have generated any poms either');
3410
- process.exit(1);
3704
+ process.exitCode = 1;
3705
+ return;
3411
3706
  }
3412
3707
  // Move the pom file to ...? initial cwd? loc will be an absolute path, or dump to stdout
3413
3708
  // TODO: what to do with multiple output files? Do we want to dump them to stdout? Raw or with separators or ?
@@ -3421,7 +3716,8 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3421
3716
  logger.logger.error('Requested out target was stdout but there are multiple generated files');
3422
3717
  poms.forEach(fn => logger.logger.error('-', fn));
3423
3718
  logger.logger.error('Exiting now...');
3424
- process.exit(1);
3719
+ process.exitCode = 1;
3720
+ return;
3425
3721
  } else {
3426
3722
  // if (verbose) {
3427
3723
  // logger.log(
@@ -3437,14 +3733,15 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
3437
3733
  logger.logger.success(`OK`);
3438
3734
  }
3439
3735
  } catch (e) {
3440
- spinner.stop();
3441
- 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)'));
3442
3737
  if (verbose) {
3443
3738
  logger.logger.group('[VERBOSE] error:');
3444
3739
  logger.logger.log(e);
3445
3740
  logger.logger.groupEnd();
3446
3741
  }
3447
- process.exit(1);
3742
+ process.exitCode = 1;
3743
+ } finally {
3744
+ spinner.stop();
3448
3745
  }
3449
3746
  }
3450
3747
 
@@ -3552,9 +3849,11 @@ async function run$o(argv, importMeta, {
3552
3849
  // options or missing arguments.
3553
3850
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
3554
3851
  process.exitCode = 2;
3555
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
3556
- - The DIR or FILE arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n
3557
- - Can only accept one DIR or FILE (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}\n`);
3852
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
3853
+
3854
+ - The DIR or FILE arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}
3855
+
3856
+ - Can only accept one DIR or FILE (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`);
3558
3857
  return;
3559
3858
  }
3560
3859
  let bin = 'sbt';
@@ -3810,9 +4109,11 @@ async function run$m(argv, importMeta, {
3810
4109
  // options or missing arguments.
3811
4110
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
3812
4111
  process.exitCode = 2;
3813
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
3814
- - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}\n
3815
- - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}\n`);
4112
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
4113
+
4114
+ - The DIR arg is required ${!target ? colors.red('(missing!)') : target === '-' ? colors.red('(stdin is not supported)') : colors.green('(ok)')}
4115
+
4116
+ - Can only accept one DIR (make sure to escape spaces!) ${cli.input.length > 1 ? colors.red(`(received ${cli.input.length}!)`) : colors.green('(ok)')}`);
3816
4117
  return;
3817
4118
  }
3818
4119
  let bin;
@@ -5210,9 +5511,10 @@ async function run$g(argv, importMeta, {
5210
5511
  // options or missing arguments.
5211
5512
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5212
5513
  process.exitCode = 2;
5213
- logger.logger.error(`
5214
- ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
5215
- - The json and markdown flags cannot be both set, pick one
5514
+ logger.logger.error(commonTags.stripIndents`
5515
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
5516
+
5517
+ - The json and markdown flags cannot be both set, pick one
5216
5518
  `);
5217
5519
  return;
5218
5520
  }
@@ -5247,13 +5549,10 @@ const config$f = {
5247
5549
  description: `Temporarily disable the Socket ${NPM} wrapper`,
5248
5550
  hidden: false,
5249
5551
  flags: {},
5250
- help: (command, config) => `
5552
+ help: command => `
5251
5553
  Usage
5252
5554
  $ ${command} <command>
5253
5555
 
5254
- Options
5255
- ${getFlagListOutput(config.flags, 6)}
5256
-
5257
5556
  Examples
5258
5557
  $ ${command} install
5259
5558
  `
@@ -5304,13 +5603,10 @@ const config$e = {
5304
5603
  description: `Temporarily disable the Socket ${NPX} wrapper`,
5305
5604
  hidden: false,
5306
5605
  flags: {},
5307
- help: (command, config) => `
5606
+ help: command => `
5308
5607
  Usage
5309
5608
  $ ${command} <command>
5310
5609
 
5311
- Options
5312
- ${getFlagListOutput(config.flags, 6)}
5313
-
5314
5610
  Examples
5315
5611
  $ ${command} install
5316
5612
  `
@@ -5357,10 +5653,8 @@ async function createReport(socketConfig, inputPaths, {
5357
5653
  cause
5358
5654
  });
5359
5655
  });
5360
- const packagePaths = await npmPaths.getPackageFiles(cwd, inputPaths, socketConfig, supportedFiles);
5361
- const {
5362
- length: packagePathsCount
5363
- } = packagePaths;
5656
+ const packagePaths = await npmPaths.getPackageFilesFullScans(cwd, inputPaths, supportedFiles, socketConfig);
5657
+ const packagePathsCount = packagePaths.length;
5364
5658
  if (packagePathsCount && debug.isDebug()) {
5365
5659
  for (const pkgPath of packagePaths) {
5366
5660
  debug.debugLog(`Uploading: ${pkgPath}`);
@@ -5450,11 +5744,12 @@ function formatReportDataOutput(reportId, data, commandName, outputJson, outputM
5450
5744
  logger.logger.log(JSON.stringify(data, undefined, 2));
5451
5745
  } else {
5452
5746
  const format = new index.ColorOrMarkdown(outputMarkdown);
5453
- logger.logger.log('\nDetailed info on socket.dev: ' + format.hyperlink(reportId, data.url, {
5747
+ logger.logger.log(commonTags.stripIndents`
5748
+ Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {
5454
5749
  fallbackToUrl: true
5455
- }));
5750
+ })}`);
5456
5751
  if (!outputMarkdown) {
5457
- logger.logger.log(colors.dim(`\nOr rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`));
5752
+ logger.logger.log(colors.dim(`Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`));
5458
5753
  }
5459
5754
  }
5460
5755
  if (strict && !data.healthy) {
@@ -5480,7 +5775,7 @@ const {
5480
5775
  } = constants;
5481
5776
  const config$d = {
5482
5777
  commandName: 'create',
5483
- description: 'Create a project report',
5778
+ description: '[Deprecated] Create a project report',
5484
5779
  hidden: false,
5485
5780
  flags: {
5486
5781
  ...commonFlags,
@@ -5498,27 +5793,9 @@ const config$d = {
5498
5793
  description: 'Will wait for and return the created report'
5499
5794
  }
5500
5795
  },
5501
- help: (command, config) => `
5502
- Usage
5503
- $ ${command} <paths-to-package-folders-and-files>
5504
-
5505
- Uploads the specified "package.json" and lock files for JavaScript, Python, and Go dependency manifests.
5506
- If any folder is specified, the ones found in there recursively are uploaded.
5507
-
5508
- Supports globbing such as "**/package.json", "**/requirements.txt", "**/pyproject.toml", and "**/go.mod".
5509
-
5510
- Ignores any file specified in your project's ".gitignore", your project's
5511
- "socket.yml" file's "projectIgnorePaths" and also has a sensible set of
5512
- default ignores from the "ignore-by-default" module.
5513
-
5514
- Options
5515
- ${getFlagListOutput(config.flags, 6)}
5516
-
5517
- Examples
5518
- $ ${command} .
5519
- $ ${command} '**/package.json'
5520
- $ ${command} /path/to/a/package.json /path/to/another/package.json
5521
- $ ${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.
5522
5799
  `
5523
5800
  };
5524
5801
  const cmdReportCreate = {
@@ -5583,22 +5860,16 @@ const {
5583
5860
  } = constants;
5584
5861
  const config$c = {
5585
5862
  commandName: 'view',
5586
- description: 'View a project report',
5863
+ description: '[Deprecated] View a project report',
5587
5864
  hidden: false,
5588
5865
  flags: {
5589
5866
  ...commonFlags,
5590
5867
  ...outputFlags,
5591
5868
  ...validationFlags
5592
5869
  },
5593
- help: (command, config) => `
5594
- Usage
5595
- $ ${command} <report-identifier>
5596
-
5597
- Options
5598
- ${getFlagListOutput(config.flags, 6)}
5599
-
5600
- Examples
5601
- $ ${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.
5602
5873
  `
5603
5874
  };
5604
5875
  const cmdReportView = {
@@ -5623,9 +5894,11 @@ async function run$c(argv, importMeta, {
5623
5894
  // options or missing arguments.
5624
5895
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5625
5896
  process.exitCode = 2;
5626
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
5627
- - Need at least one report ID ${!reportId ? colors.red('(missing!)') : colors.green('(ok)')}\n
5628
- - Can only handle a single report ID ${extraInput.length < 2 ? colors.red(`(received ${extraInput.length}!)`) : colors.green('(ok)')}\n`);
5897
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
5898
+
5899
+ - Need at least one report ID ${!reportId ? colors.red('(missing!)') : colors.green('(ok)')}
5900
+
5901
+ - Can only handle a single report ID ${extraInput.length < 2 ? colors.red(`(received ${extraInput.length}!)`) : colors.green('(ok)')}`);
5629
5902
  return;
5630
5903
  }
5631
5904
  if (cli.flags['dryRun']) {
@@ -5644,6 +5917,8 @@ async function run$c(argv, importMeta, {
5644
5917
  const description$2 = '[Deprecated] Project report related commands';
5645
5918
  const cmdReport = {
5646
5919
  description: description$2,
5920
+ hidden: true,
5921
+ // Deprecated in favor of `scan`
5647
5922
  async run(argv, importMeta, {
5648
5923
  parentName
5649
5924
  }) {
@@ -5766,9 +6041,11 @@ async function run$b(argv, importMeta, {
5766
6041
  // options or missing arguments.
5767
6042
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5768
6043
  process.exitCode = 2;
5769
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
5770
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
5771
- - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n`);
6044
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6045
+
6046
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6047
+
6048
+ - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}`);
5772
6049
  return;
5773
6050
  }
5774
6051
  if (cli.flags['dryRun']) {
@@ -5848,10 +6125,13 @@ async function run$a(argv, importMeta, {
5848
6125
  // options or missing arguments.
5849
6126
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5850
6127
  process.exitCode = 2;
5851
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
5852
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
5853
- - Repository name as the second argument ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n
5854
- - At least one TARGET (e.g. \`.\` or \`./package.json\`\n`);
6128
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6129
+
6130
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6131
+
6132
+ - Repository name as the second argument ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}
6133
+
6134
+ - At least one TARGET (e.g. \`.\` or \`./package.json\``);
5855
6135
  return;
5856
6136
  }
5857
6137
  if (cli.flags['dryRun']) {
@@ -5993,9 +6273,11 @@ async function run$9(argv, importMeta, {
5993
6273
  // options or missing arguments.
5994
6274
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
5995
6275
  process.exitCode = 2;
5996
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
5997
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
5998
- - At least one TARGET (e.g. \`.\` or \`./package.json\`\n`);
6276
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6277
+
6278
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6279
+
6280
+ - At least one TARGET (e.g. \`.\` or \`./package.json\``);
5999
6281
  return;
6000
6282
  }
6001
6283
  if (cli.flags['dryRun']) {
@@ -6125,10 +6407,13 @@ async function run$8(argv, importMeta, {
6125
6407
  // options or missing arguments.
6126
6408
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6127
6409
  process.exitCode = 2;
6128
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6129
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
6130
- - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n
6131
- - At least one TARGET (e.g. \`.\` or \`./package.json\`\n`);
6410
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6411
+
6412
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6413
+
6414
+ - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}
6415
+
6416
+ - At least one TARGET (e.g. \`.\` or \`./package.json\``);
6132
6417
  return;
6133
6418
  }
6134
6419
  if (cli.flags['dryRun']) {
@@ -6235,9 +6520,13 @@ async function run$7(argv, importMeta, {
6235
6520
  // options or missing arguments.
6236
6521
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6237
6522
  process.exitCode = 2;
6238
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6239
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
6240
- - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}\n`);
6523
+ logger.logger.error(commonTags.stripIndents`
6524
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6525
+
6526
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6527
+
6528
+ - Repository name using --repoName ${!repoName ? colors.red('(missing!)') : typeof repoName !== 'string' ? colors.red('(invalid!)') : colors.green('(ok)')}
6529
+ `);
6241
6530
  return;
6242
6531
  }
6243
6532
  if (cli.flags['dryRun']) {
@@ -6454,53 +6743,65 @@ async function createFullScan({
6454
6743
  targets = received ?? [];
6455
6744
  updatedInput = true;
6456
6745
  }
6457
- 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
+ );
6458
6754
 
6459
6755
  // We're going to need an api token to suggest data because those suggestions
6460
6756
  // must come from data we already know. Don't error on missing api token yet.
6461
6757
  // If the api-token is not set, ignore it for the sake of suggestions.
6462
6758
  const apiToken = index.getDefaultToken();
6463
- if (apiToken && !orgSlug) {
6464
- const suggestion = await suggestOrgSlug(socketSdk);
6465
- if (suggestion) orgSlug = suggestion;
6466
- updatedInput = true;
6467
- }
6468
6759
 
6469
6760
  // If the current cwd is unknown and is used as a repo slug anyways, we will
6470
6761
  // first need to register the slug before we can use it.
6471
6762
  let repoDefaultBranch = '';
6472
-
6473
- // (Don't bother asking for the rest if we didn't get an org slug above)
6474
- if (apiToken && orgSlug && !repoName) {
6475
- const suggestion = await suggestRepoSlug(socketSdk, orgSlug);
6476
- if (suggestion) {
6477
- ({
6478
- defaultBranch: repoDefaultBranch,
6479
- slug: repoName
6480
- } = 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;
6481
6778
  }
6482
- updatedInput = true;
6483
- }
6484
6779
 
6485
- // (Don't bother asking for the rest if we didn't get an org/repo above)
6486
- if (apiToken && orgSlug && repoName && !branchName) {
6487
- const suggestion = await suggestBranchSlug(repoDefaultBranch);
6488
- if (suggestion) branchName = suggestion;
6489
- 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
+ }
6490
6786
  }
6491
6787
  if (!orgSlug || !repoName || !branchName || !packagePaths.length) {
6492
6788
  // Use exit status of 2 to indicate incorrect usage, generally invalid
6493
6789
  // options or missing arguments.
6494
6790
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6495
6791
  process$1.exitCode = 2;
6496
- logger.logger.error(`
6497
- ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6498
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
6499
- - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n
6500
- - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n
6501
- - At least one TARGET (e.g. \`.\` or \`./package.json\`) ${!packagePaths.length ? colors.red(targets.length > 0 ? '(TARGET' + (targets.length ? 's' : '') + ' contained no matching/supported files!)' : '(missing)') : colors.green('(ok)')}\n
6502
- ${!apiToken ? 'Note: was unable to make suggestions because no API Token was found; this would make command fail regardless\n' : ''}
6503
- `);
6792
+ logger.logger.error(commonTags.stripIndents`
6793
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6794
+
6795
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6796
+
6797
+ - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}
6798
+
6799
+ - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}
6800
+
6801
+ - At least one TARGET (e.g. \`.\` or \`./package.json\`) ${!packagePaths.length ? colors.red(targets.length > 0 ? '(TARGET' + (targets.length ? 's' : '') + ' contained no matching/supported files!)' : '(missing)') : colors.green('(ok)')}
6802
+
6803
+ ${!apiToken ? 'Note: was unable to make suggestions because no API Token was found; this would make command fail regardless' : ''}
6804
+ `);
6504
6805
  return;
6505
6806
  }
6506
6807
  if (updatedInput) {
@@ -6516,7 +6817,7 @@ async function createFullScan({
6516
6817
  logger.logger.log('[ReadOnly] Bailing now');
6517
6818
  return;
6518
6819
  }
6519
- spinner.start('Creating a scan...');
6820
+ spinner.start(`Creating a scan with ${packagePaths.length} packages...`);
6520
6821
  const result = await handleApiCall(socketSdk.createOrgFullScan(orgSlug, {
6521
6822
  repo: repoName,
6522
6823
  branch: branchName,
@@ -6616,13 +6917,29 @@ const config$6 = {
6616
6917
  shortFlag: 't',
6617
6918
  default: false,
6618
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.'
6619
6926
  }
6620
6927
  },
6928
+ // TODO: your project's "socket.yml" file's "projectIgnorePaths"
6621
6929
  help: (command, config) => `
6622
6930
  Usage
6623
6931
  $ ${command} [...options] <org> <TARGET> [TARGET...]
6624
6932
 
6625
- 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.
6626
6943
 
6627
6944
  When a FILE is given only that FILE is targeted. Otherwise any eligible
6628
6945
  files in the given DIR will be considered.
@@ -6654,7 +6971,8 @@ async function run$6(argv, importMeta, {
6654
6971
  branch: branchName,
6655
6972
  repo: repoName
6656
6973
  } = cli.flags;
6657
- const apiToken = index.getDefaultToken();
6974
+ const apiToken = index.getDefaultToken(); // This checks if we _can_ suggest anything
6975
+
6658
6976
  if (!apiToken && (!orgSlug || !repoName || !branchName || !targets.length)) {
6659
6977
  // Without api token we cannot recover because we can't request more info
6660
6978
  // from the server, to match and help with the current cwd/git status.
@@ -6662,13 +6980,18 @@ async function run$6(argv, importMeta, {
6662
6980
  // options or missing arguments.
6663
6981
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6664
6982
  process$1.exitCode = 2;
6665
- logger.logger.error(`
6666
- ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6667
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
6668
- - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n
6669
- - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n
6670
- - At least one TARGET (e.g. \`.\` or \`./package.json\`) ${!targets.length ? '(missing)' : colors.green('(ok)')}\n
6671
- (Additionally, no API Token was set so we cannot auto-discover these details)\n
6983
+ logger.logger.error(commonTags.stripIndents`
6984
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
6985
+
6986
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
6987
+
6988
+ - Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}
6989
+
6990
+ - Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}
6991
+
6992
+ - At least one TARGET (e.g. \`.\` or \`./package.json\`) ${!targets.length ? '(missing)' : colors.green('(ok)')}
6993
+
6994
+ (Additionally, no API Token was set so we cannot auto-discover these details)
6672
6995
  `);
6673
6996
  return;
6674
6997
  }
@@ -6695,7 +7018,14 @@ async function run$6(argv, importMeta, {
6695
7018
  });
6696
7019
  }
6697
7020
 
6698
- 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) {
6699
7029
  // Lazily access constants.spinner.
6700
7030
  const {
6701
7031
  spinner
@@ -6752,35 +7082,77 @@ async function run$5(argv, importMeta, {
6752
7082
  // options or missing arguments.
6753
7083
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6754
7084
  process.exitCode = 2;
6755
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6756
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
6757
- - Full Scan ID to delete as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
7085
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
7086
+
7087
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
7088
+
7089
+ - Full Scan ID to delete as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}`);
6758
7090
  return;
6759
7091
  }
6760
7092
  if (cli.flags['dryRun']) {
6761
7093
  logger.logger.log(DRY_RUN_BAIL_TEXT$5);
6762
7094
  return;
6763
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
+ }) {
6764
7109
  const apiToken = index.getDefaultToken();
6765
7110
  if (!apiToken) {
6766
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.');
6767
7112
  }
6768
- 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
+ });
6769
7123
  }
6770
-
6771
- // @ts-ignore
6772
- 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
+ }) {
6773
7134
  // Lazily access constants.spinner.
6774
7135
  const {
6775
7136
  spinner
6776
7137
  } = constants;
6777
- spinner.start('Listing scans...');
7138
+ spinner.start('Fetching list of scans...');
6778
7139
  const socketSdk = await index.setupSdk(apiToken);
6779
- 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');
6780
7147
  if (!result.success) {
6781
7148
  handleUnsuccessfulApiResponse('getOrgFullScanList', result, spinner);
6782
7149
  return;
6783
7150
  }
7151
+ spinner.stop(`Fetch complete`);
7152
+ if (outputKind === 'json') {
7153
+ logger.logger.log(result.data);
7154
+ return;
7155
+ }
6784
7156
  const options = {
6785
7157
  columns: [{
6786
7158
  field: 'id',
@@ -6808,7 +7180,6 @@ async function listFullScans(orgSlug, input, apiToken) {
6808
7180
  branch: d.branch
6809
7181
  };
6810
7182
  });
6811
- spinner.stop(`Listing scans for: ${orgSlug}`);
6812
7183
  logger.logger.log(chalkTable(options, formattedResults));
6813
7184
  }
6814
7185
 
@@ -6890,47 +7261,64 @@ async function run$4(argv, importMeta, {
6890
7261
  // options or missing arguments.
6891
7262
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6892
7263
  process.exitCode = 2;
6893
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6894
- - Org name as the argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
7264
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
7265
+
7266
+ - Org name as the argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}`);
6895
7267
  return;
6896
7268
  }
6897
7269
  if (cli.flags['dryRun']) {
6898
7270
  logger.logger.log(DRY_RUN_BAIL_TEXT$4);
6899
7271
  return;
6900
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) {
6901
7285
  const apiToken = index.getDefaultToken();
6902
7286
  if (!apiToken) {
6903
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.');
6904
7288
  }
6905
- await listFullScans(orgSlug,
6906
- // TODO: refine this object to what we need
6907
- {
6908
- outputJson: cli.flags['json'],
6909
- outputMarkdown: cli.flags['markdown'],
6910
- orgSlug,
6911
- sort: cli.flags['sort'],
6912
- direction: cli.flags['direction'],
6913
- per_page: cli.flags['perPage'],
6914
- page: cli.flags['page'],
6915
- from_time: cli.flags['fromTime'],
6916
- until_time: cli.flags['untilTime']
6917
- }, apiToken);
7289
+ await getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind);
6918
7290
  }
6919
-
6920
- async function getOrgScanMetadata(orgSlug, scanId, apiToken) {
7291
+ async function getOrgScanMetadataWithToken(orgSlug, scanId, apiToken, outputKind) {
6921
7292
  // Lazily access constants.spinner.
6922
7293
  const {
6923
7294
  spinner
6924
7295
  } = constants;
6925
- spinner.start("Getting scan's metadata...");
7296
+ spinner.start('Fetching meta data for a full scan...');
6926
7297
  const socketSdk = await index.setupSdk(apiToken);
6927
7298
  const result = await handleApiCall(socketSdk.getOrgFullScanMetadata(orgSlug, scanId), 'Listing scans');
6928
7299
  if (!result.success) {
6929
7300
  handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result, spinner);
6930
7301
  return;
6931
7302
  }
6932
- spinner.stop('Scan metadata:');
6933
- 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
+ }
6934
7322
  }
6935
7323
 
6936
7324
  const {
@@ -6975,44 +7363,117 @@ async function run$3(argv, importMeta, {
6975
7363
  // options or missing arguments.
6976
7364
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
6977
7365
  process.exitCode = 2;
6978
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
6979
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
6980
- - Full Scan ID to inspect as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
7366
+ logger.logger.error(commonTags.stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
7367
+
7368
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
7369
+
7370
+ - Full Scan ID to inspect as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}`);
6981
7371
  return;
6982
7372
  }
6983
7373
  if (cli.flags['dryRun']) {
6984
7374
  logger.logger.log(DRY_RUN_BAIL_TEXT$3);
6985
7375
  return;
6986
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;
6987
7385
  const apiToken = index.getDefaultToken();
6988
7386
  if (!apiToken) {
6989
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.');
6990
7388
  }
6991
- 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;
6992
7398
  }
6993
7399
 
6994
- async function getFullScan(orgSlug, fullScanId, file, apiToken) {
7400
+ async function getFullScan(orgSlug, fullScanId) {
6995
7401
  // Lazily access constants.spinner.
6996
7402
  const {
6997
7403
  spinner
6998
7404
  } = constants;
6999
- spinner.start('Streaming scan...');
7000
- const socketSdk = await index.setupSdk(apiToken);
7001
- const data = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file === '-' ? undefined : file), 'Streaming a scan');
7002
- if (data?.success) {
7003
- spinner.stop(file ? `Full scan details written to ${file}` : '');
7004
- } else {
7005
- 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;
7006
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
+ });
7007
7429
  return data;
7008
7430
  }
7009
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
+
7010
7471
  const {
7011
7472
  DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$2
7012
7473
  } = constants;
7013
7474
  const config$2 = {
7014
- commandName: 'stream',
7015
- description: 'Stream the output of a scan',
7475
+ commandName: 'view',
7476
+ description: 'View the raw results of a scan',
7016
7477
  hidden: false,
7017
7478
  flags: {
7018
7479
  ...commonFlags,
@@ -7031,7 +7492,7 @@ const config$2 = {
7031
7492
  $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt
7032
7493
  `
7033
7494
  };
7034
- const cmdScanStream = {
7495
+ const cmdScanView = {
7035
7496
  description: config$2.description,
7036
7497
  hidden: config$2.hidden,
7037
7498
  run: run$2
@@ -7051,23 +7512,27 @@ async function run$2(argv, importMeta, {
7051
7512
  // options or missing arguments.
7052
7513
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
7053
7514
  process.exitCode = 2;
7054
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
7055
- - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
7056
- - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}\n`);
7515
+ logger.logger.error(commonTags.stripIndents`
7516
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
7517
+
7518
+ - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
7519
+
7520
+ - Full Scan ID to fetch as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}
7521
+ `);
7057
7522
  return;
7058
7523
  }
7059
7524
  if (cli.flags['dryRun']) {
7060
7525
  logger.logger.log(DRY_RUN_BAIL_TEXT$2);
7061
7526
  return;
7062
7527
  }
7063
- const apiToken = index.getDefaultToken();
7064
- if (!apiToken) {
7065
- 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);
7066
7532
  }
7067
- await getFullScan(orgSlug, fullScanId, file, apiToken);
7068
7533
  }
7069
7534
 
7070
- const description = 'Scans related commands';
7535
+ const description = 'Full Scan related commands';
7071
7536
  const cmdScan = {
7072
7537
  description,
7073
7538
  async run(argv, importMeta, {
@@ -7075,11 +7540,19 @@ const cmdScan = {
7075
7540
  }) {
7076
7541
  await meowWithSubcommands({
7077
7542
  create: cmdScanCreate,
7078
- stream: cmdScanStream,
7079
7543
  list: cmdScanList,
7080
7544
  del: cmdScanDel,
7081
- metadata: cmdScanMetadata
7545
+ metadata: cmdScanMetadata,
7546
+ view: cmdScanView
7082
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
+ },
7083
7556
  argv,
7084
7557
  description,
7085
7558
  importMeta,
@@ -7252,7 +7725,7 @@ function addSocketWrapper(file) {
7252
7725
  }
7253
7726
  // TODO: pretty sure you need to source the file or restart
7254
7727
  // any terminal session before changes are reflected.
7255
- logger.logger.log(`
7728
+ logger.logger.log(commonTags.stripIndents`
7256
7729
  The alias was added to ${file}. Running 'npm install' will now be wrapped in Socket's "safe npm" 🎉
7257
7730
  If you want to disable it at any time, run \`socket wrapper --disable\`
7258
7731
  `);
@@ -7405,8 +7878,11 @@ async function run(argv, importMeta, {
7405
7878
  // options or missing arguments.
7406
7879
  // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
7407
7880
  process.exitCode = 2;
7408
- logger.logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required flags:\n
7409
- - Must use --enabled or --disabled\n`);
7881
+ logger.logger.error(commonTags.stripIndents`
7882
+ ${colors.bgRed(colors.white('Input error'))}: Please provide the required flags:
7883
+
7884
+ - Must use --enabled or --disabled
7885
+ `);
7410
7886
  return;
7411
7887
  }
7412
7888
  if (cli.flags['dryRun']) {
@@ -7515,5 +7991,5 @@ void (async () => {
7515
7991
  await index.captureException(e);
7516
7992
  }
7517
7993
  })();
7518
- //# debugId=f23df080-286e-4174-a361-db1fa42ece1
7994
+ //# debugId=c0b42b8b-128e-4ec9-a7f4-e4313aab0875
7519
7995
  //# sourceMappingURL=cli.js.map