@socketsecurity/cli-with-sentry 0.14.154 → 0.15.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.
Files changed (109) hide show
  1. package/dist/.config/tsconfig.dts.tsbuildinfo +1 -1
  2. package/dist/cli.js +1470 -2680
  3. package/dist/cli.js.map +1 -1
  4. package/dist/constants.js +15 -12
  5. package/dist/constants.js.map +1 -1
  6. package/dist/instrument-with-sentry.js +10 -3
  7. package/dist/instrument-with-sentry.js.map +1 -1
  8. package/dist/shadow-bin.js +5 -7
  9. package/dist/shadow-bin.js.map +1 -1
  10. package/dist/shadow-npm-inject.js +130 -1646
  11. package/dist/shadow-npm-inject.js.map +1 -1
  12. package/dist/types/commands/analytics/cmd-analytics.d.mts.map +1 -1
  13. package/dist/types/commands/analytics/fetch-org-analytics.d.mts.map +1 -1
  14. package/dist/types/commands/analytics/fetch-repo-analytics.d.mts.map +1 -1
  15. package/dist/types/commands/analytics/handle-analytics.d.mts.map +1 -1
  16. package/dist/types/commands/analytics/output-analytics.d.mts +15 -16
  17. package/dist/types/commands/analytics/output-analytics.d.mts.map +1 -1
  18. package/dist/types/commands/audit-log/fetch-audit-log.d.mts.map +1 -1
  19. package/dist/types/commands/cdxgen/cmd-cdxgen.d.mts.map +1 -1
  20. package/dist/types/commands/ci/fetch-default-org-slug.d.mts.map +1 -1
  21. package/dist/types/commands/dependencies/fetch-dependencies.d.mts.map +1 -1
  22. package/dist/types/commands/diff-scan/fetch-diff-scan.d.mts.map +1 -1
  23. package/dist/types/commands/fix/git.d.mts +9 -1
  24. package/dist/types/commands/fix/git.d.mts.map +1 -1
  25. package/dist/types/commands/fix/npm-fix.d.mts.map +1 -1
  26. package/dist/types/commands/fix/open-pr.d.mts +30 -16
  27. package/dist/types/commands/fix/open-pr.d.mts.map +1 -1
  28. package/dist/types/commands/fix/pnpm-fix.d.mts.map +1 -1
  29. package/dist/types/commands/info/fetch-package-info.d.mts.map +1 -1
  30. package/dist/types/commands/login/attempt-login.d.mts.map +1 -1
  31. package/dist/types/commands/npm/wrap-npm.d.mts.map +1 -1
  32. package/dist/types/commands/npx/wrap-npx.d.mts.map +1 -1
  33. package/dist/types/commands/oops/cmd-oops.d.mts.map +1 -1
  34. package/dist/types/commands/optimize/deps-includes-by-agent.d.mts +1 -1
  35. package/dist/types/commands/optimize/get-overrides-by-agent.d.mts +1 -1
  36. package/dist/types/commands/optimize/lockfile-includes-by-agent.d.mts +1 -1
  37. package/dist/types/commands/optimize/ls-by-agent.d.mts +1 -1
  38. package/dist/types/commands/optimize/update-manifest-by-agent.d.mts +1 -1
  39. package/dist/types/commands/organization/fetch-license-policy.d.mts.map +1 -1
  40. package/dist/types/commands/organization/fetch-organization-list.d.mts.map +1 -1
  41. package/dist/types/commands/organization/fetch-quota.d.mts.map +1 -1
  42. package/dist/types/commands/organization/fetch-security-policy.d.mts.map +1 -1
  43. package/dist/types/commands/organization/output-organization-list.d.mts.map +1 -1
  44. package/dist/types/commands/package/cmd-package-score.d.mts.map +1 -1
  45. package/dist/types/commands/package/fetch-purl-deep-score.d.mts.map +1 -1
  46. package/dist/types/commands/package/fetch-purls-shallow-score.d.mts.map +1 -1
  47. package/dist/types/commands/repos/fetch-create-repo.d.mts.map +1 -1
  48. package/dist/types/commands/repos/fetch-delete-repo.d.mts.map +1 -1
  49. package/dist/types/commands/repos/fetch-list-repos.d.mts.map +1 -1
  50. package/dist/types/commands/repos/fetch-update-repo.d.mts.map +1 -1
  51. package/dist/types/commands/repos/fetch-view-repo.d.mts.map +1 -1
  52. package/dist/types/commands/repos/handle-create-repo.d.mts.map +1 -1
  53. package/dist/types/commands/repos/output-create-repo.d.mts +1 -1
  54. package/dist/types/commands/repos/output-create-repo.d.mts.map +1 -1
  55. package/dist/types/commands/scan/fetch-create-org-full-scan.d.mts.map +1 -1
  56. package/dist/types/commands/scan/fetch-delete-org-full-scan.d.mts.map +1 -1
  57. package/dist/types/commands/scan/fetch-diff-scan.d.mts.map +1 -1
  58. package/dist/types/commands/scan/fetch-list-scans.d.mts.map +1 -1
  59. package/dist/types/commands/scan/fetch-report-data.d.mts.map +1 -1
  60. package/dist/types/commands/scan/fetch-scan-metadata.d.mts.map +1 -1
  61. package/dist/types/commands/scan/fetch-scan.d.mts.map +1 -1
  62. package/dist/types/commands/scan/fetch-supported-scan-file-names.d.mts.map +1 -1
  63. package/dist/types/commands/scan/generate-report.d.mts.map +1 -1
  64. package/dist/types/commands/scan/stream-scan.d.mts +1 -1
  65. package/dist/types/commands/scan/stream-scan.d.mts.map +1 -1
  66. package/dist/types/commands/scan/suggest-org-slug.d.mts.map +1 -1
  67. package/dist/types/commands/scan/suggest-repo-slug.d.mts.map +1 -1
  68. package/dist/types/commands/threat-feed/fetch-threat-feed.d.mts.map +1 -1
  69. package/dist/types/commands/threat-feed/output-threat-feed.d.mts.map +1 -1
  70. package/dist/types/constants.d.mts +1 -1
  71. package/dist/types/constants.d.mts.map +1 -1
  72. package/dist/types/shadow/npm/arborist/lib/dep-valid.d.mts +2 -2
  73. package/dist/types/shadow/npm/arborist/lib/dep-valid.d.mts.map +1 -1
  74. package/dist/types/{utils → shadow/npm}/arborist-helpers.d.mts +19 -3
  75. package/dist/types/shadow/npm/arborist-helpers.d.mts.map +1 -0
  76. package/dist/types/{utils/npm.d.mts → shadow/npm/install.d.mts} +2 -2
  77. package/dist/types/shadow/npm/install.d.mts.map +1 -0
  78. package/dist/types/shadow/npm/paths.d.mts +0 -6
  79. package/dist/types/shadow/npm/paths.d.mts.map +1 -1
  80. package/dist/types/utils/agent.d.mts +2 -2
  81. package/dist/types/utils/agent.d.mts.map +1 -1
  82. package/dist/types/utils/alerts-map.d.mts +0 -11
  83. package/dist/types/utils/alerts-map.d.mts.map +1 -1
  84. package/dist/types/utils/api.d.mts +22 -12
  85. package/dist/types/utils/api.d.mts.map +1 -1
  86. package/dist/types/utils/meow-with-subcommands.d.mts.map +1 -1
  87. package/dist/types/utils/npm-paths.d.mts +7 -0
  88. package/dist/types/utils/npm-paths.d.mts.map +1 -0
  89. package/dist/types/utils/pnpm.d.mts +2 -1
  90. package/dist/types/utils/pnpm.d.mts.map +1 -1
  91. package/dist/types/utils/sdk.d.mts +4 -1
  92. package/dist/types/utils/sdk.d.mts.map +1 -1
  93. package/dist/types/utils/socket-url.d.mts +10 -2
  94. package/dist/types/utils/socket-url.d.mts.map +1 -1
  95. package/dist/types/utils/translations.d.mts.map +1 -1
  96. package/dist/utils.js +3323 -0
  97. package/dist/utils.js.map +1 -0
  98. package/dist/vendor.js +1284 -1231
  99. package/dist/vendor.js.map +1 -1
  100. package/external/@socketsecurity/registry/external/browserslist.js +382 -366
  101. package/external/@socketsecurity/registry/external/browserslist.js.map +1 -1
  102. package/external/@socketsecurity/registry/lib/constants/maintained-node-versions.js +10 -16
  103. package/external/@socketsecurity/registry/lib/fs.d.ts +6 -4
  104. package/external/@socketsecurity/registry/package.json +7 -7
  105. package/package.json +21 -21
  106. package/dist/shadow-npm-paths.js +0 -291
  107. package/dist/shadow-npm-paths.js.map +0 -1
  108. package/dist/types/utils/arborist-helpers.d.mts.map +0 -1
  109. package/dist/types/utils/npm.d.mts.map +0 -1
package/dist/utils.js ADDED
@@ -0,0 +1,3323 @@
1
+ 'use strict'
2
+
3
+ const vendor = require('./vendor.js')
4
+ const logger = require('../external/@socketsecurity/registry/lib/logger')
5
+ const debug = require('../external/@socketsecurity/registry/lib/debug')
6
+ const path = require('node:path')
7
+ const objects = require('../external/@socketsecurity/registry/lib/objects')
8
+ const path$1 = require('../external/@socketsecurity/registry/lib/path')
9
+ const regexps = require('../external/@socketsecurity/registry/lib/regexps')
10
+ const constants = require('./constants.js')
11
+ const prompts = require('../external/@socketsecurity/registry/lib/prompts')
12
+ const strings = require('../external/@socketsecurity/registry/lib/strings')
13
+ const promises = require('node:timers/promises')
14
+ const arrays = require('../external/@socketsecurity/registry/lib/arrays')
15
+ const packages = require('../external/@socketsecurity/registry/lib/packages')
16
+ const fs = require('node:fs')
17
+ const os = require('node:os')
18
+ const registry = require('../external/@socketsecurity/registry')
19
+ const sorts = require('../external/@socketsecurity/registry/lib/sorts')
20
+ const Module = require('node:module')
21
+ const spawn = require('../external/@socketsecurity/registry/lib/spawn')
22
+ const npm = require('../external/@socketsecurity/registry/lib/npm')
23
+ const words = require('../external/@socketsecurity/registry/lib/words')
24
+ const fs$1 = require('../external/@socketsecurity/registry/lib/fs')
25
+
26
+ const _documentCurrentScript =
27
+ typeof document !== 'undefined' ? document.currentScript : null
28
+ const { NPM: NPM$6, PNPM: PNPM$2 } = constants
29
+ const PNPM_WORKSPACE = `${PNPM$2}-workspace`
30
+ const ignoredDirs = [
31
+ // Taken from ignore-by-default:
32
+ // https://github.com/novemberborn/ignore-by-default/blob/v2.1.0/index.js
33
+ '.git',
34
+ // Git repository files, see <https://git-scm.com/>
35
+ '.log',
36
+ // Log files emitted by tools such as `tsserver`, see <https://github.com/Microsoft/TypeScript/wiki/Standalone-Server-%28tsserver%29>
37
+ '.nyc_output',
38
+ // Temporary directory where nyc stores coverage data, see <https://github.com/bcoe/nyc>
39
+ '.sass-cache',
40
+ // Cache folder for node-sass, see <https://github.com/sass/node-sass>
41
+ '.yarn',
42
+ // Where node modules are installed when using Yarn, see <https://yarnpkg.com/>
43
+ 'bower_components',
44
+ // Where Bower packages are installed, see <http://bower.io/>
45
+ 'coverage',
46
+ // Standard output directory for code coverage reports, see <https://github.com/gotwarlost/istanbul>
47
+ 'node_modules',
48
+ // Where Node modules are installed, see <https://nodejs.org/>
49
+ // Taken from globby:
50
+ // https://github.com/sindresorhus/globby/blob/v14.0.2/ignore.js#L11-L16
51
+ 'flow-typed'
52
+ ]
53
+ const ignoredDirPatterns = ignoredDirs.map(i => `**/${i}`)
54
+ async function getWorkspaceGlobs(agent, cwd = process.cwd()) {
55
+ let workspacePatterns
56
+ if (agent === PNPM$2) {
57
+ for (const workspacePath of [
58
+ path.join(cwd, `${PNPM_WORKSPACE}.yaml`),
59
+ path.join(cwd, `${PNPM_WORKSPACE}.yml`)
60
+ ]) {
61
+ // eslint-disable-next-line no-await-in-loop
62
+ const yml = await safeReadFile(workspacePath)
63
+ if (yml) {
64
+ try {
65
+ workspacePatterns = vendor.distExports$1.parse(yml)?.packages
66
+ } catch {}
67
+ if (workspacePatterns) {
68
+ break
69
+ }
70
+ }
71
+ }
72
+ } else {
73
+ workspacePatterns = (
74
+ await packages.readPackageJson(cwd, {
75
+ throws: false
76
+ })
77
+ )?.['workspaces']
78
+ }
79
+ return Array.isArray(workspacePatterns)
80
+ ? workspacePatterns
81
+ .filter(strings.isNonEmptyString)
82
+ .map(workspacePatternToGlobPattern)
83
+ : []
84
+ }
85
+ function ignoreFileLinesToGlobPatterns(lines, filepath, cwd) {
86
+ const base = path.relative(cwd, path.dirname(filepath)).replace(/\\/g, '/')
87
+ const patterns = []
88
+ for (let i = 0, { length } = lines; i < length; i += 1) {
89
+ const pattern = lines[i].trim()
90
+ if (pattern.length > 0 && pattern.charCodeAt(0) !== 35 /*'#'*/) {
91
+ patterns.push(
92
+ ignorePatternToMinimatch(
93
+ pattern.length && pattern.charCodeAt(0) === 33 /*'!'*/
94
+ ? `!${path.posix.join(base, pattern.slice(1))}`
95
+ : path.posix.join(base, pattern)
96
+ )
97
+ )
98
+ }
99
+ }
100
+ return patterns
101
+ }
102
+ function ignoreFileToGlobPatterns(content, filepath, cwd) {
103
+ return ignoreFileLinesToGlobPatterns(content.split(/\r?\n/), filepath, cwd)
104
+ }
105
+
106
+ // Based on `@eslint/compat` convertIgnorePatternToMinimatch.
107
+ // Apache v2.0 licensed
108
+ // Copyright Nicholas C. Zakas
109
+ // https://github.com/eslint/rewrite/blob/compat-v1.2.1/packages/compat/src/ignore-file.js#L28
110
+ function ignorePatternToMinimatch(pattern) {
111
+ const isNegated = pattern.startsWith('!')
112
+ const negatedPrefix = isNegated ? '!' : ''
113
+ const patternToTest = (isNegated ? pattern.slice(1) : pattern).trimEnd()
114
+ // Special cases.
115
+ if (
116
+ patternToTest === '' ||
117
+ patternToTest === '**' ||
118
+ patternToTest === '/**' ||
119
+ patternToTest === '**'
120
+ ) {
121
+ return `${negatedPrefix}${patternToTest}`
122
+ }
123
+ const firstIndexOfSlash = patternToTest.indexOf('/')
124
+ const matchEverywherePrefix =
125
+ firstIndexOfSlash === -1 || firstIndexOfSlash === patternToTest.length - 1
126
+ ? '**/'
127
+ : ''
128
+ const patternWithoutLeadingSlash =
129
+ firstIndexOfSlash === 0 ? patternToTest.slice(1) : patternToTest
130
+ // Escape `{` and `(` because in gitignore patterns they are just
131
+ // literal characters without any specific syntactic meaning,
132
+ // while in minimatch patterns they can form brace expansion or extglob syntax.
133
+ //
134
+ // For example, gitignore pattern `src/{a,b}.js` ignores file `src/{a,b}.js`.
135
+ // But, the same minimatch pattern `src/{a,b}.js` ignores files `src/a.js` and `src/b.js`.
136
+ // Minimatch pattern `src/\{a,b}.js` is equivalent to gitignore pattern `src/{a,b}.js`.
137
+ const escapedPatternWithoutLeadingSlash =
138
+ patternWithoutLeadingSlash.replaceAll(
139
+ /(?=((?:\\.|[^{(])*))\1([{(])/guy,
140
+ '$1\\$2'
141
+ )
142
+ const matchInsideSuffix = patternToTest.endsWith('/**') ? '/*' : ''
143
+ return `${negatedPrefix}${matchEverywherePrefix}${escapedPatternWithoutLeadingSlash}${matchInsideSuffix}`
144
+ }
145
+ function workspacePatternToGlobPattern(workspace) {
146
+ const { length } = workspace
147
+ if (!length) {
148
+ return ''
149
+ }
150
+ // If the workspace ends with "/"
151
+ if (workspace.charCodeAt(length - 1) === 47 /*'/'*/) {
152
+ return `${workspace}/*/package.json`
153
+ }
154
+ // If the workspace ends with "/**"
155
+ if (
156
+ workspace.charCodeAt(length - 1) === 42 /*'*'*/ &&
157
+ workspace.charCodeAt(length - 2) === 42 /*'*'*/ &&
158
+ workspace.charCodeAt(length - 3) === 47 /*'/'*/
159
+ ) {
160
+ return `${workspace}/*/**/package.json`
161
+ }
162
+ // Things like "packages/a" or "packages/*"
163
+ return `${workspace}/package.json`
164
+ }
165
+ async function filterGlobResultToSupportedFiles(entries, supportedFiles) {
166
+ const patterns = ['golang', NPM$6, 'maven', 'pypi', 'gem', 'nuget'].reduce(
167
+ (r, n) => {
168
+ const supported = supportedFiles[n]
169
+ r.push(
170
+ ...(supported
171
+ ? Object.values(supported).map(p => `**/${p.pattern}`)
172
+ : [])
173
+ )
174
+ return r
175
+ },
176
+ []
177
+ )
178
+ return entries.filter(p => vendor.micromatchExports.some(p, patterns))
179
+ }
180
+ async function globWithGitIgnore(patterns, options) {
181
+ const {
182
+ cwd = process.cwd(),
183
+ socketConfig,
184
+ ...additionalOptions
185
+ } = {
186
+ __proto__: null,
187
+ ...options
188
+ }
189
+ const projectIgnorePaths = socketConfig?.projectIgnorePaths
190
+ const ignoreFiles = await vendor.distExports.glob(['**/.gitignore'], {
191
+ absolute: true,
192
+ cwd,
193
+ expandDirectories: true
194
+ })
195
+ const ignores = [
196
+ ...ignoredDirPatterns,
197
+ ...(Array.isArray(projectIgnorePaths)
198
+ ? ignoreFileLinesToGlobPatterns(
199
+ projectIgnorePaths,
200
+ path.join(cwd, '.gitignore'),
201
+ cwd
202
+ )
203
+ : []),
204
+ ...(
205
+ await Promise.all(
206
+ ignoreFiles.map(async filepath =>
207
+ ignoreFileToGlobPatterns(
208
+ await fs.promises.readFile(filepath, 'utf8'),
209
+ filepath,
210
+ cwd
211
+ )
212
+ )
213
+ )
214
+ ).flat()
215
+ ]
216
+ const hasNegatedPattern = ignores.some(p => p.charCodeAt(0) === 33 /*'!'*/)
217
+ const globOptions = {
218
+ absolute: true,
219
+ cwd,
220
+ expandDirectories: false,
221
+ ignore: hasNegatedPattern ? [] : ignores,
222
+ ...additionalOptions
223
+ }
224
+ const result = await vendor.distExports.glob(patterns, globOptions)
225
+ if (!hasNegatedPattern) {
226
+ return result
227
+ }
228
+ const { absolute } = globOptions
229
+
230
+ // Note: the input files must be INSIDE the cwd. If you get strange looking
231
+ // relative path errors here, most likely your path is outside the given cwd.
232
+ const filtered = vendor
233
+ .ignoreExports()
234
+ .add(ignores)
235
+ .filter(absolute ? result.map(p => path.relative(cwd, p)) : result)
236
+ return absolute ? filtered.map(p => path.resolve(cwd, p)) : filtered
237
+ }
238
+ async function globNodeModules(cwd = process.cwd()) {
239
+ return await vendor.distExports.glob('**/node_modules/**', {
240
+ absolute: true,
241
+ cwd
242
+ })
243
+ }
244
+ async function globWorkspace(agent, cwd = process.cwd()) {
245
+ const workspaceGlobs = await getWorkspaceGlobs(agent, cwd)
246
+ return workspaceGlobs.length
247
+ ? await vendor.distExports.glob(workspaceGlobs, {
248
+ absolute: true,
249
+ cwd,
250
+ ignore: ['**/node_modules/**', '**/bower_components/**']
251
+ })
252
+ : []
253
+ }
254
+ function pathsToGlobPatterns(paths) {
255
+ // TODO: Does not support `~/` paths.
256
+ return paths.map(p => (p === '.' || p === './' ? '**/*' : p))
257
+ }
258
+
259
+ const { abortSignal } = constants
260
+ async function removeNodeModules(cwd = process.cwd()) {
261
+ const nodeModulesPaths = await globNodeModules(cwd)
262
+ await Promise.all(nodeModulesPaths.map(p => fs$1.remove(p)))
263
+ }
264
+ async function findUp(name, { cwd = process.cwd(), signal = abortSignal }) {
265
+ let dir = path.resolve(cwd)
266
+ const { root } = path.parse(dir)
267
+ const names = [name].flat()
268
+ while (dir && dir !== root) {
269
+ for (const name of names) {
270
+ if (signal?.aborted) {
271
+ return undefined
272
+ }
273
+ const filePath = path.join(dir, name)
274
+ try {
275
+ // eslint-disable-next-line no-await-in-loop
276
+ const stats = await fs.promises.stat(filePath)
277
+ if (stats.isFile()) {
278
+ return filePath
279
+ }
280
+ } catch {}
281
+ }
282
+ dir = path.dirname(dir)
283
+ }
284
+ return undefined
285
+ }
286
+ async function readFileBinary(filepath, options) {
287
+ return await fs.promises.readFile(filepath, {
288
+ signal: abortSignal,
289
+ ...options,
290
+ encoding: 'binary'
291
+ })
292
+ }
293
+ async function readFileUtf8(filepath, options) {
294
+ return await fs.promises.readFile(filepath, {
295
+ signal: abortSignal,
296
+ ...options,
297
+ encoding: 'utf8'
298
+ })
299
+ }
300
+ async function safeReadFile(filepath, options) {
301
+ try {
302
+ return await fs.promises.readFile(filepath, {
303
+ encoding: 'utf8',
304
+ signal: abortSignal,
305
+ ...(typeof options === 'string'
306
+ ? {
307
+ encoding: options
308
+ }
309
+ : options)
310
+ })
311
+ } catch {}
312
+ return undefined
313
+ }
314
+ function safeReadFileSync(filepath, options) {
315
+ try {
316
+ return fs.readFileSync(filepath, {
317
+ encoding: 'utf8',
318
+ ...(typeof options === 'string'
319
+ ? {
320
+ encoding: options
321
+ }
322
+ : options)
323
+ })
324
+ } catch {}
325
+ return undefined
326
+ }
327
+
328
+ const { LOCALAPPDATA, SOCKET_APP_DIR } = constants
329
+ const supportedConfigKeys = new Map([
330
+ ['apiBaseUrl', 'Base URL of the API endpoint'],
331
+ ['apiProxy', 'A proxy through which to access the API'],
332
+ ['apiToken', 'The API token required to access most API endpoints'],
333
+ [
334
+ 'defaultOrg',
335
+ 'The default org slug to use; usually the org your API token has access to. When set, all orgSlug arguments are implied to be this value.'
336
+ ],
337
+ [
338
+ 'enforcedOrgs',
339
+ 'Orgs in this list have their security policies enforced on this machine'
340
+ ],
341
+ ['isTestingV1', 'For development of testing the next major bump']
342
+ ])
343
+ const sensitiveConfigKeys = new Set(['apiToken'])
344
+ let _cachedConfig
345
+ // When using --config or SOCKET_CLI_CONFIG, do not persist the config.
346
+ let _readOnlyConfig = false
347
+ function overrideCachedConfig(jsonConfig) {
348
+ debug.debugLog('Overriding entire config, marking config as read-only')
349
+ let config
350
+ try {
351
+ config = JSON.parse(String(jsonConfig))
352
+ if (!config || typeof config !== 'object') {
353
+ // `null` is valid json, so are primitive values. They're not valid config objects :)
354
+ return {
355
+ ok: false,
356
+ message: 'Could not parse Config as JSON',
357
+ cause:
358
+ "Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again."
359
+ }
360
+ }
361
+ } catch {
362
+ // Force set an empty config to prevent accidentally using system settings
363
+ _cachedConfig = {}
364
+ _readOnlyConfig = true
365
+ return {
366
+ ok: false,
367
+ message: 'Could not parse Config as JSON',
368
+ cause:
369
+ "Could not JSON parse the config override. Make sure it's a proper JSON object (double-quoted keys and strings, no unquoted `undefined`) and try again."
370
+ }
371
+ }
372
+
373
+ // @ts-ignore Override an illegal object.
374
+ _cachedConfig = config
375
+ _readOnlyConfig = true
376
+
377
+ // Normalize apiKey to apiToken.
378
+ if (_cachedConfig['apiKey']) {
379
+ if (_cachedConfig['apiToken']) {
380
+ logger.logger.warn(
381
+ 'Note: The config override had both apiToken and apiKey. Using the apiToken value. Remove the apiKey to get rid of this message.'
382
+ )
383
+ }
384
+ _cachedConfig['apiToken'] = _cachedConfig['apiKey']
385
+ delete _cachedConfig['apiKey']
386
+ }
387
+ return {
388
+ ok: true,
389
+ data: undefined
390
+ }
391
+ }
392
+ function overrideConfigApiToken(apiToken) {
393
+ debug.debugLog('Overriding API token, marking config as read-only')
394
+ // Set token to the local cached config and mark it read-only so it doesn't persist
395
+ _cachedConfig = {
396
+ ...vendor.configExports,
397
+ ...(apiToken === undefined
398
+ ? {}
399
+ : {
400
+ apiToken: String(apiToken)
401
+ })
402
+ }
403
+ _readOnlyConfig = true
404
+ }
405
+ function getConfigValues() {
406
+ if (_cachedConfig === undefined) {
407
+ _cachedConfig = {}
408
+ // Order: env var > --config flag > file
409
+ const configPath = getConfigPath()
410
+ if (configPath) {
411
+ const raw = safeReadFileSync(configPath)
412
+ if (raw) {
413
+ try {
414
+ Object.assign(
415
+ _cachedConfig,
416
+ JSON.parse(Buffer.from(raw, 'base64').toString())
417
+ )
418
+ } catch {
419
+ logger.logger.warn(`Failed to parse config at ${configPath}`)
420
+ }
421
+ // Normalize apiKey to apiToken and persist it.
422
+ // This is a one time migration per user.
423
+ if (_cachedConfig['apiKey']) {
424
+ const token = _cachedConfig['apiKey']
425
+ delete _cachedConfig['apiKey']
426
+ updateConfigValue('apiToken', token)
427
+ }
428
+ } else {
429
+ fs.mkdirSync(path.dirname(configPath), {
430
+ recursive: true
431
+ })
432
+ }
433
+ }
434
+ }
435
+ return _cachedConfig
436
+ }
437
+ let _configPath
438
+ let _warnedConfigPathWin32Missing = false
439
+ function getConfigPath() {
440
+ // Get the OS app data folder:
441
+ // - Win: %LOCALAPPDATA% or fail?
442
+ // - Mac: %XDG_DATA_HOME% or fallback to "~/Library/Application Support/"
443
+ // - Linux: %XDG_DATA_HOME% or fallback to "~/.local/share/"
444
+ // Note: LOCALAPPDATA is typically: C:\Users\USERNAME\AppData
445
+ // Note: XDG stands for "X Desktop Group", nowadays "freedesktop.org"
446
+ // On most systems that path is: $HOME/.local/share
447
+ // Then append `socket/settings`, so:
448
+ // - Win: %LOCALAPPDATA%\socket\settings or return undefined
449
+ // - Mac: %XDG_DATA_HOME%/socket/settings or "~/Library/Application Support/socket/settings"
450
+ // - Linux: %XDG_DATA_HOME%/socket/settings or "~/.local/share/socket/settings"
451
+
452
+ if (_configPath === undefined) {
453
+ // Lazily access constants.WIN32.
454
+ const { WIN32 } = constants
455
+ let dataHome = WIN32
456
+ ? // Lazily access constants.ENV.LOCALAPPDATA
457
+ constants.ENV.LOCALAPPDATA
458
+ : // Lazily access constants.ENV.XDG_DATA_HOME
459
+ constants.ENV.XDG_DATA_HOME
460
+ if (!dataHome) {
461
+ if (WIN32) {
462
+ if (!_warnedConfigPathWin32Missing) {
463
+ _warnedConfigPathWin32Missing = true
464
+ logger.logger.warn(`Missing %${LOCALAPPDATA}%`)
465
+ }
466
+ } else {
467
+ dataHome = path.join(
468
+ os.homedir(),
469
+ ...(process.platform === 'darwin'
470
+ ? ['Library', 'Application Support']
471
+ : ['.local', 'share'])
472
+ )
473
+ }
474
+ }
475
+ _configPath = dataHome ? path.join(dataHome, SOCKET_APP_DIR) : undefined
476
+ }
477
+ return _configPath
478
+ }
479
+ function normalizeConfigKey(key) {
480
+ // Note: apiKey was the old name of the token. When we load a config with
481
+ // property apiKey, we'll copy that to apiToken and delete the old property.
482
+ const normalizedKey = key === 'apiKey' ? 'apiToken' : key
483
+ if (!supportedConfigKeys.has(normalizedKey)) {
484
+ return {
485
+ ok: false,
486
+ message: `Invalid config key: ${normalizedKey}`,
487
+ data: undefined
488
+ }
489
+ }
490
+ return {
491
+ ok: true,
492
+ data: key
493
+ }
494
+ }
495
+ function findSocketYmlSync(dir = process.cwd()) {
496
+ let prevDir = null
497
+ while (dir !== prevDir) {
498
+ let ymlPath = path.join(dir, 'socket.yml')
499
+ let yml = safeReadFileSync(ymlPath)
500
+ if (yml === undefined) {
501
+ ymlPath = path.join(dir, 'socket.yaml')
502
+ yml = safeReadFileSync(ymlPath)
503
+ }
504
+ if (typeof yml === 'string') {
505
+ try {
506
+ return {
507
+ path: ymlPath,
508
+ parsed: vendor.configExports.parseSocketConfig(yml)
509
+ }
510
+ } catch {
511
+ throw new Error(`Found file but was unable to parse ${ymlPath}`)
512
+ }
513
+ }
514
+ prevDir = dir
515
+ dir = path.join(dir, '..')
516
+ }
517
+ return null
518
+ }
519
+ function getConfigValue(key) {
520
+ const localConfig = getConfigValues()
521
+ const keyResult = normalizeConfigKey(key)
522
+ if (!keyResult.ok) {
523
+ return keyResult
524
+ }
525
+ return {
526
+ ok: true,
527
+ data: localConfig[keyResult.data]
528
+ }
529
+ }
530
+ // This version squashes errors, returning undefined instead.
531
+ // Should be used when we can reasonably predict the call can't fail.
532
+ function getConfigValueOrUndef(key) {
533
+ const localConfig = getConfigValues()
534
+ const keyResult = normalizeConfigKey(key)
535
+ if (!keyResult.ok) {
536
+ return undefined
537
+ }
538
+ return localConfig[keyResult.data]
539
+ }
540
+ function isReadOnlyConfig() {
541
+ return _readOnlyConfig
542
+ }
543
+ let _pendingSave = false
544
+ function updateConfigValue(key, value) {
545
+ const localConfig = getConfigValues()
546
+ const keyResult = normalizeConfigKey(key)
547
+ if (!keyResult.ok) {
548
+ return keyResult
549
+ }
550
+ localConfig[keyResult.data] = value
551
+ if (_readOnlyConfig) {
552
+ return {
553
+ ok: true,
554
+ message: `Config key '${key}' was updated`,
555
+ data: 'Change applied but not persisted; current config is overridden through env var or flag'
556
+ }
557
+ }
558
+ if (!_pendingSave) {
559
+ _pendingSave = true
560
+ process.nextTick(() => {
561
+ _pendingSave = false
562
+ const configPath = getConfigPath()
563
+ if (configPath) {
564
+ fs.writeFileSync(
565
+ configPath,
566
+ Buffer.from(JSON.stringify(localConfig)).toString('base64')
567
+ )
568
+ }
569
+ })
570
+ }
571
+ return {
572
+ ok: true,
573
+ message: `Config key '${key}' was updated`,
574
+ data: undefined
575
+ }
576
+ }
577
+ function isTestingV1() {
578
+ return !!getConfigValueOrUndef('isTestingV1')
579
+ }
580
+
581
+ const {
582
+ kInternalsSymbol,
583
+ [kInternalsSymbol]: { getSentry }
584
+ } = constants
585
+ class AuthError extends Error {}
586
+ class InputError extends Error {
587
+ constructor(message, body) {
588
+ super(message)
589
+ this.body = body
590
+ }
591
+ }
592
+ async function captureException(exception, hint) {
593
+ const result = captureExceptionSync(exception, hint)
594
+ // "Sleep" for a second, just in case, hopefully enough time to initiate fetch.
595
+ await promises.setTimeout(1000)
596
+ return result
597
+ }
598
+ function captureExceptionSync(exception, hint) {
599
+ const Sentry = getSentry()
600
+ if (!Sentry) {
601
+ return ''
602
+ }
603
+ debug.debugLog('captureException: Sending exception to Sentry')
604
+ return Sentry.captureException(exception, hint)
605
+ }
606
+
607
+ function failMsgWithBadge(badge, msg) {
608
+ return `${vendor.yoctocolorsCjsExports.bgRed(vendor.yoctocolorsCjsExports.bold(vendor.yoctocolorsCjsExports.white(` ${badge}${msg ? ': ' : ''}`)))}${msg ? ' ' + vendor.yoctocolorsCjsExports.bold(msg) : ''}`
609
+ }
610
+
611
+ const { SOCKET_PUBLIC_API_TOKEN } = constants
612
+
613
+ // The API server that should be used for operations.
614
+ function getDefaultApiBaseUrl$1() {
615
+ const baseUrl =
616
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_BASE_URL.
617
+ constants.ENV.SOCKET_SECURITY_API_BASE_URL ||
618
+ getConfigValueOrUndef('apiBaseUrl')
619
+ return strings.isNonEmptyString(baseUrl) ? baseUrl : undefined
620
+ }
621
+
622
+ // The API server that should be used for operations.
623
+ function getDefaultHttpProxy() {
624
+ const apiProxy =
625
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_PROXY.
626
+ constants.ENV.SOCKET_SECURITY_API_PROXY || getConfigValueOrUndef('apiProxy')
627
+ return strings.isNonEmptyString(apiProxy) ? apiProxy : undefined
628
+ }
629
+
630
+ // This API key should be stored globally for the duration of the CLI execution.
631
+ let _defaultToken
632
+ function getDefaultToken() {
633
+ // Lazily access constants.ENV.SOCKET_CLI_NO_API_TOKEN.
634
+ if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {
635
+ _defaultToken = undefined
636
+ } else {
637
+ const key =
638
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_TOKEN.
639
+ constants.ENV.SOCKET_SECURITY_API_TOKEN ||
640
+ getConfigValueOrUndef('apiToken') ||
641
+ _defaultToken
642
+ _defaultToken = strings.isNonEmptyString(key) ? key : undefined
643
+ }
644
+ return _defaultToken
645
+ }
646
+ function getVisibleTokenPrefix() {
647
+ const apiToken = getDefaultToken()
648
+ if (!apiToken) {
649
+ return ''
650
+ }
651
+ const PREFIX = 'sktsec_'
652
+ return apiToken.slice(PREFIX.length, PREFIX.length + 5)
653
+ }
654
+ function hasDefaultToken() {
655
+ return !!getDefaultToken()
656
+ }
657
+ function getPublicToken() {
658
+ return (
659
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_TOKEN.
660
+ (constants.ENV.SOCKET_SECURITY_API_TOKEN || getDefaultToken()) ??
661
+ SOCKET_PUBLIC_API_TOKEN
662
+ )
663
+ }
664
+ async function setupSdk(
665
+ apiToken = getDefaultToken(),
666
+ apiBaseUrl = getDefaultApiBaseUrl$1(),
667
+ proxy = getDefaultHttpProxy()
668
+ ) {
669
+ if (typeof apiToken !== 'string' && vendor.isInteractiveExports()) {
670
+ apiToken = await prompts.password({
671
+ message:
672
+ 'Enter your Socket.dev API key (not saved, use socket login to persist)'
673
+ })
674
+ _defaultToken = apiToken
675
+ }
676
+ if (!apiToken) {
677
+ return {
678
+ ok: false,
679
+ message: 'Auth Error',
680
+ cause: 'You need to provide an API Token. Run `socket login` first.'
681
+ }
682
+ }
683
+ return {
684
+ ok: true,
685
+ data: new vendor.distExports$2.SocketSdk(apiToken, {
686
+ agent: proxy
687
+ ? new vendor.HttpsProxyAgent({
688
+ proxy
689
+ })
690
+ : undefined,
691
+ baseUrl: apiBaseUrl,
692
+ userAgent: vendor.distExports$2.createUserAgentFromPkgJson({
693
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_NAME.
694
+ name: constants.ENV.INLINED_SOCKET_CLI_NAME,
695
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_VERSION.
696
+ version: constants.ENV.INLINED_SOCKET_CLI_VERSION,
697
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE.
698
+ homepage: constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE
699
+ })
700
+ })
701
+ }
702
+ }
703
+
704
+ // TODO: this function is removed after v1.0.0
705
+ function handleUnsuccessfulApiResponse(_name, error, cause, status) {
706
+ const message = `${error || 'No error message returned'}${cause ? ` (reason: ${cause})` : ''}`
707
+ if (status === 401 || status === 403) {
708
+ // Lazily access constants.spinner.
709
+ const { spinner } = constants
710
+ spinner.stop()
711
+ throw new AuthError(message)
712
+ }
713
+ logger.logger.fail(failMsgWithBadge('Socket API returned an error', message))
714
+ // eslint-disable-next-line n/no-process-exit
715
+ process.exit(1)
716
+ }
717
+ async function handleApiCall(value, fetchingDesc) {
718
+ // Lazily access constants.spinner.
719
+ const { spinner } = constants
720
+ spinner.start(`Requesting ${fetchingDesc} from API...`)
721
+ let result
722
+ try {
723
+ result = await value
724
+
725
+ // TODO: info, not success (looks weird when response is non-200)
726
+ spinner.successAndStop(
727
+ `Received API response (after requesting ${fetchingDesc}).`
728
+ )
729
+ } catch (e) {
730
+ spinner.failAndStop(`An error was thrown while requesting ${fetchingDesc}`)
731
+ debug.debugLog(`handleApiCall(${fetchingDesc}) threw error:\n`, e)
732
+ const message = `${e || 'No error message returned'}`
733
+ const cause = `${e || 'No error message returned'}`
734
+ return {
735
+ ok: false,
736
+ message: 'Socket API returned an error',
737
+ cause: `${message}${cause ? ` ( Reason: ${cause} )` : ''}`
738
+ }
739
+ } finally {
740
+ spinner.stop()
741
+ }
742
+
743
+ // Note: TS can't narrow down the type of result due to generics
744
+ if (result.success === false) {
745
+ const err = result
746
+ const message = `${err.error || 'No error message returned'}`
747
+ debug.debugLog(`handleApiCall(${fetchingDesc}) bad response:\n`, err)
748
+ return {
749
+ ok: false,
750
+ message: 'Socket API returned an error',
751
+ cause: `${message}${err.cause ? ` ( Reason: ${err.cause} )` : ''}`,
752
+ data: {
753
+ code: result.status
754
+ }
755
+ }
756
+ } else {
757
+ const ok = result
758
+ return {
759
+ ok: true,
760
+ data: ok.data
761
+ }
762
+ }
763
+ }
764
+ async function handleApiCallNoSpinner(value, description) {
765
+ let result
766
+ try {
767
+ result = await value
768
+ } catch (e) {
769
+ debug.debugLog(`handleApiCall(${description}) threw error:\n`, e)
770
+ const message = `${e || 'No error message returned'}`
771
+ const cause = `${e || 'No error message returned'}`
772
+ return {
773
+ ok: false,
774
+ message: 'Socket API returned an error',
775
+ cause: `${message}${cause ? ` ( Reason: ${cause} )` : ''}`
776
+ }
777
+ }
778
+
779
+ // Note: TS can't narrow down the type of result due to generics
780
+ if (result.success === false) {
781
+ const err = result
782
+ const message = `${err.error || 'No error message returned'}`
783
+ debug.debugLog(`handleApiCall(${description}) bad response:\n`, err)
784
+ return {
785
+ ok: false,
786
+ message: 'Socket API returned an error',
787
+ cause: `${message}${err.cause ? ` ( Reason: ${err.cause} )` : ''}`,
788
+ data: {
789
+ code: result.status
790
+ }
791
+ }
792
+ } else {
793
+ const ok = result
794
+ return {
795
+ ok: true,
796
+ data: ok.data
797
+ }
798
+ }
799
+ }
800
+ async function getErrorMessageForHttpStatusCode(code) {
801
+ if (code === 400) {
802
+ return 'One of the options passed might be incorrect'
803
+ }
804
+ if (code === 403 || code === 401) {
805
+ return 'Your API token may not have the required permissions for this command or you might be trying to access (data from) an organization that is not linked to the API key you are logged in with'
806
+ }
807
+ if (code === 404) {
808
+ return 'The requested Socket API endpoint was not found (404) or there was no result for the requested parameters. If unexpected, this could be a temporary problem caused by an incident or a bug in the CLI. If the problem persists please let us know.'
809
+ }
810
+ if (code === 500) {
811
+ return 'There was an unknown server side problem with your request. This ought to be temporary. Please let us know if this problem persists.'
812
+ }
813
+ return `Server responded with status code ${code}`
814
+ }
815
+
816
+ // The API server that should be used for operations.
817
+ function getDefaultApiBaseUrl() {
818
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_BASE_URL.
819
+ const SOCKET_SECURITY_API_BASE_URL =
820
+ constants.ENV.SOCKET_SECURITY_API_BASE_URL
821
+ const baseUrl =
822
+ SOCKET_SECURITY_API_BASE_URL || getConfigValueOrUndef('apiBaseUrl')
823
+ if (strings.isNonEmptyString(baseUrl)) {
824
+ return baseUrl
825
+ }
826
+ // Lazily access constants.API_V0_URL.
827
+ const API_V0_URL = constants.API_V0_URL
828
+ return API_V0_URL
829
+ }
830
+ async function queryApi(path, apiToken) {
831
+ const baseUrl = getDefaultApiBaseUrl() || ''
832
+ if (!baseUrl) {
833
+ logger.logger.warn(
834
+ 'API endpoint is not set and default was empty. Request is likely to fail.'
835
+ )
836
+ }
837
+ return await fetch(`${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`, {
838
+ method: 'GET',
839
+ headers: {
840
+ Authorization: `Basic ${btoa(`${apiToken}:`)}`
841
+ }
842
+ })
843
+ }
844
+ async function queryApiSafeText(path, fetchSpinnerDesc) {
845
+ const apiToken = getDefaultToken()
846
+ if (!apiToken) {
847
+ return {
848
+ ok: false,
849
+ message: 'Authentication Error',
850
+ cause:
851
+ 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
852
+ }
853
+ }
854
+ if (fetchSpinnerDesc) {
855
+ // Lazily access constants.spinner.
856
+ const { spinner } = constants
857
+ spinner.start(`Requesting ${fetchSpinnerDesc} from API...`)
858
+ }
859
+ let result
860
+ try {
861
+ result = await queryApi(path, apiToken)
862
+ if (fetchSpinnerDesc) {
863
+ // Lazily access constants.spinner.
864
+ const { spinner } = constants
865
+ spinner.successAndStop(
866
+ `Received API response (after requesting ${fetchSpinnerDesc}).`
867
+ )
868
+ }
869
+ } catch (e) {
870
+ if (fetchSpinnerDesc) {
871
+ // Lazily access constants.spinner.
872
+ const { spinner } = constants
873
+ spinner.failAndStop(
874
+ `An error was thrown while requesting ${fetchSpinnerDesc}`
875
+ )
876
+ }
877
+ debug.debugLog('Error thrown trying to await queryApi():')
878
+ debug.debugLog(e)
879
+ const msg = e?.message
880
+ return {
881
+ ok: false,
882
+ message: 'API Request failed to complete',
883
+ ...(msg
884
+ ? {
885
+ cause: msg
886
+ }
887
+ : {})
888
+ }
889
+ }
890
+ if (!result.ok) {
891
+ const cause = await getErrorMessageForHttpStatusCode(result.status)
892
+ return {
893
+ ok: false,
894
+ message: 'Socket API returned an error',
895
+ cause: `${result.statusText}${cause ? ` (cause: ${cause})` : ''}`
896
+ }
897
+ }
898
+ try {
899
+ const data = await result.text()
900
+ return {
901
+ ok: true,
902
+ data
903
+ }
904
+ } catch (e) {
905
+ debug.debugLog('Error thrown trying to await result.text():')
906
+ debug.debugLog(e)
907
+ return {
908
+ ok: false,
909
+ message: 'API Request failed to complete',
910
+ cause: 'There was an unexpected error trying to read the response text'
911
+ }
912
+ }
913
+ }
914
+ async function queryApiSafeJson(path, fetchSpinnerDesc = '') {
915
+ const result = await queryApiSafeText(path, fetchSpinnerDesc)
916
+ if (!result.ok) {
917
+ return result
918
+ }
919
+ try {
920
+ return {
921
+ ok: true,
922
+ data: JSON.parse(result.data)
923
+ }
924
+ } catch (e) {
925
+ return {
926
+ ok: false,
927
+ message: 'Server returned invalid JSON',
928
+ cause: `Please report this. JSON.parse threw an error over the following response: \`${(result.data?.slice?.(0, 100) || '<empty>').trim() + (result.data?.length > 100 ? '...' : '')}\``
929
+ }
930
+ }
931
+ }
932
+
933
+ function mdTableStringNumber(title1, title2, obj) {
934
+ // | Date | Counts |
935
+ // | ----------- | ------ |
936
+ // | Header | 201464 |
937
+ // | Paragraph | 18 |
938
+ let mw1 = title1.length
939
+ let mw2 = title2.length
940
+ for (const [key, value] of Object.entries(obj)) {
941
+ mw1 = Math.max(mw1, key.length)
942
+ mw2 = Math.max(mw2, String(value ?? '').length)
943
+ }
944
+ const lines = []
945
+ lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)
946
+ lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)
947
+ for (const [key, value] of Object.entries(obj)) {
948
+ lines.push(
949
+ `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`
950
+ )
951
+ }
952
+ lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)
953
+ return lines.join('\n')
954
+ }
955
+ function mdTable(
956
+ logs,
957
+ // This is saying "an array of strings and the strings are a valid key of elements of T"
958
+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
959
+ cols,
960
+ titles = cols
961
+ ) {
962
+ // Max col width required to fit all data in that column
963
+ const cws = cols.map(col => col.length)
964
+ for (const log of logs) {
965
+ for (let i = 0, { length } = cols; i < length; i += 1) {
966
+ // @ts-ignore
967
+ const val = log[cols[i] ?? ''] ?? ''
968
+ cws[i] = Math.max(
969
+ cws[i] ?? 0,
970
+ String(val).length,
971
+ (titles[i] || '').length
972
+ )
973
+ }
974
+ }
975
+ let div = '|'
976
+ for (const cw of cws) {
977
+ div += ' ' + '-'.repeat(cw) + ' |'
978
+ }
979
+ let header = '|'
980
+ for (let i = 0, { length } = titles; i < length; i += 1) {
981
+ header += ' ' + String(titles[i]).padEnd(cws[i] ?? 0, ' ') + ' |'
982
+ }
983
+ let body = ''
984
+ for (const log of logs) {
985
+ body += '|'
986
+ for (let i = 0, { length } = cols; i < length; i += 1) {
987
+ // @ts-ignore
988
+ const val = log[cols[i] ?? ''] ?? ''
989
+ body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'
990
+ }
991
+ body += '\n'
992
+ }
993
+ return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n')
994
+ }
995
+ function mdTableOfPairs(
996
+ arr,
997
+ // This is saying "an array of strings and the strings are a valid key of elements of T"
998
+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
999
+ cols
1000
+ ) {
1001
+ // Max col width required to fit all data in that column
1002
+ const cws = cols.map(col => col.length)
1003
+ for (const [key, val] of arr) {
1004
+ cws[0] = Math.max(cws[0] ?? 0, String(key).length)
1005
+ cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length)
1006
+ }
1007
+ let div = '|'
1008
+ for (const cw of cws) {
1009
+ div += ' ' + '-'.repeat(cw) + ' |'
1010
+ }
1011
+ let header = '|'
1012
+ for (let i = 0, { length } = cols; i < length; i += 1) {
1013
+ header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'
1014
+ }
1015
+ let body = ''
1016
+ for (const [key, val] of arr) {
1017
+ body += '|'
1018
+ body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |'
1019
+ body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |'
1020
+ body += '\n'
1021
+ }
1022
+ return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n')
1023
+ }
1024
+
1025
+ // Serialize the final result object before printing it
1026
+ // All commands that support the --json flag should call this before printing
1027
+ function serializeResultJson(data) {
1028
+ if (typeof data !== 'object' || !data) {
1029
+ process.exitCode = 1
1030
+ // We should not allow to expect the json value to be "null", or a boolean/number/string, even if they are valid "json".
1031
+ const msg =
1032
+ 'There was a problem converting the data set to JSON. The JSON was not an object. Please try again without --json'
1033
+ debug.debugLog('typeof data=', typeof data)
1034
+ if (typeof data !== 'object' && data) {
1035
+ debug.debugLog('data:\n', data)
1036
+ }
1037
+ return (
1038
+ JSON.stringify({
1039
+ ok: false,
1040
+ message: 'Unable to serialize JSON',
1041
+ data: msg
1042
+ }).trim() + '\n'
1043
+ )
1044
+ }
1045
+ try {
1046
+ return JSON.stringify(data, null, 2).trim() + '\n'
1047
+ } catch (e) {
1048
+ debug.debugLog('Error:\n', e)
1049
+ process.exitCode = 1
1050
+ // This could be caused by circular references, which is an "us" problem
1051
+ const msg =
1052
+ 'There was a problem converting the data set to JSON. Please try again without --json'
1053
+ logger.logger.error(msg)
1054
+ return (
1055
+ JSON.stringify({
1056
+ ok: false,
1057
+ message: 'Unable to serialize JSON',
1058
+ data: msg
1059
+ }).trim() + '\n'
1060
+ )
1061
+ }
1062
+ }
1063
+
1064
+ // TODO: not sure if I'm missing something but meow doesn't seem to expose this?
1065
+
1066
+ // Note: we use this description in getFlagListOutput, meow doesn't care
1067
+
1068
+ const commonFlags = {
1069
+ config: {
1070
+ type: 'string',
1071
+ default: '',
1072
+ hidden: true,
1073
+ description: 'Override the local config with this JSON'
1074
+ },
1075
+ dryRun: {
1076
+ type: 'boolean',
1077
+ default: false,
1078
+ hidden: true,
1079
+ // Only show in root command
1080
+ description: 'Do input validation for a command and exit 0 when input is ok'
1081
+ },
1082
+ help: {
1083
+ type: 'boolean',
1084
+ default: false,
1085
+ shortFlag: 'h',
1086
+ description: 'Print this help'
1087
+ },
1088
+ silent: {
1089
+ type: 'boolean',
1090
+ default: false,
1091
+ hidden: true,
1092
+ shortFlag: 's',
1093
+ description: 'Make the CLI less chatty'
1094
+ }
1095
+ }
1096
+ const outputFlags = {
1097
+ json: {
1098
+ type: 'boolean',
1099
+ shortFlag: 'j',
1100
+ default: false,
1101
+ description: 'Output result as json'
1102
+ },
1103
+ markdown: {
1104
+ type: 'boolean',
1105
+ shortFlag: 'm',
1106
+ default: false,
1107
+ description: 'Output result as markdown'
1108
+ }
1109
+ }
1110
+ const validationFlags = {
1111
+ all: {
1112
+ type: 'boolean',
1113
+ default: false,
1114
+ description: 'Include all issues'
1115
+ },
1116
+ strict: {
1117
+ type: 'boolean',
1118
+ default: false,
1119
+ description: 'Exits with an error code if any matching issues are found'
1120
+ }
1121
+ }
1122
+
1123
+ function checkCommandInput(outputKind, ...checks) {
1124
+ if (checks.every(d => d.test)) {
1125
+ return true
1126
+ }
1127
+ const msg = ['Please review the input requirements and try again', '']
1128
+ for (const d of checks) {
1129
+ // If nook, then ignore when test is ok
1130
+ if (d.nook && d.test) {
1131
+ continue
1132
+ }
1133
+ const lines = d.message.split('\n')
1134
+ const { length: lineCount } = lines
1135
+ if (!lineCount) {
1136
+ continue
1137
+ }
1138
+ // If the message has newlines then format the first line with the input
1139
+ // expectation and the rest indented below it.
1140
+ msg.push(
1141
+ ` - ${lines[0]} (${d.test ? vendor.yoctocolorsCjsExports.green(d.pass) : vendor.yoctocolorsCjsExports.red(d.fail)})`
1142
+ )
1143
+ if (lineCount > 1) {
1144
+ msg.push(...lines.slice(1).map(str => ` ${str}`))
1145
+ }
1146
+ msg.push('')
1147
+ }
1148
+
1149
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
1150
+ // options or missing arguments.
1151
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
1152
+ process.exitCode = 2
1153
+ if (outputKind === 'json') {
1154
+ logger.logger.log(
1155
+ serializeResultJson({
1156
+ ok: false,
1157
+ message: 'Input error',
1158
+ data: msg.join('\n')
1159
+ })
1160
+ )
1161
+ } else {
1162
+ logger.logger.fail(failMsgWithBadge('Input error', msg.join('\n')))
1163
+ }
1164
+ return false
1165
+ }
1166
+
1167
+ function getOutputKind(json, markdown) {
1168
+ if (json) {
1169
+ return 'json'
1170
+ }
1171
+ if (markdown) {
1172
+ return 'markdown'
1173
+ }
1174
+ return 'text'
1175
+ }
1176
+
1177
+ function getFlagListOutput(list, indent, { keyPrefix = '--', padName } = {}) {
1178
+ return getHelpListOutput(
1179
+ {
1180
+ ...list
1181
+ },
1182
+ indent,
1183
+ {
1184
+ keyPrefix,
1185
+ padName
1186
+ }
1187
+ )
1188
+ }
1189
+ function getHelpListOutput(
1190
+ list,
1191
+ indent,
1192
+ { keyPrefix = '', padName = 18 } = {}
1193
+ ) {
1194
+ let result = ''
1195
+ const names = Object.keys(list).sort()
1196
+ for (const name of names) {
1197
+ const entry = list[name]
1198
+ if (entry && 'hidden' in entry && entry?.hidden) {
1199
+ continue
1200
+ }
1201
+ const description =
1202
+ (typeof entry === 'object' ? entry.description : entry) || ''
1203
+ result +=
1204
+ ''.padEnd(indent) +
1205
+ (keyPrefix + name).padEnd(padName) +
1206
+ description +
1207
+ '\n'
1208
+ }
1209
+ return result.trim() || '(none)'
1210
+ }
1211
+
1212
+ async function meowWithSubcommands(subcommands, options) {
1213
+ const {
1214
+ aliases = {},
1215
+ argv,
1216
+ defaultSub,
1217
+ importMeta,
1218
+ name,
1219
+ ...additionalOptions
1220
+ } = {
1221
+ __proto__: null,
1222
+ ...options
1223
+ }
1224
+ const [commandOrAliasName_, ...rawCommandArgv] = argv
1225
+ let commandOrAliasName = commandOrAliasName_
1226
+ if (!commandOrAliasName && defaultSub) {
1227
+ commandOrAliasName = defaultSub
1228
+ }
1229
+ const flags = {
1230
+ ...commonFlags,
1231
+ ...additionalOptions.flags
1232
+ }
1233
+
1234
+ // No further args or first arg is a flag (shrug)
1235
+ if (
1236
+ name === 'socket' &&
1237
+ (!commandOrAliasName || commandOrAliasName?.startsWith('-'))
1238
+ ) {
1239
+ flags['dryRun'] = {
1240
+ type: 'boolean',
1241
+ default: false,
1242
+ hidden: false,
1243
+ // Only show on root
1244
+ description:
1245
+ 'Do input validation for a command and exit 0 when input is ok. Every command should support this flag (not shown on help screens)'
1246
+ }
1247
+ }
1248
+
1249
+ // This is basically a dry-run parse of cli args and flags. We use this to
1250
+ // determine config overrides and expected output mode.
1251
+ const cli1 = vendor.meow(`(this should never be printed)`, {
1252
+ argv,
1253
+ importMeta,
1254
+ ...additionalOptions,
1255
+ flags,
1256
+ // Do not strictly check for flags here.
1257
+ allowUnknownFlags: true,
1258
+ // We will emit help when we're ready
1259
+ // Plus, if we allow this then meow() can just exit here.
1260
+ autoHelp: false
1261
+ })
1262
+
1263
+ // Hard override the config if instructed to do so.
1264
+ // The env var overrides the --flag, which overrides the persisted config
1265
+ // Also, when either of these are used, config updates won't persist.
1266
+ let configOverrideResult
1267
+ // Lazily access constants.ENV.SOCKET_CLI_CONFIG.
1268
+ if (constants.ENV.SOCKET_CLI_CONFIG) {
1269
+ configOverrideResult = overrideCachedConfig(
1270
+ // Lazily access constants.ENV.SOCKET_CLI_CONFIG.
1271
+ constants.ENV.SOCKET_CLI_CONFIG
1272
+ )
1273
+ } else if (cli1.flags['config']) {
1274
+ configOverrideResult = overrideCachedConfig(
1275
+ String(cli1.flags['config'] || '')
1276
+ )
1277
+ }
1278
+
1279
+ // Lazily access constants.ENV.SOCKET_CLI_NO_API_TOKEN.
1280
+ if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {
1281
+ // This overrides the config override and even the explicit token env var.
1282
+ // The config will be marked as readOnly to prevent persisting it.
1283
+ overrideConfigApiToken(undefined)
1284
+ } else {
1285
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_TOKEN.
1286
+ const tokenOverride = constants.ENV.SOCKET_SECURITY_API_TOKEN
1287
+ if (tokenOverride) {
1288
+ // This will set the token (even if there was a config override) and
1289
+ // set it to readOnly, making sure the temp token won't be persisted.
1290
+ overrideConfigApiToken(tokenOverride)
1291
+ }
1292
+ }
1293
+ if (configOverrideResult?.ok === false) {
1294
+ emitBanner(name)
1295
+ logger.logger.fail(configOverrideResult.message)
1296
+ process.exitCode = 2
1297
+ return
1298
+ }
1299
+
1300
+ // If we got at least some args, then lets find out if we can find a command.
1301
+ if (commandOrAliasName) {
1302
+ const alias = aliases[commandOrAliasName]
1303
+ // First: Resolve argv data from alias if its an alias that's been given.
1304
+ const [commandName, ...commandArgv] = alias
1305
+ ? [...alias.argv, ...rawCommandArgv]
1306
+ : [commandOrAliasName, ...rawCommandArgv]
1307
+ // Second: Find a command definition using that data.
1308
+ const commandDefinition = commandName ? subcommands[commandName] : undefined
1309
+ // Third: If a valid command has been found, then we run it...
1310
+ if (commandDefinition) {
1311
+ return await commandDefinition.run(commandArgv, importMeta, {
1312
+ parentName: name
1313
+ })
1314
+ }
1315
+ }
1316
+ if (isTestingV1()) {
1317
+ delete subcommands['diff-scan']
1318
+ delete subcommands['info']
1319
+ delete subcommands['report']
1320
+ }
1321
+
1322
+ // Parse it again. Config overrides should now be applied (may affect help).
1323
+ const cli2 = vendor.meow(
1324
+ `
1325
+ Usage
1326
+ $ ${name} <command>
1327
+
1328
+ Commands
1329
+ ${getHelpListOutput(
1330
+ {
1331
+ ...objects.toSortedObject(
1332
+ Object.fromEntries(
1333
+ Object.entries(subcommands).filter(
1334
+ ({ 1: subcommand }) => !subcommand.hidden
1335
+ )
1336
+ )
1337
+ ),
1338
+ ...objects.toSortedObject(
1339
+ Object.fromEntries(
1340
+ Object.entries(aliases).filter(({ 1: alias }) => {
1341
+ const { hidden } = alias
1342
+ const cmdName = hidden ? '' : alias.argv[0]
1343
+ const subcommand = cmdName ? subcommands[cmdName] : undefined
1344
+ return subcommand && !subcommand.hidden
1345
+ })
1346
+ )
1347
+ )
1348
+ },
1349
+ 6
1350
+ )}
1351
+
1352
+ Options
1353
+ ${getFlagListOutput(flags, 6)}
1354
+
1355
+ Examples
1356
+ $ ${name} --help
1357
+ `,
1358
+ {
1359
+ argv,
1360
+ importMeta,
1361
+ ...additionalOptions,
1362
+ flags,
1363
+ // Do not strictly check for flags here.
1364
+ allowUnknownFlags: true,
1365
+ // We will emit help when we're ready
1366
+ // Plus, if we allow this then meow() can just exit here.
1367
+ autoHelp: false
1368
+ }
1369
+ )
1370
+
1371
+ // ...else we provide basic instructions and help.
1372
+ if (!cli2.flags['silent']) {
1373
+ emitBanner(name)
1374
+ }
1375
+ if (!cli2.flags['help'] && cli2.flags['dryRun']) {
1376
+ process.exitCode = 0
1377
+ // Lazily access constants.DRY_RUN_LABEL.
1378
+ logger.logger.log(
1379
+ `${constants.DRY_RUN_LABEL}: No-op, call a sub-command; ok`
1380
+ )
1381
+ } else {
1382
+ // When you explicitly request --help, the command should be successful
1383
+ // so we exit(0). If we do it because we need more input, we exit(2).
1384
+ cli2.showHelp(cli2.flags['help'] ? 0 : 2)
1385
+ }
1386
+ }
1387
+
1388
+ /**
1389
+ * Note: meow will exit immediately if it calls its .showHelp()
1390
+ */
1391
+ function meowOrExit({
1392
+ allowUnknownFlags,
1393
+ // commands that pass-through args need to allow this
1394
+ argv,
1395
+ config,
1396
+ importMeta,
1397
+ parentName
1398
+ }) {
1399
+ const command = `${parentName} ${config.commandName}`
1400
+
1401
+ // This exits if .printHelp() is called either by meow itself or by us.
1402
+ const cli = vendor.meow({
1403
+ argv,
1404
+ description: config.description,
1405
+ help: config.help(command, config),
1406
+ importMeta,
1407
+ flags: config.flags,
1408
+ allowUnknownFlags: true,
1409
+ // meow will exit(1) before printing the banner
1410
+ autoHelp: false // meow will exit(0) before printing the banner
1411
+ })
1412
+ if (!cli.flags['silent']) {
1413
+ emitBanner(command)
1414
+ }
1415
+ if (!allowUnknownFlags) {
1416
+ // Run meow specifically with the flag setting. It will exit(2) if an
1417
+ // invalid flag is set and print a message.
1418
+ vendor.meow({
1419
+ argv,
1420
+ description: config.description,
1421
+ help: config.help(command, config),
1422
+ importMeta,
1423
+ flags: config.flags,
1424
+ allowUnknownFlags: false,
1425
+ autoHelp: false
1426
+ })
1427
+ }
1428
+ if (cli.flags['help']) {
1429
+ cli.showHelp(0)
1430
+ }
1431
+ // Now test for help state. Run meow again. If it exits now, it must be due
1432
+ // to wanting to print the help screen. But it would exit(0) and we want a
1433
+ // consistent exit(2) for that case (missing input). TODO: move away from meow
1434
+ process.exitCode = 2
1435
+ vendor.meow({
1436
+ argv,
1437
+ description: config.description,
1438
+ help: config.help(command, config),
1439
+ importMeta,
1440
+ flags: config.flags,
1441
+ allowUnknownFlags: Boolean(allowUnknownFlags),
1442
+ autoHelp: false
1443
+ })
1444
+ // Ok, no help, reset to default.
1445
+ process.exitCode = 0
1446
+ return cli
1447
+ }
1448
+ function emitBanner(name) {
1449
+ // Print a banner at the top of each command.
1450
+ // This helps with brand recognition and marketing.
1451
+ // It also helps with debugging since it contains version and command details.
1452
+ // Note: print over stderr to preserve stdout for flags like --json and
1453
+ // --markdown. If we don't do this, you can't use --json in particular
1454
+ // and pipe the result to other tools. By emitting the banner over stderr
1455
+ // you can do something like `socket scan view xyz | jq | process`.
1456
+ // The spinner also emits over stderr for example.
1457
+ logger.logger.error(getAsciiHeader(name))
1458
+ }
1459
+ function getAsciiHeader(command) {
1460
+ // Note: In tests we return <redacted> because otherwise snapshots will fail.
1461
+ const { REDACTED } = constants
1462
+ // Lazily access constants.ENV.VITEST.
1463
+ const redacting = constants.ENV.VITEST
1464
+ const cliVersion = redacting
1465
+ ? REDACTED
1466
+ : // Lazily access constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH.
1467
+ constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH
1468
+ const nodeVersion = redacting ? REDACTED : process.version
1469
+ const defaultOrg = getConfigValueOrUndef('defaultOrg')
1470
+ const readOnlyConfig = isReadOnlyConfig() ? '*' : '.'
1471
+ const v1test = isTestingV1() ? ' (is testing v1)' : ''
1472
+ const feedback = isTestingV1()
1473
+ ? vendor.yoctocolorsCjsExports.green(
1474
+ ' (Thank you for testing the v1 bump! Please send us any feedback you might have!)\n'
1475
+ )
1476
+ : ''
1477
+ const shownToken = redacting ? REDACTED : getVisibleTokenPrefix() || 'no'
1478
+ const relCwd = redacting
1479
+ ? REDACTED
1480
+ : path$1.normalizePath(
1481
+ process
1482
+ .cwd()
1483
+ .replace(
1484
+ new RegExp(
1485
+ `^${regexps.escapeRegExp(constants.homePath)}(?:${path.sep}|$)`,
1486
+ 'i'
1487
+ ),
1488
+ '~/'
1489
+ )
1490
+ )
1491
+ let nodeVerWarn = ''
1492
+ if ((vendor.semverExports.parse(constants.NODE_VERSION)?.major ?? 0) < 20) {
1493
+ nodeVerWarn += vendor.yoctocolorsCjsExports.bold(
1494
+ ` ${vendor.yoctocolorsCjsExports.red('Warning:')} NodeJS version 19 and lower will be ${vendor.yoctocolorsCjsExports.red('unsupported')} after April 30th, 2025.`
1495
+ )
1496
+ nodeVerWarn += '\n'
1497
+ nodeVerWarn +=
1498
+ ' Soon after the Socket CLI will require NodeJS version 20 or higher.'
1499
+ nodeVerWarn += '\n'
1500
+ }
1501
+ const body = `
1502
+ _____ _ _ /---------------
1503
+ | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}${v1test}
1504
+ |__ | ${readOnlyConfig} | _| '_| -_| _| | Node: ${nodeVersion}, API token set: ${shownToken}${defaultOrg ? `, default org: ${redacting ? REDACTED : defaultOrg}` : ''}
1505
+ |_____|___|___|_,_|___|_|.dev | Command: \`${command}\`, cwd: ${relCwd}`.trimStart()
1506
+ return ` ${body}\n${nodeVerWarn}${feedback}`
1507
+ }
1508
+
1509
+ async function suggestOrgSlug() {
1510
+ const sockSdkResult = await setupSdk()
1511
+ if (!sockSdkResult.ok) {
1512
+ return
1513
+ }
1514
+ const sockSdk = sockSdkResult.data
1515
+ const result = await handleApiCall(
1516
+ sockSdk.getOrganizations(),
1517
+ 'list of organizations'
1518
+ )
1519
+
1520
+ // Ignore a failed request here. It was not the primary goal of
1521
+ // running this command and reporting it only leads to end-user confusion.
1522
+ if (result.ok) {
1523
+ const proceed = await prompts.select({
1524
+ message:
1525
+ 'Missing org name; do you want to use any of these orgs for this scan?',
1526
+ choices: [
1527
+ ...Object.values(result.data.organizations).map(org => {
1528
+ const name = org.name ?? org.slug
1529
+ return {
1530
+ name: `Yes [${name}]`,
1531
+ value: name,
1532
+ description: `Use "${name}" as the organization`
1533
+ }
1534
+ }),
1535
+ {
1536
+ name: 'No',
1537
+ value: '',
1538
+ description:
1539
+ 'Do not use any of these organizations (will end in a no-op)'
1540
+ }
1541
+ ]
1542
+ })
1543
+ if (proceed) {
1544
+ return proceed
1545
+ }
1546
+ } else {
1547
+ logger.logger.fail(
1548
+ 'Failed to lookup organization list from API, unable to suggest'
1549
+ )
1550
+ }
1551
+ }
1552
+
1553
+ async function determineOrgSlug(orgFlag, firstArg, interactive, dryRun) {
1554
+ const defaultOrgSlug = getConfigValueOrUndef('defaultOrg')
1555
+ let orgSlug = String(orgFlag || defaultOrgSlug || '')
1556
+ if (!orgSlug) {
1557
+ if (isTestingV1()) {
1558
+ // ask from server
1559
+ logger.logger.error(
1560
+ 'Missing the org slug and no --org flag set. Trying to auto-discover the org now...'
1561
+ )
1562
+ logger.logger.error(
1563
+ 'Note: you can set the default org slug to prevent this issue. You can also override all that with the --org flag.'
1564
+ )
1565
+ if (dryRun) {
1566
+ logger.logger.fail('Skipping auto-discovery of org in dry-run mode')
1567
+ } else if (!interactive) {
1568
+ logger.logger.fail(
1569
+ 'Skipping auto-discovery of org when interactive = false'
1570
+ )
1571
+ } else {
1572
+ orgSlug = (await suggestOrgSlug()) || ''
1573
+ }
1574
+ } else {
1575
+ orgSlug = firstArg || ''
1576
+ }
1577
+ }
1578
+ return [orgSlug, defaultOrgSlug]
1579
+ }
1580
+
1581
+ const { NODE_MODULES: NODE_MODULES$1, NPM: NPM$5, shadowBinPath } = constants
1582
+ function findBinPathDetailsSync(binName) {
1583
+ const binPaths =
1584
+ vendor.libExports$1.sync(binName, {
1585
+ all: true,
1586
+ nothrow: true
1587
+ }) ?? []
1588
+ let shadowIndex = -1
1589
+ let theBinPath
1590
+ for (let i = 0, { length } = binPaths; i < length; i += 1) {
1591
+ const binPath = binPaths[i]
1592
+ // Skip our bin directory if it's in the front.
1593
+ if (path.dirname(binPath) === shadowBinPath) {
1594
+ shadowIndex = i
1595
+ } else {
1596
+ theBinPath = npm.resolveBinPath(binPath)
1597
+ break
1598
+ }
1599
+ }
1600
+ return {
1601
+ name: binName,
1602
+ path: theBinPath,
1603
+ shadowed: shadowIndex !== -1
1604
+ }
1605
+ }
1606
+ function findNpmPathSync(npmBinPath) {
1607
+ // Lazily access constants.WIN32.
1608
+ const { WIN32 } = constants
1609
+ let thePath = npmBinPath
1610
+ while (true) {
1611
+ const libNmNpmPath = path.join(thePath, 'lib', NODE_MODULES$1, NPM$5)
1612
+ // mise puts its npm bin in a path like:
1613
+ // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/bin/npm.
1614
+ // HOWEVER, the location of the npm install is:
1615
+ // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/lib/node_modules/npm.
1616
+ if (
1617
+ // Use existsSync here because statsSync, even with { throwIfNoEntry: false },
1618
+ // will throw an ENOTDIR error for paths like ./a-file-that-exists/a-directory-that-does-not.
1619
+ // See https://github.com/nodejs/node/issues/56993.
1620
+ fs.existsSync(libNmNpmPath) &&
1621
+ fs
1622
+ .statSync(libNmNpmPath, {
1623
+ throwIfNoEntry: false
1624
+ })
1625
+ ?.isDirectory()
1626
+ ) {
1627
+ thePath = path.join(libNmNpmPath, NPM$5)
1628
+ }
1629
+ const nmPath = path.join(thePath, NODE_MODULES$1)
1630
+ if (
1631
+ // npm bin paths may look like:
1632
+ // /usr/local/share/npm/bin/npm
1633
+ // /Users/SomeUsername/.nvm/versions/node/vX.X.X/bin/npm
1634
+ // C:\Users\SomeUsername\AppData\Roaming\npm\bin\npm.cmd
1635
+ // OR
1636
+ // C:\Program Files\nodejs\npm.cmd
1637
+ //
1638
+ // In practically all cases the npm path contains a node_modules folder:
1639
+ // /usr/local/share/npm/bin/npm/node_modules
1640
+ // C:\Program Files\nodejs\node_modules
1641
+ fs.existsSync(nmPath) &&
1642
+ fs
1643
+ .statSync(nmPath, {
1644
+ throwIfNoEntry: false
1645
+ })
1646
+ ?.isDirectory() &&
1647
+ // Optimistically look for the default location.
1648
+ (path.basename(thePath) === NPM$5 ||
1649
+ // Chocolatey installs npm bins in the same directory as node bins.
1650
+ (WIN32 && fs.existsSync(path.join(thePath, `${NPM$5}.cmd`))))
1651
+ ) {
1652
+ return thePath
1653
+ }
1654
+ const parent = path.dirname(thePath)
1655
+ if (parent === thePath) {
1656
+ return undefined
1657
+ }
1658
+ thePath = parent
1659
+ }
1660
+ }
1661
+ async function getPackageFilesForScan(cwd, inputPaths, supportedFiles, config) {
1662
+ debug.debugLog(
1663
+ `getPackageFilesForScan: resolving ${inputPaths.length} paths:\n`,
1664
+ inputPaths
1665
+ )
1666
+
1667
+ // Lazily access constants.spinner.
1668
+ const { spinner } = constants
1669
+ const patterns = pathsToGlobPatterns(inputPaths)
1670
+ spinner.start('Searching for local files to include in scan...')
1671
+ const entries = await globWithGitIgnore(patterns, {
1672
+ cwd,
1673
+ socketConfig: config
1674
+ })
1675
+ if (debug.isDebug()) {
1676
+ spinner.stop()
1677
+ debug.debugLog(
1678
+ `Resolved ${inputPaths.length} paths to ${entries.length} local paths:\n`,
1679
+ entries
1680
+ )
1681
+ spinner.start('Searching for files now...')
1682
+ } else {
1683
+ spinner.start(
1684
+ `Resolved ${inputPaths.length} paths to ${entries.length} local paths, searching for files now...`
1685
+ )
1686
+ }
1687
+ const packageFiles = await filterGlobResultToSupportedFiles(
1688
+ entries,
1689
+ supportedFiles
1690
+ )
1691
+ spinner.successAndStop(
1692
+ `Found ${packageFiles.length} local ${words.pluralize('file', packageFiles.length)}`
1693
+ )
1694
+ debug.debugLog('Absolute paths:\n', packageFiles)
1695
+ return packageFiles
1696
+ }
1697
+
1698
+ const { NODE_MODULES, NPM: NPM$4, NPX, SOCKET_CLI_ISSUES_URL } = constants
1699
+ function exitWithBinPathError(binName) {
1700
+ logger.logger.fail(
1701
+ `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`
1702
+ )
1703
+ // The exit code 127 indicates that the command or binary being executed
1704
+ // could not be found.
1705
+ // eslint-disable-next-line n/no-process-exit
1706
+ process.exit(127)
1707
+ }
1708
+ let _npmBinPathDetails
1709
+ function getNpmBinPathDetails() {
1710
+ if (_npmBinPathDetails === undefined) {
1711
+ _npmBinPathDetails = findBinPathDetailsSync(NPM$4)
1712
+ }
1713
+ return _npmBinPathDetails
1714
+ }
1715
+ let _npxBinPathDetails
1716
+ function getNpxBinPathDetails() {
1717
+ if (_npxBinPathDetails === undefined) {
1718
+ _npxBinPathDetails = findBinPathDetailsSync(NPX)
1719
+ }
1720
+ return _npxBinPathDetails
1721
+ }
1722
+ function isNpmBinPathShadowed() {
1723
+ return getNpmBinPathDetails().shadowed
1724
+ }
1725
+ function isNpxBinPathShadowed() {
1726
+ return getNpxBinPathDetails().shadowed
1727
+ }
1728
+ let _npmBinPath
1729
+ function getNpmBinPath() {
1730
+ if (_npmBinPath === undefined) {
1731
+ _npmBinPath = getNpmBinPathDetails().path
1732
+ if (!_npmBinPath) {
1733
+ exitWithBinPathError(NPM$4)
1734
+ }
1735
+ }
1736
+ return _npmBinPath
1737
+ }
1738
+ let _npmPath
1739
+ function getNpmPath() {
1740
+ if (_npmPath === undefined) {
1741
+ const npmBinPath = getNpmBinPath()
1742
+ _npmPath = npmBinPath ? findNpmPathSync(npmBinPath) : undefined
1743
+ if (!_npmPath) {
1744
+ let message = 'Unable to find npm CLI install directory.'
1745
+ if (npmBinPath) {
1746
+ message += `\nSearched parent directories of ${path.dirname(npmBinPath)}.`
1747
+ }
1748
+ message += `\n\nThis is may be a bug with socket-npm related to changes to the npm CLI.\nPlease report to ${SOCKET_CLI_ISSUES_URL}.`
1749
+ logger.logger.fail(message)
1750
+ // The exit code 127 indicates that the command or binary being executed
1751
+ // could not be found.
1752
+ // eslint-disable-next-line n/no-process-exit
1753
+ process.exit(127)
1754
+ }
1755
+ }
1756
+ return _npmPath
1757
+ }
1758
+ let _npmRequire
1759
+ function getNpmRequire() {
1760
+ if (_npmRequire === undefined) {
1761
+ const npmPath = getNpmPath()
1762
+ const npmNmPath = path.join(npmPath, NODE_MODULES, NPM$4)
1763
+ _npmRequire = Module.createRequire(
1764
+ path.join(
1765
+ fs.existsSync(npmNmPath) ? npmNmPath : npmPath,
1766
+ '<dummy-basename>'
1767
+ )
1768
+ )
1769
+ }
1770
+ return _npmRequire
1771
+ }
1772
+ let _npxBinPath
1773
+ function getNpxBinPath() {
1774
+ if (_npxBinPath === undefined) {
1775
+ _npxBinPath = getNpxBinPathDetails().path
1776
+ if (!_npxBinPath) {
1777
+ exitWithBinPathError(NPX)
1778
+ }
1779
+ }
1780
+ return _npxBinPath
1781
+ }
1782
+
1783
+ const helpFlags = new Set(['--help', '-h'])
1784
+ function cmdFlagsToString(args) {
1785
+ const result = []
1786
+ for (let i = 0, { length } = args; i < length; i += 1) {
1787
+ if (args[i].startsWith('--')) {
1788
+ // Check if the next item exists and is NOT another flag.
1789
+ if (i + 1 < length && !args[i + 1].startsWith('--')) {
1790
+ result.push(`${args[i]}=${args[i + 1]}`)
1791
+ i += 1
1792
+ } else {
1793
+ result.push(args[i])
1794
+ }
1795
+ }
1796
+ }
1797
+ return result.join(' ')
1798
+ }
1799
+ function cmdPrefixMessage(cmdName, text) {
1800
+ const cmdPrefix = cmdName ? `${cmdName}: ` : ''
1801
+ return `${cmdPrefix}${text}`
1802
+ }
1803
+ function isHelpFlag(cmdArg) {
1804
+ return helpFlags.has(cmdArg)
1805
+ }
1806
+
1807
+ function getPkgFullNameFromPurlObj(purlObj) {
1808
+ const { name, namespace } = purlObj
1809
+ return namespace
1810
+ ? `${namespace}${purlObj.type === 'maven' ? ':' : '/'}${name}`
1811
+ : name
1812
+ }
1813
+ function getSocketDevAlertUrl(alertType) {
1814
+ return `https://socket.dev/alerts/${alertType}`
1815
+ }
1816
+ function getSocketDevPackageOverviewUrlFromPurl(purlObj) {
1817
+ const fullName = getPkgFullNameFromPurlObj(purlObj)
1818
+ return getSocketDevPackageOverviewUrl(purlObj.type, fullName, purlObj.version)
1819
+ }
1820
+ function getSocketDevPackageOverviewUrl(ecosystem, fullName, version) {
1821
+ if (ecosystem === 'go') {
1822
+ return `https://socket.dev/go/package/${fullName}${version ? `?section=overview&version=${version}` : ''}`
1823
+ } else {
1824
+ return `https://socket.dev/${ecosystem}/package/${fullName}${version ? `/overview/${version}` : ''}`
1825
+ }
1826
+ }
1827
+
1828
+ /**
1829
+ * Convert a Map<string, Map|string> to a nested object of similar shape.
1830
+ * The goal is to serialize it with JSON.stringify, which Map can't do.
1831
+ */
1832
+ function mapToObject(map) {
1833
+ return Object.fromEntries(
1834
+ Array.from(map.entries()).map(([k, v]) => [
1835
+ k,
1836
+ v instanceof Map ? mapToObject(v) : v
1837
+ ])
1838
+ )
1839
+ }
1840
+
1841
+ function* walkNestedMap(map, keys = []) {
1842
+ for (const [key, value] of map.entries()) {
1843
+ if (value instanceof Map) {
1844
+ yield* walkNestedMap(value, keys.concat(key))
1845
+ } else {
1846
+ yield {
1847
+ keys: keys.concat(key),
1848
+ value: value
1849
+ }
1850
+ }
1851
+ }
1852
+ }
1853
+
1854
+ const {
1855
+ ALERT_TYPE_CRITICAL_CVE,
1856
+ ALERT_TYPE_CVE,
1857
+ ALERT_TYPE_MEDIUM_CVE,
1858
+ ALERT_TYPE_MILD_CVE
1859
+ } = constants
1860
+ function isArtifactAlertCve(alert) {
1861
+ const { type } = alert
1862
+ return (
1863
+ type === ALERT_TYPE_CVE ||
1864
+ type === ALERT_TYPE_MEDIUM_CVE ||
1865
+ type === ALERT_TYPE_MILD_CVE ||
1866
+ type === ALERT_TYPE_CRITICAL_CVE
1867
+ )
1868
+ }
1869
+
1870
+ function createEnum(obj) {
1871
+ return Object.freeze({
1872
+ __proto__: null,
1873
+ ...obj
1874
+ })
1875
+ }
1876
+ function pick(input, keys) {
1877
+ const result = {}
1878
+ for (const key of keys) {
1879
+ result[key] = input[key]
1880
+ }
1881
+ return result
1882
+ }
1883
+
1884
+ const ALERT_FIX_TYPE = createEnum({
1885
+ cve: 'cve',
1886
+ remove: 'remove',
1887
+ upgrade: 'upgrade'
1888
+ })
1889
+
1890
+ function stringJoinWithSeparateFinalSeparator(list, separator = ' and ') {
1891
+ const values = list.filter(Boolean)
1892
+ const { length } = values
1893
+ if (!length) {
1894
+ return ''
1895
+ }
1896
+ if (length === 1) {
1897
+ return values[0]
1898
+ }
1899
+ const finalValue = values.pop()
1900
+ return `${values.join(', ')}${separator}${finalValue}`
1901
+ }
1902
+
1903
+ const ALERT_SEVERITY = createEnum({
1904
+ critical: 'critical',
1905
+ high: 'high',
1906
+ middle: 'middle',
1907
+ low: 'low'
1908
+ })
1909
+ // Ordered from most severe to least.
1910
+ const ALERT_SEVERITIES_SORTED = Object.freeze([
1911
+ 'critical',
1912
+ 'high',
1913
+ 'middle',
1914
+ 'low'
1915
+ ])
1916
+ function getDesiredSeverities(lowestToInclude) {
1917
+ const result = []
1918
+ for (const severity of ALERT_SEVERITIES_SORTED) {
1919
+ result.push(severity)
1920
+ if (severity === lowestToInclude) {
1921
+ break
1922
+ }
1923
+ }
1924
+ return result
1925
+ }
1926
+ function formatSeverityCount(severityCount) {
1927
+ const summary = []
1928
+ for (const severity of ALERT_SEVERITIES_SORTED) {
1929
+ if (severityCount[severity]) {
1930
+ summary.push(`${severityCount[severity]} ${severity}`)
1931
+ }
1932
+ }
1933
+ return stringJoinWithSeparateFinalSeparator(summary)
1934
+ }
1935
+ function getSeverityCount(issues, lowestToInclude) {
1936
+ const severityCount = pick(
1937
+ {
1938
+ low: 0,
1939
+ middle: 0,
1940
+ high: 0,
1941
+ critical: 0
1942
+ },
1943
+ getDesiredSeverities(lowestToInclude)
1944
+ )
1945
+ for (const issue of issues) {
1946
+ const { value } = issue
1947
+ if (!value) {
1948
+ continue
1949
+ }
1950
+ const { severity } = value
1951
+ if (severityCount[severity] !== undefined) {
1952
+ severityCount[severity] += 1
1953
+ }
1954
+ }
1955
+ return severityCount
1956
+ }
1957
+
1958
+ class ColorOrMarkdown {
1959
+ constructor(useMarkdown) {
1960
+ this.useMarkdown = !!useMarkdown
1961
+ }
1962
+ bold(text) {
1963
+ return this.useMarkdown
1964
+ ? `**${text}**`
1965
+ : vendor.yoctocolorsCjsExports.bold(`${text}`)
1966
+ }
1967
+ header(text, level = 1) {
1968
+ return this.useMarkdown
1969
+ ? `\n${''.padStart(level, '#')} ${text}\n`
1970
+ : vendor.yoctocolorsCjsExports.underline(
1971
+ `\n${level === 1 ? vendor.yoctocolorsCjsExports.bold(text) : text}\n`
1972
+ )
1973
+ }
1974
+ hyperlink(text, url, { fallback = true, fallbackToUrl } = {}) {
1975
+ if (url) {
1976
+ return this.useMarkdown
1977
+ ? `[${text}](${url})`
1978
+ : vendor.terminalLinkExports(text, url, {
1979
+ fallback: fallbackToUrl ? (_text, url) => url : fallback
1980
+ })
1981
+ }
1982
+ return text
1983
+ }
1984
+ indent(...args) {
1985
+ return vendor.indentStringExports(...args)
1986
+ }
1987
+ italic(text) {
1988
+ return this.useMarkdown
1989
+ ? `_${text}_`
1990
+ : vendor.yoctocolorsCjsExports.italic(`${text}`)
1991
+ }
1992
+ json(value) {
1993
+ return this.useMarkdown
1994
+ ? '```json\n' + JSON.stringify(value) + '\n```'
1995
+ : JSON.stringify(value)
1996
+ }
1997
+ list(items) {
1998
+ const indentedContent = items.map(item => this.indent(item).trimStart())
1999
+ return this.useMarkdown
2000
+ ? `* ${indentedContent.join('\n* ')}\n`
2001
+ : `${indentedContent.join('\n')}\n`
2002
+ }
2003
+ }
2004
+
2005
+ const require$1 = Module.createRequire(
2006
+ require('u' + 'rl').pathToFileURL(__filename).href
2007
+ )
2008
+ let _translations
2009
+ function getTranslations() {
2010
+ if (_translations === undefined) {
2011
+ _translations = require$1(
2012
+ // Lazily access constants.rootPath.
2013
+ path.join(constants.rootPath, 'translations.json')
2014
+ )
2015
+ }
2016
+ return _translations
2017
+ }
2018
+
2019
+ function idToPurl(id) {
2020
+ return `pkg:npm/${id}`
2021
+ }
2022
+ function resolvePackageVersion(purlObj) {
2023
+ const { version } = purlObj
2024
+ return version
2025
+ ? (vendor.semverExports.coerce(stripPeerSuffix(version))?.version ?? '')
2026
+ : ''
2027
+ }
2028
+ function stripLeadingSlash(path) {
2029
+ return path.startsWith('/') ? path.slice(1) : path
2030
+ }
2031
+ function stripPeerSuffix(depPath) {
2032
+ const idx = depPath.indexOf('(')
2033
+ return idx === -1 ? depPath : depPath.slice(0, idx)
2034
+ }
2035
+
2036
+ const ALERT_SEVERITY_COLOR = createEnum({
2037
+ critical: 'magenta',
2038
+ high: 'red',
2039
+ middle: 'yellow',
2040
+ low: 'white'
2041
+ })
2042
+ const ALERT_SEVERITY_ORDER = createEnum({
2043
+ critical: 0,
2044
+ high: 1,
2045
+ middle: 2,
2046
+ low: 3,
2047
+ none: 4
2048
+ })
2049
+ const { CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER, NPM: NPM$3 } =
2050
+ constants
2051
+ const MIN_ABOVE_THE_FOLD_COUNT = 3
2052
+ const MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1
2053
+ const format = new ColorOrMarkdown(false)
2054
+ function alertsHaveBlocked(alerts) {
2055
+ return alerts.find(a => a.blocked) !== undefined
2056
+ }
2057
+ function alertsHaveSeverity(alerts, severity) {
2058
+ return alerts.find(a => a.raw.severity === severity) !== undefined
2059
+ }
2060
+ function alertSeverityComparator(a, b) {
2061
+ return getAlertSeverityOrder(a) - getAlertSeverityOrder(b)
2062
+ }
2063
+ function getAlertSeverityOrder(alert) {
2064
+ const { severity } = alert.raw
2065
+ return severity === ALERT_SEVERITY.critical
2066
+ ? 0
2067
+ : severity === ALERT_SEVERITY.high
2068
+ ? 1
2069
+ : severity === ALERT_SEVERITY.middle
2070
+ ? 2
2071
+ : severity === ALERT_SEVERITY.low
2072
+ ? 3
2073
+ : 4
2074
+ }
2075
+ function getAlertsSeverityOrder(alerts) {
2076
+ return alertsHaveBlocked(alerts) ||
2077
+ alertsHaveSeverity(alerts, ALERT_SEVERITY.critical)
2078
+ ? 0
2079
+ : alertsHaveSeverity(alerts, ALERT_SEVERITY.high)
2080
+ ? 1
2081
+ : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle)
2082
+ ? 2
2083
+ : alertsHaveSeverity(alerts, ALERT_SEVERITY.low)
2084
+ ? 3
2085
+ : 4
2086
+ }
2087
+ function getHiddenRiskCounts(hiddenAlerts) {
2088
+ const riskCounts = {
2089
+ critical: 0,
2090
+ high: 0,
2091
+ middle: 0,
2092
+ low: 0
2093
+ }
2094
+ for (const alert of hiddenAlerts) {
2095
+ switch (getAlertSeverityOrder(alert)) {
2096
+ case ALERT_SEVERITY_ORDER.critical:
2097
+ riskCounts.critical += 1
2098
+ break
2099
+ case ALERT_SEVERITY_ORDER.high:
2100
+ riskCounts.high += 1
2101
+ break
2102
+ case ALERT_SEVERITY_ORDER.middle:
2103
+ riskCounts.middle += 1
2104
+ break
2105
+ case ALERT_SEVERITY_ORDER.low:
2106
+ riskCounts.low += 1
2107
+ break
2108
+ }
2109
+ }
2110
+ return riskCounts
2111
+ }
2112
+ function getHiddenRisksDescription(riskCounts) {
2113
+ const descriptions = []
2114
+ if (riskCounts.critical) {
2115
+ descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`)
2116
+ }
2117
+ if (riskCounts.high) {
2118
+ descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`)
2119
+ }
2120
+ if (riskCounts.middle) {
2121
+ descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`)
2122
+ }
2123
+ if (riskCounts.low) {
2124
+ descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`)
2125
+ }
2126
+ return `(${descriptions.join('; ')})`
2127
+ }
2128
+ function getSeverityLabel(severity) {
2129
+ return severity === 'middle' ? 'moderate' : severity
2130
+ }
2131
+ async function addArtifactToAlertsMap(artifact, alertsByPkgId, options) {
2132
+ // Make TypeScript happy.
2133
+ if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
2134
+ return alertsByPkgId
2135
+ }
2136
+ const {
2137
+ consolidate = false,
2138
+ include: _include,
2139
+ overrides
2140
+ } = {
2141
+ __proto__: null,
2142
+ ...options
2143
+ }
2144
+ const include = {
2145
+ __proto__: null,
2146
+ blocked: true,
2147
+ critical: true,
2148
+ cve: true,
2149
+ unfixable: true,
2150
+ upgradable: false,
2151
+ ..._include
2152
+ }
2153
+ const name = packages.resolvePackageName(artifact)
2154
+ const { version } = artifact
2155
+ const pkgId = `${name}@${version}`
2156
+ const major = vendor.semverExports.major(version)
2157
+ const socketYml = findSocketYmlSync()
2158
+ const enabledState = {
2159
+ __proto__: null,
2160
+ ...socketYml?.parsed.issueRules
2161
+ }
2162
+ let sockPkgAlerts = []
2163
+ for (const alert of artifact.alerts) {
2164
+ const action = alert.action ?? ''
2165
+ const enabledFlag = enabledState[alert.type]
2166
+ if (
2167
+ (action === 'ignore' && enabledFlag !== true) ||
2168
+ enabledFlag === false
2169
+ ) {
2170
+ continue
2171
+ }
2172
+ const blocked = action === 'error'
2173
+ const critical = alert.severity === ALERT_SEVERITY.critical
2174
+ const cve = isArtifactAlertCve(alert)
2175
+ const fixType = alert.fix?.type ?? ''
2176
+ const fixableCve = fixType === ALERT_FIX_TYPE.cve
2177
+ const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade
2178
+ const fixable = fixableCve || fixableUpgrade
2179
+ const upgradable = fixableUpgrade && !objects.hasOwn(overrides, name)
2180
+ if (
2181
+ (include.blocked && blocked) ||
2182
+ (include.critical && critical) ||
2183
+ (include.cve && cve) ||
2184
+ (include.unfixable && !fixable) ||
2185
+ (include.upgradable && upgradable)
2186
+ ) {
2187
+ sockPkgAlerts.push({
2188
+ name,
2189
+ version,
2190
+ key: alert.key,
2191
+ type: alert.type,
2192
+ blocked,
2193
+ critical,
2194
+ fixable,
2195
+ raw: alert,
2196
+ upgradable
2197
+ })
2198
+ }
2199
+ }
2200
+ if (!sockPkgAlerts.length) {
2201
+ return alertsByPkgId
2202
+ }
2203
+ if (consolidate) {
2204
+ const highestForCve = new Map()
2205
+ const highestForUpgrade = new Map()
2206
+ const unfixableAlerts = []
2207
+ for (const sockPkgAlert of sockPkgAlerts) {
2208
+ const alert = sockPkgAlert.raw
2209
+ const fixType = alert.fix?.type ?? ''
2210
+ if (fixType === ALERT_FIX_TYPE.cve) {
2211
+ const patchedVersion =
2212
+ alert.props[CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER]
2213
+ const patchedMajor = vendor.semverExports.major(patchedVersion)
2214
+ const oldHighest = highestForCve.get(patchedMajor)
2215
+ const highest = oldHighest?.version ?? '0.0.0'
2216
+ if (vendor.semverExports.gt(patchedVersion, highest)) {
2217
+ highestForCve.set(patchedMajor, {
2218
+ alert: sockPkgAlert,
2219
+ version: patchedVersion
2220
+ })
2221
+ }
2222
+ } else if (fixType === ALERT_FIX_TYPE.upgrade) {
2223
+ const oldHighest = highestForUpgrade.get(major)
2224
+ const highest = oldHighest?.version ?? '0.0.0'
2225
+ if (vendor.semverExports.gt(version, highest)) {
2226
+ highestForUpgrade.set(major, {
2227
+ alert: sockPkgAlert,
2228
+ version
2229
+ })
2230
+ }
2231
+ } else {
2232
+ unfixableAlerts.push(sockPkgAlert)
2233
+ }
2234
+ }
2235
+ sockPkgAlerts = [
2236
+ ...unfixableAlerts,
2237
+ ...[...highestForCve.values()].map(d => d.alert),
2238
+ ...[...highestForUpgrade.values()].map(d => d.alert)
2239
+ ]
2240
+ }
2241
+ if (sockPkgAlerts.length) {
2242
+ sockPkgAlerts.sort((a, b) => sorts.naturalCompare(a.type, b.type))
2243
+ alertsByPkgId.set(pkgId, sockPkgAlerts)
2244
+ }
2245
+ return alertsByPkgId
2246
+ }
2247
+ function getCveInfoByAlertsMap(alertsMap, options) {
2248
+ const { exclude: _exclude, limit = Infinity } = {
2249
+ __proto__: null,
2250
+ ...options
2251
+ }
2252
+ const exclude = {
2253
+ __proto__: null,
2254
+ upgradable: true,
2255
+ ..._exclude
2256
+ }
2257
+ let count = 0
2258
+ let infoByPkg = null
2259
+ alertsMapLoop: for (const [pkgId, sockPkgAlerts] of alertsMap) {
2260
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2261
+ idToPurl(pkgId)
2262
+ )
2263
+ const name = packages.resolvePackageName(purlObj)
2264
+ for (const sockPkgAlert of sockPkgAlerts) {
2265
+ const alert = sockPkgAlert.raw
2266
+ if (
2267
+ alert.fix?.type !== ALERT_FIX_TYPE.cve ||
2268
+ (exclude.upgradable && registry.getManifestData(NPM$3, name))
2269
+ ) {
2270
+ continue
2271
+ }
2272
+ if (!infoByPkg) {
2273
+ infoByPkg = new Map()
2274
+ }
2275
+ let infos = infoByPkg.get(name)
2276
+ if (!infos) {
2277
+ infos = []
2278
+ infoByPkg.set(name, infos)
2279
+ }
2280
+ const { firstPatchedVersionIdentifier, vulnerableVersionRange } =
2281
+ alert.props
2282
+ try {
2283
+ infos.push({
2284
+ firstPatchedVersionIdentifier,
2285
+ vulnerableVersionRange: new vendor.semverExports.Range(
2286
+ // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that
2287
+ // semver.Range will parse it without erroring.
2288
+ vulnerableVersionRange.replace(/, +/g, ' ')
2289
+ ).format()
2290
+ })
2291
+ if (++count >= limit) {
2292
+ break alertsMapLoop
2293
+ }
2294
+ } catch (e) {
2295
+ debug.debugLog('getCveInfoByAlertsMap', {
2296
+ firstPatchedVersionIdentifier,
2297
+ vulnerableVersionRange
2298
+ })
2299
+ debug.debugLog(e)
2300
+ }
2301
+ }
2302
+ }
2303
+ return infoByPkg
2304
+ }
2305
+ function logAlertsMap(alertsMap, options) {
2306
+ const { hideAt = 'middle', output = process.stderr } = {
2307
+ __proto__: null,
2308
+ ...options
2309
+ }
2310
+ const translations = getTranslations()
2311
+ const sortedEntries = [...alertsMap.entries()].sort(
2312
+ (a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1])
2313
+ )
2314
+ const aboveTheFoldPkgIds = new Set()
2315
+ const viewableAlertsByPkgId = new Map()
2316
+ const hiddenAlertsByPkgId = new Map()
2317
+ for (let i = 0, { length } = sortedEntries; i < length; i += 1) {
2318
+ const { 0: pkgId, 1: alerts } = sortedEntries[i]
2319
+ const hiddenAlerts = []
2320
+ const viewableAlerts = alerts.filter(a => {
2321
+ const keep =
2322
+ a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt]
2323
+ if (!keep) {
2324
+ hiddenAlerts.push(a)
2325
+ }
2326
+ return keep
2327
+ })
2328
+ if (hiddenAlerts.length) {
2329
+ hiddenAlertsByPkgId.set(pkgId, hiddenAlerts.sort(alertSeverityComparator))
2330
+ }
2331
+ if (!viewableAlerts.length) {
2332
+ continue
2333
+ }
2334
+ viewableAlerts.sort(alertSeverityComparator)
2335
+ viewableAlertsByPkgId.set(pkgId, viewableAlerts)
2336
+ if (
2337
+ viewableAlerts.find(
2338
+ a => a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle
2339
+ )
2340
+ ) {
2341
+ aboveTheFoldPkgIds.add(pkgId)
2342
+ }
2343
+ }
2344
+
2345
+ // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.
2346
+ for (const { 0: pkgId } of viewableAlertsByPkgId.entries()) {
2347
+ if (aboveTheFoldPkgIds.size >= MIN_ABOVE_THE_FOLD_COUNT) {
2348
+ break
2349
+ }
2350
+ aboveTheFoldPkgIds.add(pkgId)
2351
+ }
2352
+ // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.
2353
+ for (const { 0: pkgId, 1: hiddenAlerts } of hiddenAlertsByPkgId.entries()) {
2354
+ if (aboveTheFoldPkgIds.size >= MIN_ABOVE_THE_FOLD_COUNT) {
2355
+ break
2356
+ }
2357
+ aboveTheFoldPkgIds.add(pkgId)
2358
+ const viewableAlerts = viewableAlertsByPkgId.get(pkgId) ?? []
2359
+ if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {
2360
+ const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length
2361
+ let removedHiddenAlerts
2362
+ if (hiddenAlerts.length - neededCount > 0) {
2363
+ removedHiddenAlerts = hiddenAlerts.splice(
2364
+ 0,
2365
+ MIN_ABOVE_THE_FOLD_ALERT_COUNT
2366
+ )
2367
+ } else {
2368
+ removedHiddenAlerts = hiddenAlerts
2369
+ hiddenAlertsByPkgId.delete(pkgId)
2370
+ }
2371
+ viewableAlertsByPkgId.set(pkgId, [
2372
+ ...viewableAlerts,
2373
+ ...removedHiddenAlerts
2374
+ ])
2375
+ }
2376
+ }
2377
+ const mentionedPkgIdsWithHiddenAlerts = new Set()
2378
+ for (
2379
+ let i = 0,
2380
+ prevAboveTheFold = true,
2381
+ entries = [...viewableAlertsByPkgId.entries()],
2382
+ { length } = entries;
2383
+ i < length;
2384
+ i += 1
2385
+ ) {
2386
+ const { 0: pkgId, 1: alerts } = entries[i]
2387
+ const lines = new Set()
2388
+ for (const alert of alerts) {
2389
+ const { type } = alert
2390
+ const severity = alert.raw.severity ?? ''
2391
+ const attributes = [
2392
+ ...(severity
2393
+ ? [
2394
+ vendor.yoctocolorsCjsExports[ALERT_SEVERITY_COLOR[severity]](
2395
+ getSeverityLabel(severity)
2396
+ )
2397
+ ]
2398
+ : []),
2399
+ ...(alert.blocked
2400
+ ? [
2401
+ vendor.yoctocolorsCjsExports.bold(
2402
+ vendor.yoctocolorsCjsExports.red('blocked')
2403
+ )
2404
+ ]
2405
+ : []),
2406
+ ...(alert.fixable ? ['fixable'] : [])
2407
+ ]
2408
+ const maybeAttributes = attributes.length
2409
+ ? ` ${vendor.yoctocolorsCjsExports.italic(`(${attributes.join('; ')})`)}`
2410
+ : ''
2411
+ // Based data from { pageProps: { alertTypes } } of:
2412
+ // https://socket.dev/_next/data/94666139314b6437ee4491a0864e72b264547585/en-US.json
2413
+ const info = translations.alerts[type]
2414
+ const title = info?.title ?? type
2415
+ const maybeDesc = info?.description ? ` - ${info.description}` : ''
2416
+ const content = `${title}${maybeAttributes}${maybeDesc}`
2417
+ // TODO: emoji seems to mis-align terminals sometimes
2418
+ lines.add(` ${content}`)
2419
+ }
2420
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2421
+ idToPurl(pkgId)
2422
+ )
2423
+ const hyperlink = format.hyperlink(
2424
+ pkgId,
2425
+ getSocketDevPackageOverviewUrl(
2426
+ NPM$3,
2427
+ packages.resolvePackageName(purlObj),
2428
+ purlObj.version
2429
+ )
2430
+ )
2431
+ const isAboveTheFold = aboveTheFoldPkgIds.has(pkgId)
2432
+ if (isAboveTheFold) {
2433
+ aboveTheFoldPkgIds.add(pkgId)
2434
+ output.write(`${i ? '\n' : ''}${hyperlink}:\n`)
2435
+ } else {
2436
+ output.write(`${prevAboveTheFold ? '\n' : ''}${hyperlink}:\n`)
2437
+ }
2438
+ for (const line of lines) {
2439
+ output.write(`${line}\n`)
2440
+ }
2441
+ const hiddenAlerts = hiddenAlertsByPkgId.get(pkgId) ?? []
2442
+ const { length: hiddenAlertsCount } = hiddenAlerts
2443
+ if (hiddenAlertsCount) {
2444
+ mentionedPkgIdsWithHiddenAlerts.add(pkgId)
2445
+ if (hiddenAlertsCount === 1) {
2446
+ output.write(
2447
+ ` ${vendor.yoctocolorsCjsExports.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0].raw.severity ?? 'low')} risk alert`)}\n`
2448
+ )
2449
+ } else {
2450
+ output.write(
2451
+ ` ${vendor.yoctocolorsCjsExports.dim(`+${hiddenAlertsCount} Hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\n`
2452
+ )
2453
+ }
2454
+ }
2455
+ prevAboveTheFold = isAboveTheFold
2456
+ }
2457
+ const additionalHiddenCount =
2458
+ hiddenAlertsByPkgId.size - mentionedPkgIdsWithHiddenAlerts.size
2459
+ if (additionalHiddenCount) {
2460
+ const totalRiskCounts = {
2461
+ critical: 0,
2462
+ high: 0,
2463
+ middle: 0,
2464
+ low: 0
2465
+ }
2466
+ for (const { 0: pkgId, 1: alerts } of hiddenAlertsByPkgId.entries()) {
2467
+ if (mentionedPkgIdsWithHiddenAlerts.has(pkgId)) {
2468
+ continue
2469
+ }
2470
+ const riskCounts = getHiddenRiskCounts(alerts)
2471
+ totalRiskCounts.critical += riskCounts.critical
2472
+ totalRiskCounts.high += riskCounts.high
2473
+ totalRiskCounts.middle += riskCounts.middle
2474
+ totalRiskCounts.low += riskCounts.low
2475
+ }
2476
+ output.write(
2477
+ `${aboveTheFoldPkgIds.size ? '\n' : ''}${vendor.yoctocolorsCjsExports.dim(`${aboveTheFoldPkgIds.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\n`
2478
+ )
2479
+ }
2480
+ output.write('\n')
2481
+ }
2482
+
2483
+ const RangeStyles = ['caret', 'gt', 'lt', 'pin', 'preserve', 'tilde']
2484
+ function applyRange(refRange, version, style = 'preserve') {
2485
+ switch (style) {
2486
+ case 'caret':
2487
+ return `^${version}`
2488
+ case 'gt':
2489
+ return `>${version}`
2490
+ case 'gte':
2491
+ return `>=${version}`
2492
+ case 'lt':
2493
+ return `<${version}`
2494
+ case 'lte':
2495
+ return `<=${version}`
2496
+ case 'preserve': {
2497
+ const range = new vendor.semverExports.Range(refRange)
2498
+ const { raw } = range
2499
+ const comparators = [...range.set].flat()
2500
+ const { length } = comparators
2501
+ if (length === 1) {
2502
+ const char = /^[<>]=?/.exec(raw)?.[0]
2503
+ if (char) {
2504
+ return `${char}${version}`
2505
+ }
2506
+ } else if (length === 2) {
2507
+ const char = /^[~^]/.exec(raw)?.[0]
2508
+ if (char) {
2509
+ return `${char}${version}`
2510
+ }
2511
+ }
2512
+ return version
2513
+ }
2514
+ case 'tilde':
2515
+ return `~${version}`
2516
+ case 'pin':
2517
+ default:
2518
+ return version
2519
+ }
2520
+ }
2521
+ function getMajor(version) {
2522
+ const coerced = vendor.semverExports.coerce(version)
2523
+ if (coerced) {
2524
+ try {
2525
+ return vendor.semverExports.major(coerced)
2526
+ } catch (e) {
2527
+ debug.debugLog(`Error parsing '${version}':\n`, e)
2528
+ }
2529
+ }
2530
+ return null
2531
+ }
2532
+
2533
+ function extractPurlsFromPnpmLockfileV6(lockfile) {
2534
+ const deps = new Set()
2535
+ for (const importer of Object.values(lockfile.importers || {})) {
2536
+ if (importer.dependencies) {
2537
+ for (const { 0: alias, 1: ref } of Object.entries(
2538
+ importer.dependencies
2539
+ )) {
2540
+ const id = resolvePnpmPackageId(alias, ref)
2541
+ if (id) {
2542
+ deps.add(idToPurl(id))
2543
+ }
2544
+ }
2545
+ }
2546
+ if (importer.devDependencies) {
2547
+ for (const { 0: alias, 1: ref } of Object.entries(
2548
+ importer.devDependencies
2549
+ )) {
2550
+ const id = resolvePnpmPackageId(alias, ref)
2551
+ if (id) {
2552
+ deps.add(idToPurl(id))
2553
+ }
2554
+ }
2555
+ }
2556
+ if (importer.optionalDependencies) {
2557
+ for (const { 0: alias, 1: ref } of Object.entries(
2558
+ importer.optionalDependencies
2559
+ )) {
2560
+ const id = resolvePnpmPackageId(alias, ref)
2561
+ if (id) {
2562
+ deps.add(idToPurl(id))
2563
+ }
2564
+ }
2565
+ }
2566
+ }
2567
+ if (lockfile.packages) {
2568
+ for (const pkgPath of Object.keys(lockfile.packages)) {
2569
+ const id = resolvePnpmPackageIdFromPath(pkgPath, '')
2570
+ if (id) {
2571
+ deps.add(idToPurl(id))
2572
+ }
2573
+ }
2574
+ }
2575
+ return Array.from(deps)
2576
+ }
2577
+ function extractPurlsFromPnpmLockfileV9(lockfile) {
2578
+ const depTypes = vendor.libExports$2.detectDepTypes(lockfile)
2579
+ return Object.keys(depTypes).map(refId => {
2580
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2581
+ idToPurl(refId)
2582
+ )
2583
+ const name = packages.resolvePackageName(purlObj)
2584
+ const version = resolvePackageVersion(purlObj)
2585
+ return idToPurl(`${name}@${version}`)
2586
+ })
2587
+ }
2588
+ function extractPurlsFromPnpmLockfile(lockfile) {
2589
+ return parsePnpmLockfileVersion(lockfile.lockfileVersion).major <= 6
2590
+ ? extractPurlsFromPnpmLockfileV6(lockfile)
2591
+ : extractPurlsFromPnpmLockfileV9(lockfile)
2592
+ }
2593
+ function parsePnpmLockfileVersion(version) {
2594
+ return vendor.semverExports.coerce(version)
2595
+ }
2596
+ function resolvePnpmPackageId(alias, ref) {
2597
+ return ref.startsWith('/')
2598
+ ? resolvePnpmPackageIdFromPath(ref, alias)
2599
+ : `${alias}@${stripPeerSuffix(ref)}`
2600
+ }
2601
+ function resolvePnpmPackageIdFromPath(ref, alias) {
2602
+ const relative = vendor.libExports$3.refToRelative(ref, alias)
2603
+ if (relative) {
2604
+ const id = stripLeadingSlash(relative)
2605
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2606
+ idToPurl(id)
2607
+ )
2608
+ const name = packages.resolvePackageName(purlObj)
2609
+ const version = resolvePackageVersion(purlObj)
2610
+ return `${name}@${version}`
2611
+ }
2612
+ return null
2613
+ }
2614
+
2615
+ async function getAlertsMapFromPnpmLockfile(lockfile, options_) {
2616
+ const options = {
2617
+ __proto__: null,
2618
+ consolidate: false,
2619
+ limit: Infinity,
2620
+ nothrow: false,
2621
+ ...options_
2622
+ }
2623
+ const purls = extractPurlsFromPnpmLockfile(lockfile)
2624
+ return await getAlertsMapFromPurls(purls, {
2625
+ overrides: lockfile.overrides,
2626
+ ...options
2627
+ })
2628
+ }
2629
+ async function getAlertsMapFromPurls(purls, options_) {
2630
+ const options = {
2631
+ __proto__: null,
2632
+ consolidate: false,
2633
+ nothrow: false,
2634
+ ...options_
2635
+ }
2636
+ const include = {
2637
+ __proto__: null,
2638
+ actions: undefined,
2639
+ blocked: true,
2640
+ critical: true,
2641
+ cve: true,
2642
+ existing: false,
2643
+ unfixable: true,
2644
+ upgradable: false,
2645
+ ...options.include
2646
+ }
2647
+ const { spinner } = options
2648
+ const uniqPurls = arrays.arrayUnique(purls)
2649
+ let { length: remaining } = uniqPurls
2650
+ const alertsByPkgId = new Map()
2651
+ if (!remaining) {
2652
+ return alertsByPkgId
2653
+ }
2654
+ const getText = () => `Looking up data for ${remaining} packages`
2655
+ spinner?.start(getText())
2656
+ const sockSdkResult = await setupSdk(getPublicToken())
2657
+ if (!sockSdkResult.ok) {
2658
+ throw new Error('Auth error: Try to run `socket login` first')
2659
+ }
2660
+ const sockSdk = sockSdkResult.data
2661
+ const toAlertsMapOptions = {
2662
+ overrides: options.overrides,
2663
+ consolidate: options.consolidate,
2664
+ include,
2665
+ spinner
2666
+ }
2667
+ for await (const batchResult of sockSdk.batchPackageStream(
2668
+ {
2669
+ alerts: 'true',
2670
+ compact: 'true',
2671
+ ...(include.actions
2672
+ ? {
2673
+ actions: include.actions.join(',')
2674
+ }
2675
+ : {}),
2676
+ ...(include.unfixable
2677
+ ? {}
2678
+ : {
2679
+ fixable: 'true'
2680
+ })
2681
+ },
2682
+ {
2683
+ components: uniqPurls.map(purl => ({
2684
+ purl
2685
+ }))
2686
+ }
2687
+ )) {
2688
+ if (batchResult.success) {
2689
+ await addArtifactToAlertsMap(
2690
+ batchResult.data,
2691
+ alertsByPkgId,
2692
+ toAlertsMapOptions
2693
+ )
2694
+ } else if (!options.nothrow) {
2695
+ const statusCode = batchResult.status ?? 'unknown'
2696
+ const statusMessage = batchResult.error ?? 'No status message'
2697
+ throw new Error(
2698
+ `Socket API server error (${statusCode}): ${statusMessage}`
2699
+ )
2700
+ }
2701
+ remaining -= 1
2702
+ if (spinner && remaining > 0) {
2703
+ spinner.start()
2704
+ spinner.setText(getText())
2705
+ }
2706
+ }
2707
+ spinner?.stop()
2708
+ return alertsByPkgId
2709
+ }
2710
+
2711
+ const {
2712
+ NPM: NPM$2,
2713
+ SOCKET_CLI_SAFE_BIN,
2714
+ SOCKET_CLI_SAFE_PROGRESS,
2715
+ SOCKET_IPC_HANDSHAKE
2716
+ } = constants
2717
+ function safeNpmInstall(options) {
2718
+ const {
2719
+ agentExecPath = getNpmBinPath(),
2720
+ args = [],
2721
+ ipc,
2722
+ spinner,
2723
+ ...spawnOptions
2724
+ } = {
2725
+ __proto__: null,
2726
+ ...options
2727
+ }
2728
+ let stdio = spawnOptions.stdio
2729
+ const useIpc = objects.isObject(ipc)
2730
+ // Include 'ipc' in the spawnOptions.stdio when an options.ipc object is provided.
2731
+ // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166
2732
+ // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.
2733
+ if (typeof stdio === 'string') {
2734
+ stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio]
2735
+ } else if (useIpc && Array.isArray(stdio) && !stdio.includes('ipc')) {
2736
+ stdio = stdio.concat('ipc')
2737
+ }
2738
+ const useDebug = debug.isDebug()
2739
+ const terminatorPos = args.indexOf('--')
2740
+ const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)
2741
+ const progressArg =
2742
+ rawBinArgs.findLast(npm.isProgressFlag) !== '--no-progress'
2743
+ const binArgs = rawBinArgs.filter(
2744
+ a => !npm.isAuditFlag(a) && !npm.isFundFlag(a) && !npm.isProgressFlag(a)
2745
+ )
2746
+ const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)
2747
+ const isSilent = !useDebug && !binArgs.some(npm.isLoglevelFlag)
2748
+ const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : []
2749
+ const spawnPromise = spawn.spawn(
2750
+ // Lazily access constants.execPath.
2751
+ constants.execPath,
2752
+ [
2753
+ // Lazily access constants.nodeHardenFlags.
2754
+ ...constants.nodeHardenFlags,
2755
+ // Lazily access constants.nodeNoWarningsFlags.
2756
+ ...constants.nodeNoWarningsFlags,
2757
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD.
2758
+ ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD
2759
+ ? [
2760
+ '--require',
2761
+ // Lazily access constants.distInstrumentWithSentryPath.
2762
+ constants.distInstrumentWithSentryPath
2763
+ ]
2764
+ : []),
2765
+ '--require',
2766
+ // Lazily access constants.distShadowNpmInjectPath.
2767
+ constants.distShadowNpmInjectPath,
2768
+ npm.realExecPathSync(agentExecPath),
2769
+ 'install',
2770
+ // Avoid code paths for 'audit' and 'fund'.
2771
+ '--no-audit',
2772
+ '--no-fund',
2773
+ // Add '--no-progress' to fix input being swallowed by the npm spinner.
2774
+ '--no-progress',
2775
+ // Add '--loglevel=silent' if a loglevel flag is not provided and the
2776
+ // SOCKET_CLI_DEBUG environment variable is not truthy.
2777
+ ...logLevelArgs,
2778
+ ...binArgs,
2779
+ ...otherArgs
2780
+ ],
2781
+ {
2782
+ spinner,
2783
+ ...spawnOptions,
2784
+ stdio,
2785
+ env: {
2786
+ ...process.env,
2787
+ ...spawnOptions.env
2788
+ }
2789
+ }
2790
+ )
2791
+ if (useIpc) {
2792
+ spawnPromise.process.send({
2793
+ [SOCKET_IPC_HANDSHAKE]: {
2794
+ [SOCKET_CLI_SAFE_BIN]: NPM$2,
2795
+ [SOCKET_CLI_SAFE_PROGRESS]: progressArg,
2796
+ ...ipc
2797
+ }
2798
+ })
2799
+ }
2800
+ return spawnPromise
2801
+ }
2802
+
2803
+ const { NPM: NPM$1, PNPM: PNPM$1 } = constants
2804
+ function runAgentInstall(pkgEnvDetails, options) {
2805
+ const { agent, agentExecPath } = pkgEnvDetails
2806
+ // All package managers support the "install" command.
2807
+ if (agent === NPM$1) {
2808
+ return safeNpmInstall({
2809
+ agentExecPath,
2810
+ ...options
2811
+ })
2812
+ }
2813
+ const {
2814
+ args = [],
2815
+ spinner,
2816
+ ...spawnOptions
2817
+ } = {
2818
+ __proto__: null,
2819
+ ...options
2820
+ }
2821
+ const skipNodeHardenFlags =
2822
+ agent === PNPM$1 && pkgEnvDetails.agentVersion.major < 11
2823
+ return spawn.spawn(agentExecPath, ['install', ...args], {
2824
+ // Lazily access constants.WIN32.
2825
+ shell: constants.WIN32,
2826
+ spinner,
2827
+ stdio: 'inherit',
2828
+ ...spawnOptions,
2829
+ env: {
2830
+ ...process.env,
2831
+ NODE_OPTIONS: cmdFlagsToString([
2832
+ ...(skipNodeHardenFlags
2833
+ ? []
2834
+ : // Lazily access constants.nodeHardenFlags.
2835
+ constants.nodeHardenFlags),
2836
+ // Lazily access constants.nodeNoWarningsFlags.
2837
+ ...constants.nodeNoWarningsFlags
2838
+ ]),
2839
+ ...spawnOptions.env
2840
+ }
2841
+ })
2842
+ }
2843
+
2844
+ const {
2845
+ BINARY_LOCK_EXT,
2846
+ BUN,
2847
+ HIDDEN_PACKAGE_LOCK_JSON,
2848
+ LOCK_EXT,
2849
+ NPM,
2850
+ NPM_BUGGY_OVERRIDES_PATCHED_VERSION,
2851
+ PACKAGE_JSON,
2852
+ PNPM,
2853
+ VLT,
2854
+ YARN,
2855
+ YARN_BERRY,
2856
+ YARN_CLASSIC
2857
+ } = constants
2858
+ const AGENTS = new Set([BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT])
2859
+ const binByAgent = new Map([
2860
+ [BUN, BUN],
2861
+ [NPM, NPM],
2862
+ [PNPM, PNPM],
2863
+ [YARN_BERRY, YARN],
2864
+ [YARN_CLASSIC, YARN],
2865
+ [VLT, VLT]
2866
+ ])
2867
+ async function getAgentExecPath(agent) {
2868
+ const binName = binByAgent.get(agent)
2869
+ if (binName === NPM) {
2870
+ // Lazily access constants.npmExecPath.
2871
+ return constants.npmExecPath
2872
+ }
2873
+ return (
2874
+ (await vendor.libExports$1(binName, {
2875
+ nothrow: true
2876
+ })) ?? binName
2877
+ )
2878
+ }
2879
+ async function getAgentVersion(agentExecPath, cwd) {
2880
+ let result
2881
+ try {
2882
+ result =
2883
+ // Coerce version output into a valid semver version by passing it through
2884
+ // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
2885
+ // and tildes (~).
2886
+ vendor.semverExports.coerce(
2887
+ // All package managers support the "--version" flag.
2888
+ (
2889
+ await spawn.spawn(agentExecPath, ['--version'], {
2890
+ cwd,
2891
+ // Lazily access constants.WIN32.
2892
+ shell: constants.WIN32
2893
+ })
2894
+ ).stdout
2895
+ ) ?? undefined
2896
+ } catch (e) {
2897
+ debug.debugLog('getAgentVersion error:\n', e)
2898
+ }
2899
+ return result
2900
+ }
2901
+
2902
+ // The order of LOCKS properties IS significant as it affects iteration order.
2903
+ const LOCKS = {
2904
+ [`bun${LOCK_EXT}`]: BUN,
2905
+ [`bun${BINARY_LOCK_EXT}`]: BUN,
2906
+ // If both package-lock.json and npm-shrinkwrap.json are present in the root
2907
+ // of a project, npm-shrinkwrap.json will take precedence and package-lock.json
2908
+ // will be ignored.
2909
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson
2910
+ 'npm-shrinkwrap.json': NPM,
2911
+ 'package-lock.json': NPM,
2912
+ 'pnpm-lock.yaml': PNPM,
2913
+ 'pnpm-lock.yml': PNPM,
2914
+ [`yarn${LOCK_EXT}`]: YARN_CLASSIC,
2915
+ 'vlt-lock.json': VLT,
2916
+ // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:
2917
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles
2918
+ //
2919
+ // Unlike the other LOCKS keys this key contains a directory AND filename so
2920
+ // it has to be handled differently.
2921
+ 'node_modules/.package-lock.json': NPM
2922
+ }
2923
+ const readLockFileByAgent = (() => {
2924
+ function wrapReader(reader) {
2925
+ return async (...args) => {
2926
+ try {
2927
+ return await reader(...args)
2928
+ } catch {}
2929
+ return undefined
2930
+ }
2931
+ }
2932
+ const binaryReader = wrapReader(readFileBinary)
2933
+ const defaultReader = wrapReader(
2934
+ async lockPath => await readFileUtf8(lockPath)
2935
+ )
2936
+ return new Map([
2937
+ [
2938
+ BUN,
2939
+ wrapReader(async (lockPath, agentExecPath, cwd = process.cwd()) => {
2940
+ const ext = path.extname(lockPath)
2941
+ if (ext === LOCK_EXT) {
2942
+ return await defaultReader(lockPath)
2943
+ }
2944
+ if (ext === BINARY_LOCK_EXT) {
2945
+ const lockBuffer = await binaryReader(lockPath)
2946
+ if (lockBuffer) {
2947
+ try {
2948
+ return vendor.hyrious__bun_lockbExports.parse(lockBuffer)
2949
+ } catch {}
2950
+ }
2951
+ // To print a Yarn lockfile to your console without writing it to disk
2952
+ // use `bun bun.lockb`.
2953
+ // https://bun.sh/guides/install/yarnlock
2954
+ return (
2955
+ await spawn.spawn(agentExecPath, [lockPath], {
2956
+ cwd,
2957
+ // Lazily access constants.WIN32.
2958
+ shell: constants.WIN32
2959
+ })
2960
+ ).stdout.trim()
2961
+ }
2962
+ return undefined
2963
+ })
2964
+ ],
2965
+ [NPM, defaultReader],
2966
+ [PNPM, defaultReader],
2967
+ [VLT, defaultReader],
2968
+ [YARN_BERRY, defaultReader],
2969
+ [YARN_CLASSIC, defaultReader]
2970
+ ])
2971
+ })()
2972
+ async function detectPackageEnvironment({
2973
+ cwd = process.cwd(),
2974
+ onUnknown
2975
+ } = {}) {
2976
+ let lockPath = await findUp(Object.keys(LOCKS), {
2977
+ cwd
2978
+ })
2979
+ let lockName = lockPath ? path.basename(lockPath) : undefined
2980
+ const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON
2981
+ const pkgJsonPath = lockPath
2982
+ ? path.resolve(
2983
+ lockPath,
2984
+ `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`
2985
+ )
2986
+ : await findUp(PACKAGE_JSON, {
2987
+ cwd
2988
+ })
2989
+ const pkgPath =
2990
+ pkgJsonPath && fs.existsSync(pkgJsonPath)
2991
+ ? path.dirname(pkgJsonPath)
2992
+ : undefined
2993
+ const editablePkgJson = pkgPath
2994
+ ? await packages.readPackageJson(pkgPath, {
2995
+ editable: true
2996
+ })
2997
+ : undefined
2998
+ // Read Corepack `packageManager` field in package.json:
2999
+ // https://nodejs.org/api/packages.html#packagemanager
3000
+ const pkgManager = strings.isNonEmptyString(
3001
+ editablePkgJson?.content?.packageManager
3002
+ )
3003
+ ? editablePkgJson.content.packageManager
3004
+ : undefined
3005
+ let agent
3006
+ if (pkgManager) {
3007
+ // A valid "packageManager" field value is "<package manager name>@<version>".
3008
+ // https://nodejs.org/api/packages.html#packagemanager
3009
+ const atSignIndex = pkgManager.lastIndexOf('@')
3010
+ if (atSignIndex !== -1) {
3011
+ const name = pkgManager.slice(0, atSignIndex)
3012
+ const version = pkgManager.slice(atSignIndex + 1)
3013
+ if (version && AGENTS.has(name)) {
3014
+ agent = name
3015
+ }
3016
+ }
3017
+ }
3018
+ if (
3019
+ agent === undefined &&
3020
+ !isHiddenLockFile &&
3021
+ typeof pkgJsonPath === 'string' &&
3022
+ typeof lockName === 'string'
3023
+ ) {
3024
+ agent = LOCKS[lockName]
3025
+ }
3026
+ if (agent === undefined) {
3027
+ agent = NPM
3028
+ onUnknown?.(pkgManager)
3029
+ }
3030
+ const agentExecPath = await getAgentExecPath(agent)
3031
+ const agentVersion = await getAgentVersion(agentExecPath, cwd)
3032
+ if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {
3033
+ agent = YARN_BERRY
3034
+ }
3035
+ // Lazily access constants.maintainedNodeVersions.
3036
+ const { maintainedNodeVersions } = constants
3037
+ // Lazily access constants.minimumVersionByAgent.
3038
+ const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent)
3039
+ const minSupportedNodeVersion = maintainedNodeVersions.last
3040
+ const nodeVersion = vendor.semverExports.coerce(process.version)
3041
+ let lockSrc
3042
+ let pkgAgentRange
3043
+ let pkgNodeRange
3044
+ let pkgMinAgentVersion = minSupportedAgentVersion
3045
+ let pkgMinNodeVersion = minSupportedNodeVersion
3046
+ if (editablePkgJson?.content) {
3047
+ const { engines } = editablePkgJson.content
3048
+ const engineAgentRange = engines?.[agent]
3049
+ const engineNodeRange = engines?.['node']
3050
+ if (strings.isNonEmptyString(engineAgentRange)) {
3051
+ pkgAgentRange = engineAgentRange
3052
+ // Roughly check agent range as semver.coerce will strip leading
3053
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3054
+ const coerced = vendor.semverExports.coerce(pkgAgentRange)
3055
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinAgentVersion)) {
3056
+ pkgMinAgentVersion = coerced.version
3057
+ }
3058
+ }
3059
+ if (strings.isNonEmptyString(engineNodeRange)) {
3060
+ pkgNodeRange = engineNodeRange
3061
+ // Roughly check Node range as semver.coerce will strip leading
3062
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
3063
+ const coerced = vendor.semverExports.coerce(pkgNodeRange)
3064
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
3065
+ pkgMinNodeVersion = coerced.version
3066
+ }
3067
+ }
3068
+ const browserslistQuery = editablePkgJson.content['browserslist']
3069
+ if (Array.isArray(browserslistQuery)) {
3070
+ // List Node targets in ascending version order.
3071
+ const browserslistNodeTargets = vendor
3072
+ .browserslistExports(browserslistQuery)
3073
+ .filter(v => /^node /i.test(v))
3074
+ .map(v => v.slice(5 /*'node '.length*/))
3075
+ .sort(sorts.naturalCompare)
3076
+ if (browserslistNodeTargets.length) {
3077
+ // browserslistNodeTargets[0] is the lowest Node target version.
3078
+ const coerced = vendor.semverExports.coerce(browserslistNodeTargets[0])
3079
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
3080
+ pkgMinNodeVersion = coerced.version
3081
+ }
3082
+ }
3083
+ }
3084
+ lockSrc =
3085
+ typeof lockPath === 'string'
3086
+ ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath, cwd)
3087
+ : undefined
3088
+ } else {
3089
+ lockName = undefined
3090
+ lockPath = undefined
3091
+ }
3092
+ // Does the system agent version meet our minimum supported agent version?
3093
+ const agentSupported =
3094
+ !!agentVersion &&
3095
+ vendor.semverExports.satisfies(
3096
+ agentVersion,
3097
+ `>=${minSupportedAgentVersion}`
3098
+ )
3099
+
3100
+ // Does the system Node version meet our minimum supported Node version?
3101
+ const nodeSupported = vendor.semverExports.satisfies(
3102
+ nodeVersion,
3103
+ `>=${minSupportedNodeVersion}`
3104
+ )
3105
+ const npmExecPath =
3106
+ agent === NPM ? agentExecPath : await getAgentExecPath(NPM)
3107
+ const npmBuggyOverrides =
3108
+ agent === NPM &&
3109
+ !!agentVersion &&
3110
+ vendor.semverExports.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)
3111
+ return {
3112
+ agent,
3113
+ agentExecPath,
3114
+ agentSupported,
3115
+ agentVersion,
3116
+ editablePkgJson,
3117
+ features: {
3118
+ npmBuggyOverrides
3119
+ },
3120
+ lockName,
3121
+ lockPath,
3122
+ lockSrc,
3123
+ nodeSupported,
3124
+ nodeVersion,
3125
+ npmExecPath,
3126
+ pkgPath,
3127
+ pkgRequirements: {
3128
+ agent: pkgAgentRange ?? `>=${pkgMinAgentVersion}`,
3129
+ node: pkgNodeRange ?? `>=${pkgMinNodeVersion}`
3130
+ },
3131
+ pkgSupports: {
3132
+ // Does our minimum supported agent version meet the package's requirements?
3133
+ agent: vendor.semverExports.satisfies(
3134
+ minSupportedAgentVersion,
3135
+ `>=${pkgMinAgentVersion}`
3136
+ ),
3137
+ // Does our supported Node versions meet the package's requirements?
3138
+ node: maintainedNodeVersions.some(v =>
3139
+ vendor.semverExports.satisfies(v, `>=${pkgMinNodeVersion}`)
3140
+ )
3141
+ }
3142
+ }
3143
+ }
3144
+ async function detectAndValidatePackageEnvironment(cwd, options) {
3145
+ const {
3146
+ cmdName = '',
3147
+ logger,
3148
+ prod
3149
+ } = {
3150
+ __proto__: null,
3151
+ ...options
3152
+ }
3153
+ const details = await detectPackageEnvironment({
3154
+ cwd,
3155
+ onUnknown(pkgManager) {
3156
+ logger?.warn(
3157
+ cmdPrefixMessage(
3158
+ cmdName,
3159
+ `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`
3160
+ )
3161
+ )
3162
+ }
3163
+ })
3164
+ const { agent, nodeVersion, pkgRequirements } = details
3165
+ const agentVersion = details.agentVersion ?? 'unknown'
3166
+ if (!details.agentSupported) {
3167
+ const minVersion = constants.minimumVersionByAgent.get(agent)
3168
+ logger?.fail(
3169
+ cmdPrefixMessage(
3170
+ cmdName,
3171
+ `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`
3172
+ )
3173
+ )
3174
+ return
3175
+ }
3176
+ if (!details.nodeSupported) {
3177
+ const minVersion = constants.maintainedNodeVersions.last
3178
+ logger?.fail(
3179
+ cmdPrefixMessage(
3180
+ cmdName,
3181
+ `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`
3182
+ )
3183
+ )
3184
+ return
3185
+ }
3186
+ if (!details.pkgSupports.agent) {
3187
+ logger?.fail(
3188
+ cmdPrefixMessage(
3189
+ cmdName,
3190
+ `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`
3191
+ )
3192
+ )
3193
+ return
3194
+ }
3195
+ if (!details.pkgSupports.node) {
3196
+ logger?.fail(
3197
+ cmdPrefixMessage(
3198
+ cmdName,
3199
+ `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`
3200
+ )
3201
+ )
3202
+ return
3203
+ }
3204
+ if (agent === VLT) {
3205
+ logger?.fail(
3206
+ cmdPrefixMessage(
3207
+ cmdName,
3208
+ `${agent} does not support overrides. Soon, though ⚡`
3209
+ )
3210
+ )
3211
+ return
3212
+ }
3213
+ const lockName = details.lockName ?? 'lock file'
3214
+ if (details.lockName === undefined || details.lockSrc === undefined) {
3215
+ logger?.fail(cmdPrefixMessage(cmdName, `No ${lockName} found`))
3216
+ return
3217
+ }
3218
+ if (details.lockSrc.trim() === '') {
3219
+ logger?.fail(cmdPrefixMessage(cmdName, `${lockName} is empty`))
3220
+ return
3221
+ }
3222
+ if (details.pkgPath === undefined) {
3223
+ logger?.fail(cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`))
3224
+ return
3225
+ }
3226
+ if (prod && (agent === BUN || agent === YARN_BERRY)) {
3227
+ logger?.fail(
3228
+ cmdPrefixMessage(
3229
+ cmdName,
3230
+ `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`
3231
+ )
3232
+ )
3233
+ return
3234
+ }
3235
+ if (
3236
+ details.lockPath &&
3237
+ path.relative(cwd, details.lockPath).startsWith('.')
3238
+ ) {
3239
+ // Note: In tests we return <redacted> because otherwise snapshots will fail.
3240
+ const { REDACTED } = constants
3241
+ // Lazily access constants.ENV.VITEST.
3242
+ const redacting = constants.ENV.VITEST
3243
+ logger?.warn(
3244
+ cmdPrefixMessage(
3245
+ cmdName,
3246
+ `Package ${lockName} found at ${redacting ? REDACTED : details.lockPath}`
3247
+ )
3248
+ )
3249
+ }
3250
+ return details
3251
+ }
3252
+
3253
+ exports.ALERT_SEVERITY = ALERT_SEVERITY
3254
+ exports.AuthError = AuthError
3255
+ exports.ColorOrMarkdown = ColorOrMarkdown
3256
+ exports.InputError = InputError
3257
+ exports.RangeStyles = RangeStyles
3258
+ exports.applyRange = applyRange
3259
+ exports.captureException = captureException
3260
+ exports.checkCommandInput = checkCommandInput
3261
+ exports.cmdPrefixMessage = cmdPrefixMessage
3262
+ exports.commonFlags = commonFlags
3263
+ exports.createEnum = createEnum
3264
+ exports.detectAndValidatePackageEnvironment =
3265
+ detectAndValidatePackageEnvironment
3266
+ exports.determineOrgSlug = determineOrgSlug
3267
+ exports.failMsgWithBadge = failMsgWithBadge
3268
+ exports.formatSeverityCount = formatSeverityCount
3269
+ exports.getAlertsMapFromPnpmLockfile = getAlertsMapFromPnpmLockfile
3270
+ exports.getAlertsMapFromPurls = getAlertsMapFromPurls
3271
+ exports.getConfigValue = getConfigValue
3272
+ exports.getConfigValueOrUndef = getConfigValueOrUndef
3273
+ exports.getCveInfoByAlertsMap = getCveInfoByAlertsMap
3274
+ exports.getFlagListOutput = getFlagListOutput
3275
+ exports.getMajor = getMajor
3276
+ exports.getNpmBinPath = getNpmBinPath
3277
+ exports.getNpmRequire = getNpmRequire
3278
+ exports.getNpxBinPath = getNpxBinPath
3279
+ exports.getOutputKind = getOutputKind
3280
+ exports.getPackageFilesForScan = getPackageFilesForScan
3281
+ exports.getPkgFullNameFromPurlObj = getPkgFullNameFromPurlObj
3282
+ exports.getPublicToken = getPublicToken
3283
+ exports.getSeverityCount = getSeverityCount
3284
+ exports.getSocketDevAlertUrl = getSocketDevAlertUrl
3285
+ exports.getSocketDevPackageOverviewUrl = getSocketDevPackageOverviewUrl
3286
+ exports.getSocketDevPackageOverviewUrlFromPurl =
3287
+ getSocketDevPackageOverviewUrlFromPurl
3288
+ exports.getVisibleTokenPrefix = getVisibleTokenPrefix
3289
+ exports.globWorkspace = globWorkspace
3290
+ exports.handleApiCall = handleApiCall
3291
+ exports.handleApiCallNoSpinner = handleApiCallNoSpinner
3292
+ exports.handleUnsuccessfulApiResponse = handleUnsuccessfulApiResponse
3293
+ exports.hasDefaultToken = hasDefaultToken
3294
+ exports.idToPurl = idToPurl
3295
+ exports.isHelpFlag = isHelpFlag
3296
+ exports.isNpmBinPathShadowed = isNpmBinPathShadowed
3297
+ exports.isNpxBinPathShadowed = isNpxBinPathShadowed
3298
+ exports.isReadOnlyConfig = isReadOnlyConfig
3299
+ exports.isTestingV1 = isTestingV1
3300
+ exports.logAlertsMap = logAlertsMap
3301
+ exports.mapToObject = mapToObject
3302
+ exports.mdTable = mdTable
3303
+ exports.mdTableOfPairs = mdTableOfPairs
3304
+ exports.mdTableStringNumber = mdTableStringNumber
3305
+ exports.meowOrExit = meowOrExit
3306
+ exports.meowWithSubcommands = meowWithSubcommands
3307
+ exports.outputFlags = outputFlags
3308
+ exports.parsePnpmLockfileVersion = parsePnpmLockfileVersion
3309
+ exports.queryApiSafeJson = queryApiSafeJson
3310
+ exports.queryApiSafeText = queryApiSafeText
3311
+ exports.removeNodeModules = removeNodeModules
3312
+ exports.runAgentInstall = runAgentInstall
3313
+ exports.safeReadFile = safeReadFile
3314
+ exports.sensitiveConfigKeys = sensitiveConfigKeys
3315
+ exports.serializeResultJson = serializeResultJson
3316
+ exports.setupSdk = setupSdk
3317
+ exports.suggestOrgSlug = suggestOrgSlug
3318
+ exports.supportedConfigKeys = supportedConfigKeys
3319
+ exports.updateConfigValue = updateConfigValue
3320
+ exports.validationFlags = validationFlags
3321
+ exports.walkNestedMap = walkNestedMap
3322
+ //# debugId=ce901e44-4e3e-43e6-8016-50895b08fc53
3323
+ //# sourceMappingURL=utils.js.map