@socketsecurity/cli 0.9.3 → 0.10.0

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.
@@ -0,0 +1,115 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import chalk from 'chalk'
4
+ import meow from 'meow'
5
+ import ora from 'ora'
6
+
7
+ import { outputFlags } from '../../flags/index.js'
8
+ import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
9
+ import { printFlagList } from '../../utils/formatting.js'
10
+ import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
11
+
12
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
13
+ export const stream = {
14
+ description: 'Stream the output of a scan',
15
+ async run (argv, importMeta, { parentName }) {
16
+ const name = parentName + ' stream'
17
+
18
+ const input = setupCommand(name, stream.description, argv, importMeta)
19
+ if (input) {
20
+ const spinnerText = 'Streaming scan... \n'
21
+ const spinner = ora(spinnerText).start()
22
+ await getOrgFullScan(input.orgSlug, input.fullScanId, input.file, spinner)
23
+ }
24
+ }
25
+ }
26
+
27
+ // Internal functions
28
+
29
+ /**
30
+ * @typedef CommandContext
31
+ * @property {boolean} outputJson
32
+ * @property {boolean} outputMarkdown
33
+ * @property {string} orgSlug
34
+ * @property {string} fullScanId
35
+ * @property {string | undefined} file
36
+ */
37
+
38
+ /**
39
+ * @param {string} name
40
+ * @param {string} description
41
+ * @param {readonly string[]} argv
42
+ * @param {ImportMeta} importMeta
43
+ * @returns {void|CommandContext}
44
+ */
45
+ function setupCommand (name, description, argv, importMeta) {
46
+ const flags = {
47
+ ...outputFlags
48
+ }
49
+
50
+ const cli = meow(`
51
+ Usage
52
+ $ ${name} <org slug> <scan ID> <path to output file>
53
+
54
+ Options
55
+ ${printFlagList(flags, 6)}
56
+
57
+ Examples
58
+ $ ${name} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 ./stream.txt
59
+ `, {
60
+ argv,
61
+ description,
62
+ importMeta,
63
+ flags
64
+ })
65
+
66
+ const {
67
+ json: outputJson,
68
+ markdown: outputMarkdown,
69
+ } = cli.flags
70
+
71
+ if (cli.input.length < 2) {
72
+ console.error(`${chalk.bgRed('Input error')}: Please specify an organization slug and a scan ID.\n`)
73
+ cli.showHelp()
74
+ return
75
+ }
76
+
77
+ const [orgSlug = '', fullScanId = '', file] = cli.input
78
+
79
+ return {
80
+ outputJson,
81
+ outputMarkdown,
82
+ orgSlug,
83
+ fullScanId,
84
+ file
85
+ }
86
+ }
87
+
88
+ /**
89
+ * @typedef FullScanData
90
+ * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getOrgFullScan'>["data"]} data
91
+ */
92
+
93
+ /**
94
+ * @param {string} orgSlug
95
+ * @param {string} fullScanId
96
+ * @param {string | undefined} file
97
+ * @param {import('ora').Ora} spinner
98
+ * @returns {Promise<void|FullScanData>}
99
+ */
100
+ async function getOrgFullScan (orgSlug, fullScanId, file, spinner) {
101
+ const socketSdk = await setupSdk(getDefaultKey())
102
+ const result = await handleApiCall(socketSdk.getOrgFullScan(orgSlug, fullScanId, file), 'Streaming a scan')
103
+
104
+ if (!result?.success) {
105
+ return handleUnsuccessfulApiResponse('getOrgFullScan', result, spinner)
106
+ }
107
+
108
+ spinner.stop()
109
+
110
+ console.log(file ? `\nFull scan details written to ${file} \n` : '\nFull scan details: \n')
111
+
112
+ return {
113
+ data: result.data
114
+ }
115
+ }
@@ -11,7 +11,7 @@ import { printFlagList } from '../../utils/formatting.js'
11
11
  const BASH_FILE = `${homedir.homedir()}/.bashrc`
12
12
  const ZSH_BASH_FILE = `${homedir.homedir()}/.zshrc`
13
13
 
14
- /** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
14
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
15
15
  export const wrapper = {
16
16
  description: 'Enable or disable the Socket npm/npx wrapper',
17
17
  async run (argv, importMeta, { parentName }) {
@@ -268,7 +268,17 @@ function findRoot (filepath) {
268
268
  }
269
269
  const npmDir = findRoot(path.dirname(npmEntrypoint))
270
270
  const arboristLibClassPath = path.join(npmDir, 'node_modules', '@npmcli', 'arborist', 'lib', 'arborist', 'index.js')
271
- const npmlog = require(path.join(npmDir, 'node_modules', 'npmlog', 'lib', 'log.js'))
271
+
272
+ const npmVersion = process.env.NPM_VERSION.split('.')
273
+ let npmlog
274
+
275
+ if(npmVersion[0] === '10' && npmVersion[1] >= '6'){
276
+ const { log } = require(path.join(npmDir, 'node_modules', 'proc-log', 'lib', 'index.js'))
277
+ npmlog = log
278
+ } else {
279
+ npmlog = require(path.join(npmDir, 'node_modules', 'npmlog', 'lib', 'log.js'))
280
+ }
281
+
272
282
  /**
273
283
  * @type {import('pacote')}
274
284
  */
@@ -27,7 +27,7 @@ const SEVERITIES_BY_ORDER = /** @type {const} */ ([
27
27
 
28
28
  return result
29
29
  }
30
-
30
+ /* TODO: Delete this function when we remove the report command */
31
31
  /**
32
32
  * @param {SocketIssueList} issues
33
33
  * @param {SocketIssue['severity']} [lowestToInclude]
@@ -54,6 +54,33 @@ export function getSeverityCount (issues, lowestToInclude) {
54
54
  return severityCount
55
55
  }
56
56
 
57
+ /* The following function is the updated one */
58
+ /**
59
+ * @param {Array<SocketIssue>} issues
60
+ * @param {SocketIssue['severity']} [lowestToInclude]
61
+ * @returns {Record<SocketIssue['severity'], number>}
62
+ */
63
+ export function getCountSeverity (issues, lowestToInclude) {
64
+ const severityCount = pick(
65
+ { low: 0, middle: 0, high: 0, critical: 0 },
66
+ getDesiredSeverities(lowestToInclude)
67
+ )
68
+
69
+ for (const issue of issues) {
70
+ const severity = issue.severity
71
+
72
+ if (!severity) {
73
+ continue
74
+ }
75
+
76
+ if (severityCount[severity] !== undefined) {
77
+ severityCount[severity] += 1
78
+ }
79
+ }
80
+
81
+ return severityCount
82
+ }
83
+
57
84
  /**
58
85
  * @param {Record<SocketIssue['severity'], number>} severityCount
59
86
  * @returns {string}
@@ -25,9 +25,8 @@ import { printFlagList, printHelpList } from './formatting.js'
25
25
  */
26
26
 
27
27
  /**
28
- * @template {import('meow').AnyFlags} Flags
29
28
  * @param {Record<string, CliSubcommand>} subcommands
30
- * @param {import('meow').Options<Flags> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
29
+ * @param {import('meow').Options<any> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
31
30
  * @returns {Promise<void>}
32
31
  */
33
32
  export async function meowWithSubcommands (subcommands, options) {
package/lib/utils/misc.js CHANGED
@@ -30,7 +30,6 @@ export function stringJoinWithSeparateFinalSeparator (list, separator = ' and ')
30
30
 
31
31
  /**
32
32
  * Returns a new object with only the specified keys from the input object
33
- *
34
33
  * @template {Record<string,any>} T
35
34
  * @template {keyof T} K
36
35
  * @param {T} input
@@ -12,7 +12,6 @@ import { isErrnoException } from './type-helpers.cjs'
12
12
 
13
13
  /**
14
14
  * There are a lot of possible folders that we should not be looking in and "ignore-by-default" helps us with defining those
15
- *
16
15
  * @type {readonly string[]}
17
16
  */
18
17
  const ignoreByDefault = directories()
@@ -31,7 +30,6 @@ const BASE_GLOBBY_OPTS = {
31
30
 
32
31
  /**
33
32
  * Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
34
- *
35
33
  * @param {string} cwd The working directory to use when resolving paths
36
34
  * @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
37
35
  * @param {import('@socketsecurity/config').SocketYml|undefined} config
@@ -57,18 +55,46 @@ export async function getPackageFiles (cwd, inputPaths, config, supportedFiles,
57
55
  debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)
58
56
 
59
57
  const includedPackageFiles = config?.projectIgnorePaths?.length
58
+ // @ts-ignore
60
59
  ? ignore()
61
60
  .add(config.projectIgnorePaths)
62
61
  .filter(packageFiles.map(item => path.relative(cwd, item)))
63
- .map(item => path.resolve(cwd, item))
62
+ .map((/** @type {string} */ item) => path.resolve(cwd, item))
64
63
  : packageFiles
65
64
 
66
65
  return includedPackageFiles
67
66
  }
68
67
 
68
+ /**
69
+ * Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
70
+ * @param {string} cwd The working directory to use when resolving paths
71
+ * @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
72
+ * @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
73
+ * @param {typeof console.error} debugLog
74
+ * @returns {Promise<string[]>}
75
+ * @throws {InputError}
76
+ */
77
+ export async function getPackageFilesFullScans (cwd, inputPaths, supportedFiles, debugLog) {
78
+ debugLog(`Globbed resolving ${inputPaths.length} paths:`, inputPaths)
79
+
80
+ // TODO: Does not support `~/` paths
81
+ const entries = await globby(inputPaths, {
82
+ ...BASE_GLOBBY_OPTS,
83
+ cwd,
84
+ onlyFiles: false
85
+ })
86
+
87
+ debugLog(`Globbed resolved ${inputPaths.length} paths to ${entries.length} paths:`, entries)
88
+
89
+ const packageFiles = await mapGlobResultToFiles(entries, supportedFiles)
90
+
91
+ debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)
92
+
93
+ return packageFiles
94
+ }
95
+
69
96
  /**
70
97
  * Takes paths to folders, package.json and/or recognized lock files and resolves them to package.json + lockfile pairs (where possible)
71
- *
72
98
  * @param {string[]} entries
73
99
  * @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
74
100
  * @returns {Promise<string[]>}
@@ -86,7 +112,6 @@ export async function mapGlobResultToFiles (entries, supportedFiles) {
86
112
 
87
113
  /**
88
114
  * Takes a single path to a folder, package.json or a recognized lock file and resolves to a package.json + lockfile pair (where possible)
89
- *
90
115
  * @param {string} entry
91
116
  * @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getReportSupportedFiles'>['data']} supportedFiles
92
117
  * @returns {Promise<string[]>}
package/lib/utils/sdk.js CHANGED
@@ -13,7 +13,6 @@ export const FREE_API_KEY = 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo
13
13
 
14
14
  /**
15
15
  * This API key should be stored globally for the duration of the CLI execution
16
- *
17
16
  * @type {string | undefined}
18
17
  */
19
18
  let defaultKey
@@ -26,7 +25,6 @@ export function getDefaultKey () {
26
25
 
27
26
  /**
28
27
  * The API server that should be used for operations
29
- *
30
28
  * @type {string | undefined}
31
29
  */
32
30
  let defaultAPIBaseUrl
@@ -41,7 +39,6 @@ export function getDefaultAPIBaseUrl () {
41
39
 
42
40
  /**
43
41
  * The API server that should be used for operations
44
- *
45
42
  * @type {string | undefined}
46
43
  */
47
44
  let defaultApiProxy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/cli",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "CLI tool for Socket.dev",
5
5
  "homepage": "http://github.com/SocketDev/socket-cli-js",
6
6
  "repository": {
@@ -14,9 +14,6 @@
14
14
  "url": "https://socket.dev"
15
15
  },
16
16
  "license": "MIT",
17
- "engines": {
18
- "node": "^16.13.0 || >=18.0.0"
19
- },
20
17
  "type": "module",
21
18
  "bin": {
22
19
  "socket": "cli.js",
@@ -30,8 +27,83 @@
30
27
  "lib/**/*.cjs",
31
28
  "lib/shadow/**"
32
29
  ],
30
+ "dependencies": {
31
+ "@apideck/better-ajv-errors": "^0.3.6",
32
+ "@cyclonedx/cdxgen": "^10.7.0",
33
+ "@inquirer/select": "^2.3.5",
34
+ "@socketsecurity/config": "^2.1.3",
35
+ "@socketsecurity/sdk": "^1.2.0",
36
+ "chalk": "^5.3.0",
37
+ "chalk-table": "^1.0.2",
38
+ "execa": "^9.1.0",
39
+ "globby": "^14.0.1",
40
+ "hpagent": "^1.2.0",
41
+ "ignore": "^5.3.1",
42
+ "ignore-by-default": "^2.1.0",
43
+ "inquirer": "^9.2.23",
44
+ "is-interactive": "^2.0.0",
45
+ "is-unicode-supported": "^2.0.0",
46
+ "meow": "^13.2.0",
47
+ "open": "^10.1.0",
48
+ "ora": "^8.0.1",
49
+ "pony-cause": "^2.1.11",
50
+ "prompts": "^2.4.2",
51
+ "synp": "^1.9.13",
52
+ "terminal-link": "^3.0.0",
53
+ "update-notifier": "^7.0.0",
54
+ "which": "^4.0.0",
55
+ "yargs-parser": "^21.1.1"
56
+ },
57
+ "devDependencies": {
58
+ "@socketsecurity/eslint-config": "^5.0.1",
59
+ "@tsconfig/node20": "^20.1.4",
60
+ "@types/chai": "^4.3.16",
61
+ "@types/chai-as-promised": "^7.1.8",
62
+ "@types/inquirer": "^9.0.7",
63
+ "@types/micromatch": "^4.0.7",
64
+ "@types/mocha": "^10.0.6",
65
+ "@types/mock-fs": "^4.13.4",
66
+ "@types/node": "^20.12.13",
67
+ "@types/npm": "^7.19.3",
68
+ "@types/npmcli__arborist": "^5.6.6",
69
+ "@types/prompts": "^2.4.9",
70
+ "@types/update-notifier": "^6.0.8",
71
+ "@types/which": "^3.0.4",
72
+ "@types/yargs-parser": "^21.0.3",
73
+ "@typescript-eslint/eslint-plugin": "^7.11.0",
74
+ "@typescript-eslint/parser": "7.10.0",
75
+ "c8": "^10.1.2",
76
+ "dependency-check": "^5.0.0-7",
77
+ "eslint": "^8.56.0",
78
+ "eslint-config-standard": "^17.1.0",
79
+ "eslint-config-standard-jsx": "^11.0.0",
80
+ "eslint-import-resolver-typescript": "^3.6.1",
81
+ "eslint-plugin-import": "^2.29.1",
82
+ "eslint-plugin-jsdoc": "^48.2.7",
83
+ "eslint-plugin-n": "^16.6.2",
84
+ "eslint-plugin-promise": "^6.2.0",
85
+ "eslint-plugin-react": "^7.34.2",
86
+ "eslint-plugin-react-hooks": "^4.6.2",
87
+ "eslint-plugin-unicorn": "^48.0.1",
88
+ "husky": "^9.0.11",
89
+ "installed-check": "^9.3.0",
90
+ "mock-fs": "^5.2.0",
91
+ "nock": "^13.5.4",
92
+ "npm-run-all2": "^6.2.0",
93
+ "type-coverage": "^2.29.0",
94
+ "typescript": "~5.5.2"
95
+ },
96
+ "overrides": {
97
+ "@cyclonedx/cdxgen": {
98
+ "packageurl-js": "https://registry.npmjs.org/@jdalton/packageurl-js/-/packageurl-js-1.2.7.tgz"
99
+ }
100
+ },
101
+ "engines": {
102
+ "node": "^20.9.0 || >=21.1.0"
103
+ },
104
+
33
105
  "scripts": {
34
- "check:dependency-check": "dependency-check '*.js' 'lib/shadow/*.cjs' '*.mjs' 'test/**/*.js' --no-dev --ignore-module node:test --ignore-module node:assert/strict",
106
+ "check:dependency-check": "dependency-check '*.js' 'lib/shadow/*.cjs' '*.mjs' 'test/**/*.js' --no-dev --ignore-module node:* --ignore-module @cyclonedx/* --ignore-module synp",
35
107
  "check:installed-check": "installed-check -i eslint-plugin-jsdoc",
36
108
  "check:lint": "eslint --report-unused-disable-directives .",
37
109
  "check:tsc": "tsc",
@@ -42,61 +114,5 @@
42
114
  "test-ci": "run-s test:*",
43
115
  "test": "run-s check test:*",
44
116
  "//postinstall": "node ./cli.js wrapper --postinstall"
45
- },
46
- "devDependencies": {
47
- "@socketsecurity/eslint-config": "^3.0.1",
48
- "@tsconfig/node14": "^14.1.0",
49
- "@types/chai": "^4.3.3",
50
- "@types/chai-as-promised": "^7.1.5",
51
- "@types/micromatch": "^4.0.2",
52
- "@types/mocha": "^10.0.1",
53
- "@types/mock-fs": "^4.13.1",
54
- "@types/node": "^20.4.2",
55
- "@types/npm": "^7.19.0",
56
- "@types/npmcli__arborist": "^5.6.1",
57
- "@types/prompts": "^2.4.1",
58
- "@types/update-notifier": "^6.0.2",
59
- "@types/which": "^3.0.0",
60
- "@typescript-eslint/eslint-plugin": "^5.51.0",
61
- "@typescript-eslint/parser": "^5.51.0",
62
- "c8": "^8.0.0",
63
- "dependency-check": "^5.0.0-7",
64
- "eslint": "^8.34.0",
65
- "eslint-config-standard": "^17.0.0",
66
- "eslint-config-standard-jsx": "^11.0.0",
67
- "eslint-import-resolver-typescript": "^3.5.3",
68
- "eslint-plugin-import": "^2.27.5",
69
- "eslint-plugin-jsdoc": "^40.0.0",
70
- "eslint-plugin-n": "^15.6.1",
71
- "eslint-plugin-promise": "^6.1.1",
72
- "eslint-plugin-react": "^7.32.2",
73
- "eslint-plugin-react-hooks": "^4.6.0",
74
- "eslint-plugin-unicorn": "^45.0.2",
75
- "husky": "^8.0.1",
76
- "installed-check": "^6.0.5",
77
- "mock-fs": "^5.2.0",
78
- "nock": "^13.3.0",
79
- "npm-run-all2": "^6.0.2",
80
- "type-coverage": "^2.24.1",
81
- "typescript": "~5.1.6"
82
- },
83
- "dependencies": {
84
- "@apideck/better-ajv-errors": "^0.3.6",
85
- "@socketsecurity/config": "^2.0.0",
86
- "@socketsecurity/sdk": "^0.7.3",
87
- "chalk": "^5.1.2",
88
- "globby": "^13.1.3",
89
- "hpagent": "^1.2.0",
90
- "ignore": "^5.2.1",
91
- "ignore-by-default": "^2.1.0",
92
- "is-interactive": "^2.0.0",
93
- "is-unicode-supported": "^1.3.0",
94
- "meow": "^12.0.1",
95
- "ora": "^6.1.2",
96
- "pony-cause": "^2.1.8",
97
- "prompts": "^2.4.2",
98
- "terminal-link": "^3.0.0",
99
- "update-notifier": "^6.0.2",
100
- "which": "^3.0.0"
101
117
  }
102
118
  }