@socketsecurity/cli 0.11.0 → 0.11.1

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.
Files changed (82) hide show
  1. package/README.md +22 -22
  2. package/bin/npm +2 -0
  3. package/bin/npx +2 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +3419 -0
  7. package/dist/errors.d.ts +7 -0
  8. package/dist/link.d.ts +2 -0
  9. package/dist/link.js +45 -0
  10. package/dist/npm-cli.d.ts +2 -0
  11. package/dist/npm-cli.js +84 -0
  12. package/dist/npm-injection.d.ts +1 -0
  13. package/dist/npm-injection.js +913 -0
  14. package/dist/npm-injection2.d.ts +25 -0
  15. package/dist/npm-injection2.js +899 -0
  16. package/dist/npx-cli.d.ts +2 -0
  17. package/dist/npx-cli.js +60 -0
  18. package/dist/path-resolve.d.ts +12 -0
  19. package/dist/path-resolve.js +139 -0
  20. package/dist/sdk.d.ts +27 -0
  21. package/dist/sdk.js +224 -0
  22. package/dist/settings.d.ts +9 -0
  23. package/dist/type-helpers.d.ts +3 -0
  24. package/dist/vendor.js +25421 -0
  25. package/package.json +105 -52
  26. package/{lib/shadow/translations.json → translations.json} +20 -20
  27. package/cli.js +0 -72
  28. package/lib/commands/audit-log/index.js +0 -162
  29. package/lib/commands/cdxgen/index.js +0 -211
  30. package/lib/commands/dependencies/index.js +0 -150
  31. package/lib/commands/index.js +0 -15
  32. package/lib/commands/info/index.js +0 -287
  33. package/lib/commands/login/index.js +0 -170
  34. package/lib/commands/logout/index.js +0 -35
  35. package/lib/commands/npm/index.js +0 -27
  36. package/lib/commands/npx/index.js +0 -22
  37. package/lib/commands/organizations/index.js +0 -81
  38. package/lib/commands/raw-npm/index.js +0 -59
  39. package/lib/commands/raw-npx/index.js +0 -59
  40. package/lib/commands/report/create.js +0 -251
  41. package/lib/commands/report/index.js +0 -24
  42. package/lib/commands/report/view.js +0 -176
  43. package/lib/commands/repos/create.js +0 -166
  44. package/lib/commands/repos/delete.js +0 -93
  45. package/lib/commands/repos/index.js +0 -30
  46. package/lib/commands/repos/list.js +0 -170
  47. package/lib/commands/repos/update.js +0 -166
  48. package/lib/commands/repos/view.js +0 -128
  49. package/lib/commands/scan/create.js +0 -245
  50. package/lib/commands/scan/delete.js +0 -112
  51. package/lib/commands/scan/index.js +0 -30
  52. package/lib/commands/scan/list.js +0 -192
  53. package/lib/commands/scan/metadata.js +0 -113
  54. package/lib/commands/scan/stream.js +0 -115
  55. package/lib/commands/wrapper/index.js +0 -199
  56. package/lib/flags/command.js +0 -14
  57. package/lib/flags/index.js +0 -3
  58. package/lib/flags/output.js +0 -16
  59. package/lib/flags/validation.js +0 -14
  60. package/lib/shadow/bin/npm +0 -2
  61. package/lib/shadow/bin/npx +0 -2
  62. package/lib/shadow/link.cjs +0 -50
  63. package/lib/shadow/npm-cli.cjs +0 -27
  64. package/lib/shadow/npm-injection.cjs +0 -649
  65. package/lib/shadow/npx-cli.cjs +0 -27
  66. package/lib/shadow/package.json +0 -3
  67. package/lib/shadow/tty-server.cjs +0 -222
  68. package/lib/shadow/update-notifier.mjs +0 -3
  69. package/lib/utils/api-helpers.js +0 -42
  70. package/lib/utils/chalk-markdown.js +0 -125
  71. package/lib/utils/errors.js +0 -14
  72. package/lib/utils/flags.js +0 -27
  73. package/lib/utils/format-issues.js +0 -99
  74. package/lib/utils/formatting.js +0 -47
  75. package/lib/utils/issue-rules.cjs +0 -180
  76. package/lib/utils/meow-with-subcommands.js +0 -87
  77. package/lib/utils/misc.js +0 -61
  78. package/lib/utils/path-resolve.js +0 -204
  79. package/lib/utils/sdk.js +0 -99
  80. package/lib/utils/settings.js +0 -69
  81. package/lib/utils/type-helpers.cjs +0 -13
  82. package/lib/utils/update-notifier.js +0 -18
@@ -1,180 +0,0 @@
1
- //#region UX Constants
2
- /**
3
- * @typedef {{block: boolean, display: boolean}} RuleActionUX
4
- */
5
- const IGNORE_UX = {
6
- block: false,
7
- display: false
8
- }
9
- const WARN_UX = {
10
- block: false,
11
- display: true
12
- }
13
- const ERROR_UX = {
14
- block: true,
15
- display: true
16
- }
17
- //#endregion
18
- //#region utils
19
- /**
20
- * @typedef { NonNullable<NonNullable<NonNullable<(Awaited<ReturnType<import('@socketsecurity/sdk').SocketSdk['postSettings']>> & {success: true})['data']['entries'][number]['settings'][string]>['issueRules']>>[string] | boolean } NonNormalizedIssueRule
21
- */
22
- /**
23
- * @typedef { (NonNullable<NonNullable<(Awaited<ReturnType<import('@socketsecurity/sdk').SocketSdk['postSettings']>> & {success: true})['data']['defaults']['issueRules']>[string]> & { action: string }) | boolean } NonNormalizedResolvedIssueRule
24
- */
25
- /**
26
- * Iterates over all entries with ordered issue rule for deferal
27
- * Iterates over all issue rules and finds the first defined value that does not defer otherwise uses the defaultValue
28
- * Takes the value and converts into a UX workflow
29
- *
30
- * @param {Iterable<Iterable<NonNormalizedIssueRule>>} entriesOrderedIssueRules
31
- * @param {NonNormalizedResolvedIssueRule} defaultValue
32
- * @returns {RuleActionUX}
33
- */
34
- function resolveIssueRuleUX (entriesOrderedIssueRules, defaultValue) {
35
- if (defaultValue === true || defaultValue == null) {
36
- defaultValue = {
37
- action: 'error'
38
- }
39
- } else if (defaultValue === false) {
40
- defaultValue = {
41
- action: 'ignore'
42
- }
43
- }
44
- let block = false
45
- let display = false
46
- let needDefault = true
47
- iterate_entries:
48
- for (const issueRuleArr of entriesOrderedIssueRules) {
49
- for (const rule of issueRuleArr) {
50
- if (issueRuleValueDoesNotDefer(rule)) {
51
- // there was a rule, even if a defer, don't narrow to the default
52
- needDefault = false
53
- const narrowingFilter = uxForDefinedNonDeferValue(rule)
54
- block = block || narrowingFilter.block
55
- display = display || narrowingFilter.display
56
- continue iterate_entries
57
- }
58
- }
59
- // all rules defer, narrow
60
- const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)
61
- block = block || narrowingFilter.block
62
- display = display || narrowingFilter.display
63
- }
64
- if (needDefault) {
65
- // no config set a
66
- const narrowingFilter = uxForDefinedNonDeferValue(defaultValue)
67
- block = block || narrowingFilter.block
68
- display = display || narrowingFilter.display
69
- }
70
- return {
71
- block,
72
- display
73
- }
74
- }
75
-
76
- /**
77
- * Negative form because it is narrowing the type
78
- *
79
- * @type {(issueRuleValue: NonNormalizedIssueRule) => issueRuleValue is NonNormalizedResolvedIssueRule}
80
- */
81
- function issueRuleValueDoesNotDefer (issueRule) {
82
- if (issueRule === undefined) {
83
- return false
84
- } else if (typeof issueRule === 'object' && issueRule) {
85
- const { action } = issueRule
86
- if (action === undefined || action === 'defer') {
87
- return false
88
- }
89
- }
90
- return true
91
- }
92
-
93
- /**
94
- * Handles booleans for backwards compatibility
95
- *
96
- * @param {NonNormalizedResolvedIssueRule} issueRuleValue
97
- * @returns {RuleActionUX}
98
- */
99
- function uxForDefinedNonDeferValue (issueRuleValue) {
100
- if (typeof issueRuleValue === 'boolean') {
101
- return issueRuleValue ? ERROR_UX : IGNORE_UX
102
- }
103
- const { action } = issueRuleValue
104
- if (action === 'warn') {
105
- return WARN_UX
106
- } else if (action === 'ignore') {
107
- return IGNORE_UX
108
- }
109
- return ERROR_UX
110
- }
111
- //#endregion
112
- //#region exports
113
- module.exports = {
114
- /**
115
- *
116
- * @param {(Awaited<ReturnType<import('@socketsecurity/sdk').SocketSdk['postSettings']>> & {success: true})['data']} settings
117
- * @returns {(context: {package: {name: string, version: string}, issue: {type: string}}) => RuleActionUX}
118
- */
119
- createIssueUXLookup (settings) {
120
- /**
121
- * @type {Map<keyof (typeof settings.defaults.issueRules), RuleActionUX>}
122
- */
123
- const cachedUX = new Map()
124
- return (context) => {
125
- const key = context.issue.type
126
- /**
127
- * @type {RuleActionUX | undefined}
128
- */
129
- let ux = cachedUX.get(key)
130
- if (ux) {
131
- return ux
132
- }
133
- /**
134
- * @type {Array<Array<NonNormalizedIssueRule>>}
135
- */
136
- const entriesOrderedIssueRules = []
137
- for (const settingsEntry of settings.entries) {
138
- /**
139
- * @type {Array<NonNormalizedIssueRule>}
140
- */
141
- const orderedIssueRules = []
142
- let target = settingsEntry.start
143
- while (target !== null) {
144
- const resolvedTarget = settingsEntry.settings[target]
145
- if (!resolvedTarget) {
146
- break
147
- }
148
- const issueRuleValue = resolvedTarget.issueRules?.[key]
149
- if (typeof issueRuleValue !== 'undefined') {
150
- orderedIssueRules.push(issueRuleValue)
151
- }
152
- target = resolvedTarget.deferTo ?? null
153
- }
154
- entriesOrderedIssueRules.push(orderedIssueRules)
155
- }
156
- const defaultValue = settings.defaults.issueRules[key]
157
- /**
158
- * @type {NonNormalizedResolvedIssueRule}
159
- */
160
- let resolvedDefaultValue = {
161
- action: 'error'
162
- }
163
- // @ts-ignore backcompat, cover with tests
164
- if (defaultValue === false) {
165
- resolvedDefaultValue = {
166
- action: 'ignore'
167
- }
168
- // @ts-ignore backcompat, cover with tests
169
- } else if (defaultValue && defaultValue !== true) {
170
- resolvedDefaultValue = {
171
- action: defaultValue.action ?? 'error'
172
- }
173
- }
174
- ux = resolveIssueRuleUX(entriesOrderedIssueRules, resolvedDefaultValue)
175
- cachedUX.set(key, ux)
176
- return ux
177
- }
178
- }
179
- }
180
- //#endregion
@@ -1,87 +0,0 @@
1
- import meow from 'meow'
2
-
3
- import { printFlagList, printHelpList } from './formatting.js'
4
-
5
- /**
6
- * @typedef CliAlias
7
- * @property {string} description
8
- * @property {readonly string[]} argv
9
- */
10
-
11
- /** @typedef {Record<string, CliAlias>} CliAliases */
12
-
13
- /**
14
- * @callback CliSubcommandRun
15
- * @param {readonly string[]} argv
16
- * @param {ImportMeta} importMeta
17
- * @param {{ parentName: string }} context
18
- * @returns {Promise<void>|void}
19
- */
20
-
21
- /**
22
- * @typedef CliSubcommand
23
- * @property {string} description
24
- * @property {CliSubcommandRun} run
25
- */
26
-
27
- /**
28
- * @param {Record<string, CliSubcommand>} subcommands
29
- * @param {import('meow').Options<any> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
30
- * @returns {Promise<void>}
31
- */
32
- export async function meowWithSubcommands (subcommands, options) {
33
- const {
34
- aliases = {},
35
- argv,
36
- name,
37
- importMeta,
38
- ...additionalOptions
39
- } = options
40
-
41
- const [commandOrAliasName, ...rawCommandArgv] = argv
42
-
43
- // If we got at least some args, then lets find out if we can find a command
44
- if (commandOrAliasName) {
45
- const alias = aliases[commandOrAliasName]
46
-
47
- // First: Resolve argv data from alias if its an alias that's been given
48
- const [commandName, ...commandArgv] = alias
49
- ? [...alias.argv, ...rawCommandArgv]
50
- : [commandOrAliasName, ...rawCommandArgv]
51
-
52
- // Second: Find a command definition using that data
53
- const commandDefinition = commandName ? subcommands[commandName] : undefined
54
-
55
- // Third: If a valid command has been found, then we run it...
56
- if (commandDefinition) {
57
- return await commandDefinition.run(
58
- commandArgv,
59
- importMeta,
60
- {
61
- parentName: name
62
- }
63
- )
64
- }
65
- }
66
-
67
- // ...else we provide basic instructions and help
68
- const cli = meow(`
69
- Usage
70
- $ ${name} <command>
71
-
72
- Commands
73
- ${printHelpList({ ...subcommands, ...aliases }, 6)}
74
-
75
- Options
76
- ${printFlagList({}, 6)}
77
-
78
- Examples
79
- $ ${name} --help
80
- `, {
81
- argv,
82
- importMeta,
83
- ...additionalOptions,
84
- })
85
-
86
- cli.showHelp()
87
- }
package/lib/utils/misc.js DELETED
@@ -1,61 +0,0 @@
1
- import { logSymbols } from './chalk-markdown.js'
2
-
3
- /**
4
- * @param {boolean|undefined} printDebugLogs
5
- * @returns {typeof console.error}
6
- */
7
- export function createDebugLogger (printDebugLogs) {
8
- return printDebugLogs
9
- // eslint-disable-next-line no-console
10
- ? /** @type { (...params: unknown[]) => void } */(...params) => console.error(logSymbols.info, ...params)
11
- : () => {}
12
- }
13
-
14
- /**
15
- * @param {(string|undefined)[]} list
16
- * @param {string} separator
17
- * @returns {string}
18
- */
19
- export function stringJoinWithSeparateFinalSeparator (list, separator = ' and ') {
20
- const values = list.filter(value => !!value)
21
-
22
- if (values.length < 2) {
23
- return values[0] || ''
24
- }
25
-
26
- const finalValue = values.pop()
27
-
28
- return values.join(', ') + separator + finalValue
29
- }
30
-
31
- /**
32
- * Returns a new object with only the specified keys from the input object
33
- * @template {Record<string,any>} T
34
- * @template {keyof T} K
35
- * @param {T} input
36
- * @param {K[]|ReadonlyArray<K>} keys
37
- * @returns {Pick<T, K>}
38
- */
39
- export function pick (input, keys) {
40
- /** @type {Partial<Pick<T, K>>} */
41
- const result = {}
42
-
43
- for (const key of keys) {
44
- result[key] = input[key]
45
- }
46
-
47
- return /** @type {Pick<T, K>} */ (result)
48
- }
49
-
50
- /**
51
- * @param {Record<string,any>} obj
52
- * @returns {boolean}
53
- */
54
- export function objectSome (obj) {
55
- for (const key in obj) {
56
- if (obj[key]) {
57
- return true
58
- }
59
- }
60
- return false
61
- }
@@ -1,204 +0,0 @@
1
- import { stat } from 'node:fs/promises'
2
- import path from 'node:path'
3
-
4
- import { globby } from 'globby'
5
- import ignore from 'ignore'
6
- // @ts-ignore This package provides no types
7
- import { directories } from 'ignore-by-default'
8
- import { ErrorWithCause } from 'pony-cause'
9
-
10
- import { InputError } from './errors.js'
11
- import { isErrnoException } from './type-helpers.cjs'
12
-
13
- /**
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
- * @type {readonly string[]}
16
- */
17
- const ignoreByDefault = directories()
18
-
19
- /** @type {import('globby').Options} */
20
- const BASE_GLOBBY_OPTS = {
21
- absolute: true,
22
- expandDirectories: false,
23
- gitignore: true,
24
- ignore: [
25
- ...ignoreByDefault.map(item => '**/' + item)
26
- ],
27
- markDirectories: true,
28
- unique: true,
29
- }
30
-
31
- /**
32
- * Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
33
- * @param {string} cwd The working directory to use when resolving paths
34
- * @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
35
- * @param {import('@socketsecurity/config').SocketYml|undefined} config
36
- * @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
37
- * @param {typeof console.error} debugLog
38
- * @returns {Promise<string[]>}
39
- * @throws {InputError}
40
- */
41
- export async function getPackageFiles (cwd, inputPaths, config, supportedFiles, debugLog) {
42
- debugLog(`Globbed resolving ${inputPaths.length} paths:`, inputPaths)
43
-
44
- // TODO: Does not support `~/` paths
45
- const entries = await globby(inputPaths, {
46
- ...BASE_GLOBBY_OPTS,
47
- cwd,
48
- onlyFiles: false
49
- })
50
-
51
- debugLog(`Globbed resolved ${inputPaths.length} paths to ${entries.length} paths:`, entries)
52
-
53
- const packageFiles = await mapGlobResultToFiles(entries, supportedFiles)
54
-
55
- debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)
56
-
57
- const includedPackageFiles = config?.projectIgnorePaths?.length
58
- // @ts-ignore
59
- ? ignore()
60
- .add(config.projectIgnorePaths)
61
- .filter(packageFiles.map(item => path.relative(cwd, item)))
62
- .map((/** @type {string} */ item) => path.resolve(cwd, item))
63
- : packageFiles
64
-
65
- return includedPackageFiles
66
- }
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
-
96
- /**
97
- * Takes paths to folders, package.json and/or recognized lock files and resolves them to package.json + lockfile pairs (where possible)
98
- * @param {string[]} entries
99
- * @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
100
- * @returns {Promise<string[]>}
101
- * @throws {InputError}
102
- */
103
- export async function mapGlobResultToFiles (entries, supportedFiles) {
104
- const packageFiles = await Promise.all(
105
- entries.map(entry => mapGlobEntryToFiles(entry, supportedFiles))
106
- )
107
-
108
- const uniquePackageFiles = [...new Set(packageFiles.flat())]
109
-
110
- return uniquePackageFiles
111
- }
112
-
113
- /**
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)
115
- * @param {string} entry
116
- * @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getReportSupportedFiles'>['data']} supportedFiles
117
- * @returns {Promise<string[]>}
118
- * @throws {InputError}
119
- */
120
- export async function mapGlobEntryToFiles (entry, supportedFiles) {
121
- const jsSupported = supportedFiles['npm'] || {}
122
- const jsLockFilePatterns = Object.values(jsSupported)
123
- // .filter(key => key !== 'packagejson')
124
- .map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
125
-
126
- const pyFilePatterns = Object.values(supportedFiles['pypi'] || {})
127
- .map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
128
-
129
- const goSupported = supportedFiles['golang'] || {}
130
- const goSupplementalPatterns = Object.values(goSupported)
131
- // .filter(key => key !== 'gomod')
132
- .map(p => `**/${/** @type {{ pattern: string }} */ (p).pattern}`)
133
-
134
- const files = await globby([
135
- ...jsLockFilePatterns,
136
- ...pyFilePatterns,
137
- ...goSupplementalPatterns
138
- ], {
139
- ...BASE_GLOBBY_OPTS,
140
- onlyFiles: true,
141
- cwd: path.resolve((await stat(entry)).isDirectory() ? entry : path.dirname(entry))
142
- })
143
- return files
144
-
145
- // if (entry.endsWith('/')) {
146
- // // If the match is a folder and that folder contains a package.json file, then include it
147
- // const jsPkg = path.resolve(entry, 'package.json')
148
- // if (await fileExists(jsPkg)) pkgJSFile = jsPkg
149
-
150
- // const goPkg = path.resolve(entry, 'go.mod')
151
- // if (await fileExists(goPkg)) pkgGoFile = goPkg
152
-
153
- // pyFiles = await globby(pyFilePatterns, {
154
- // ...BASE_GLOBBY_OPTS,
155
- // cwd: entry
156
- // })
157
- // } else {
158
- // const entryFile = path.basename(entry)
159
-
160
- // if (entryFile === 'package.json') {
161
- // // If the match is a package.json file, then include it
162
- // pkgJSFile = entry
163
- // } else if (micromatch.isMatch(entryFile, jsLockFilePatterns)) {
164
- // jsLockFiles = [entry]
165
- // pkgJSFile = path.resolve(path.dirname(entry), 'package.json')
166
- // if (!(await fileExists(pkgJSFile))) return []
167
- // } else if (entryFile === 'go.mod') {
168
- // pkgGoFile = entry
169
- // } else if (micromatch.isMatch(entryFile, goSupplementalPatterns)) {
170
- // goExtraFiles = [entry]
171
- // pkgGoFile = path.resolve(path.dirname(entry), 'go.mod')
172
- // } else if (micromatch.isMatch(entryFile, pyFilePatterns)) {
173
- // pyFiles = [entry]
174
- // }
175
- // }
176
-
177
- // return [...jsLockFiles, ...pyFiles, ...goExtraFiles]
178
- // .concat(pkgJSFile ? [pkgJSFile] : [])
179
- // .concat(pkgGoFile ? [pkgGoFile] : [])
180
- }
181
-
182
- /**
183
- * @param {string} filePath
184
- * @returns {Promise<boolean>}
185
- */
186
- export async function fileExists (filePath) {
187
- /** @type {import('node:fs').Stats} */
188
- let pathStat
189
-
190
- try {
191
- pathStat = await stat(filePath)
192
- } catch (err) {
193
- if (isErrnoException(err) && err.code === 'ENOENT') {
194
- return false
195
- }
196
- throw new ErrorWithCause('Error while checking if file exists', { cause: err })
197
- }
198
-
199
- if (!pathStat.isFile()) {
200
- throw new InputError(`Expected '${filePath}' to be a file`)
201
- }
202
-
203
- return true
204
- }
package/lib/utils/sdk.js DELETED
@@ -1,99 +0,0 @@
1
- import { readFile } from 'node:fs/promises'
2
- import { dirname, join } from 'node:path'
3
- import { fileURLToPath } from 'node:url'
4
-
5
- import { SocketSdk, createUserAgentFromPkgJson } from '@socketsecurity/sdk'
6
- import isInteractive from 'is-interactive'
7
- import prompts from 'prompts'
8
-
9
- import { AuthError } from './errors.js'
10
- import { getSetting } from './settings.js'
11
-
12
- export const FREE_API_KEY = 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo_api'
13
-
14
- /**
15
- * This API key should be stored globally for the duration of the CLI execution
16
- * @type {string | undefined}
17
- */
18
- let defaultKey
19
-
20
- /** @returns {string | undefined} */
21
- export function getDefaultKey () {
22
- defaultKey = process.env['SOCKET_SECURITY_API_KEY'] || getSetting('apiKey') || defaultKey
23
- return defaultKey
24
- }
25
-
26
- /**
27
- * The API server that should be used for operations
28
- * @type {string | undefined}
29
- */
30
- let defaultAPIBaseUrl
31
-
32
- /**
33
- * @returns {string | undefined}
34
- */
35
- export function getDefaultAPIBaseUrl () {
36
- defaultAPIBaseUrl = process.env['SOCKET_SECURITY_API_BASE_URL'] || getSetting('apiBaseUrl') || undefined
37
- return defaultAPIBaseUrl
38
- }
39
-
40
- /**
41
- * The API server that should be used for operations
42
- * @type {string | undefined}
43
- */
44
- let defaultApiProxy
45
-
46
- /**
47
- * @returns {string | undefined}
48
- */
49
- export function getDefaultHTTPProxy () {
50
- defaultApiProxy = process.env['SOCKET_SECURITY_API_PROXY'] || getSetting('apiProxy') || undefined
51
- return defaultApiProxy
52
- }
53
-
54
- /**
55
- * @param {string} [apiKey]
56
- * @param {string} [apiBaseUrl]
57
- * @param {string} [proxy]
58
- * @returns {Promise<import('@socketsecurity/sdk').SocketSdk>}
59
- */
60
- export async function setupSdk (apiKey = getDefaultKey(), apiBaseUrl = getDefaultAPIBaseUrl(), proxy = getDefaultHTTPProxy()) {
61
- if (apiKey == null && isInteractive()) {
62
- /**
63
- * @type {{ apiKey: string }}
64
- */
65
- const input = await prompts({
66
- type: 'password',
67
- name: 'apiKey',
68
- message: 'Enter your Socket.dev API key (not saved, use socket login to persist)',
69
- })
70
-
71
- apiKey = defaultKey = input.apiKey
72
- }
73
-
74
- if (!apiKey) {
75
- throw new AuthError('You need to provide an API key')
76
- }
77
-
78
- /** @type {import('@socketsecurity/sdk').SocketSdkOptions["agent"]} */
79
- let agent
80
-
81
- if (proxy) {
82
- const { HttpProxyAgent, HttpsProxyAgent } = await import('hpagent')
83
- agent = {
84
- http: new HttpProxyAgent({ proxy }),
85
- https: new HttpsProxyAgent({ proxy }),
86
- }
87
- }
88
- const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), '../../package.json')
89
- const packageJson = await readFile(packageJsonPath, 'utf8')
90
-
91
- /** @type {import('@socketsecurity/sdk').SocketSdkOptions} */
92
- const sdkOptions = {
93
- agent,
94
- baseUrl: apiBaseUrl,
95
- userAgent: createUserAgentFromPkgJson(JSON.parse(packageJson))
96
- }
97
-
98
- return new SocketSdk(apiKey || '', sdkOptions)
99
- }
@@ -1,69 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as os from 'os'
3
- import * as path from 'path'
4
-
5
- import ora from 'ora'
6
-
7
- let dataHome = process.platform === 'win32'
8
- ? process.env['LOCALAPPDATA']
9
- : process.env['XDG_DATA_HOME']
10
-
11
- if (!dataHome) {
12
- if (process.platform === 'win32') throw new Error('missing %LOCALAPPDATA%')
13
- const home = os.homedir()
14
- dataHome = path.join(home, ...(process.platform === 'darwin'
15
- ? ['Library', 'Application Support']
16
- : ['.local', 'share']
17
- ))
18
- }
19
-
20
- const settingsPath = path.join(dataHome, 'socket', 'settings')
21
-
22
- /**
23
- * @typedef {Record<string, boolean | {action: 'error' | 'warn' | 'ignore' | 'defer'}>} IssueRules
24
- */
25
-
26
- /** @type {{apiKey?: string | null, enforcedOrgs?: string[] | null, apiBaseUrl?: string | null, apiProxy?: string | null}} */
27
- let settings = {}
28
-
29
- if (fs.existsSync(settingsPath)) {
30
- const raw = fs.readFileSync(settingsPath, 'utf-8')
31
- try {
32
- settings = JSON.parse(Buffer.from(raw, 'base64').toString())
33
- } catch (e) {
34
- ora(`Failed to parse settings at ${settingsPath}`).warn()
35
- }
36
- } else {
37
- fs.mkdirSync(path.dirname(settingsPath), { recursive: true })
38
- }
39
-
40
- /**
41
- * @template {keyof typeof settings} Key
42
- * @param {Key} key
43
- * @returns {typeof settings[Key]}
44
- */
45
- export function getSetting (key) {
46
- return settings[key]
47
- }
48
-
49
- let pendingSave = false
50
-
51
- /**
52
- * @template {keyof typeof settings} Key
53
- * @param {Key} key
54
- * @param {typeof settings[Key]} value
55
- * @returns {void}
56
- */
57
- export function updateSetting (key, value) {
58
- settings[key] = value
59
- if (!pendingSave) {
60
- pendingSave = true
61
- process.nextTick(() => {
62
- pendingSave = false
63
- fs.writeFileSync(
64
- settingsPath,
65
- Buffer.from(JSON.stringify(settings)).toString('base64')
66
- )
67
- })
68
- }
69
- }