@socketsecurity/cli 0.4.0 → 0.4.2-provenance

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.
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2022 Socket Inc
4
4
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Socket CLI
2
2
 
3
- [![Socket Badge](https://socket.dev/api/badge/npm/pkg/@socketsecurity/cli)](https://socket.dev/npm/package/@socketsecurity/cli)
3
+ [![Socket Badge](https://socket.dev/api/badge/npm/package/@socketsecurity/cli)](https://socket.dev/npm/package/@socketsecurity/cli)
4
4
  [![npm version](https://img.shields.io/npm/v/@socketsecurity/cli.svg?style=flat)](https://www.npmjs.com/package/@socketsecurity/cli)
5
5
  [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](https://github.com/SocketDev/eslint-config)
6
6
  [![Follow @SocketSecurity](https://img.shields.io/twitter/follow/SocketSecurity?style=social)](https://twitter.com/SocketSecurity)
@@ -34,6 +34,12 @@ socket report view QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
34
34
 
35
35
  * `socket report view <report-id>` - looks up issues and scores from a report
36
36
 
37
+ ## Aliases
38
+
39
+ All aliases supports flags and arguments of the commands they alias.
40
+
41
+ * `socket ci` - alias for `socket report create --view --strict` which creates a report and quits with an exit code if the result is unhealthy. Use like eg. `socket ci .` for a report for the current folder
42
+
37
43
  ## Flags
38
44
 
39
45
  ### Command specific flags
package/cli.js CHANGED
@@ -18,6 +18,12 @@ try {
18
18
  await meowWithSubcommands(
19
19
  cliCommands,
20
20
  {
21
+ aliases: {
22
+ ci: {
23
+ description: 'Alias for "report create --view --strict"',
24
+ argv: ['report', 'create', '--view', '--strict']
25
+ },
26
+ },
21
27
  argv: process.argv.slice(2),
22
28
  name: 'socket',
23
29
  importMeta: import.meta
@@ -4,6 +4,7 @@ import chalk from 'chalk'
4
4
  import meow from 'meow'
5
5
  import ora from 'ora'
6
6
 
7
+ import { outputFlags, validationFlags } from '../../flags/index.js'
7
8
  import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
8
9
  import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
9
10
  import { InputError } from '../../utils/errors.js'
@@ -47,17 +48,17 @@ export const info = {
47
48
  * @returns {void|CommandContext}
48
49
  */
49
50
  function setupCommand (name, description, argv, importMeta) {
51
+ const flags = {
52
+ ...outputFlags,
53
+ ...validationFlags,
54
+ }
55
+
50
56
  const cli = meow(`
51
57
  Usage
52
58
  $ ${name} <name>
53
59
 
54
60
  Options
55
- ${printFlagList({
56
- '--all': 'Include all issues',
57
- '--json': 'Output result as json',
58
- '--markdown': 'Output result as markdown',
59
- '--strict': 'Exits with an error code if any matching issues are found',
60
- }, 6)}
61
+ ${printFlagList(flags, 6)}
61
62
 
62
63
  Examples
63
64
  $ ${name} webtorrent
@@ -66,26 +67,7 @@ function setupCommand (name, description, argv, importMeta) {
66
67
  argv,
67
68
  description,
68
69
  importMeta,
69
- flags: {
70
- all: {
71
- type: 'boolean',
72
- default: false,
73
- },
74
- json: {
75
- type: 'boolean',
76
- alias: 'j',
77
- default: false,
78
- },
79
- markdown: {
80
- type: 'boolean',
81
- alias: 'm',
82
- default: false,
83
- },
84
- strict: {
85
- type: 'boolean',
86
- default: false,
87
- },
88
- }
70
+ flags
89
71
  })
90
72
 
91
73
  const {
@@ -147,7 +129,7 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
147
129
  const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), spinner, 'looking up package')
148
130
 
149
131
  if (result.success === false) {
150
- return handleUnsuccessfulApiResponse(result, spinner)
132
+ return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result, spinner)
151
133
  }
152
134
 
153
135
  // Conclude the status of the API call
@@ -9,9 +9,11 @@ import ora from 'ora'
9
9
  import { ErrorWithCause } from 'pony-cause'
10
10
 
11
11
  import { fetchReportData, formatReportDataOutput } from './view.js'
12
+ import { outputFlags, validationFlags } from '../../flags/index.js'
12
13
  import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
13
14
  import { ChalkOrMarkdown, logSymbols } from '../../utils/chalk-markdown.js'
14
15
  import { InputError } from '../../utils/errors.js'
16
+ import { prepareFlags } from '../../utils/flags.js'
15
17
  import { printFlagList } from '../../utils/formatting.js'
16
18
  import { createDebugLogger } from '../../utils/misc.js'
17
19
  import { getPackageFiles } from '../../utils/path-resolve.js'
@@ -79,6 +81,28 @@ export const create = {
79
81
  * @returns {Promise<void|CommandContext>}
80
82
  */
81
83
  async function setupCommand (name, description, argv, importMeta) {
84
+ const flags = prepareFlags({
85
+ ...outputFlags,
86
+ ...validationFlags,
87
+ debug: {
88
+ type: 'boolean',
89
+ alias: 'd',
90
+ default: false,
91
+ description: 'Output debug information',
92
+ },
93
+ dryRun: {
94
+ type: 'boolean',
95
+ default: false,
96
+ description: 'Only output what will be done without actually doing it',
97
+ },
98
+ view: {
99
+ type: 'boolean',
100
+ alias: 'v',
101
+ default: false,
102
+ description: 'Will wait for and return the created report'
103
+ },
104
+ })
105
+
82
106
  const cli = meow(`
83
107
  Usage
84
108
  $ ${name} <paths-to-package-folders-and-files>
@@ -114,40 +138,7 @@ async function setupCommand (name, description, argv, importMeta) {
114
138
  argv,
115
139
  description,
116
140
  importMeta,
117
- flags: {
118
- all: {
119
- type: 'boolean',
120
- default: false,
121
- },
122
- debug: {
123
- type: 'boolean',
124
- alias: 'd',
125
- default: false,
126
- },
127
- dryRun: {
128
- type: 'boolean',
129
- default: false,
130
- },
131
- json: {
132
- type: 'boolean',
133
- alias: 'j',
134
- default: false,
135
- },
136
- markdown: {
137
- type: 'boolean',
138
- alias: 'm',
139
- default: false,
140
- },
141
- strict: {
142
- type: 'boolean',
143
- default: false,
144
- },
145
- view: {
146
- type: 'boolean',
147
- alias: 'v',
148
- default: false,
149
- },
150
- }
141
+ flags,
151
142
  })
152
143
 
153
144
  const {
@@ -224,7 +215,7 @@ async function createReport (packagePaths, { config, cwd, debugLog, dryRun }) {
224
215
  const result = await handleApiCall(apiCall, spinner, 'creating report')
225
216
 
226
217
  if (result.success === false) {
227
- return handleUnsuccessfulApiResponse(result, spinner)
218
+ return handleUnsuccessfulApiResponse('createReport', result, spinner)
228
219
  }
229
220
 
230
221
  // Conclude the status of the API call
@@ -4,6 +4,7 @@ import chalk from 'chalk'
4
4
  import meow from 'meow'
5
5
  import ora from 'ora'
6
6
 
7
+ import { outputFlags, validationFlags } from '../../flags/index.js'
7
8
  import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
8
9
  import { ChalkOrMarkdown } from '../../utils/chalk-markdown.js'
9
10
  import { InputError } from '../../utils/errors.js'
@@ -28,7 +29,6 @@ export const view = {
28
29
 
29
30
  // Internal functions
30
31
 
31
- // TODO: Share more of the flag setup inbetween the commands
32
32
  /**
33
33
  * @typedef CommandContext
34
34
  * @property {boolean} includeAllIssues
@@ -46,17 +46,17 @@ export const view = {
46
46
  * @returns {void|CommandContext}
47
47
  */
48
48
  function setupCommand (name, description, argv, importMeta) {
49
+ const flags = {
50
+ ...outputFlags,
51
+ ...validationFlags,
52
+ }
53
+
49
54
  const cli = meow(`
50
55
  Usage
51
56
  $ ${name} <report-identifier>
52
57
 
53
58
  Options
54
- ${printFlagList({
55
- '--all': 'Include all issues',
56
- '--json': 'Output result as json',
57
- '--markdown': 'Output result as markdown',
58
- '--strict': 'Exits with an error code if report result is deemed unhealthy',
59
- }, 6)}
59
+ ${printFlagList(flags, 6)}
60
60
 
61
61
  Examples
62
62
  $ ${name} QXU8PmK7LfH608RAwfIKdbcHgwEd_ZeWJ9QEGv05FJUQ
@@ -64,26 +64,7 @@ function setupCommand (name, description, argv, importMeta) {
64
64
  argv,
65
65
  description,
66
66
  importMeta,
67
- flags: {
68
- all: {
69
- type: 'boolean',
70
- default: false,
71
- },
72
- json: {
73
- type: 'boolean',
74
- alias: 'j',
75
- default: false,
76
- },
77
- markdown: {
78
- type: 'boolean',
79
- alias: 'm',
80
- default: false,
81
- },
82
- strict: {
83
- type: 'boolean',
84
- default: false,
85
- },
86
- }
67
+ flags,
87
68
  })
88
69
 
89
70
  // Extract the input
@@ -134,7 +115,7 @@ export async function fetchReportData (reportId, { includeAllIssues, strict }) {
134
115
  const result = await handleApiCall(socketSdk.getReport(reportId), spinner, 'fetching report')
135
116
 
136
117
  if (result.success === false) {
137
- return handleUnsuccessfulApiResponse(result, spinner)
118
+ return handleUnsuccessfulApiResponse('getReport', result, spinner)
138
119
  }
139
120
 
140
121
  // Conclude the status of the API call
@@ -0,0 +1,2 @@
1
+ export { outputFlags } from './output.js'
2
+ export { validationFlags } from './validation.js'
@@ -0,0 +1,16 @@
1
+ import { prepareFlags } from '../utils/flags.js'
2
+
3
+ export const outputFlags = prepareFlags({
4
+ json: {
5
+ type: 'boolean',
6
+ alias: 'j',
7
+ default: false,
8
+ description: 'Output result as json',
9
+ },
10
+ markdown: {
11
+ type: 'boolean',
12
+ alias: 'm',
13
+ default: false,
14
+ description: 'Output result as markdown',
15
+ },
16
+ })
@@ -0,0 +1,14 @@
1
+ import { prepareFlags } from '../utils/flags.js'
2
+
3
+ export const validationFlags = prepareFlags({
4
+ all: {
5
+ type: 'boolean',
6
+ default: false,
7
+ description: 'Include all issues',
8
+ },
9
+ strict: {
10
+ type: 'boolean',
11
+ default: false,
12
+ description: 'Exits with an error code if any matching issues are found',
13
+ },
14
+ })
@@ -4,12 +4,13 @@ import { ErrorWithCause } from 'pony-cause'
4
4
  import { AuthError } from './errors.js'
5
5
 
6
6
  /**
7
- * @template T
7
+ * @template {import('@socketsecurity/sdk').SocketSdkOperations} T
8
+ * @param {T} _name
8
9
  * @param {import('@socketsecurity/sdk').SocketSdkErrorType<T>} result
9
10
  * @param {import('ora').Ora} spinner
10
11
  * @returns {void}
11
12
  */
12
- export function handleUnsuccessfulApiResponse (result, spinner) {
13
+ export function handleUnsuccessfulApiResponse (_name, result, spinner) {
13
14
  const resultError = 'error' in result && result.error && typeof result.error === 'object' ? result.error : {}
14
15
  const message = 'message' in resultError && typeof resultError.message === 'string' ? resultError.message : 'No error message returned'
15
16
 
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @typedef FlagExtensions
3
+ * @property {string} description
4
+ */
5
+
6
+ /**
7
+ * @template {import('meow').FlagType} Type
8
+ * @template Default
9
+ * @template {boolean} [IsMultiple=false]
10
+ * @typedef {import('meow').Flag<Type, Default, IsMultiple> & FlagExtensions} Flag
11
+ */
12
+
13
+ /** @typedef {Flag<'string', string> | Flag<'string', string[], true>} StringFlag */
14
+ /** @typedef {Flag<'boolean', boolean> | Flag<'boolean', boolean[], true>} BooleanFlag */
15
+ /** @typedef {Flag<'number', number> | Flag<'number', number[], true>} NumberFlag */
16
+ /** @typedef {StringFlag | BooleanFlag | NumberFlag} AnyFlag */
17
+ /** @typedef {Record<string, AnyFlag>} AnyFlags */
18
+
19
+ /**
20
+ * @template {AnyFlags} Flags
21
+ * @param {Flags} flags
22
+ * @returns {Readonly<Flags>}
23
+ */
24
+ export function prepareFlags (flags) {
25
+ // As we can't do "satisfies AnyFlags" in JS yet (+ we add a bonus through Readonly<>)
26
+ return flags
27
+ }
@@ -1,12 +1,23 @@
1
1
  /** @typedef {string|{ description: string }} ListDescription */
2
2
 
3
+ /**
4
+ * @typedef HelpListOptions
5
+ * @property {string} [keyPrefix]
6
+ * @property {number} [padName]
7
+ */
8
+
3
9
  /**
4
10
  * @param {Record<string,ListDescription>} list
5
11
  * @param {number} indent
6
- * @param {number} padName
12
+ * @param {HelpListOptions} options
7
13
  * @returns {string}
8
14
  */
9
- export function printHelpList (list, indent, padName = 18) {
15
+ export function printHelpList (list, indent, options = {}) {
16
+ const {
17
+ keyPrefix = '',
18
+ padName = 18,
19
+ } = options
20
+
10
21
  const names = Object.keys(list).sort()
11
22
 
12
23
  let result = ''
@@ -15,22 +26,22 @@ export function printHelpList (list, indent, padName = 18) {
15
26
  const rawDescription = list[name]
16
27
  const description = (typeof rawDescription === 'object' ? rawDescription.description : rawDescription) || ''
17
28
 
18
- result += ''.padEnd(indent) + name.padEnd(padName) + description + '\n'
29
+ result += ''.padEnd(indent) + (keyPrefix + name).padEnd(padName) + description + '\n'
19
30
  }
20
31
 
21
32
  return result.trim()
22
33
  }
23
34
 
24
35
  /**
25
- * @param {Record<string,ListDescription>} list
36
+ * @param {Record<string, ListDescription>} list
26
37
  * @param {number} indent
27
- * @param {number} padName
38
+ * @param {HelpListOptions} options
28
39
  * @returns {string}
29
40
  */
30
- export function printFlagList (list, indent, padName = 18) {
41
+ export function printFlagList (list, indent, options = {}) {
31
42
  return printHelpList({
32
- '--help': 'Print this help and exits.',
33
- '--version': 'Prints current version and exits.',
43
+ 'help': 'Print this help and exits.',
44
+ 'version': 'Prints current version and exits.',
34
45
  ...list,
35
- }, indent, padName)
46
+ }, indent, { keyPrefix: '--', ...options })
36
47
  }
@@ -1,7 +1,14 @@
1
1
  import meow from 'meow'
2
2
 
3
3
  import { printFlagList, printHelpList } from './formatting.js'
4
- import { ensureIsKeyOf } from './type-helpers.js'
4
+
5
+ /**
6
+ * @typedef CliAlias
7
+ * @property {string} description
8
+ * @property {readonly string[]} argv
9
+ */
10
+
11
+ /** @typedef {Record<string, CliAlias>} CliAliases */
5
12
 
6
13
  /**
7
14
  * @callback CliSubcommandRun
@@ -20,39 +27,51 @@ import { ensureIsKeyOf } from './type-helpers.js'
20
27
  /**
21
28
  * @template {import('meow').AnyFlags} Flags
22
29
  * @param {Record<string, CliSubcommand>} subcommands
23
- * @param {import('meow').Options<Flags> & { argv: readonly string[], name: string }} options
30
+ * @param {import('meow').Options<Flags> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
24
31
  * @returns {Promise<void>}
25
32
  */
26
33
  export async function meowWithSubcommands (subcommands, options) {
27
34
  const {
35
+ aliases = {},
28
36
  argv,
29
37
  name,
30
38
  importMeta,
31
39
  ...additionalOptions
32
40
  } = options
33
- const [rawCommandName, ...commandArgv] = argv
34
-
35
- const commandName = ensureIsKeyOf(subcommands, rawCommandName)
36
- const command = commandName ? subcommands[commandName] : undefined
37
-
38
- // If a valid command has been specified, run it...
39
- if (command) {
40
- return await command.run(
41
- commandArgv,
42
- importMeta,
43
- {
44
- parentName: name
45
- }
46
- )
41
+
42
+ const [commandOrAliasName, ...rawCommandArgv] = argv
43
+
44
+ // If we got at least some args, then lets find out if we can find a command
45
+ if (commandOrAliasName) {
46
+ const alias = aliases[commandOrAliasName]
47
+
48
+ // First: Resolve argv data from alias if its an alias that's been given
49
+ const [commandName, ...commandArgv] = alias
50
+ ? [...alias.argv, ...rawCommandArgv]
51
+ : [commandOrAliasName, ...rawCommandArgv]
52
+
53
+ // Second: Find a command definition using that data
54
+ const commandDefinition = commandName ? subcommands[commandName] : undefined
55
+
56
+ // Third: If a valid command has been found, then we run it...
57
+ if (commandDefinition) {
58
+ return await commandDefinition.run(
59
+ commandArgv,
60
+ importMeta,
61
+ {
62
+ parentName: name
63
+ }
64
+ )
65
+ }
47
66
  }
48
67
 
49
- // ...else provide basic instructions and help
68
+ // ...else we provide basic instructions and help
50
69
  const cli = meow(`
51
70
  Usage
52
71
  $ ${name} <command>
53
72
 
54
73
  Commands
55
- ${printHelpList(subcommands, 6)}
74
+ ${printHelpList({ ...subcommands, ...aliases }, 6)}
56
75
 
57
76
  Options
58
77
  ${printFlagList({}, 6)}
@@ -39,6 +39,9 @@ const GLOB_IGNORE = [
39
39
  * @throws {InputError}
40
40
  */
41
41
  export async function getPackageFiles (cwd, inputPaths, config, debugLog) {
42
+ debugLog(`Globbed resolving ${inputPaths.length} paths:`, inputPaths)
43
+
44
+ // TODO: Does not support `~/` paths
42
45
  const entries = await globby(inputPaths, {
43
46
  absolute: true,
44
47
  cwd,
package/lib/utils/sdk.js CHANGED
@@ -9,10 +9,17 @@ import prompts from 'prompts'
9
9
  import { AuthError } from './errors.js'
10
10
 
11
11
  /**
12
- * @returns {Promise<import('@socketsecurity/sdk').SocketSdk>}
12
+ * The API key should be stored globally for the duration of the CLI execution
13
+ *
14
+ * @type {string | undefined}
13
15
  */
16
+ let apiKey
17
+
18
+ /** @returns {Promise<import('@socketsecurity/sdk').SocketSdk>} */
14
19
  export async function setupSdk () {
15
- let apiKey = process.env['SOCKET_SECURITY_API_KEY']
20
+ if (!apiKey) {
21
+ apiKey = process.env['SOCKET_SECURITY_API_KEY']
22
+ }
16
23
 
17
24
  if (!apiKey && isInteractive()) {
18
25
  const input = await prompts({
@@ -1,13 +1,3 @@
1
- /**
2
- * @template T
3
- * @param {T} obj
4
- * @param {string|undefined} key
5
- * @returns {(keyof T) | undefined}
6
- */
7
- export function ensureIsKeyOf (obj, key) {
8
- return /** @type {keyof T} */ (key && Object.prototype.hasOwnProperty.call(obj, key) ? key : undefined)
9
- }
10
-
11
1
  /**
12
2
  * @param {unknown} value
13
3
  * @returns {value is NodeJS.ErrnoException}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2-provenance",
4
4
  "description": "CLI tool for Socket.dev",
5
5
  "homepage": "http://github.com/SocketDev/socket-cli-js",
6
6
  "repository": {
@@ -38,7 +38,7 @@
38
38
  "test": "run-s check test:*"
39
39
  },
40
40
  "devDependencies": {
41
- "@socketsecurity/eslint-config": "^1.0.0",
41
+ "@socketsecurity/eslint-config": "^2.0.0",
42
42
  "@tsconfig/node14": "^1.0.3",
43
43
  "@types/chai": "^4.3.3",
44
44
  "@types/chai-as-promised": "^7.1.5",
@@ -46,22 +46,22 @@
46
46
  "@types/mock-fs": "^4.13.1",
47
47
  "@types/node": "^14.18.31",
48
48
  "@types/prompts": "^2.4.1",
49
- "@types/update-notifier": "^6.0.1",
50
- "@typescript-eslint/eslint-plugin": "^5.48.2",
51
- "@typescript-eslint/parser": "^5.48.2",
49
+ "@types/update-notifier": "^6.0.2",
50
+ "@typescript-eslint/eslint-plugin": "^5.51.0",
51
+ "@typescript-eslint/parser": "^5.51.0",
52
52
  "c8": "^7.12.0",
53
53
  "chai": "^4.3.6",
54
54
  "chai-as-promised": "^7.1.1",
55
55
  "dependency-check": "^5.0.0-7",
56
- "eslint": "^8.32.0",
56
+ "eslint": "^8.34.0",
57
57
  "eslint-config-standard": "^17.0.0",
58
58
  "eslint-config-standard-jsx": "^11.0.0",
59
59
  "eslint-import-resolver-typescript": "^3.5.3",
60
60
  "eslint-plugin-import": "^2.27.5",
61
- "eslint-plugin-jsdoc": "^39.5.0",
61
+ "eslint-plugin-jsdoc": "^40.0.0",
62
62
  "eslint-plugin-n": "^15.6.1",
63
63
  "eslint-plugin-promise": "^6.1.1",
64
- "eslint-plugin-react": "^7.32.1",
64
+ "eslint-plugin-react": "^7.32.2",
65
65
  "eslint-plugin-react-hooks": "^4.6.0",
66
66
  "eslint-plugin-unicorn": "^45.0.2",
67
67
  "husky": "^8.0.1",
@@ -71,12 +71,12 @@
71
71
  "nock": "^13.3.0",
72
72
  "npm-run-all2": "^6.0.2",
73
73
  "type-coverage": "^2.24.1",
74
- "typescript": "~4.9.4"
74
+ "typescript": "~4.9.5"
75
75
  },
76
76
  "dependencies": {
77
77
  "@apideck/better-ajv-errors": "^0.3.6",
78
78
  "@socketsecurity/config": "^2.0.0",
79
- "@socketsecurity/sdk": "^0.5.2",
79
+ "@socketsecurity/sdk": "^0.5.4",
80
80
  "chalk": "^5.1.2",
81
81
  "globby": "^13.1.3",
82
82
  "hpagent": "^1.2.0",