@socketsecurity/cli-with-sentry 0.14.154 → 0.14.155

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 (97) hide show
  1. package/dist/.config/tsconfig.dts.tsbuildinfo +1 -1
  2. package/dist/cli.js +1289 -2453
  3. package/dist/cli.js.map +1 -1
  4. package/dist/constants.js +7 -6
  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/optimize/deps-includes-by-agent.d.mts +1 -1
  34. package/dist/types/commands/optimize/get-overrides-by-agent.d.mts +1 -1
  35. package/dist/types/commands/optimize/lockfile-includes-by-agent.d.mts +1 -1
  36. package/dist/types/commands/optimize/ls-by-agent.d.mts +1 -1
  37. package/dist/types/commands/optimize/update-manifest-by-agent.d.mts +1 -1
  38. package/dist/types/commands/organization/fetch-license-policy.d.mts.map +1 -1
  39. package/dist/types/commands/organization/fetch-organization-list.d.mts.map +1 -1
  40. package/dist/types/commands/organization/fetch-quota.d.mts.map +1 -1
  41. package/dist/types/commands/organization/fetch-security-policy.d.mts.map +1 -1
  42. package/dist/types/commands/package/fetch-purl-deep-score.d.mts.map +1 -1
  43. package/dist/types/commands/package/fetch-purls-shallow-score.d.mts.map +1 -1
  44. package/dist/types/commands/repos/fetch-create-repo.d.mts.map +1 -1
  45. package/dist/types/commands/repos/fetch-delete-repo.d.mts.map +1 -1
  46. package/dist/types/commands/repos/fetch-list-repos.d.mts.map +1 -1
  47. package/dist/types/commands/repos/fetch-update-repo.d.mts.map +1 -1
  48. package/dist/types/commands/repos/fetch-view-repo.d.mts.map +1 -1
  49. package/dist/types/commands/repos/handle-create-repo.d.mts.map +1 -1
  50. package/dist/types/commands/repos/output-create-repo.d.mts +1 -1
  51. package/dist/types/commands/repos/output-create-repo.d.mts.map +1 -1
  52. package/dist/types/commands/scan/fetch-create-org-full-scan.d.mts.map +1 -1
  53. package/dist/types/commands/scan/fetch-delete-org-full-scan.d.mts.map +1 -1
  54. package/dist/types/commands/scan/fetch-diff-scan.d.mts.map +1 -1
  55. package/dist/types/commands/scan/fetch-list-scans.d.mts.map +1 -1
  56. package/dist/types/commands/scan/fetch-report-data.d.mts.map +1 -1
  57. package/dist/types/commands/scan/fetch-scan-metadata.d.mts.map +1 -1
  58. package/dist/types/commands/scan/fetch-supported-scan-file-names.d.mts.map +1 -1
  59. package/dist/types/commands/scan/stream-scan.d.mts +1 -1
  60. package/dist/types/commands/scan/stream-scan.d.mts.map +1 -1
  61. package/dist/types/commands/scan/suggest-org-slug.d.mts.map +1 -1
  62. package/dist/types/commands/threat-feed/output-threat-feed.d.mts.map +1 -1
  63. package/dist/types/constants.d.mts +1 -1
  64. package/dist/types/constants.d.mts.map +1 -1
  65. package/dist/types/shadow/npm/arborist/lib/dep-valid.d.mts +2 -2
  66. package/dist/types/shadow/npm/arborist/lib/dep-valid.d.mts.map +1 -1
  67. package/dist/types/{utils → shadow/npm}/arborist-helpers.d.mts +19 -3
  68. package/dist/types/shadow/npm/arborist-helpers.d.mts.map +1 -0
  69. package/dist/types/{utils/npm.d.mts → shadow/npm/install.d.mts} +2 -2
  70. package/dist/types/shadow/npm/install.d.mts.map +1 -0
  71. package/dist/types/shadow/npm/paths.d.mts +0 -6
  72. package/dist/types/shadow/npm/paths.d.mts.map +1 -1
  73. package/dist/types/utils/agent.d.mts +2 -2
  74. package/dist/types/utils/agent.d.mts.map +1 -1
  75. package/dist/types/utils/alerts-map.d.mts +0 -11
  76. package/dist/types/utils/alerts-map.d.mts.map +1 -1
  77. package/dist/types/utils/api.d.mts +15 -5
  78. package/dist/types/utils/api.d.mts.map +1 -1
  79. package/dist/types/utils/npm-paths.d.mts +7 -0
  80. package/dist/types/utils/npm-paths.d.mts.map +1 -0
  81. package/dist/types/utils/pnpm.d.mts +2 -1
  82. package/dist/types/utils/pnpm.d.mts.map +1 -1
  83. package/dist/types/utils/translations.d.mts.map +1 -1
  84. package/dist/utils.js +3169 -0
  85. package/dist/utils.js.map +1 -0
  86. package/dist/vendor.js +1284 -1231
  87. package/dist/vendor.js.map +1 -1
  88. package/external/@socketsecurity/registry/external/browserslist.js +382 -366
  89. package/external/@socketsecurity/registry/external/browserslist.js.map +1 -1
  90. package/external/@socketsecurity/registry/lib/constants/maintained-node-versions.js +7 -8
  91. package/external/@socketsecurity/registry/lib/fs.d.ts +6 -4
  92. package/external/@socketsecurity/registry/package.json +5 -5
  93. package/package.json +19 -19
  94. package/dist/shadow-npm-paths.js +0 -291
  95. package/dist/shadow-npm-paths.js.map +0 -1
  96. package/dist/types/utils/arborist-helpers.d.mts.map +0 -1
  97. package/dist/types/utils/npm.d.mts.map +0 -1
package/dist/utils.js ADDED
@@ -0,0 +1,3169 @@
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 arrays = require('../external/@socketsecurity/registry/lib/arrays')
14
+ const packages = require('../external/@socketsecurity/registry/lib/packages')
15
+ const fs = require('node:fs')
16
+ const os = require('node:os')
17
+ const promises = require('node:timers/promises')
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
+ // TODO: this function is removed after v1.0.0
612
+ function handleUnsuccessfulApiResponse(_name, error, cause, status) {
613
+ const message = `${error || 'No error message returned'}${cause ? ` (reason: ${cause})` : ''}`
614
+ if (status === 401 || status === 403) {
615
+ // Lazily access constants.spinner.
616
+ const { spinner } = constants
617
+ spinner.stop()
618
+ throw new AuthError(message)
619
+ }
620
+ logger.logger.fail(failMsgWithBadge('Socket API returned an error', message))
621
+ // eslint-disable-next-line n/no-process-exit
622
+ process.exit(1)
623
+ }
624
+ function handleFailedApiResponse(_name, { cause, error }) {
625
+ const message = `${error || 'No error message returned'}`
626
+ // logger.error(failMsgWithBadge('Socket API returned an error', message))
627
+ return {
628
+ ok: false,
629
+ message: 'Socket API returned an error',
630
+ cause: `${message}${cause ? ` ( Reason: ${cause} )` : ''}`
631
+ }
632
+ }
633
+ async function handleApiCall(value, fetchingDesc) {
634
+ // Lazily access constants.spinner.
635
+ const { spinner } = constants
636
+ spinner.start(`Requesting ${fetchingDesc} from API...`)
637
+ let result
638
+ try {
639
+ result = await value
640
+
641
+ // TODO: info, not success (looks weird when response is non-200)
642
+ spinner.successAndStop(
643
+ `Received API response (after requesting ${fetchingDesc}).`
644
+ )
645
+ } catch (e) {
646
+ spinner.failAndStop(`An error was thrown while requesting ${fetchingDesc}`)
647
+ debug.debugLog(`handleApiCall(${fetchingDesc}) threw error:\n`, e)
648
+ const message = `${e || 'No error message returned'}`
649
+ const cause = `${e || 'No error message returned'}`
650
+ return {
651
+ ok: false,
652
+ message: 'Socket API returned an error',
653
+ cause: `${message}${cause ? ` ( Reason: ${cause} )` : ''}`
654
+ }
655
+ } finally {
656
+ spinner.stop()
657
+ }
658
+
659
+ // Note: TS can't narrow down the type of result due to generics
660
+ if (result.success === false) {
661
+ const err = result
662
+ const message = `${err.error || 'No error message returned'}`
663
+ debug.debugLog(`handleApiCall(${fetchingDesc}) bad response:\n`, err)
664
+ return {
665
+ ok: false,
666
+ message: 'Socket API returned an error',
667
+ cause: `${message}${err.cause ? ` ( Reason: ${err.cause} )` : ''}`,
668
+ data: {
669
+ code: result.status
670
+ }
671
+ }
672
+ } else {
673
+ const ok = result
674
+ return {
675
+ ok: true,
676
+ data: ok.data
677
+ }
678
+ }
679
+ }
680
+ async function tmpHandleApiCall(value, description) {
681
+ try {
682
+ return await value
683
+ } catch (e) {
684
+ debug.debugLog(`handleApiCall[${description}] error:\n`, e)
685
+ // TODO: eliminate this throw in favor of CResult (or anything else)
686
+ throw new Error(`Failed ${description}`, {
687
+ cause: e
688
+ })
689
+ }
690
+ }
691
+ async function handleApiCallNoSpinner(value, description) {
692
+ let result
693
+ try {
694
+ result = await value
695
+ } catch (e) {
696
+ debug.debugLog(`handleApiCall(${description}) threw error:\n`, e)
697
+ const message = `${e || 'No error message returned'}`
698
+ const cause = `${e || 'No error message returned'}`
699
+ return {
700
+ ok: false,
701
+ message: 'Socket API returned an error',
702
+ cause: `${message}${cause ? ` ( Reason: ${cause} )` : ''}`
703
+ }
704
+ }
705
+
706
+ // Note: TS can't narrow down the type of result due to generics
707
+ if (result.success === false) {
708
+ const err = result
709
+ const message = `${err.error || 'No error message returned'}`
710
+ debug.debugLog(`handleApiCall(${description}) bad response:\n`, err)
711
+ return {
712
+ ok: false,
713
+ message: 'Socket API returned an error',
714
+ cause: `${message}${err.cause ? ` ( Reason: ${err.cause} )` : ''}`,
715
+ data: {
716
+ code: result.status
717
+ }
718
+ }
719
+ } else {
720
+ const ok = result
721
+ return {
722
+ ok: true,
723
+ data: ok.data
724
+ }
725
+ }
726
+ }
727
+ async function handleApiError(code) {
728
+ if (code === 400) {
729
+ return 'One of the options passed might be incorrect'
730
+ }
731
+ if (code === 403) {
732
+ 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'
733
+ }
734
+ if (code === 404) {
735
+ return 'The requested Socket API endpoint was not found (404) or there was no result for the requested parameters. This could be a temporary problem caused by an incident or a bug in the CLI. If the problem persists please let us know.'
736
+ }
737
+ return `Server responded with status code ${code}`
738
+ }
739
+ function getLastFiveOfApiToken(token) {
740
+ // Get the last 5 characters of the API token before the trailing "_api".
741
+ return token.slice(-9, -4)
742
+ }
743
+
744
+ // The API server that should be used for operations.
745
+ function getDefaultApiBaseUrl$1() {
746
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_BASE_URL.
747
+ const SOCKET_SECURITY_API_BASE_URL =
748
+ constants.ENV.SOCKET_SECURITY_API_BASE_URL
749
+ const baseUrl =
750
+ SOCKET_SECURITY_API_BASE_URL || getConfigValueOrUndef('apiBaseUrl')
751
+ if (strings.isNonEmptyString(baseUrl)) {
752
+ return baseUrl
753
+ }
754
+ // Lazily access constants.API_V0_URL.
755
+ const API_V0_URL = constants.API_V0_URL
756
+ return API_V0_URL
757
+ }
758
+ async function queryApi(path, apiToken) {
759
+ const baseUrl = getDefaultApiBaseUrl$1() || ''
760
+ if (!baseUrl) {
761
+ logger.logger.warn(
762
+ 'API endpoint is not set and default was empty. Request is likely to fail.'
763
+ )
764
+ }
765
+ return await fetch(`${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}`, {
766
+ method: 'GET',
767
+ headers: {
768
+ Authorization: `Basic ${btoa(`${apiToken}:`)}`
769
+ }
770
+ })
771
+ }
772
+
773
+ const { SOCKET_PUBLIC_API_TOKEN } = constants
774
+
775
+ // The API server that should be used for operations.
776
+ function getDefaultApiBaseUrl() {
777
+ const baseUrl =
778
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_BASE_URL.
779
+ constants.ENV.SOCKET_SECURITY_API_BASE_URL ||
780
+ getConfigValueOrUndef('apiBaseUrl')
781
+ return strings.isNonEmptyString(baseUrl) ? baseUrl : undefined
782
+ }
783
+
784
+ // The API server that should be used for operations.
785
+ function getDefaultHttpProxy() {
786
+ const apiProxy =
787
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_PROXY.
788
+ constants.ENV.SOCKET_SECURITY_API_PROXY || getConfigValueOrUndef('apiProxy')
789
+ return strings.isNonEmptyString(apiProxy) ? apiProxy : undefined
790
+ }
791
+
792
+ // This API key should be stored globally for the duration of the CLI execution.
793
+ let _defaultToken
794
+ function getDefaultToken() {
795
+ // Lazily access constants.ENV.SOCKET_CLI_NO_API_TOKEN.
796
+ if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {
797
+ _defaultToken = undefined
798
+ } else {
799
+ const key =
800
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_TOKEN.
801
+ constants.ENV.SOCKET_SECURITY_API_TOKEN ||
802
+ getConfigValueOrUndef('apiToken') ||
803
+ _defaultToken
804
+ _defaultToken = strings.isNonEmptyString(key) ? key : undefined
805
+ }
806
+ return _defaultToken
807
+ }
808
+ function getPublicToken() {
809
+ return (
810
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_TOKEN.
811
+ (constants.ENV.SOCKET_SECURITY_API_TOKEN || getDefaultToken()) ??
812
+ SOCKET_PUBLIC_API_TOKEN
813
+ )
814
+ }
815
+ async function setupSdk(
816
+ apiToken = getDefaultToken(),
817
+ apiBaseUrl = getDefaultApiBaseUrl(),
818
+ proxy = getDefaultHttpProxy()
819
+ ) {
820
+ if (typeof apiToken !== 'string' && vendor.isInteractiveExports()) {
821
+ apiToken = await prompts.password({
822
+ message:
823
+ 'Enter your Socket.dev API key (not saved, use socket login to persist)'
824
+ })
825
+ _defaultToken = apiToken
826
+ }
827
+ if (!apiToken) {
828
+ // TODO: eliminate this throw in favor of CResult (or anything else)
829
+ throw new AuthError('You need to provide an API key')
830
+ }
831
+ return new vendor.distExports$2.SocketSdk(apiToken, {
832
+ agent: proxy
833
+ ? new vendor.HttpsProxyAgent({
834
+ proxy
835
+ })
836
+ : undefined,
837
+ baseUrl: apiBaseUrl,
838
+ userAgent: vendor.distExports$2.createUserAgentFromPkgJson({
839
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_NAME.
840
+ name: constants.ENV.INLINED_SOCKET_CLI_NAME,
841
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_VERSION.
842
+ version: constants.ENV.INLINED_SOCKET_CLI_VERSION,
843
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE.
844
+ homepage: constants.ENV.INLINED_SOCKET_CLI_HOMEPAGE
845
+ })
846
+ })
847
+ }
848
+
849
+ function mdTableStringNumber(title1, title2, obj) {
850
+ // | Date | Counts |
851
+ // | ----------- | ------ |
852
+ // | Header | 201464 |
853
+ // | Paragraph | 18 |
854
+ let mw1 = title1.length
855
+ let mw2 = title2.length
856
+ for (const [key, value] of Object.entries(obj)) {
857
+ mw1 = Math.max(mw1, key.length)
858
+ mw2 = Math.max(mw2, String(value ?? '').length)
859
+ }
860
+ const lines = []
861
+ lines.push(`| ${title1.padEnd(mw1, ' ')} | ${title2.padEnd(mw2)} |`)
862
+ lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)
863
+ for (const [key, value] of Object.entries(obj)) {
864
+ lines.push(
865
+ `| ${key.padEnd(mw1, ' ')} | ${String(value ?? '').padStart(mw2, ' ')} |`
866
+ )
867
+ }
868
+ lines.push(`| ${'-'.repeat(mw1)} | ${'-'.repeat(mw2)} |`)
869
+ return lines.join('\n')
870
+ }
871
+ function mdTable(
872
+ logs,
873
+ // This is saying "an array of strings and the strings are a valid key of elements of T"
874
+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
875
+ cols,
876
+ titles = cols
877
+ ) {
878
+ // Max col width required to fit all data in that column
879
+ const cws = cols.map(col => col.length)
880
+ for (const log of logs) {
881
+ for (let i = 0, { length } = cols; i < length; i += 1) {
882
+ // @ts-ignore
883
+ const val = log[cols[i] ?? ''] ?? ''
884
+ cws[i] = Math.max(
885
+ cws[i] ?? 0,
886
+ String(val).length,
887
+ (titles[i] || '').length
888
+ )
889
+ }
890
+ }
891
+ let div = '|'
892
+ for (const cw of cws) {
893
+ div += ' ' + '-'.repeat(cw) + ' |'
894
+ }
895
+ let header = '|'
896
+ for (let i = 0, { length } = titles; i < length; i += 1) {
897
+ header += ' ' + String(titles[i]).padEnd(cws[i] ?? 0, ' ') + ' |'
898
+ }
899
+ let body = ''
900
+ for (const log of logs) {
901
+ body += '|'
902
+ for (let i = 0, { length } = cols; i < length; i += 1) {
903
+ // @ts-ignore
904
+ const val = log[cols[i] ?? ''] ?? ''
905
+ body += ' ' + String(val).padEnd(cws[i] ?? 0, ' ') + ' |'
906
+ }
907
+ body += '\n'
908
+ }
909
+ return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n')
910
+ }
911
+ function mdTableOfPairs(
912
+ arr,
913
+ // This is saying "an array of strings and the strings are a valid key of elements of T"
914
+ // In turn, T is defined above as the audit log event type from our OpenAPI docs.
915
+ cols
916
+ ) {
917
+ // Max col width required to fit all data in that column
918
+ const cws = cols.map(col => col.length)
919
+ for (const [key, val] of arr) {
920
+ cws[0] = Math.max(cws[0] ?? 0, String(key).length)
921
+ cws[1] = Math.max(cws[1] ?? 0, String(val ?? '').length)
922
+ }
923
+ let div = '|'
924
+ for (const cw of cws) {
925
+ div += ' ' + '-'.repeat(cw) + ' |'
926
+ }
927
+ let header = '|'
928
+ for (let i = 0, { length } = cols; i < length; i += 1) {
929
+ header += ' ' + String(cols[i]).padEnd(cws[i] ?? 0, ' ') + ' |'
930
+ }
931
+ let body = ''
932
+ for (const [key, val] of arr) {
933
+ body += '|'
934
+ body += ' ' + String(key).padEnd(cws[0] ?? 0, ' ') + ' |'
935
+ body += ' ' + String(val ?? '').padEnd(cws[1] ?? 0, ' ') + ' |'
936
+ body += '\n'
937
+ }
938
+ return [div, header, div, body.trim(), div].filter(s => !!s.trim()).join('\n')
939
+ }
940
+
941
+ // Serialize the final result object before printing it
942
+ // All commands that support the --json flag should call this before printing
943
+ function serializeResultJson(data) {
944
+ if (typeof data !== 'object' || !data) {
945
+ process.exitCode = 1
946
+ // We should not allow to expect the json value to be "null", or a boolean/number/string, even if they are valid "json".
947
+ const msg =
948
+ 'There was a problem converting the data set to JSON. The JSON was not an object. Please try again without --json'
949
+ debug.debugLog('typeof data=', typeof data)
950
+ if (typeof data !== 'object' && data) {
951
+ debug.debugLog('data:\n', data)
952
+ }
953
+ return (
954
+ JSON.stringify({
955
+ ok: false,
956
+ message: 'Unable to serialize JSON',
957
+ data: msg
958
+ }).trim() + '\n'
959
+ )
960
+ }
961
+ try {
962
+ return JSON.stringify(data, null, 2).trim() + '\n'
963
+ } catch (e) {
964
+ debug.debugLog('Error:\n', e)
965
+ process.exitCode = 1
966
+ // This could be caused by circular references, which is an "us" problem
967
+ const msg =
968
+ 'There was a problem converting the data set to JSON. Please try again without --json'
969
+ logger.logger.error(msg)
970
+ return (
971
+ JSON.stringify({
972
+ ok: false,
973
+ message: 'Unable to serialize JSON',
974
+ data: msg
975
+ }).trim() + '\n'
976
+ )
977
+ }
978
+ }
979
+
980
+ // TODO: not sure if I'm missing something but meow doesn't seem to expose this?
981
+
982
+ // Note: we use this description in getFlagListOutput, meow doesn't care
983
+
984
+ const commonFlags = {
985
+ config: {
986
+ type: 'string',
987
+ default: '',
988
+ hidden: true,
989
+ description: 'Override the local config with this JSON'
990
+ },
991
+ dryRun: {
992
+ type: 'boolean',
993
+ default: false,
994
+ hidden: true,
995
+ // Only show in root command
996
+ description: 'Do input validation for a command and exit 0 when input is ok'
997
+ },
998
+ help: {
999
+ type: 'boolean',
1000
+ default: false,
1001
+ shortFlag: 'h',
1002
+ description: 'Print this help'
1003
+ },
1004
+ silent: {
1005
+ type: 'boolean',
1006
+ default: false,
1007
+ hidden: true,
1008
+ shortFlag: 's',
1009
+ description: 'Make the CLI less chatty'
1010
+ }
1011
+ }
1012
+ const outputFlags = {
1013
+ json: {
1014
+ type: 'boolean',
1015
+ shortFlag: 'j',
1016
+ default: false,
1017
+ description: 'Output result as json'
1018
+ },
1019
+ markdown: {
1020
+ type: 'boolean',
1021
+ shortFlag: 'm',
1022
+ default: false,
1023
+ description: 'Output result as markdown'
1024
+ }
1025
+ }
1026
+ const validationFlags = {
1027
+ all: {
1028
+ type: 'boolean',
1029
+ default: false,
1030
+ description: 'Include all issues'
1031
+ },
1032
+ strict: {
1033
+ type: 'boolean',
1034
+ default: false,
1035
+ description: 'Exits with an error code if any matching issues are found'
1036
+ }
1037
+ }
1038
+
1039
+ function checkCommandInput(outputKind, ...checks) {
1040
+ if (checks.every(d => d.test)) {
1041
+ return true
1042
+ }
1043
+ const msg = ['Please review the input requirements and try again', '']
1044
+ for (const d of checks) {
1045
+ // If nook, then ignore when test is ok
1046
+ if (d.nook && d.test) {
1047
+ continue
1048
+ }
1049
+ const lines = d.message.split('\n')
1050
+ const { length: lineCount } = lines
1051
+ if (!lineCount) {
1052
+ continue
1053
+ }
1054
+ // If the message has newlines then format the first line with the input
1055
+ // expectation and the rest indented below it.
1056
+ msg.push(
1057
+ ` - ${lines[0]} (${d.test ? vendor.yoctocolorsCjsExports.green(d.pass) : vendor.yoctocolorsCjsExports.red(d.fail)})`
1058
+ )
1059
+ if (lineCount > 1) {
1060
+ msg.push(...lines.slice(1).map(str => ` ${str}`))
1061
+ }
1062
+ msg.push('')
1063
+ }
1064
+
1065
+ // Use exit status of 2 to indicate incorrect usage, generally invalid
1066
+ // options or missing arguments.
1067
+ // https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
1068
+ process.exitCode = 2
1069
+ if (outputKind === 'json') {
1070
+ logger.logger.log(
1071
+ serializeResultJson({
1072
+ ok: false,
1073
+ message: 'Input error',
1074
+ data: msg.join('\n')
1075
+ })
1076
+ )
1077
+ } else {
1078
+ logger.logger.fail(failMsgWithBadge('Input error', msg.join('\n')))
1079
+ }
1080
+ return false
1081
+ }
1082
+
1083
+ function getOutputKind(json, markdown) {
1084
+ if (json) {
1085
+ return 'json'
1086
+ }
1087
+ if (markdown) {
1088
+ return 'markdown'
1089
+ }
1090
+ return 'text'
1091
+ }
1092
+
1093
+ function getFlagListOutput(list, indent, { keyPrefix = '--', padName } = {}) {
1094
+ return getHelpListOutput(
1095
+ {
1096
+ ...list
1097
+ },
1098
+ indent,
1099
+ {
1100
+ keyPrefix,
1101
+ padName
1102
+ }
1103
+ )
1104
+ }
1105
+ function getHelpListOutput(
1106
+ list,
1107
+ indent,
1108
+ { keyPrefix = '', padName = 18 } = {}
1109
+ ) {
1110
+ let result = ''
1111
+ const names = Object.keys(list).sort()
1112
+ for (const name of names) {
1113
+ const entry = list[name]
1114
+ if (entry && 'hidden' in entry && entry?.hidden) {
1115
+ continue
1116
+ }
1117
+ const description =
1118
+ (typeof entry === 'object' ? entry.description : entry) || ''
1119
+ result +=
1120
+ ''.padEnd(indent) +
1121
+ (keyPrefix + name).padEnd(padName) +
1122
+ description +
1123
+ '\n'
1124
+ }
1125
+ return result.trim() || '(none)'
1126
+ }
1127
+
1128
+ async function meowWithSubcommands(subcommands, options) {
1129
+ const {
1130
+ aliases = {},
1131
+ argv,
1132
+ defaultSub,
1133
+ importMeta,
1134
+ name,
1135
+ ...additionalOptions
1136
+ } = {
1137
+ __proto__: null,
1138
+ ...options
1139
+ }
1140
+ const [commandOrAliasName_, ...rawCommandArgv] = argv
1141
+ let commandOrAliasName = commandOrAliasName_
1142
+ if (!commandOrAliasName && defaultSub) {
1143
+ commandOrAliasName = defaultSub
1144
+ }
1145
+ const flags = {
1146
+ ...commonFlags,
1147
+ ...additionalOptions.flags
1148
+ }
1149
+
1150
+ // No further args or first arg is a flag (shrug)
1151
+ if (
1152
+ name === 'socket' &&
1153
+ (!commandOrAliasName || commandOrAliasName?.startsWith('-'))
1154
+ ) {
1155
+ flags['dryRun'] = {
1156
+ type: 'boolean',
1157
+ default: false,
1158
+ hidden: false,
1159
+ // Only show on root
1160
+ description:
1161
+ 'Do input validation for a command and exit 0 when input is ok. Every command should support this flag (not shown on help screens)'
1162
+ }
1163
+ }
1164
+ const cli = vendor.meow(
1165
+ `
1166
+ Usage
1167
+ $ ${name} <command>
1168
+
1169
+ Commands
1170
+ ${getHelpListOutput(
1171
+ {
1172
+ ...objects.toSortedObject(
1173
+ Object.fromEntries(
1174
+ Object.entries(subcommands).filter(
1175
+ ({ 1: subcommand }) => !subcommand.hidden
1176
+ )
1177
+ )
1178
+ ),
1179
+ ...objects.toSortedObject(
1180
+ Object.fromEntries(
1181
+ Object.entries(aliases).filter(({ 1: alias }) => {
1182
+ const { hidden } = alias
1183
+ const cmdName = hidden ? '' : alias.argv[0]
1184
+ const subcommand = cmdName ? subcommands[cmdName] : undefined
1185
+ return subcommand && !subcommand.hidden
1186
+ })
1187
+ )
1188
+ )
1189
+ },
1190
+ 6
1191
+ )}
1192
+
1193
+ Options
1194
+ ${getFlagListOutput(flags, 6)}
1195
+
1196
+ Examples
1197
+ $ ${name} --help
1198
+ `,
1199
+ {
1200
+ argv,
1201
+ importMeta,
1202
+ ...additionalOptions,
1203
+ flags,
1204
+ // Do not strictly check for flags here.
1205
+ allowUnknownFlags: true,
1206
+ // We will emit help when we're ready
1207
+ // Plus, if we allow this then meow() can just exit here.
1208
+ autoHelp: false
1209
+ }
1210
+ )
1211
+
1212
+ // Hard override the config if instructed to do so.
1213
+ // The env var overrides the --flag, which overrides the persisted config
1214
+ // Also, when either of these are used, config updates won't persist.
1215
+ let configOverrideResult
1216
+ // Lazily access constants.ENV.SOCKET_CLI_CONFIG.
1217
+ if (constants.ENV.SOCKET_CLI_CONFIG) {
1218
+ configOverrideResult = overrideCachedConfig(
1219
+ // Lazily access constants.ENV.SOCKET_CLI_CONFIG.
1220
+ constants.ENV.SOCKET_CLI_CONFIG
1221
+ )
1222
+ } else if (cli.flags['config']) {
1223
+ configOverrideResult = overrideCachedConfig(
1224
+ String(cli.flags['config'] || '')
1225
+ )
1226
+ }
1227
+
1228
+ // Lazily access constants.ENV.SOCKET_CLI_NO_API_TOKEN.
1229
+ if (constants.ENV.SOCKET_CLI_NO_API_TOKEN) {
1230
+ // This overrides the config override and even the explicit token env var.
1231
+ // The config will be marked as readOnly to prevent persisting it.
1232
+ overrideConfigApiToken(undefined)
1233
+ } else {
1234
+ // Lazily access constants.ENV.SOCKET_SECURITY_API_TOKEN.
1235
+ const tokenOverride = constants.ENV.SOCKET_SECURITY_API_TOKEN
1236
+ if (tokenOverride) {
1237
+ // This will set the token (even if there was a config override) and
1238
+ // set it to readOnly, making sure the temp token won't be persisted.
1239
+ overrideConfigApiToken(tokenOverride)
1240
+ }
1241
+ }
1242
+ if (configOverrideResult?.ok === false) {
1243
+ emitBanner(name)
1244
+ logger.logger.fail(configOverrideResult.message)
1245
+ process.exitCode = 2
1246
+ return
1247
+ }
1248
+
1249
+ // If we got at least some args, then lets find out if we can find a command.
1250
+ if (commandOrAliasName) {
1251
+ const alias = aliases[commandOrAliasName]
1252
+ // First: Resolve argv data from alias if its an alias that's been given.
1253
+ const [commandName, ...commandArgv] = alias
1254
+ ? [...alias.argv, ...rawCommandArgv]
1255
+ : [commandOrAliasName, ...rawCommandArgv]
1256
+ // Second: Find a command definition using that data.
1257
+ const commandDefinition = commandName ? subcommands[commandName] : undefined
1258
+ // Third: If a valid command has been found, then we run it...
1259
+ if (commandDefinition) {
1260
+ return await commandDefinition.run(commandArgv, importMeta, {
1261
+ parentName: name
1262
+ })
1263
+ }
1264
+ }
1265
+
1266
+ // ...else we provide basic instructions and help.
1267
+ if (!cli.flags['silent']) {
1268
+ emitBanner(name)
1269
+ }
1270
+ if (!cli.flags['help'] && cli.flags['dryRun']) {
1271
+ process.exitCode = 0
1272
+ // Lazily access constants.DRY_RUN_LABEL.
1273
+ logger.logger.log(
1274
+ `${constants.DRY_RUN_LABEL}: No-op, call a sub-command; ok`
1275
+ )
1276
+ } else {
1277
+ cli.showHelp()
1278
+ }
1279
+ }
1280
+
1281
+ /**
1282
+ * Note: meow will exit immediately if it calls its .showHelp()
1283
+ */
1284
+ function meowOrExit({
1285
+ allowUnknownFlags,
1286
+ // commands that pass-through args need to allow this
1287
+ argv,
1288
+ config,
1289
+ importMeta,
1290
+ parentName
1291
+ }) {
1292
+ const command = `${parentName} ${config.commandName}`
1293
+
1294
+ // This exits if .printHelp() is called either by meow itself or by us.
1295
+ const cli = vendor.meow({
1296
+ argv,
1297
+ description: config.description,
1298
+ help: config.help(command, config),
1299
+ importMeta,
1300
+ flags: config.flags,
1301
+ allowUnknownFlags: Boolean(allowUnknownFlags),
1302
+ autoHelp: false // otherwise we can't exit(0)
1303
+ })
1304
+ if (!cli.flags['silent']) {
1305
+ emitBanner(command)
1306
+ }
1307
+ if (cli.flags['help']) {
1308
+ cli.showHelp()
1309
+ }
1310
+ return cli
1311
+ }
1312
+ function emitBanner(name) {
1313
+ // Print a banner at the top of each command.
1314
+ // This helps with brand recognition and marketing.
1315
+ // It also helps with debugging since it contains version and command details.
1316
+ // Note: print over stderr to preserve stdout for flags like --json and
1317
+ // --markdown. If we don't do this, you can't use --json in particular
1318
+ // and pipe the result to other tools. By emitting the banner over stderr
1319
+ // you can do something like `socket scan view xyz | jq | process`.
1320
+ // The spinner also emits over stderr for example.
1321
+ logger.logger.error(getAsciiHeader(name))
1322
+ }
1323
+ function getAsciiHeader(command) {
1324
+ // Note: In tests we return <redacted> because otherwise snapshots will fail.
1325
+ const { REDACTED } = constants
1326
+ // Lazily access constants.ENV.VITEST.
1327
+ const redacting = constants.ENV.VITEST
1328
+ const cliVersion = redacting
1329
+ ? REDACTED
1330
+ : // Lazily access constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH.
1331
+ constants.ENV.INLINED_SOCKET_CLI_VERSION_HASH
1332
+ const nodeVersion = redacting ? REDACTED : process.version
1333
+ const apiToken = getDefaultToken()
1334
+ const defaultOrg = getConfigValueOrUndef('defaultOrg')
1335
+ const readOnlyConfig = isReadOnlyConfig() ? '*' : '.'
1336
+ const v1test = isTestingV1() ? ' (is testing v1)' : ''
1337
+ const feedback = isTestingV1()
1338
+ ? vendor.yoctocolorsCjsExports.green(
1339
+ ' (Thank you for testing the v1 bump! Please send us any feedback you might have!)\n'
1340
+ )
1341
+ : ''
1342
+ const shownToken = redacting
1343
+ ? REDACTED
1344
+ : apiToken
1345
+ ? getLastFiveOfApiToken(apiToken)
1346
+ : 'no'
1347
+ const relCwd = redacting
1348
+ ? REDACTED
1349
+ : path$1.normalizePath(
1350
+ process
1351
+ .cwd()
1352
+ .replace(
1353
+ new RegExp(
1354
+ `^${regexps.escapeRegExp(constants.homePath)}(?:${path.sep}|$)`,
1355
+ 'i'
1356
+ ),
1357
+ '~/'
1358
+ )
1359
+ )
1360
+ let nodeVerWarn = ''
1361
+ if ((vendor.semverExports.parse(constants.NODE_VERSION)?.major ?? 0) < 20) {
1362
+ nodeVerWarn += vendor.yoctocolorsCjsExports.bold(
1363
+ ` ${vendor.yoctocolorsCjsExports.red('Warning:')} NodeJS version 19 and lower will be ${vendor.yoctocolorsCjsExports.red('unsupported')} after April 30th, 2025.`
1364
+ )
1365
+ nodeVerWarn += '\n'
1366
+ nodeVerWarn +=
1367
+ ' Soon after the Socket CLI will require NodeJS version 20 or higher.'
1368
+ nodeVerWarn += '\n'
1369
+ }
1370
+ const body = `
1371
+ _____ _ _ /---------------
1372
+ | __|___ ___| |_ ___| |_ | Socket.dev CLI ver ${cliVersion}${v1test}
1373
+ |__ | ${readOnlyConfig} | _| '_| -_| _| | Node: ${nodeVersion}, API token set: ${shownToken}${defaultOrg ? `, default org: ${redacting ? REDACTED : defaultOrg}` : ''}
1374
+ |_____|___|___|_,_|___|_|.dev | Command: \`${command}\`, cwd: ${relCwd}`.trimStart()
1375
+ return ` ${body}\n${nodeVerWarn}${feedback}`
1376
+ }
1377
+
1378
+ async function suggestOrgSlug() {
1379
+ const sockSdk = await setupSdk()
1380
+ const result = await handleApiCall(
1381
+ sockSdk.getOrganizations(),
1382
+ 'list of organizations'
1383
+ )
1384
+
1385
+ // Ignore a failed request here. It was not the primary goal of
1386
+ // running this command and reporting it only leads to end-user confusion.
1387
+ if (result.ok) {
1388
+ const proceed = await prompts.select({
1389
+ message:
1390
+ 'Missing org name; do you want to use any of these orgs for this scan?',
1391
+ choices: [
1392
+ ...Object.values(result.data.organizations).map(org => {
1393
+ const name = org.name ?? org.slug
1394
+ return {
1395
+ name: `Yes [${name}]`,
1396
+ value: name,
1397
+ description: `Use "${name}" as the organization`
1398
+ }
1399
+ }),
1400
+ {
1401
+ name: 'No',
1402
+ value: '',
1403
+ description:
1404
+ 'Do not use any of these organizations (will end in a no-op)'
1405
+ }
1406
+ ]
1407
+ })
1408
+ if (proceed) {
1409
+ return proceed
1410
+ }
1411
+ } else {
1412
+ logger.logger.fail(
1413
+ 'Failed to lookup organization list from API, unable to suggest'
1414
+ )
1415
+ }
1416
+ }
1417
+
1418
+ async function determineOrgSlug(orgFlag, firstArg, interactive, dryRun) {
1419
+ const defaultOrgSlug = getConfigValueOrUndef('defaultOrg')
1420
+ let orgSlug = String(orgFlag || defaultOrgSlug || '')
1421
+ if (!orgSlug) {
1422
+ if (isTestingV1()) {
1423
+ // ask from server
1424
+ logger.logger.error(
1425
+ 'Missing the org slug and no --org flag set. Trying to auto-discover the org now...'
1426
+ )
1427
+ logger.logger.error(
1428
+ 'Note: you can set the default org slug to prevent this issue. You can also override all that with the --org flag.'
1429
+ )
1430
+ if (dryRun) {
1431
+ logger.logger.fail('Skipping auto-discovery of org in dry-run mode')
1432
+ } else if (!interactive) {
1433
+ logger.logger.fail(
1434
+ 'Skipping auto-discovery of org when interactive = false'
1435
+ )
1436
+ } else {
1437
+ orgSlug = (await suggestOrgSlug()) || ''
1438
+ }
1439
+ } else {
1440
+ orgSlug = firstArg || ''
1441
+ }
1442
+ }
1443
+ return [orgSlug, defaultOrgSlug]
1444
+ }
1445
+
1446
+ const { NODE_MODULES: NODE_MODULES$1, NPM: NPM$5, shadowBinPath } = constants
1447
+ function findBinPathDetailsSync(binName) {
1448
+ const binPaths =
1449
+ vendor.libExports$1.sync(binName, {
1450
+ all: true,
1451
+ nothrow: true
1452
+ }) ?? []
1453
+ let shadowIndex = -1
1454
+ let theBinPath
1455
+ for (let i = 0, { length } = binPaths; i < length; i += 1) {
1456
+ const binPath = binPaths[i]
1457
+ // Skip our bin directory if it's in the front.
1458
+ if (path.dirname(binPath) === shadowBinPath) {
1459
+ shadowIndex = i
1460
+ } else {
1461
+ theBinPath = npm.resolveBinPath(binPath)
1462
+ break
1463
+ }
1464
+ }
1465
+ return {
1466
+ name: binName,
1467
+ path: theBinPath,
1468
+ shadowed: shadowIndex !== -1
1469
+ }
1470
+ }
1471
+ function findNpmPathSync(npmBinPath) {
1472
+ // Lazily access constants.WIN32.
1473
+ const { WIN32 } = constants
1474
+ let thePath = npmBinPath
1475
+ while (true) {
1476
+ const libNmNpmPath = path.join(thePath, 'lib', NODE_MODULES$1, NPM$5)
1477
+ // mise puts its npm bin in a path like:
1478
+ // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/bin/npm.
1479
+ // HOWEVER, the location of the npm install is:
1480
+ // /Users/SomeUsername/.local/share/mise/installs/node/vX.X.X/lib/node_modules/npm.
1481
+ if (
1482
+ // Use existsSync here because statsSync, even with { throwIfNoEntry: false },
1483
+ // will throw an ENOTDIR error for paths like ./a-file-that-exists/a-directory-that-does-not.
1484
+ // See https://github.com/nodejs/node/issues/56993.
1485
+ fs.existsSync(libNmNpmPath) &&
1486
+ fs
1487
+ .statSync(libNmNpmPath, {
1488
+ throwIfNoEntry: false
1489
+ })
1490
+ ?.isDirectory()
1491
+ ) {
1492
+ thePath = path.join(libNmNpmPath, NPM$5)
1493
+ }
1494
+ const nmPath = path.join(thePath, NODE_MODULES$1)
1495
+ if (
1496
+ // npm bin paths may look like:
1497
+ // /usr/local/share/npm/bin/npm
1498
+ // /Users/SomeUsername/.nvm/versions/node/vX.X.X/bin/npm
1499
+ // C:\Users\SomeUsername\AppData\Roaming\npm\bin\npm.cmd
1500
+ // OR
1501
+ // C:\Program Files\nodejs\npm.cmd
1502
+ //
1503
+ // In practically all cases the npm path contains a node_modules folder:
1504
+ // /usr/local/share/npm/bin/npm/node_modules
1505
+ // C:\Program Files\nodejs\node_modules
1506
+ fs.existsSync(nmPath) &&
1507
+ fs
1508
+ .statSync(nmPath, {
1509
+ throwIfNoEntry: false
1510
+ })
1511
+ ?.isDirectory() &&
1512
+ // Optimistically look for the default location.
1513
+ (path.basename(thePath) === NPM$5 ||
1514
+ // Chocolatey installs npm bins in the same directory as node bins.
1515
+ (WIN32 && fs.existsSync(path.join(thePath, `${NPM$5}.cmd`))))
1516
+ ) {
1517
+ return thePath
1518
+ }
1519
+ const parent = path.dirname(thePath)
1520
+ if (parent === thePath) {
1521
+ return undefined
1522
+ }
1523
+ thePath = parent
1524
+ }
1525
+ }
1526
+ async function getPackageFilesForScan(cwd, inputPaths, supportedFiles, config) {
1527
+ debug.debugLog(
1528
+ `getPackageFilesForScan: resolving ${inputPaths.length} paths:\n`,
1529
+ inputPaths
1530
+ )
1531
+
1532
+ // Lazily access constants.spinner.
1533
+ const { spinner } = constants
1534
+ const patterns = pathsToGlobPatterns(inputPaths)
1535
+ spinner.start('Searching for local files to include in scan...')
1536
+ const entries = await globWithGitIgnore(patterns, {
1537
+ cwd,
1538
+ socketConfig: config
1539
+ })
1540
+ if (debug.isDebug()) {
1541
+ spinner.stop()
1542
+ debug.debugLog(
1543
+ `Resolved ${inputPaths.length} paths to ${entries.length} local paths:\n`,
1544
+ entries
1545
+ )
1546
+ spinner.start('Searching for files now...')
1547
+ } else {
1548
+ spinner.start(
1549
+ `Resolved ${inputPaths.length} paths to ${entries.length} local paths, searching for files now...`
1550
+ )
1551
+ }
1552
+ const packageFiles = await filterGlobResultToSupportedFiles(
1553
+ entries,
1554
+ supportedFiles
1555
+ )
1556
+ spinner.successAndStop(
1557
+ `Found ${packageFiles.length} local ${words.pluralize('file', packageFiles.length)}`
1558
+ )
1559
+ debug.debugLog('Absolute paths:\n', packageFiles)
1560
+ return packageFiles
1561
+ }
1562
+
1563
+ const { NODE_MODULES, NPM: NPM$4, NPX, SOCKET_CLI_ISSUES_URL } = constants
1564
+ function exitWithBinPathError(binName) {
1565
+ logger.logger.fail(
1566
+ `Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`
1567
+ )
1568
+ // The exit code 127 indicates that the command or binary being executed
1569
+ // could not be found.
1570
+ // eslint-disable-next-line n/no-process-exit
1571
+ process.exit(127)
1572
+ }
1573
+ let _npmBinPathDetails
1574
+ function getNpmBinPathDetails() {
1575
+ if (_npmBinPathDetails === undefined) {
1576
+ _npmBinPathDetails = findBinPathDetailsSync(NPM$4)
1577
+ }
1578
+ return _npmBinPathDetails
1579
+ }
1580
+ let _npxBinPathDetails
1581
+ function getNpxBinPathDetails() {
1582
+ if (_npxBinPathDetails === undefined) {
1583
+ _npxBinPathDetails = findBinPathDetailsSync(NPX)
1584
+ }
1585
+ return _npxBinPathDetails
1586
+ }
1587
+ function isNpmBinPathShadowed() {
1588
+ return getNpmBinPathDetails().shadowed
1589
+ }
1590
+ function isNpxBinPathShadowed() {
1591
+ return getNpxBinPathDetails().shadowed
1592
+ }
1593
+ let _npmBinPath
1594
+ function getNpmBinPath() {
1595
+ if (_npmBinPath === undefined) {
1596
+ _npmBinPath = getNpmBinPathDetails().path
1597
+ if (!_npmBinPath) {
1598
+ exitWithBinPathError(NPM$4)
1599
+ }
1600
+ }
1601
+ return _npmBinPath
1602
+ }
1603
+ let _npmPath
1604
+ function getNpmPath() {
1605
+ if (_npmPath === undefined) {
1606
+ const npmBinPath = getNpmBinPath()
1607
+ _npmPath = npmBinPath ? findNpmPathSync(npmBinPath) : undefined
1608
+ if (!_npmPath) {
1609
+ let message = 'Unable to find npm CLI install directory.'
1610
+ if (npmBinPath) {
1611
+ message += `\nSearched parent directories of ${path.dirname(npmBinPath)}.`
1612
+ }
1613
+ 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}.`
1614
+ logger.logger.fail(message)
1615
+ // The exit code 127 indicates that the command or binary being executed
1616
+ // could not be found.
1617
+ // eslint-disable-next-line n/no-process-exit
1618
+ process.exit(127)
1619
+ }
1620
+ }
1621
+ return _npmPath
1622
+ }
1623
+ let _npmRequire
1624
+ function getNpmRequire() {
1625
+ if (_npmRequire === undefined) {
1626
+ const npmPath = getNpmPath()
1627
+ const npmNmPath = path.join(npmPath, NODE_MODULES, NPM$4)
1628
+ _npmRequire = Module.createRequire(
1629
+ path.join(
1630
+ fs.existsSync(npmNmPath) ? npmNmPath : npmPath,
1631
+ '<dummy-basename>'
1632
+ )
1633
+ )
1634
+ }
1635
+ return _npmRequire
1636
+ }
1637
+ let _npxBinPath
1638
+ function getNpxBinPath() {
1639
+ if (_npxBinPath === undefined) {
1640
+ _npxBinPath = getNpxBinPathDetails().path
1641
+ if (!_npxBinPath) {
1642
+ exitWithBinPathError(NPX)
1643
+ }
1644
+ }
1645
+ return _npxBinPath
1646
+ }
1647
+
1648
+ const helpFlags = new Set(['--help', '-h'])
1649
+ function cmdFlagsToString(args) {
1650
+ const result = []
1651
+ for (let i = 0, { length } = args; i < length; i += 1) {
1652
+ if (args[i].startsWith('--')) {
1653
+ // Check if the next item exists and is NOT another flag.
1654
+ if (i + 1 < length && !args[i + 1].startsWith('--')) {
1655
+ result.push(`${args[i]}=${args[i + 1]}`)
1656
+ i += 1
1657
+ } else {
1658
+ result.push(args[i])
1659
+ }
1660
+ }
1661
+ }
1662
+ return result.join(' ')
1663
+ }
1664
+ function cmdPrefixMessage(cmdName, text) {
1665
+ const cmdPrefix = cmdName ? `${cmdName}: ` : ''
1666
+ return `${cmdPrefix}${text}`
1667
+ }
1668
+ function isHelpFlag(cmdArg) {
1669
+ return helpFlags.has(cmdArg)
1670
+ }
1671
+
1672
+ /**
1673
+ * Convert a Map<string, Map|string> to a nested object of similar shape.
1674
+ * The goal is to serialize it with JSON.stringify, which Map can't do.
1675
+ */
1676
+ function mapToObject(map) {
1677
+ return Object.fromEntries(
1678
+ Array.from(map.entries()).map(([k, v]) => [
1679
+ k,
1680
+ v instanceof Map ? mapToObject(v) : v
1681
+ ])
1682
+ )
1683
+ }
1684
+
1685
+ function* walkNestedMap(map, keys = []) {
1686
+ for (const [key, value] of map.entries()) {
1687
+ if (value instanceof Map) {
1688
+ yield* walkNestedMap(value, keys.concat(key))
1689
+ } else {
1690
+ yield {
1691
+ keys: keys.concat(key),
1692
+ value: value
1693
+ }
1694
+ }
1695
+ }
1696
+ }
1697
+
1698
+ const {
1699
+ ALERT_TYPE_CRITICAL_CVE,
1700
+ ALERT_TYPE_CVE,
1701
+ ALERT_TYPE_MEDIUM_CVE,
1702
+ ALERT_TYPE_MILD_CVE
1703
+ } = constants
1704
+ function isArtifactAlertCve(alert) {
1705
+ const { type } = alert
1706
+ return (
1707
+ type === ALERT_TYPE_CVE ||
1708
+ type === ALERT_TYPE_MEDIUM_CVE ||
1709
+ type === ALERT_TYPE_MILD_CVE ||
1710
+ type === ALERT_TYPE_CRITICAL_CVE
1711
+ )
1712
+ }
1713
+
1714
+ function createEnum(obj) {
1715
+ return Object.freeze({
1716
+ __proto__: null,
1717
+ ...obj
1718
+ })
1719
+ }
1720
+ function pick(input, keys) {
1721
+ const result = {}
1722
+ for (const key of keys) {
1723
+ result[key] = input[key]
1724
+ }
1725
+ return result
1726
+ }
1727
+
1728
+ const ALERT_FIX_TYPE = createEnum({
1729
+ cve: 'cve',
1730
+ remove: 'remove',
1731
+ upgrade: 'upgrade'
1732
+ })
1733
+
1734
+ function stringJoinWithSeparateFinalSeparator(list, separator = ' and ') {
1735
+ const values = list.filter(Boolean)
1736
+ const { length } = values
1737
+ if (!length) {
1738
+ return ''
1739
+ }
1740
+ if (length === 1) {
1741
+ return values[0]
1742
+ }
1743
+ const finalValue = values.pop()
1744
+ return `${values.join(', ')}${separator}${finalValue}`
1745
+ }
1746
+
1747
+ const ALERT_SEVERITY = createEnum({
1748
+ critical: 'critical',
1749
+ high: 'high',
1750
+ middle: 'middle',
1751
+ low: 'low'
1752
+ })
1753
+ // Ordered from most severe to least.
1754
+ const ALERT_SEVERITIES_SORTED = Object.freeze([
1755
+ 'critical',
1756
+ 'high',
1757
+ 'middle',
1758
+ 'low'
1759
+ ])
1760
+ function getDesiredSeverities(lowestToInclude) {
1761
+ const result = []
1762
+ for (const severity of ALERT_SEVERITIES_SORTED) {
1763
+ result.push(severity)
1764
+ if (severity === lowestToInclude) {
1765
+ break
1766
+ }
1767
+ }
1768
+ return result
1769
+ }
1770
+ function formatSeverityCount(severityCount) {
1771
+ const summary = []
1772
+ for (const severity of ALERT_SEVERITIES_SORTED) {
1773
+ if (severityCount[severity]) {
1774
+ summary.push(`${severityCount[severity]} ${severity}`)
1775
+ }
1776
+ }
1777
+ return stringJoinWithSeparateFinalSeparator(summary)
1778
+ }
1779
+ function getSeverityCount(issues, lowestToInclude) {
1780
+ const severityCount = pick(
1781
+ {
1782
+ low: 0,
1783
+ middle: 0,
1784
+ high: 0,
1785
+ critical: 0
1786
+ },
1787
+ getDesiredSeverities(lowestToInclude)
1788
+ )
1789
+ for (const issue of issues) {
1790
+ const { value } = issue
1791
+ if (!value) {
1792
+ continue
1793
+ }
1794
+ const { severity } = value
1795
+ if (severityCount[severity] !== undefined) {
1796
+ severityCount[severity] += 1
1797
+ }
1798
+ }
1799
+ return severityCount
1800
+ }
1801
+
1802
+ class ColorOrMarkdown {
1803
+ constructor(useMarkdown) {
1804
+ this.useMarkdown = !!useMarkdown
1805
+ }
1806
+ bold(text) {
1807
+ return this.useMarkdown
1808
+ ? `**${text}**`
1809
+ : vendor.yoctocolorsCjsExports.bold(`${text}`)
1810
+ }
1811
+ header(text, level = 1) {
1812
+ return this.useMarkdown
1813
+ ? `\n${''.padStart(level, '#')} ${text}\n`
1814
+ : vendor.yoctocolorsCjsExports.underline(
1815
+ `\n${level === 1 ? vendor.yoctocolorsCjsExports.bold(text) : text}\n`
1816
+ )
1817
+ }
1818
+ hyperlink(text, url, { fallback = true, fallbackToUrl } = {}) {
1819
+ if (url) {
1820
+ return this.useMarkdown
1821
+ ? `[${text}](${url})`
1822
+ : vendor.terminalLinkExports(text, url, {
1823
+ fallback: fallbackToUrl ? (_text, url) => url : fallback
1824
+ })
1825
+ }
1826
+ return text
1827
+ }
1828
+ indent(...args) {
1829
+ return vendor.indentStringExports(...args)
1830
+ }
1831
+ italic(text) {
1832
+ return this.useMarkdown
1833
+ ? `_${text}_`
1834
+ : vendor.yoctocolorsCjsExports.italic(`${text}`)
1835
+ }
1836
+ json(value) {
1837
+ return this.useMarkdown
1838
+ ? '```json\n' + JSON.stringify(value) + '\n```'
1839
+ : JSON.stringify(value)
1840
+ }
1841
+ list(items) {
1842
+ const indentedContent = items.map(item => this.indent(item).trimStart())
1843
+ return this.useMarkdown
1844
+ ? `* ${indentedContent.join('\n* ')}\n`
1845
+ : `${indentedContent.join('\n')}\n`
1846
+ }
1847
+ }
1848
+
1849
+ function getSocketDevAlertUrl(alertType) {
1850
+ return `https://socket.dev/alerts/${alertType}`
1851
+ }
1852
+ function getSocketDevPackageOverviewUrl(eco, name, version) {
1853
+ return `https://socket.dev/${eco}/package/${name}${version ? `/overview/${version}` : ''}`
1854
+ }
1855
+
1856
+ const require$1 = Module.createRequire(
1857
+ require('u' + 'rl').pathToFileURL(__filename).href
1858
+ )
1859
+ let _translations
1860
+ function getTranslations() {
1861
+ if (_translations === undefined) {
1862
+ _translations = require$1(
1863
+ // Lazily access constants.rootPath.
1864
+ path.join(constants.rootPath, 'translations.json')
1865
+ )
1866
+ }
1867
+ return _translations
1868
+ }
1869
+
1870
+ function idToPurl(id) {
1871
+ return `pkg:npm/${id}`
1872
+ }
1873
+ function resolvePackageVersion(purlObj) {
1874
+ const { version } = purlObj
1875
+ return version
1876
+ ? (vendor.semverExports.coerce(stripPeerSuffix(version))?.version ?? '')
1877
+ : ''
1878
+ }
1879
+ function stripLeadingSlash(path) {
1880
+ return path.startsWith('/') ? path.slice(1) : path
1881
+ }
1882
+ function stripPeerSuffix(depPath) {
1883
+ const idx = depPath.indexOf('(')
1884
+ return idx === -1 ? depPath : depPath.slice(0, idx)
1885
+ }
1886
+
1887
+ const ALERT_SEVERITY_COLOR = createEnum({
1888
+ critical: 'magenta',
1889
+ high: 'red',
1890
+ middle: 'yellow',
1891
+ low: 'white'
1892
+ })
1893
+ const ALERT_SEVERITY_ORDER = createEnum({
1894
+ critical: 0,
1895
+ high: 1,
1896
+ middle: 2,
1897
+ low: 3,
1898
+ none: 4
1899
+ })
1900
+ const { CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER, NPM: NPM$3 } =
1901
+ constants
1902
+ const MIN_ABOVE_THE_FOLD_COUNT = 3
1903
+ const MIN_ABOVE_THE_FOLD_ALERT_COUNT = 1
1904
+ const format = new ColorOrMarkdown(false)
1905
+ function alertsHaveBlocked(alerts) {
1906
+ return alerts.find(a => a.blocked) !== undefined
1907
+ }
1908
+ function alertsHaveSeverity(alerts, severity) {
1909
+ return alerts.find(a => a.raw.severity === severity) !== undefined
1910
+ }
1911
+ function alertSeverityComparator(a, b) {
1912
+ return getAlertSeverityOrder(a) - getAlertSeverityOrder(b)
1913
+ }
1914
+ function getAlertSeverityOrder(alert) {
1915
+ const { severity } = alert.raw
1916
+ return severity === ALERT_SEVERITY.critical
1917
+ ? 0
1918
+ : severity === ALERT_SEVERITY.high
1919
+ ? 1
1920
+ : severity === ALERT_SEVERITY.middle
1921
+ ? 2
1922
+ : severity === ALERT_SEVERITY.low
1923
+ ? 3
1924
+ : 4
1925
+ }
1926
+ function getAlertsSeverityOrder(alerts) {
1927
+ return alertsHaveBlocked(alerts) ||
1928
+ alertsHaveSeverity(alerts, ALERT_SEVERITY.critical)
1929
+ ? 0
1930
+ : alertsHaveSeverity(alerts, ALERT_SEVERITY.high)
1931
+ ? 1
1932
+ : alertsHaveSeverity(alerts, ALERT_SEVERITY.middle)
1933
+ ? 2
1934
+ : alertsHaveSeverity(alerts, ALERT_SEVERITY.low)
1935
+ ? 3
1936
+ : 4
1937
+ }
1938
+ function getHiddenRiskCounts(hiddenAlerts) {
1939
+ const riskCounts = {
1940
+ critical: 0,
1941
+ high: 0,
1942
+ middle: 0,
1943
+ low: 0
1944
+ }
1945
+ for (const alert of hiddenAlerts) {
1946
+ switch (getAlertSeverityOrder(alert)) {
1947
+ case ALERT_SEVERITY_ORDER.critical:
1948
+ riskCounts.critical += 1
1949
+ break
1950
+ case ALERT_SEVERITY_ORDER.high:
1951
+ riskCounts.high += 1
1952
+ break
1953
+ case ALERT_SEVERITY_ORDER.middle:
1954
+ riskCounts.middle += 1
1955
+ break
1956
+ case ALERT_SEVERITY_ORDER.low:
1957
+ riskCounts.low += 1
1958
+ break
1959
+ }
1960
+ }
1961
+ return riskCounts
1962
+ }
1963
+ function getHiddenRisksDescription(riskCounts) {
1964
+ const descriptions = []
1965
+ if (riskCounts.critical) {
1966
+ descriptions.push(`${riskCounts.critical} ${getSeverityLabel('critical')}`)
1967
+ }
1968
+ if (riskCounts.high) {
1969
+ descriptions.push(`${riskCounts.high} ${getSeverityLabel('high')}`)
1970
+ }
1971
+ if (riskCounts.middle) {
1972
+ descriptions.push(`${riskCounts.middle} ${getSeverityLabel('middle')}`)
1973
+ }
1974
+ if (riskCounts.low) {
1975
+ descriptions.push(`${riskCounts.low} ${getSeverityLabel('low')}`)
1976
+ }
1977
+ return `(${descriptions.join('; ')})`
1978
+ }
1979
+ function getSeverityLabel(severity) {
1980
+ return severity === 'middle' ? 'moderate' : severity
1981
+ }
1982
+ async function addArtifactToAlertsMap(artifact, alertsByPkgId, options) {
1983
+ // Make TypeScript happy.
1984
+ if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
1985
+ return alertsByPkgId
1986
+ }
1987
+ const {
1988
+ consolidate = false,
1989
+ include: _include,
1990
+ overrides
1991
+ } = {
1992
+ __proto__: null,
1993
+ ...options
1994
+ }
1995
+ const include = {
1996
+ __proto__: null,
1997
+ blocked: true,
1998
+ critical: true,
1999
+ cve: true,
2000
+ unfixable: true,
2001
+ upgradable: false,
2002
+ ..._include
2003
+ }
2004
+ const name = packages.resolvePackageName(artifact)
2005
+ const { version } = artifact
2006
+ const pkgId = `${name}@${version}`
2007
+ const major = vendor.semverExports.major(version)
2008
+ const socketYml = findSocketYmlSync()
2009
+ const enabledState = {
2010
+ __proto__: null,
2011
+ ...socketYml?.parsed.issueRules
2012
+ }
2013
+ let sockPkgAlerts = []
2014
+ for (const alert of artifact.alerts) {
2015
+ const action = alert.action ?? ''
2016
+ const enabledFlag = enabledState[alert.type]
2017
+ if (
2018
+ (action === 'ignore' && enabledFlag !== true) ||
2019
+ enabledFlag === false
2020
+ ) {
2021
+ continue
2022
+ }
2023
+ const blocked = action === 'error'
2024
+ const critical = alert.severity === ALERT_SEVERITY.critical
2025
+ const cve = isArtifactAlertCve(alert)
2026
+ const fixType = alert.fix?.type ?? ''
2027
+ const fixableCve = fixType === ALERT_FIX_TYPE.cve
2028
+ const fixableUpgrade = fixType === ALERT_FIX_TYPE.upgrade
2029
+ const fixable = fixableCve || fixableUpgrade
2030
+ const upgradable = fixableUpgrade && !objects.hasOwn(overrides, name)
2031
+ if (
2032
+ (include.blocked && blocked) ||
2033
+ (include.critical && critical) ||
2034
+ (include.cve && cve) ||
2035
+ (include.unfixable && !fixable) ||
2036
+ (include.upgradable && upgradable)
2037
+ ) {
2038
+ sockPkgAlerts.push({
2039
+ name,
2040
+ version,
2041
+ key: alert.key,
2042
+ type: alert.type,
2043
+ blocked,
2044
+ critical,
2045
+ fixable,
2046
+ raw: alert,
2047
+ upgradable
2048
+ })
2049
+ }
2050
+ }
2051
+ if (!sockPkgAlerts.length) {
2052
+ return alertsByPkgId
2053
+ }
2054
+ if (consolidate) {
2055
+ const highestForCve = new Map()
2056
+ const highestForUpgrade = new Map()
2057
+ const unfixableAlerts = []
2058
+ for (const sockPkgAlert of sockPkgAlerts) {
2059
+ const alert = sockPkgAlert.raw
2060
+ const fixType = alert.fix?.type ?? ''
2061
+ if (fixType === ALERT_FIX_TYPE.cve) {
2062
+ const patchedVersion =
2063
+ alert.props[CVE_ALERT_PROPS_FIRST_PATCHED_VERSION_IDENTIFIER]
2064
+ const patchedMajor = vendor.semverExports.major(patchedVersion)
2065
+ const oldHighest = highestForCve.get(patchedMajor)
2066
+ const highest = oldHighest?.version ?? '0.0.0'
2067
+ if (vendor.semverExports.gt(patchedVersion, highest)) {
2068
+ highestForCve.set(patchedMajor, {
2069
+ alert: sockPkgAlert,
2070
+ version: patchedVersion
2071
+ })
2072
+ }
2073
+ } else if (fixType === ALERT_FIX_TYPE.upgrade) {
2074
+ const oldHighest = highestForUpgrade.get(major)
2075
+ const highest = oldHighest?.version ?? '0.0.0'
2076
+ if (vendor.semverExports.gt(version, highest)) {
2077
+ highestForUpgrade.set(major, {
2078
+ alert: sockPkgAlert,
2079
+ version
2080
+ })
2081
+ }
2082
+ } else {
2083
+ unfixableAlerts.push(sockPkgAlert)
2084
+ }
2085
+ }
2086
+ sockPkgAlerts = [
2087
+ ...unfixableAlerts,
2088
+ ...[...highestForCve.values()].map(d => d.alert),
2089
+ ...[...highestForUpgrade.values()].map(d => d.alert)
2090
+ ]
2091
+ }
2092
+ if (sockPkgAlerts.length) {
2093
+ sockPkgAlerts.sort((a, b) => sorts.naturalCompare(a.type, b.type))
2094
+ alertsByPkgId.set(pkgId, sockPkgAlerts)
2095
+ }
2096
+ return alertsByPkgId
2097
+ }
2098
+ function getCveInfoByAlertsMap(alertsMap, options) {
2099
+ const { exclude: _exclude, limit = Infinity } = {
2100
+ __proto__: null,
2101
+ ...options
2102
+ }
2103
+ const exclude = {
2104
+ __proto__: null,
2105
+ upgradable: true,
2106
+ ..._exclude
2107
+ }
2108
+ let count = 0
2109
+ let infoByPkg = null
2110
+ alertsMapLoop: for (const [pkgId, sockPkgAlerts] of alertsMap) {
2111
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2112
+ idToPurl(pkgId)
2113
+ )
2114
+ const name = packages.resolvePackageName(purlObj)
2115
+ for (const sockPkgAlert of sockPkgAlerts) {
2116
+ const alert = sockPkgAlert.raw
2117
+ if (
2118
+ alert.fix?.type !== ALERT_FIX_TYPE.cve ||
2119
+ (exclude.upgradable && registry.getManifestData(NPM$3, name))
2120
+ ) {
2121
+ continue
2122
+ }
2123
+ if (!infoByPkg) {
2124
+ infoByPkg = new Map()
2125
+ }
2126
+ let infos = infoByPkg.get(name)
2127
+ if (!infos) {
2128
+ infos = []
2129
+ infoByPkg.set(name, infos)
2130
+ }
2131
+ const { firstPatchedVersionIdentifier, vulnerableVersionRange } =
2132
+ alert.props
2133
+ try {
2134
+ infos.push({
2135
+ firstPatchedVersionIdentifier,
2136
+ vulnerableVersionRange: new vendor.semverExports.Range(
2137
+ // Replace ', ' in a range like '>= 1.0.0, < 1.8.2' with ' ' so that
2138
+ // semver.Range will parse it without erroring.
2139
+ vulnerableVersionRange.replace(/, +/g, ' ')
2140
+ ).format()
2141
+ })
2142
+ if (++count >= limit) {
2143
+ break alertsMapLoop
2144
+ }
2145
+ } catch (e) {
2146
+ debug.debugLog('getCveInfoByAlertsMap', {
2147
+ firstPatchedVersionIdentifier,
2148
+ vulnerableVersionRange
2149
+ })
2150
+ debug.debugLog(e)
2151
+ }
2152
+ }
2153
+ }
2154
+ return infoByPkg
2155
+ }
2156
+ function logAlertsMap(alertsMap, options) {
2157
+ const { hideAt = 'middle', output = process.stderr } = {
2158
+ __proto__: null,
2159
+ ...options
2160
+ }
2161
+ const translations = getTranslations()
2162
+ const sortedEntries = [...alertsMap.entries()].sort(
2163
+ (a, b) => getAlertsSeverityOrder(a[1]) - getAlertsSeverityOrder(b[1])
2164
+ )
2165
+ const aboveTheFoldPkgIds = new Set()
2166
+ const viewableAlertsByPkgId = new Map()
2167
+ const hiddenAlertsByPkgId = new Map()
2168
+ for (let i = 0, { length } = sortedEntries; i < length; i += 1) {
2169
+ const { 0: pkgId, 1: alerts } = sortedEntries[i]
2170
+ const hiddenAlerts = []
2171
+ const viewableAlerts = alerts.filter(a => {
2172
+ const keep =
2173
+ a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER[hideAt]
2174
+ if (!keep) {
2175
+ hiddenAlerts.push(a)
2176
+ }
2177
+ return keep
2178
+ })
2179
+ if (hiddenAlerts.length) {
2180
+ hiddenAlertsByPkgId.set(pkgId, hiddenAlerts.sort(alertSeverityComparator))
2181
+ }
2182
+ if (!viewableAlerts.length) {
2183
+ continue
2184
+ }
2185
+ viewableAlerts.sort(alertSeverityComparator)
2186
+ viewableAlertsByPkgId.set(pkgId, viewableAlerts)
2187
+ if (
2188
+ viewableAlerts.find(
2189
+ a => a.blocked || getAlertSeverityOrder(a) < ALERT_SEVERITY_ORDER.middle
2190
+ )
2191
+ ) {
2192
+ aboveTheFoldPkgIds.add(pkgId)
2193
+ }
2194
+ }
2195
+
2196
+ // If MIN_ABOVE_THE_FOLD_COUNT is NOT met add more from viewable pkg ids.
2197
+ for (const { 0: pkgId } of viewableAlertsByPkgId.entries()) {
2198
+ if (aboveTheFoldPkgIds.size >= MIN_ABOVE_THE_FOLD_COUNT) {
2199
+ break
2200
+ }
2201
+ aboveTheFoldPkgIds.add(pkgId)
2202
+ }
2203
+ // If MIN_ABOVE_THE_FOLD_COUNT is STILL NOT met add more from hidden pkg ids.
2204
+ for (const { 0: pkgId, 1: hiddenAlerts } of hiddenAlertsByPkgId.entries()) {
2205
+ if (aboveTheFoldPkgIds.size >= MIN_ABOVE_THE_FOLD_COUNT) {
2206
+ break
2207
+ }
2208
+ aboveTheFoldPkgIds.add(pkgId)
2209
+ const viewableAlerts = viewableAlertsByPkgId.get(pkgId) ?? []
2210
+ if (viewableAlerts.length < MIN_ABOVE_THE_FOLD_ALERT_COUNT) {
2211
+ const neededCount = MIN_ABOVE_THE_FOLD_ALERT_COUNT - viewableAlerts.length
2212
+ let removedHiddenAlerts
2213
+ if (hiddenAlerts.length - neededCount > 0) {
2214
+ removedHiddenAlerts = hiddenAlerts.splice(
2215
+ 0,
2216
+ MIN_ABOVE_THE_FOLD_ALERT_COUNT
2217
+ )
2218
+ } else {
2219
+ removedHiddenAlerts = hiddenAlerts
2220
+ hiddenAlertsByPkgId.delete(pkgId)
2221
+ }
2222
+ viewableAlertsByPkgId.set(pkgId, [
2223
+ ...viewableAlerts,
2224
+ ...removedHiddenAlerts
2225
+ ])
2226
+ }
2227
+ }
2228
+ const mentionedPkgIdsWithHiddenAlerts = new Set()
2229
+ for (
2230
+ let i = 0,
2231
+ prevAboveTheFold = true,
2232
+ entries = [...viewableAlertsByPkgId.entries()],
2233
+ { length } = entries;
2234
+ i < length;
2235
+ i += 1
2236
+ ) {
2237
+ const { 0: pkgId, 1: alerts } = entries[i]
2238
+ const lines = new Set()
2239
+ for (const alert of alerts) {
2240
+ const { type } = alert
2241
+ const severity = alert.raw.severity ?? ''
2242
+ const attributes = [
2243
+ ...(severity
2244
+ ? [
2245
+ vendor.yoctocolorsCjsExports[ALERT_SEVERITY_COLOR[severity]](
2246
+ getSeverityLabel(severity)
2247
+ )
2248
+ ]
2249
+ : []),
2250
+ ...(alert.blocked
2251
+ ? [
2252
+ vendor.yoctocolorsCjsExports.bold(
2253
+ vendor.yoctocolorsCjsExports.red('blocked')
2254
+ )
2255
+ ]
2256
+ : []),
2257
+ ...(alert.fixable ? ['fixable'] : [])
2258
+ ]
2259
+ const maybeAttributes = attributes.length
2260
+ ? ` ${vendor.yoctocolorsCjsExports.italic(`(${attributes.join('; ')})`)}`
2261
+ : ''
2262
+ // Based data from { pageProps: { alertTypes } } of:
2263
+ // https://socket.dev/_next/data/94666139314b6437ee4491a0864e72b264547585/en-US.json
2264
+ const info = translations.alerts[type]
2265
+ const title = info?.title ?? type
2266
+ const maybeDesc = info?.description ? ` - ${info.description}` : ''
2267
+ const content = `${title}${maybeAttributes}${maybeDesc}`
2268
+ // TODO: emoji seems to mis-align terminals sometimes
2269
+ lines.add(` ${content}`)
2270
+ }
2271
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2272
+ idToPurl(pkgId)
2273
+ )
2274
+ const hyperlink = format.hyperlink(
2275
+ pkgId,
2276
+ getSocketDevPackageOverviewUrl(
2277
+ NPM$3,
2278
+ packages.resolvePackageName(purlObj),
2279
+ purlObj.version
2280
+ )
2281
+ )
2282
+ const isAboveTheFold = aboveTheFoldPkgIds.has(pkgId)
2283
+ if (isAboveTheFold) {
2284
+ aboveTheFoldPkgIds.add(pkgId)
2285
+ output.write(`${i ? '\n' : ''}${hyperlink}:\n`)
2286
+ } else {
2287
+ output.write(`${prevAboveTheFold ? '\n' : ''}${hyperlink}:\n`)
2288
+ }
2289
+ for (const line of lines) {
2290
+ output.write(`${line}\n`)
2291
+ }
2292
+ const hiddenAlerts = hiddenAlertsByPkgId.get(pkgId) ?? []
2293
+ const { length: hiddenAlertsCount } = hiddenAlerts
2294
+ if (hiddenAlertsCount) {
2295
+ mentionedPkgIdsWithHiddenAlerts.add(pkgId)
2296
+ if (hiddenAlertsCount === 1) {
2297
+ output.write(
2298
+ ` ${vendor.yoctocolorsCjsExports.dim(`+1 Hidden ${getSeverityLabel(hiddenAlerts[0].raw.severity ?? 'low')} risk alert`)}\n`
2299
+ )
2300
+ } else {
2301
+ output.write(
2302
+ ` ${vendor.yoctocolorsCjsExports.dim(`+${hiddenAlertsCount} Hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(getHiddenRiskCounts(hiddenAlerts)))}`)}\n`
2303
+ )
2304
+ }
2305
+ }
2306
+ prevAboveTheFold = isAboveTheFold
2307
+ }
2308
+ const additionalHiddenCount =
2309
+ hiddenAlertsByPkgId.size - mentionedPkgIdsWithHiddenAlerts.size
2310
+ if (additionalHiddenCount) {
2311
+ const totalRiskCounts = {
2312
+ critical: 0,
2313
+ high: 0,
2314
+ middle: 0,
2315
+ low: 0
2316
+ }
2317
+ for (const { 0: pkgId, 1: alerts } of hiddenAlertsByPkgId.entries()) {
2318
+ if (mentionedPkgIdsWithHiddenAlerts.has(pkgId)) {
2319
+ continue
2320
+ }
2321
+ const riskCounts = getHiddenRiskCounts(alerts)
2322
+ totalRiskCounts.critical += riskCounts.critical
2323
+ totalRiskCounts.high += riskCounts.high
2324
+ totalRiskCounts.middle += riskCounts.middle
2325
+ totalRiskCounts.low += riskCounts.low
2326
+ }
2327
+ output.write(
2328
+ `${aboveTheFoldPkgIds.size ? '\n' : ''}${vendor.yoctocolorsCjsExports.dim(`${aboveTheFoldPkgIds.size ? '+' : ''}${additionalHiddenCount} Packages with hidden alerts ${vendor.yoctocolorsCjsExports.italic(getHiddenRisksDescription(totalRiskCounts))}`)}\n`
2329
+ )
2330
+ }
2331
+ output.write('\n')
2332
+ }
2333
+
2334
+ const RangeStyles = ['caret', 'gt', 'lt', 'pin', 'preserve', 'tilde']
2335
+ function applyRange(refRange, version, style = 'preserve') {
2336
+ switch (style) {
2337
+ case 'caret':
2338
+ return `^${version}`
2339
+ case 'gt':
2340
+ return `>${version}`
2341
+ case 'gte':
2342
+ return `>=${version}`
2343
+ case 'lt':
2344
+ return `<${version}`
2345
+ case 'lte':
2346
+ return `<=${version}`
2347
+ case 'preserve': {
2348
+ const range = new vendor.semverExports.Range(refRange)
2349
+ const { raw } = range
2350
+ const comparators = [...range.set].flat()
2351
+ const { length } = comparators
2352
+ if (length === 1) {
2353
+ const char = /^[<>]=?/.exec(raw)?.[0]
2354
+ if (char) {
2355
+ return `${char}${version}`
2356
+ }
2357
+ } else if (length === 2) {
2358
+ const char = /^[~^]/.exec(raw)?.[0]
2359
+ if (char) {
2360
+ return `${char}${version}`
2361
+ }
2362
+ }
2363
+ return version
2364
+ }
2365
+ case 'tilde':
2366
+ return `~${version}`
2367
+ case 'pin':
2368
+ default:
2369
+ return version
2370
+ }
2371
+ }
2372
+ function getMajor(version) {
2373
+ const coerced = vendor.semverExports.coerce(version)
2374
+ if (coerced) {
2375
+ try {
2376
+ return vendor.semverExports.major(coerced)
2377
+ } catch (e) {
2378
+ debug.debugLog(`Error parsing '${version}':\n`, e)
2379
+ }
2380
+ }
2381
+ return null
2382
+ }
2383
+
2384
+ function extractPurlsFromPnpmLockfileV6(lockfile) {
2385
+ const deps = new Set()
2386
+ for (const importer of Object.values(lockfile.importers || {})) {
2387
+ if (importer.dependencies) {
2388
+ for (const { 0: alias, 1: ref } of Object.entries(
2389
+ importer.dependencies
2390
+ )) {
2391
+ const id = resolvePnpmPackageId(alias, ref)
2392
+ if (id) {
2393
+ deps.add(idToPurl(id))
2394
+ }
2395
+ }
2396
+ }
2397
+ if (importer.devDependencies) {
2398
+ for (const { 0: alias, 1: ref } of Object.entries(
2399
+ importer.devDependencies
2400
+ )) {
2401
+ const id = resolvePnpmPackageId(alias, ref)
2402
+ if (id) {
2403
+ deps.add(idToPurl(id))
2404
+ }
2405
+ }
2406
+ }
2407
+ if (importer.optionalDependencies) {
2408
+ for (const { 0: alias, 1: ref } of Object.entries(
2409
+ importer.optionalDependencies
2410
+ )) {
2411
+ const id = resolvePnpmPackageId(alias, ref)
2412
+ if (id) {
2413
+ deps.add(idToPurl(id))
2414
+ }
2415
+ }
2416
+ }
2417
+ }
2418
+ if (lockfile.packages) {
2419
+ for (const pkgPath of Object.keys(lockfile.packages)) {
2420
+ const id = resolvePnpmPackageIdFromPath(pkgPath, '')
2421
+ if (id) {
2422
+ deps.add(idToPurl(id))
2423
+ }
2424
+ }
2425
+ }
2426
+ return Array.from(deps)
2427
+ }
2428
+ function extractPurlsFromPnpmLockfileV9(lockfile) {
2429
+ const depTypes = vendor.libExports$2.detectDepTypes(lockfile)
2430
+ return Object.keys(depTypes).map(refId => {
2431
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2432
+ idToPurl(refId)
2433
+ )
2434
+ const name = packages.resolvePackageName(purlObj)
2435
+ const version = resolvePackageVersion(purlObj)
2436
+ return idToPurl(`${name}@${version}`)
2437
+ })
2438
+ }
2439
+ function extractPurlsFromPnpmLockfile(lockfile) {
2440
+ return parsePnpmLockfileVersion(lockfile.lockfileVersion).major <= 6
2441
+ ? extractPurlsFromPnpmLockfileV6(lockfile)
2442
+ : extractPurlsFromPnpmLockfileV9(lockfile)
2443
+ }
2444
+ function parsePnpmLockfileVersion(version) {
2445
+ return vendor.semverExports.coerce(version)
2446
+ }
2447
+ function resolvePnpmPackageId(alias, ref) {
2448
+ return ref.startsWith('/')
2449
+ ? resolvePnpmPackageIdFromPath(ref, alias)
2450
+ : `${alias}@${stripPeerSuffix(ref)}`
2451
+ }
2452
+ function resolvePnpmPackageIdFromPath(ref, alias) {
2453
+ const relative = vendor.libExports$3.refToRelative(ref, alias)
2454
+ if (relative) {
2455
+ const id = stripLeadingSlash(relative)
2456
+ const purlObj = vendor.packageurlJsExports.PackageURL.fromString(
2457
+ idToPurl(id)
2458
+ )
2459
+ const name = packages.resolvePackageName(purlObj)
2460
+ const version = resolvePackageVersion(purlObj)
2461
+ return `${name}@${version}`
2462
+ }
2463
+ return null
2464
+ }
2465
+
2466
+ async function getAlertsMapFromPnpmLockfile(lockfile, options_) {
2467
+ const options = {
2468
+ __proto__: null,
2469
+ consolidate: false,
2470
+ limit: Infinity,
2471
+ nothrow: false,
2472
+ ...options_
2473
+ }
2474
+ const purls = extractPurlsFromPnpmLockfile(lockfile)
2475
+ return await getAlertsMapFromPurls(purls, {
2476
+ overrides: lockfile.overrides,
2477
+ ...options
2478
+ })
2479
+ }
2480
+ async function getAlertsMapFromPurls(purls, options_) {
2481
+ const options = {
2482
+ __proto__: null,
2483
+ consolidate: false,
2484
+ nothrow: false,
2485
+ ...options_
2486
+ }
2487
+ const include = {
2488
+ __proto__: null,
2489
+ actions: undefined,
2490
+ blocked: true,
2491
+ critical: true,
2492
+ cve: true,
2493
+ existing: false,
2494
+ unfixable: true,
2495
+ upgradable: false,
2496
+ ...options.include
2497
+ }
2498
+ const { spinner } = options
2499
+ const uniqPurls = arrays.arrayUnique(purls)
2500
+ let { length: remaining } = uniqPurls
2501
+ const alertsByPkgId = new Map()
2502
+ if (!remaining) {
2503
+ return alertsByPkgId
2504
+ }
2505
+ const getText = () => `Looking up data for ${remaining} packages`
2506
+ spinner?.start(getText())
2507
+ const sockSdk = await setupSdk(getPublicToken())
2508
+ const toAlertsMapOptions = {
2509
+ overrides: options.overrides,
2510
+ consolidate: options.consolidate,
2511
+ include,
2512
+ spinner
2513
+ }
2514
+ for await (const batchResult of sockSdk.batchPackageStream(
2515
+ {
2516
+ alerts: 'true',
2517
+ compact: 'true',
2518
+ ...(include.actions
2519
+ ? {
2520
+ actions: include.actions.join(',')
2521
+ }
2522
+ : {}),
2523
+ ...(include.unfixable
2524
+ ? {}
2525
+ : {
2526
+ fixable: 'true'
2527
+ })
2528
+ },
2529
+ {
2530
+ components: uniqPurls.map(purl => ({
2531
+ purl
2532
+ }))
2533
+ }
2534
+ )) {
2535
+ if (batchResult.success) {
2536
+ await addArtifactToAlertsMap(
2537
+ batchResult.data,
2538
+ alertsByPkgId,
2539
+ toAlertsMapOptions
2540
+ )
2541
+ } else if (!options.nothrow) {
2542
+ const statusCode = batchResult.status ?? 'unknown'
2543
+ const statusMessage = batchResult.error ?? 'No status message'
2544
+ throw new Error(
2545
+ `Socket API server error (${statusCode}): ${statusMessage}`
2546
+ )
2547
+ }
2548
+ remaining -= 1
2549
+ if (spinner && remaining > 0) {
2550
+ spinner.start()
2551
+ spinner.setText(getText())
2552
+ }
2553
+ }
2554
+ spinner?.stop()
2555
+ return alertsByPkgId
2556
+ }
2557
+
2558
+ const {
2559
+ NPM: NPM$2,
2560
+ SOCKET_CLI_SAFE_BIN,
2561
+ SOCKET_CLI_SAFE_PROGRESS,
2562
+ SOCKET_IPC_HANDSHAKE
2563
+ } = constants
2564
+ function safeNpmInstall(options) {
2565
+ const {
2566
+ agentExecPath = getNpmBinPath(),
2567
+ args = [],
2568
+ ipc,
2569
+ spinner,
2570
+ ...spawnOptions
2571
+ } = {
2572
+ __proto__: null,
2573
+ ...options
2574
+ }
2575
+ let stdio = spawnOptions.stdio
2576
+ const useIpc = objects.isObject(ipc)
2577
+ // Include 'ipc' in the spawnOptions.stdio when an options.ipc object is provided.
2578
+ // See https://github.com/nodejs/node/blob/v23.6.0/lib/child_process.js#L161-L166
2579
+ // and https://github.com/nodejs/node/blob/v23.6.0/lib/internal/child_process.js#L238.
2580
+ if (typeof stdio === 'string') {
2581
+ stdio = useIpc ? [stdio, stdio, stdio, 'ipc'] : [stdio, stdio, stdio]
2582
+ } else if (useIpc && Array.isArray(stdio) && !stdio.includes('ipc')) {
2583
+ stdio = stdio.concat('ipc')
2584
+ }
2585
+ const useDebug = debug.isDebug()
2586
+ const terminatorPos = args.indexOf('--')
2587
+ const rawBinArgs = terminatorPos === -1 ? args : args.slice(0, terminatorPos)
2588
+ const progressArg =
2589
+ rawBinArgs.findLast(npm.isProgressFlag) !== '--no-progress'
2590
+ const binArgs = rawBinArgs.filter(
2591
+ a => !npm.isAuditFlag(a) && !npm.isFundFlag(a) && !npm.isProgressFlag(a)
2592
+ )
2593
+ const otherArgs = terminatorPos === -1 ? [] : args.slice(terminatorPos)
2594
+ const isSilent = !useDebug && !binArgs.some(npm.isLoglevelFlag)
2595
+ const logLevelArgs = isSilent ? ['--loglevel', 'silent'] : []
2596
+ const spawnPromise = spawn.spawn(
2597
+ // Lazily access constants.execPath.
2598
+ constants.execPath,
2599
+ [
2600
+ // Lazily access constants.nodeHardenFlags.
2601
+ ...constants.nodeHardenFlags,
2602
+ // Lazily access constants.nodeNoWarningsFlags.
2603
+ ...constants.nodeNoWarningsFlags,
2604
+ // Lazily access constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD.
2605
+ ...(constants.ENV.INLINED_SOCKET_CLI_SENTRY_BUILD
2606
+ ? [
2607
+ '--require',
2608
+ // Lazily access constants.distInstrumentWithSentryPath.
2609
+ constants.distInstrumentWithSentryPath
2610
+ ]
2611
+ : []),
2612
+ '--require',
2613
+ // Lazily access constants.distShadowNpmInjectPath.
2614
+ constants.distShadowNpmInjectPath,
2615
+ npm.realExecPathSync(agentExecPath),
2616
+ 'install',
2617
+ // Avoid code paths for 'audit' and 'fund'.
2618
+ '--no-audit',
2619
+ '--no-fund',
2620
+ // Add '--no-progress' to fix input being swallowed by the npm spinner.
2621
+ '--no-progress',
2622
+ // Add '--loglevel=silent' if a loglevel flag is not provided and the
2623
+ // SOCKET_CLI_DEBUG environment variable is not truthy.
2624
+ ...logLevelArgs,
2625
+ ...binArgs,
2626
+ ...otherArgs
2627
+ ],
2628
+ {
2629
+ spinner,
2630
+ ...spawnOptions,
2631
+ stdio,
2632
+ env: {
2633
+ ...process.env,
2634
+ ...spawnOptions.env
2635
+ }
2636
+ }
2637
+ )
2638
+ if (useIpc) {
2639
+ spawnPromise.process.send({
2640
+ [SOCKET_IPC_HANDSHAKE]: {
2641
+ [SOCKET_CLI_SAFE_BIN]: NPM$2,
2642
+ [SOCKET_CLI_SAFE_PROGRESS]: progressArg,
2643
+ ...ipc
2644
+ }
2645
+ })
2646
+ }
2647
+ return spawnPromise
2648
+ }
2649
+
2650
+ const { NPM: NPM$1, PNPM: PNPM$1 } = constants
2651
+ function runAgentInstall(pkgEnvDetails, options) {
2652
+ const { agent, agentExecPath } = pkgEnvDetails
2653
+ // All package managers support the "install" command.
2654
+ if (agent === NPM$1) {
2655
+ return safeNpmInstall({
2656
+ agentExecPath,
2657
+ ...options
2658
+ })
2659
+ }
2660
+ const {
2661
+ args = [],
2662
+ spinner,
2663
+ ...spawnOptions
2664
+ } = {
2665
+ __proto__: null,
2666
+ ...options
2667
+ }
2668
+ const skipNodeHardenFlags =
2669
+ agent === PNPM$1 && pkgEnvDetails.agentVersion.major < 11
2670
+ return spawn.spawn(agentExecPath, ['install', ...args], {
2671
+ // Lazily access constants.WIN32.
2672
+ shell: constants.WIN32,
2673
+ spinner,
2674
+ stdio: 'inherit',
2675
+ ...spawnOptions,
2676
+ env: {
2677
+ ...process.env,
2678
+ NODE_OPTIONS: cmdFlagsToString([
2679
+ ...(skipNodeHardenFlags
2680
+ ? []
2681
+ : // Lazily access constants.nodeHardenFlags.
2682
+ constants.nodeHardenFlags),
2683
+ // Lazily access constants.nodeNoWarningsFlags.
2684
+ ...constants.nodeNoWarningsFlags
2685
+ ]),
2686
+ ...spawnOptions.env
2687
+ }
2688
+ })
2689
+ }
2690
+
2691
+ const {
2692
+ BINARY_LOCK_EXT,
2693
+ BUN,
2694
+ HIDDEN_PACKAGE_LOCK_JSON,
2695
+ LOCK_EXT,
2696
+ NPM,
2697
+ NPM_BUGGY_OVERRIDES_PATCHED_VERSION,
2698
+ PACKAGE_JSON,
2699
+ PNPM,
2700
+ VLT,
2701
+ YARN,
2702
+ YARN_BERRY,
2703
+ YARN_CLASSIC
2704
+ } = constants
2705
+ const AGENTS = new Set([BUN, NPM, PNPM, YARN_BERRY, YARN_CLASSIC, VLT])
2706
+ const binByAgent = new Map([
2707
+ [BUN, BUN],
2708
+ [NPM, NPM],
2709
+ [PNPM, PNPM],
2710
+ [YARN_BERRY, YARN],
2711
+ [YARN_CLASSIC, YARN],
2712
+ [VLT, VLT]
2713
+ ])
2714
+ async function getAgentExecPath(agent) {
2715
+ const binName = binByAgent.get(agent)
2716
+ if (binName === NPM) {
2717
+ // Lazily access constants.npmExecPath.
2718
+ return constants.npmExecPath
2719
+ }
2720
+ return (
2721
+ (await vendor.libExports$1(binName, {
2722
+ nothrow: true
2723
+ })) ?? binName
2724
+ )
2725
+ }
2726
+ async function getAgentVersion(agentExecPath, cwd) {
2727
+ let result
2728
+ try {
2729
+ result =
2730
+ // Coerce version output into a valid semver version by passing it through
2731
+ // semver.coerce which strips leading v's, carets (^), comparators (<,<=,>,>=,=),
2732
+ // and tildes (~).
2733
+ vendor.semverExports.coerce(
2734
+ // All package managers support the "--version" flag.
2735
+ (
2736
+ await spawn.spawn(agentExecPath, ['--version'], {
2737
+ cwd,
2738
+ // Lazily access constants.WIN32.
2739
+ shell: constants.WIN32
2740
+ })
2741
+ ).stdout
2742
+ ) ?? undefined
2743
+ } catch (e) {
2744
+ debug.debugLog('getAgentVersion error:\n', e)
2745
+ }
2746
+ return result
2747
+ }
2748
+
2749
+ // The order of LOCKS properties IS significant as it affects iteration order.
2750
+ const LOCKS = {
2751
+ [`bun${LOCK_EXT}`]: BUN,
2752
+ [`bun${BINARY_LOCK_EXT}`]: BUN,
2753
+ // If both package-lock.json and npm-shrinkwrap.json are present in the root
2754
+ // of a project, npm-shrinkwrap.json will take precedence and package-lock.json
2755
+ // will be ignored.
2756
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#package-lockjson-vs-npm-shrinkwrapjson
2757
+ 'npm-shrinkwrap.json': NPM,
2758
+ 'package-lock.json': NPM,
2759
+ 'pnpm-lock.yaml': PNPM,
2760
+ 'pnpm-lock.yml': PNPM,
2761
+ [`yarn${LOCK_EXT}`]: YARN_CLASSIC,
2762
+ 'vlt-lock.json': VLT,
2763
+ // Lastly, look for a hidden lock file which is present if .npmrc has package-lock=false:
2764
+ // https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#hidden-lockfiles
2765
+ //
2766
+ // Unlike the other LOCKS keys this key contains a directory AND filename so
2767
+ // it has to be handled differently.
2768
+ 'node_modules/.package-lock.json': NPM
2769
+ }
2770
+ const readLockFileByAgent = (() => {
2771
+ function wrapReader(reader) {
2772
+ return async (...args) => {
2773
+ try {
2774
+ return await reader(...args)
2775
+ } catch {}
2776
+ return undefined
2777
+ }
2778
+ }
2779
+ const binaryReader = wrapReader(readFileBinary)
2780
+ const defaultReader = wrapReader(
2781
+ async lockPath => await readFileUtf8(lockPath)
2782
+ )
2783
+ return new Map([
2784
+ [
2785
+ BUN,
2786
+ wrapReader(async (lockPath, agentExecPath, cwd = process.cwd()) => {
2787
+ const ext = path.extname(lockPath)
2788
+ if (ext === LOCK_EXT) {
2789
+ return await defaultReader(lockPath)
2790
+ }
2791
+ if (ext === BINARY_LOCK_EXT) {
2792
+ const lockBuffer = await binaryReader(lockPath)
2793
+ if (lockBuffer) {
2794
+ try {
2795
+ return vendor.hyrious__bun_lockbExports.parse(lockBuffer)
2796
+ } catch {}
2797
+ }
2798
+ // To print a Yarn lockfile to your console without writing it to disk
2799
+ // use `bun bun.lockb`.
2800
+ // https://bun.sh/guides/install/yarnlock
2801
+ return (
2802
+ await spawn.spawn(agentExecPath, [lockPath], {
2803
+ cwd,
2804
+ // Lazily access constants.WIN32.
2805
+ shell: constants.WIN32
2806
+ })
2807
+ ).stdout.trim()
2808
+ }
2809
+ return undefined
2810
+ })
2811
+ ],
2812
+ [NPM, defaultReader],
2813
+ [PNPM, defaultReader],
2814
+ [VLT, defaultReader],
2815
+ [YARN_BERRY, defaultReader],
2816
+ [YARN_CLASSIC, defaultReader]
2817
+ ])
2818
+ })()
2819
+ async function detectPackageEnvironment({
2820
+ cwd = process.cwd(),
2821
+ onUnknown
2822
+ } = {}) {
2823
+ let lockPath = await findUp(Object.keys(LOCKS), {
2824
+ cwd
2825
+ })
2826
+ let lockName = lockPath ? path.basename(lockPath) : undefined
2827
+ const isHiddenLockFile = lockName === HIDDEN_PACKAGE_LOCK_JSON
2828
+ const pkgJsonPath = lockPath
2829
+ ? path.resolve(
2830
+ lockPath,
2831
+ `${isHiddenLockFile ? '../' : ''}../${PACKAGE_JSON}`
2832
+ )
2833
+ : await findUp(PACKAGE_JSON, {
2834
+ cwd
2835
+ })
2836
+ const pkgPath =
2837
+ pkgJsonPath && fs.existsSync(pkgJsonPath)
2838
+ ? path.dirname(pkgJsonPath)
2839
+ : undefined
2840
+ const editablePkgJson = pkgPath
2841
+ ? await packages.readPackageJson(pkgPath, {
2842
+ editable: true
2843
+ })
2844
+ : undefined
2845
+ // Read Corepack `packageManager` field in package.json:
2846
+ // https://nodejs.org/api/packages.html#packagemanager
2847
+ const pkgManager = strings.isNonEmptyString(
2848
+ editablePkgJson?.content?.packageManager
2849
+ )
2850
+ ? editablePkgJson.content.packageManager
2851
+ : undefined
2852
+ let agent
2853
+ if (pkgManager) {
2854
+ // A valid "packageManager" field value is "<package manager name>@<version>".
2855
+ // https://nodejs.org/api/packages.html#packagemanager
2856
+ const atSignIndex = pkgManager.lastIndexOf('@')
2857
+ if (atSignIndex !== -1) {
2858
+ const name = pkgManager.slice(0, atSignIndex)
2859
+ const version = pkgManager.slice(atSignIndex + 1)
2860
+ if (version && AGENTS.has(name)) {
2861
+ agent = name
2862
+ }
2863
+ }
2864
+ }
2865
+ if (
2866
+ agent === undefined &&
2867
+ !isHiddenLockFile &&
2868
+ typeof pkgJsonPath === 'string' &&
2869
+ typeof lockName === 'string'
2870
+ ) {
2871
+ agent = LOCKS[lockName]
2872
+ }
2873
+ if (agent === undefined) {
2874
+ agent = NPM
2875
+ onUnknown?.(pkgManager)
2876
+ }
2877
+ const agentExecPath = await getAgentExecPath(agent)
2878
+ const agentVersion = await getAgentVersion(agentExecPath, cwd)
2879
+ if (agent === YARN_CLASSIC && (agentVersion?.major ?? 0) > 1) {
2880
+ agent = YARN_BERRY
2881
+ }
2882
+ // Lazily access constants.maintainedNodeVersions.
2883
+ const { maintainedNodeVersions } = constants
2884
+ // Lazily access constants.minimumVersionByAgent.
2885
+ const minSupportedAgentVersion = constants.minimumVersionByAgent.get(agent)
2886
+ const minSupportedNodeVersion = maintainedNodeVersions.last
2887
+ const nodeVersion = vendor.semverExports.coerce(process.version)
2888
+ let lockSrc
2889
+ let pkgAgentRange
2890
+ let pkgNodeRange
2891
+ let pkgMinAgentVersion = minSupportedAgentVersion
2892
+ let pkgMinNodeVersion = minSupportedNodeVersion
2893
+ if (editablePkgJson?.content) {
2894
+ const { engines } = editablePkgJson.content
2895
+ const engineAgentRange = engines?.[agent]
2896
+ const engineNodeRange = engines?.['node']
2897
+ if (strings.isNonEmptyString(engineAgentRange)) {
2898
+ pkgAgentRange = engineAgentRange
2899
+ // Roughly check agent range as semver.coerce will strip leading
2900
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
2901
+ const coerced = vendor.semverExports.coerce(pkgAgentRange)
2902
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinAgentVersion)) {
2903
+ pkgMinAgentVersion = coerced.version
2904
+ }
2905
+ }
2906
+ if (strings.isNonEmptyString(engineNodeRange)) {
2907
+ pkgNodeRange = engineNodeRange
2908
+ // Roughly check Node range as semver.coerce will strip leading
2909
+ // v's, carets (^), comparators (<,<=,>,>=,=), and tildes (~).
2910
+ const coerced = vendor.semverExports.coerce(pkgNodeRange)
2911
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
2912
+ pkgMinNodeVersion = coerced.version
2913
+ }
2914
+ }
2915
+ const browserslistQuery = editablePkgJson.content['browserslist']
2916
+ if (Array.isArray(browserslistQuery)) {
2917
+ // List Node targets in ascending version order.
2918
+ const browserslistNodeTargets = vendor
2919
+ .browserslistExports(browserslistQuery)
2920
+ .filter(v => /^node /i.test(v))
2921
+ .map(v => v.slice(5 /*'node '.length*/))
2922
+ .sort(sorts.naturalCompare)
2923
+ if (browserslistNodeTargets.length) {
2924
+ // browserslistNodeTargets[0] is the lowest Node target version.
2925
+ const coerced = vendor.semverExports.coerce(browserslistNodeTargets[0])
2926
+ if (coerced && vendor.semverExports.lt(coerced, pkgMinNodeVersion)) {
2927
+ pkgMinNodeVersion = coerced.version
2928
+ }
2929
+ }
2930
+ }
2931
+ lockSrc =
2932
+ typeof lockPath === 'string'
2933
+ ? await readLockFileByAgent.get(agent)(lockPath, agentExecPath, cwd)
2934
+ : undefined
2935
+ } else {
2936
+ lockName = undefined
2937
+ lockPath = undefined
2938
+ }
2939
+ // Does the system agent version meet our minimum supported agent version?
2940
+ const agentSupported =
2941
+ !!agentVersion &&
2942
+ vendor.semverExports.satisfies(
2943
+ agentVersion,
2944
+ `>=${minSupportedAgentVersion}`
2945
+ )
2946
+
2947
+ // Does the system Node version meet our minimum supported Node version?
2948
+ const nodeSupported = vendor.semverExports.satisfies(
2949
+ nodeVersion,
2950
+ `>=${minSupportedNodeVersion}`
2951
+ )
2952
+ const npmExecPath =
2953
+ agent === NPM ? agentExecPath : await getAgentExecPath(NPM)
2954
+ const npmBuggyOverrides =
2955
+ agent === NPM &&
2956
+ !!agentVersion &&
2957
+ vendor.semverExports.lt(agentVersion, NPM_BUGGY_OVERRIDES_PATCHED_VERSION)
2958
+ return {
2959
+ agent,
2960
+ agentExecPath,
2961
+ agentSupported,
2962
+ agentVersion,
2963
+ editablePkgJson,
2964
+ features: {
2965
+ npmBuggyOverrides
2966
+ },
2967
+ lockName,
2968
+ lockPath,
2969
+ lockSrc,
2970
+ nodeSupported,
2971
+ nodeVersion,
2972
+ npmExecPath,
2973
+ pkgPath,
2974
+ pkgRequirements: {
2975
+ agent: pkgAgentRange ?? `>=${pkgMinAgentVersion}`,
2976
+ node: pkgNodeRange ?? `>=${pkgMinNodeVersion}`
2977
+ },
2978
+ pkgSupports: {
2979
+ // Does our minimum supported agent version meet the package's requirements?
2980
+ agent: vendor.semverExports.satisfies(
2981
+ minSupportedAgentVersion,
2982
+ `>=${pkgMinAgentVersion}`
2983
+ ),
2984
+ // Does our supported Node versions meet the package's requirements?
2985
+ node: maintainedNodeVersions.some(v =>
2986
+ vendor.semverExports.satisfies(v, `>=${pkgMinNodeVersion}`)
2987
+ )
2988
+ }
2989
+ }
2990
+ }
2991
+ async function detectAndValidatePackageEnvironment(cwd, options) {
2992
+ const {
2993
+ cmdName = '',
2994
+ logger,
2995
+ prod
2996
+ } = {
2997
+ __proto__: null,
2998
+ ...options
2999
+ }
3000
+ const details = await detectPackageEnvironment({
3001
+ cwd,
3002
+ onUnknown(pkgManager) {
3003
+ logger?.warn(
3004
+ cmdPrefixMessage(
3005
+ cmdName,
3006
+ `Unknown package manager${pkgManager ? ` ${pkgManager}` : ''}, defaulting to npm`
3007
+ )
3008
+ )
3009
+ }
3010
+ })
3011
+ const { agent, nodeVersion, pkgRequirements } = details
3012
+ const agentVersion = details.agentVersion ?? 'unknown'
3013
+ if (!details.agentSupported) {
3014
+ const minVersion = constants.minimumVersionByAgent.get(agent)
3015
+ logger?.fail(
3016
+ cmdPrefixMessage(
3017
+ cmdName,
3018
+ `Requires ${agent} >=${minVersion}. Current version: ${agentVersion}.`
3019
+ )
3020
+ )
3021
+ return
3022
+ }
3023
+ if (!details.nodeSupported) {
3024
+ const minVersion = constants.maintainedNodeVersions.last
3025
+ logger?.fail(
3026
+ cmdPrefixMessage(
3027
+ cmdName,
3028
+ `Requires Node >=${minVersion}. Current version: ${nodeVersion}.`
3029
+ )
3030
+ )
3031
+ return
3032
+ }
3033
+ if (!details.pkgSupports.agent) {
3034
+ logger?.fail(
3035
+ cmdPrefixMessage(
3036
+ cmdName,
3037
+ `Package engine "${agent}" requires ${pkgRequirements.agent}. Current version: ${agentVersion}`
3038
+ )
3039
+ )
3040
+ return
3041
+ }
3042
+ if (!details.pkgSupports.node) {
3043
+ logger?.fail(
3044
+ cmdPrefixMessage(
3045
+ cmdName,
3046
+ `Package engine "node" requires ${pkgRequirements.node}. Current version: ${nodeVersion}`
3047
+ )
3048
+ )
3049
+ return
3050
+ }
3051
+ if (agent === VLT) {
3052
+ logger?.fail(
3053
+ cmdPrefixMessage(
3054
+ cmdName,
3055
+ `${agent} does not support overrides. Soon, though ⚡`
3056
+ )
3057
+ )
3058
+ return
3059
+ }
3060
+ const lockName = details.lockName ?? 'lock file'
3061
+ if (details.lockName === undefined || details.lockSrc === undefined) {
3062
+ logger?.fail(cmdPrefixMessage(cmdName, `No ${lockName} found`))
3063
+ return
3064
+ }
3065
+ if (details.lockSrc.trim() === '') {
3066
+ logger?.fail(cmdPrefixMessage(cmdName, `${lockName} is empty`))
3067
+ return
3068
+ }
3069
+ if (details.pkgPath === undefined) {
3070
+ logger?.fail(cmdPrefixMessage(cmdName, `No ${PACKAGE_JSON} found`))
3071
+ return
3072
+ }
3073
+ if (prod && (agent === BUN || agent === YARN_BERRY)) {
3074
+ logger?.fail(
3075
+ cmdPrefixMessage(
3076
+ cmdName,
3077
+ `--prod not supported for ${agent}${agentVersion ? `@${agentVersion}` : ''}`
3078
+ )
3079
+ )
3080
+ return
3081
+ }
3082
+ if (
3083
+ details.lockPath &&
3084
+ path.relative(cwd, details.lockPath).startsWith('.')
3085
+ ) {
3086
+ // Note: In tests we return <redacted> because otherwise snapshots will fail.
3087
+ const { REDACTED } = constants
3088
+ // Lazily access constants.ENV.VITEST.
3089
+ const redacting = constants.ENV.VITEST
3090
+ logger?.warn(
3091
+ cmdPrefixMessage(
3092
+ cmdName,
3093
+ `Package ${lockName} found at ${redacting ? REDACTED : details.lockPath}`
3094
+ )
3095
+ )
3096
+ }
3097
+ return details
3098
+ }
3099
+
3100
+ exports.ALERT_SEVERITY = ALERT_SEVERITY
3101
+ exports.AuthError = AuthError
3102
+ exports.ColorOrMarkdown = ColorOrMarkdown
3103
+ exports.InputError = InputError
3104
+ exports.RangeStyles = RangeStyles
3105
+ exports.applyRange = applyRange
3106
+ exports.captureException = captureException
3107
+ exports.checkCommandInput = checkCommandInput
3108
+ exports.cmdPrefixMessage = cmdPrefixMessage
3109
+ exports.commonFlags = commonFlags
3110
+ exports.createEnum = createEnum
3111
+ exports.detectAndValidatePackageEnvironment =
3112
+ detectAndValidatePackageEnvironment
3113
+ exports.determineOrgSlug = determineOrgSlug
3114
+ exports.failMsgWithBadge = failMsgWithBadge
3115
+ exports.formatSeverityCount = formatSeverityCount
3116
+ exports.getAlertsMapFromPnpmLockfile = getAlertsMapFromPnpmLockfile
3117
+ exports.getAlertsMapFromPurls = getAlertsMapFromPurls
3118
+ exports.getConfigValue = getConfigValue
3119
+ exports.getConfigValueOrUndef = getConfigValueOrUndef
3120
+ exports.getCveInfoByAlertsMap = getCveInfoByAlertsMap
3121
+ exports.getDefaultToken = getDefaultToken
3122
+ exports.getFlagListOutput = getFlagListOutput
3123
+ exports.getLastFiveOfApiToken = getLastFiveOfApiToken
3124
+ exports.getMajor = getMajor
3125
+ exports.getNpmBinPath = getNpmBinPath
3126
+ exports.getNpmRequire = getNpmRequire
3127
+ exports.getNpxBinPath = getNpxBinPath
3128
+ exports.getOutputKind = getOutputKind
3129
+ exports.getPackageFilesForScan = getPackageFilesForScan
3130
+ exports.getPublicToken = getPublicToken
3131
+ exports.getSeverityCount = getSeverityCount
3132
+ exports.getSocketDevAlertUrl = getSocketDevAlertUrl
3133
+ exports.getSocketDevPackageOverviewUrl = getSocketDevPackageOverviewUrl
3134
+ exports.globWorkspace = globWorkspace
3135
+ exports.handleApiCall = handleApiCall
3136
+ exports.handleApiCallNoSpinner = handleApiCallNoSpinner
3137
+ exports.handleApiError = handleApiError
3138
+ exports.handleFailedApiResponse = handleFailedApiResponse
3139
+ exports.handleUnsuccessfulApiResponse = handleUnsuccessfulApiResponse
3140
+ exports.idToPurl = idToPurl
3141
+ exports.isHelpFlag = isHelpFlag
3142
+ exports.isNpmBinPathShadowed = isNpmBinPathShadowed
3143
+ exports.isNpxBinPathShadowed = isNpxBinPathShadowed
3144
+ exports.isReadOnlyConfig = isReadOnlyConfig
3145
+ exports.isTestingV1 = isTestingV1
3146
+ exports.logAlertsMap = logAlertsMap
3147
+ exports.mapToObject = mapToObject
3148
+ exports.mdTable = mdTable
3149
+ exports.mdTableOfPairs = mdTableOfPairs
3150
+ exports.mdTableStringNumber = mdTableStringNumber
3151
+ exports.meowOrExit = meowOrExit
3152
+ exports.meowWithSubcommands = meowWithSubcommands
3153
+ exports.outputFlags = outputFlags
3154
+ exports.parsePnpmLockfileVersion = parsePnpmLockfileVersion
3155
+ exports.queryApi = queryApi
3156
+ exports.removeNodeModules = removeNodeModules
3157
+ exports.runAgentInstall = runAgentInstall
3158
+ exports.safeReadFile = safeReadFile
3159
+ exports.sensitiveConfigKeys = sensitiveConfigKeys
3160
+ exports.serializeResultJson = serializeResultJson
3161
+ exports.setupSdk = setupSdk
3162
+ exports.suggestOrgSlug = suggestOrgSlug
3163
+ exports.supportedConfigKeys = supportedConfigKeys
3164
+ exports.tmpHandleApiCall = tmpHandleApiCall
3165
+ exports.updateConfigValue = updateConfigValue
3166
+ exports.validationFlags = validationFlags
3167
+ exports.walkNestedMap = walkNestedMap
3168
+ //# debugId=afe4bbb8-337c-4c20-bce5-d244f7695dd8
3169
+ //# sourceMappingURL=utils.js.map