socket 0.14.68 → 0.14.69

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.
@@ -19,6 +19,7 @@ const logger = require('@socketsecurity/registry/lib/logger')
19
19
  const assert = require('node:assert')
20
20
  const fs = require('node:fs/promises')
21
21
  const commonTags = _socketInterop(require('common-tags'))
22
+ const debug = require('@socketsecurity/registry/lib/debug')
22
23
  const strings = require('@socketsecurity/registry/lib/strings')
23
24
  const shadowNpmInject = require('./shadow-npm-inject.js')
24
25
  const constants = require('./constants.js')
@@ -28,22 +29,22 @@ const meow = _socketInterop(require('meow'))
28
29
  const objects = require('@socketsecurity/registry/lib/objects')
29
30
  const path = require('@socketsecurity/registry/lib/path')
30
31
  const regexps = require('@socketsecurity/registry/lib/regexps')
31
- const prompts = require('@socketsecurity/registry/lib/prompts')
32
32
  const yargsParse = _socketInterop(require('yargs-parser'))
33
33
  const words = require('@socketsecurity/registry/lib/words')
34
34
  const fs$1 = require('node:fs')
35
35
  const shadowBin = require('./shadow-bin.js')
36
+ const prompts = require('@socketsecurity/registry/lib/prompts')
36
37
  const chalkTable = _socketInterop(require('chalk-table'))
37
38
  const util = require('node:util')
38
39
  const registry = require('@socketsecurity/registry')
39
40
  const npm = require('@socketsecurity/registry/lib/npm')
40
41
  const packages = require('@socketsecurity/registry/lib/packages')
42
+ const rest = _socketInterop(require('@octokit/rest'))
41
43
  const lockfile_fs = _socketInterop(require('@pnpm/lockfile.fs'))
44
+ const spawn = require('@socketsecurity/registry/lib/spawn')
42
45
  const lockfile_detectDepTypes = _socketInterop(
43
46
  require('@pnpm/lockfile.detect-dep-types')
44
47
  )
45
- const debug = require('@socketsecurity/registry/lib/debug')
46
- const spawn = require('@socketsecurity/registry/lib/spawn')
47
48
  const shadowNpmPaths = require('./shadow-npm-paths.js')
48
49
  const browserslist = _socketInterop(require('browserslist'))
49
50
  const semver = _socketInterop(require('semver'))
@@ -57,8 +58,6 @@ const npa = _socketInterop(require('npm-package-arg'))
57
58
  const tinyglobby = _socketInterop(require('tinyglobby'))
58
59
  const promises = require('@socketsecurity/registry/lib/promises')
59
60
  const yaml = _socketInterop(require('yaml'))
60
- const betterAjvErrors = _socketInterop(require('@apideck/better-ajv-errors'))
61
- const config$K = require('@socketsecurity/config')
62
61
  const open = _socketInterop(require('open'))
63
62
 
64
63
  function failMsgWithBadge(badge, msg) {
@@ -74,7 +73,7 @@ function handleUnsuccessfulApiResponse(_name, sockSdkError) {
74
73
  spinner.stop()
75
74
  throw new shadowNpmInject.AuthError(message)
76
75
  }
77
- logger.logger.fail(failMsgWithBadge('API returned an error:', message))
76
+ logger.logger.fail(failMsgWithBadge('Socket API returned an error', message))
78
77
 
79
78
  process$1.exit(1)
80
79
  }
@@ -82,23 +81,25 @@ async function handleApiCall(value, description) {
82
81
  let result
83
82
  try {
84
83
  result = await value
85
- } catch (cause) {
84
+ } catch (e) {
85
+ debug.debugLog(`handleApiCall[${description}] error:\n`, e)
86
86
  throw new Error(`Failed ${description}`, {
87
- cause
87
+ cause: e
88
88
  })
89
89
  }
90
90
  return result
91
91
  }
92
92
  async function handleApiError(code) {
93
93
  if (code === 400) {
94
- return 'One of the options passed might be incorrect.'
95
- } else if (code === 403) {
96
- 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.'
97
- } else if (code === 404) {
94
+ return 'One of the options passed might be incorrect'
95
+ }
96
+ if (code === 403) {
97
+ 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'
98
+ }
99
+ if (code === 404) {
98
100
  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.'
99
- } else {
100
- return `Server responded with status code ${code}`
101
101
  }
102
+ return `Server responded with status code ${code}`
102
103
  }
103
104
  function getLastFiveOfApiToken(token) {
104
105
  // Get the last 5 characters of the API token before the trailing "_api".
@@ -143,14 +144,13 @@ async function fetchOrgAnalyticsData(time, spinner) {
143
144
  )
144
145
  if (result.success === false) {
145
146
  handleUnsuccessfulApiResponse('getOrgAnalytics', result)
146
- return undefined
147
147
  }
148
148
  spinner.stop()
149
149
  if (!result.data.length) {
150
150
  logger.logger.log(
151
151
  'No analytics data is available for this organization yet.'
152
152
  )
153
- return undefined
153
+ return
154
154
  }
155
155
  return result.data
156
156
  }
@@ -163,14 +163,13 @@ async function fetchRepoAnalyticsData(repo, time, spinner) {
163
163
  )
164
164
  if (result.success === false) {
165
165
  handleUnsuccessfulApiResponse('getRepoAnalytics', result)
166
- return undefined
167
166
  }
168
167
  spinner.stop()
169
168
  if (!result.data.length) {
170
169
  logger.logger.log(
171
170
  'No analytics data is available for this organization yet.'
172
171
  )
173
- return undefined
172
+ return
174
173
  }
175
174
  return result.data
176
175
  }
@@ -732,7 +731,7 @@ function getHelpListOutput(
732
731
  return result.trim() || '(none)'
733
732
  }
734
733
 
735
- const { DRY_RUN_LABEL: DRY_RUN_LABEL$1, REDACTED } = constants
734
+ const { DRY_RUN_LABEL, REDACTED: REDACTED$1 } = constants
736
735
  async function meowWithSubcommands(subcommands, options) {
737
736
  const {
738
737
  aliases = {},
@@ -805,15 +804,43 @@ async function meowWithSubcommands(subcommands, options) {
805
804
  // Hard override the config if instructed to do so.
806
805
  // The env var overrides the --flag, which overrides the persisted config
807
806
  // Also, when either of these are used, config updates won't persist.
807
+ let configOverrideResult
808
808
  if (process$1.env['SOCKET_CLI_CONFIG']) {
809
- shadowNpmInject.overrideCachedConfig(
810
- JSON.parse(process$1.env['SOCKET_CLI_CONFIG'])
809
+ configOverrideResult = shadowNpmInject.overrideCachedConfig(
810
+ process$1.env['SOCKET_CLI_CONFIG']
811
811
  )
812
812
  } else if (cli.flags['config']) {
813
- shadowNpmInject.overrideCachedConfig(
814
- JSON.parse(String(cli.flags['config'] || ''))
813
+ configOverrideResult = shadowNpmInject.overrideCachedConfig(
814
+ String(cli.flags['config'] || '')
815
815
  )
816
816
  }
817
+ if (process$1.env['SOCKET_CLI_NO_API_TOKEN']) {
818
+ // This overrides the config override and even the explicit token env var.
819
+ // The config will be marked as readOnly to prevent persisting it.
820
+ shadowNpmInject.overrideConfigApiToken(undefined)
821
+ } else {
822
+ // Note: these are SOCKET_SECURITY prefixed because they're not specific to
823
+ // the CLI. For the sake of consistency we'll also support the env
824
+ // keys that do have the SOCKET_CLI prefix, it's an easy mistake.
825
+ // In case multiple are supplied, the tokens supersede the keys and the
826
+ // security prefix supersedes the cli prefix. "Adventure mode" ;)
827
+ const tokenOverride =
828
+ process$1.env['SOCKET_CLI_API_KEY'] ||
829
+ process$1.env['SOCKET_SECURITY_API_KEY'] ||
830
+ process$1.env['SOCKET_CLI_API_TOKEN'] ||
831
+ process$1.env['SOCKET_SECURITY_API_TOKEN']
832
+ if (tokenOverride) {
833
+ // This will set the token (even if there was a config override) and
834
+ // set it to readOnly, making sure the temp token won't be persisted.
835
+ shadowNpmInject.overrideConfigApiToken(tokenOverride)
836
+ }
837
+ }
838
+ if (configOverrideResult?.ok === false) {
839
+ emitBanner(name)
840
+ logger.logger.fail(configOverrideResult.message)
841
+ process$1.exitCode = 2
842
+ return
843
+ }
817
844
 
818
845
  // If we got at least some args, then lets find out if we can find a command.
819
846
  if (commandOrAliasName) {
@@ -838,7 +865,7 @@ async function meowWithSubcommands(subcommands, options) {
838
865
  }
839
866
  if (!cli.flags['help'] && cli.flags['dryRun']) {
840
867
  process$1.exitCode = 0
841
- logger.logger.log(`${DRY_RUN_LABEL$1}: No-op, call a sub-command; ok`)
868
+ logger.logger.log(`${DRY_RUN_LABEL}: No-op, call a sub-command; ok`)
842
869
  } else {
843
870
  cli.showHelp()
844
871
  }
@@ -887,9 +914,9 @@ function emitBanner(name) {
887
914
  logger.logger.error(getAsciiHeader(name))
888
915
  }
889
916
  function getAsciiHeader(command) {
890
- const cliVersion = '0.14.68:23c5456:b9186436:pub' // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION_HASH']".
917
+ const cliVersion = '0.14.69:2aebac3:ca167d28:pub' // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION_HASH']".
891
918
  const nodeVersion = process$1.version
892
- const apiToken = shadowNpmInject.getConfigValue('apiToken')
919
+ const apiToken = shadowNpmInject.getDefaultToken()
893
920
  const shownToken = apiToken ? getLastFiveOfApiToken(apiToken) : 'no'
894
921
  const relCwd = path.normalizePath(
895
922
  process$1
@@ -910,7 +937,7 @@ function getAsciiHeader(command) {
910
937
  return ` ${body}\n`
911
938
  }
912
939
 
913
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$I } = constants
940
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$G } = constants
914
941
  const config$J = {
915
942
  commandName: 'analytics',
916
943
  description: `Look up analytics data`,
@@ -923,7 +950,7 @@ const config$J = {
923
950
  shortFlag: 'f',
924
951
  default: '-',
925
952
  description:
926
- 'Path to a local file to save the output. Only valid with --json/--markdown. Defaults to stdout.'
953
+ 'Filepath to save output. Only valid with --json/--markdown. Defaults to stdout.'
927
954
  },
928
955
  repo: {
929
956
  type: 'string',
@@ -949,6 +976,10 @@ const config$J = {
949
976
  Usage
950
977
  $ ${command} --scope=<scope> --time=<time filter>
951
978
 
979
+ API Token Requirements
980
+ - Quota: 1 unit
981
+ - Permissions: report:write
982
+
952
983
  Default parameters are set to show the organization-level analytics over the
953
984
  last 7 days.
954
985
 
@@ -1024,7 +1055,7 @@ async function run$J(argv, importMeta, { parentName }) {
1024
1055
  return
1025
1056
  }
1026
1057
  if (cli.flags['dryRun']) {
1027
- logger.logger.log(DRY_RUN_BAIL_TEXT$I)
1058
+ logger.logger.log(DRY_RUN_BAIL_TEXT$G)
1028
1059
  return
1029
1060
  }
1030
1061
  assert(assertScope(scope))
@@ -1065,31 +1096,43 @@ async function fetchAuditLog({ logType, orgSlug, outputKind, page, perPage }) {
1065
1096
  )
1066
1097
  if (!result.success) {
1067
1098
  handleUnsuccessfulApiResponse('getAuditLogEvents', result)
1068
- return
1069
1099
  }
1070
1100
  spinner.stop()
1071
1101
  return result.data
1072
1102
  }
1073
1103
 
1104
+ const { REDACTED } = constants
1074
1105
  async function outputAuditLog(
1075
1106
  auditLogs,
1076
1107
  { logType, orgSlug, outputKind, page, perPage }
1077
1108
  ) {
1078
1109
  if (outputKind === 'json') {
1079
- await outputAsJson(auditLogs.results, orgSlug, logType, page, perPage)
1080
- } else if (outputKind === 'markdown') {
1081
- await outputAsMarkdown(auditLogs.results, orgSlug, logType, page, perPage)
1110
+ logger.logger.log(
1111
+ await outputAsJson(auditLogs.results, {
1112
+ logType,
1113
+ orgSlug,
1114
+ page,
1115
+ perPage
1116
+ })
1117
+ )
1082
1118
  } else {
1083
- await outputAsPrint(auditLogs.results, orgSlug, logType)
1119
+ logger.logger.log(
1120
+ await outputAsMarkdown(auditLogs.results, {
1121
+ logType,
1122
+ orgSlug,
1123
+ page,
1124
+ perPage
1125
+ })
1126
+ )
1084
1127
  }
1085
1128
  }
1086
- async function outputAsJson(auditLogs, orgSlug, logType, page, perPage) {
1129
+ async function outputAsJson(auditLogs, { logType, orgSlug, page, perPage }) {
1087
1130
  let json
1088
1131
  try {
1089
1132
  json = JSON.stringify(
1090
1133
  {
1091
1134
  desc: 'Audit logs for given query',
1092
- generated: new Date().toISOString(),
1135
+ generated: false ? REDACTED : new Date().toISOString(),
1093
1136
  org: orgSlug,
1094
1137
  logType,
1095
1138
  page,
@@ -1118,15 +1161,21 @@ async function outputAsJson(auditLogs, orgSlug, logType, page, perPage) {
1118
1161
  2
1119
1162
  )
1120
1163
  } catch (e) {
1121
- process.exitCode = 1
1164
+ process$1.exitCode = 1
1122
1165
  logger.logger.fail(
1123
1166
  'There was a problem converting the logs to JSON, please try without the `--json` flag'
1124
1167
  )
1125
- return
1168
+ if (debug.isDebug()) {
1169
+ debug.debugLog('Error:\n', e)
1170
+ }
1171
+ return '{}'
1126
1172
  }
1127
- logger.logger.log(json)
1173
+ return json
1128
1174
  }
1129
- async function outputAsMarkdown(auditLogs, orgSlug, logType, page, perPage) {
1175
+ async function outputAsMarkdown(
1176
+ auditLogs,
1177
+ { logType, orgSlug, page, perPage }
1178
+ ) {
1130
1179
  try {
1131
1180
  const table = mdTable(auditLogs, [
1132
1181
  'event_id',
@@ -1136,7 +1185,7 @@ async function outputAsMarkdown(auditLogs, orgSlug, logType, page, perPage) {
1136
1185
  'ip_address',
1137
1186
  'user_agent'
1138
1187
  ])
1139
- logger.logger.log(commonTags.stripIndents`
1188
+ return `
1140
1189
  # Socket Audit Logs
1141
1190
 
1142
1191
  These are the Socket.dev audit logs as per requested query.
@@ -1144,50 +1193,21 @@ These are the Socket.dev audit logs as per requested query.
1144
1193
  - type filter: ${logType || '(none)'}
1145
1194
  - page: ${page}
1146
1195
  - per page: ${perPage}
1147
- - generated: ${new Date().toISOString()}
1196
+ - generated: ${false ? REDACTED : new Date().toISOString()}
1148
1197
 
1149
1198
  ${table}
1150
- `)
1199
+ `
1151
1200
  } catch (e) {
1152
- process.exitCode = 1
1201
+ process$1.exitCode = 1
1153
1202
  logger.logger.fail(
1154
- 'There was a problem converting the logs to JSON, please try without the `--json` flag'
1203
+ 'There was a problem converting the logs to Markdown, please try the `--json` flag'
1155
1204
  )
1156
- logger.logger.error(e)
1157
- return
1158
- }
1159
- }
1160
- async function outputAsPrint(auditLogs, orgSlug, logType) {
1161
- const data = []
1162
- const logDetails = {}
1163
- for (const d of auditLogs) {
1164
- const { created_at } = d
1165
- if (created_at) {
1166
- const name = `${new Date(created_at).toLocaleDateString('en-us', {
1167
- year: 'numeric',
1168
- month: 'numeric',
1169
- day: 'numeric'
1170
- })} - ${d.user_email} - ${d.type} - ${d.ip_address} - ${d.user_agent}`
1171
- data.push(
1172
- {
1173
- name
1174
- },
1175
- new prompts.Separator()
1176
- )
1177
- logDetails[name] = JSON.stringify(d.payload)
1205
+ if (debug.isDebug()) {
1206
+ debug.debugLog('Error:\n', e)
1178
1207
  }
1208
+ // logger.error(e)
1209
+ return ''
1179
1210
  }
1180
- logger.logger.log(
1181
- logDetails[
1182
- await prompts.select({
1183
- message: logType
1184
- ? `\n Audit log for: ${orgSlug} with type: ${logType}\n`
1185
- : `\n Audit log for: ${orgSlug}\n`,
1186
- choices: data,
1187
- pageSize: 30
1188
- })
1189
- ]
1190
- )
1191
1211
  }
1192
1212
 
1193
1213
  async function handleAuditLog({ logType, orgSlug, outputKind, page, perPage }) {
@@ -1210,7 +1230,7 @@ async function handleAuditLog({ logType, orgSlug, outputKind, page, perPage }) {
1210
1230
  })
1211
1231
  }
1212
1232
 
1213
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$H } = constants
1233
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$F } = constants
1214
1234
  const config$I = {
1215
1235
  commandName: 'audit-log',
1216
1236
  description: 'Look up the audit log for an organization',
@@ -1241,6 +1261,10 @@ const config$I = {
1241
1261
  Usage
1242
1262
  $ ${command} <org slug>
1243
1263
 
1264
+ API Token Requirements
1265
+ - Quota: 1 unit
1266
+ - Permissions: audit-log:list
1267
+
1244
1268
  This feature requires an Enterprise Plan. To learn more about getting access
1245
1269
  to this feature and many more, please visit https://socket.dev/pricing
1246
1270
 
@@ -1296,7 +1320,7 @@ async function run$I(argv, importMeta, { parentName }) {
1296
1320
  return
1297
1321
  }
1298
1322
  if (cli.flags['dryRun']) {
1299
- logger.logger.log(DRY_RUN_BAIL_TEXT$H)
1323
+ logger.logger.log(DRY_RUN_BAIL_TEXT$F)
1300
1324
  return
1301
1325
  }
1302
1326
  await handleAuditLog({
@@ -1424,7 +1448,7 @@ function isHelpFlag(cmdArg) {
1424
1448
  }
1425
1449
 
1426
1450
  // import { meowOrExit } from '../../utils/meow-with-subcommands'
1427
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$G } = constants
1451
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$E } = constants
1428
1452
 
1429
1453
  // TODO: convert yargs to meow. Or convert all the other things to yargs.
1430
1454
  const toLower = arg => arg.toLowerCase()
@@ -1587,7 +1611,7 @@ async function run$H(argv, importMeta, { parentName }) {
1587
1611
  return
1588
1612
  }
1589
1613
  if (cli.flags['dryRun']) {
1590
- logger.logger.log(DRY_RUN_BAIL_TEXT$G)
1614
+ logger.logger.log(DRY_RUN_BAIL_TEXT$E)
1591
1615
  return
1592
1616
  }
1593
1617
  if (yargv.output === undefined) {
@@ -1623,7 +1647,7 @@ async function discoverConfigValue(key) {
1623
1647
  success: false,
1624
1648
  value: undefined,
1625
1649
  message:
1626
- 'When uncertain, unset this key. Otherwise ask your network administrator.'
1650
+ 'When uncertain, unset this key. Otherwise ask your network administrator'
1627
1651
  }
1628
1652
  }
1629
1653
  if (key === 'apiToken') {
@@ -1837,7 +1861,7 @@ async function handleConfigAuto({ key, outputKind }) {
1837
1861
  await outputConfigAuto(key, result, outputKind)
1838
1862
  }
1839
1863
 
1840
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$F } = constants
1864
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$D } = constants
1841
1865
  const config$G = {
1842
1866
  commandName: 'auto',
1843
1867
  description: 'Automatically discover and set the correct value config item',
@@ -1902,7 +1926,7 @@ async function run$G(argv, importMeta, { parentName }) {
1902
1926
  return
1903
1927
  }
1904
1928
  if (cli.flags['dryRun']) {
1905
- logger.logger.log(DRY_RUN_BAIL_TEXT$F)
1929
+ logger.logger.log(DRY_RUN_BAIL_TEXT$D)
1906
1930
  return
1907
1931
  }
1908
1932
  await handleConfigAuto({
@@ -1911,7 +1935,13 @@ async function run$G(argv, importMeta, { parentName }) {
1911
1935
  })
1912
1936
  }
1913
1937
 
1914
- async function outputConfigGet(key, value, outputKind) {
1938
+ async function outputConfigGet(
1939
+ key,
1940
+ value,
1941
+ readOnly,
1942
+ // Is config in read-only mode? (Overrides applied)
1943
+ outputKind
1944
+ ) {
1915
1945
  if (outputKind === 'json') {
1916
1946
  logger.logger.log(
1917
1947
  JSON.stringify({
@@ -1919,24 +1949,38 @@ async function outputConfigGet(key, value, outputKind) {
1919
1949
  result: {
1920
1950
  key,
1921
1951
  value
1922
- }
1952
+ },
1953
+ readOnly
1923
1954
  })
1924
1955
  )
1925
1956
  } else if (outputKind === 'markdown') {
1926
1957
  logger.logger.log(`# Config Value`)
1927
1958
  logger.logger.log('')
1928
1959
  logger.logger.log(`Config key '${key}' has value '${value}`)
1960
+ if (readOnly) {
1961
+ logger.logger.log('')
1962
+ logger.logger.log(
1963
+ 'Note: the config is in read-only mode, meaning at least one key was temporarily\n overridden from an env var or command flag.'
1964
+ )
1965
+ }
1929
1966
  } else {
1930
1967
  logger.logger.log(`${key}: ${value}`)
1968
+ if (readOnly) {
1969
+ logger.logger.log('')
1970
+ logger.logger.log(
1971
+ 'Note: the config is in read-only mode, meaning at least one key was temporarily overridden from an env var or command flag.'
1972
+ )
1973
+ }
1931
1974
  }
1932
1975
  }
1933
1976
 
1934
1977
  async function handleConfigGet({ key, outputKind }) {
1935
1978
  const value = shadowNpmInject.getConfigValue(key)
1936
- await outputConfigGet(key, value, outputKind)
1979
+ const readOnly = shadowNpmInject.isReadOnlyConfig()
1980
+ await outputConfigGet(key, value, readOnly, outputKind)
1937
1981
  }
1938
1982
 
1939
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$E } = constants
1983
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$C } = constants
1940
1984
  const config$F = {
1941
1985
  commandName: 'get',
1942
1986
  description: 'Get the value of a local CLI config item',
@@ -1996,7 +2040,7 @@ async function run$F(argv, importMeta, { parentName }) {
1996
2040
  return
1997
2041
  }
1998
2042
  if (cli.flags['dryRun']) {
1999
- logger.logger.log(DRY_RUN_BAIL_TEXT$E)
2043
+ logger.logger.log(DRY_RUN_BAIL_TEXT$C)
2000
2044
  return
2001
2045
  }
2002
2046
  await handleConfigGet({
@@ -2006,6 +2050,7 @@ async function run$F(argv, importMeta, { parentName }) {
2006
2050
  }
2007
2051
 
2008
2052
  async function outputConfigList({ full, outputKind }) {
2053
+ const readOnly = shadowNpmInject.isReadOnlyConfig()
2009
2054
  if (outputKind === 'json') {
2010
2055
  const obj = {}
2011
2056
  for (const key of shadowNpmInject.supportedConfigKeys.keys()) {
@@ -2022,7 +2067,8 @@ async function outputConfigList({ full, outputKind }) {
2022
2067
  {
2023
2068
  success: true,
2024
2069
  full,
2025
- config: obj
2070
+ config: obj,
2071
+ readOnly
2026
2072
  },
2027
2073
  null,
2028
2074
  2
@@ -2047,10 +2093,16 @@ async function outputConfigList({ full, outputKind }) {
2047
2093
  )
2048
2094
  }
2049
2095
  }
2096
+ if (readOnly) {
2097
+ logger.logger.log('')
2098
+ logger.logger.log(
2099
+ 'Note: the config is in read-only mode, meaning at least one key was temporarily\n overridden from an env var or command flag.'
2100
+ )
2101
+ }
2050
2102
  }
2051
2103
  }
2052
2104
 
2053
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$D } = constants
2105
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$B } = constants
2054
2106
  const config$E = {
2055
2107
  commandName: 'list',
2056
2108
  description: 'Show all local CLI config items and their values',
@@ -2106,7 +2158,7 @@ async function run$E(argv, importMeta, { parentName }) {
2106
2158
  return
2107
2159
  }
2108
2160
  if (cli.flags['dryRun']) {
2109
- logger.logger.log(DRY_RUN_BAIL_TEXT$D)
2161
+ logger.logger.log(DRY_RUN_BAIL_TEXT$B)
2110
2162
  return
2111
2163
  }
2112
2164
  await outputConfigList({
@@ -2115,29 +2167,43 @@ async function run$E(argv, importMeta, { parentName }) {
2115
2167
  })
2116
2168
  }
2117
2169
 
2118
- async function outputConfigSet(key, _value, outputKind) {
2170
+ async function outputConfigSet(key, _value, readOnly, outputKind) {
2119
2171
  if (outputKind === 'json') {
2120
2172
  logger.logger.log(
2121
2173
  JSON.stringify({
2122
2174
  success: true,
2123
- message: `Config key '${key}' was updated`
2175
+ message: `Config key '${key}' was updated${readOnly ? ' (Note: since at least one value was overridden from flag/env, the config was not persisted)' : ''}`,
2176
+ readOnly
2124
2177
  })
2125
2178
  )
2126
2179
  } else if (outputKind === 'markdown') {
2127
2180
  logger.logger.log(`# Update config`)
2128
2181
  logger.logger.log('')
2129
2182
  logger.logger.log(`Config key '${key}' was updated`)
2183
+ if (readOnly) {
2184
+ logger.logger.log('')
2185
+ logger.logger.log(
2186
+ 'Note: The change was not persisted because the config is in read-only mode,\n meaning at least one key was temporarily overridden from an env var or\n command flag.'
2187
+ )
2188
+ }
2130
2189
  } else {
2131
2190
  logger.logger.log(`OK`)
2191
+ if (readOnly) {
2192
+ logger.logger.log('')
2193
+ logger.logger.log(
2194
+ 'Note: The change was not persisted because the config is in read-only mode, meaning at least one key was temporarily overridden from an env var or command flag.'
2195
+ )
2196
+ }
2132
2197
  }
2133
2198
  }
2134
2199
 
2135
2200
  async function handleConfigSet({ key, outputKind, value }) {
2136
2201
  shadowNpmInject.updateConfigValue(key, value)
2137
- await outputConfigSet(key, value, outputKind)
2202
+ const readOnly = shadowNpmInject.isReadOnlyConfig()
2203
+ await outputConfigSet(key, value, readOnly, outputKind)
2138
2204
  }
2139
2205
 
2140
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$C } = constants
2206
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$A } = constants
2141
2207
  const config$D = {
2142
2208
  commandName: 'set',
2143
2209
  description: 'Update the value of a local CLI config item',
@@ -2211,7 +2277,7 @@ async function run$D(argv, importMeta, { parentName }) {
2211
2277
  return
2212
2278
  }
2213
2279
  if (cli.flags['dryRun']) {
2214
- logger.logger.log(DRY_RUN_BAIL_TEXT$C)
2280
+ logger.logger.log(DRY_RUN_BAIL_TEXT$A)
2215
2281
  return
2216
2282
  }
2217
2283
  await handleConfigSet({
@@ -2243,7 +2309,7 @@ async function handleConfigUnset({ key, outputKind }) {
2243
2309
  await outputConfigUnset(key, outputKind)
2244
2310
  }
2245
2311
 
2246
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$B } = constants
2312
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$z } = constants
2247
2313
  const config$C = {
2248
2314
  commandName: 'unset',
2249
2315
  description: 'Clear the value of a local CLI config item',
@@ -2303,7 +2369,7 @@ async function run$C(argv, importMeta, { parentName }) {
2303
2369
  return
2304
2370
  }
2305
2371
  if (cli.flags['dryRun']) {
2306
- logger.logger.log(DRY_RUN_BAIL_TEXT$B)
2372
+ logger.logger.log(DRY_RUN_BAIL_TEXT$z)
2307
2373
  return
2308
2374
  }
2309
2375
  await handleConfigUnset({
@@ -2352,7 +2418,6 @@ async function fetchDependencies({ limit, offset }) {
2352
2418
  spinner.successAndStop('Received organization dependencies response.')
2353
2419
  if (!result.success) {
2354
2420
  handleUnsuccessfulApiResponse('searchDependencies', result)
2355
- return
2356
2421
  }
2357
2422
  return result.data
2358
2423
  }
@@ -2431,7 +2496,7 @@ async function handleDependencies({ limit, offset, outputKind }) {
2431
2496
  })
2432
2497
  }
2433
2498
 
2434
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$A } = constants
2499
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$y } = constants
2435
2500
  const config$B = {
2436
2501
  commandName: 'dependencies',
2437
2502
  description:
@@ -2457,6 +2522,10 @@ const config$B = {
2457
2522
  Usage
2458
2523
  ${command}
2459
2524
 
2525
+ API Token Requirements
2526
+ - Quota: 1 unit
2527
+ - Permissions: none (does need token with access to target org)
2528
+
2460
2529
  Options
2461
2530
  ${getFlagListOutput(config.flags, 6)}
2462
2531
 
@@ -2500,7 +2569,7 @@ async function run$B(argv, importMeta, { parentName }) {
2500
2569
  return
2501
2570
  }
2502
2571
  if (cli.flags['dryRun']) {
2503
- logger.logger.log(DRY_RUN_BAIL_TEXT$A)
2572
+ logger.logger.log(DRY_RUN_BAIL_TEXT$y)
2504
2573
  return
2505
2574
  }
2506
2575
  await handleDependencies({
@@ -2614,7 +2683,7 @@ async function handleDiffScan({
2614
2683
  })
2615
2684
  }
2616
2685
 
2617
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$z } = constants
2686
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$x } = constants
2618
2687
  const config$A = {
2619
2688
  commandName: 'get',
2620
2689
  description: 'Get a diff scan for an organization',
@@ -2658,6 +2727,10 @@ const config$A = {
2658
2727
  Usage
2659
2728
  $ ${command} <org slug> --before=<before> --after=<after>
2660
2729
 
2730
+ API Token Requirements
2731
+ - Quota: 1 unit
2732
+ - Permissions: full-scans:list
2733
+
2661
2734
  This command displays the package changes between two scans. The full output
2662
2735
  can be pretty large depending on the size of your repo and time range. It is
2663
2736
  best stored to disk to be further analyzed by other tools.
@@ -2689,7 +2762,7 @@ async function run$A(argv, importMeta, { parentName }) {
2689
2762
  {
2690
2763
  test: !!(before && after),
2691
2764
  message:
2692
- 'Specify a before and after scan ID\nThe args are expecting a full `aaa0aa0a-aaaa-0000-0a0a-0000000a00a0` scan ID.',
2765
+ 'Specify a before and after scan ID.\nThe args are expecting a full `aaa0aa0a-aaaa-0000-0a0a-0000000a00a0` scan ID.',
2693
2766
  pass: 'ok',
2694
2767
  fail:
2695
2768
  !before && !after
@@ -2726,7 +2799,7 @@ async function run$A(argv, importMeta, { parentName }) {
2726
2799
  return
2727
2800
  }
2728
2801
  if (cli.flags['dryRun']) {
2729
- logger.logger.log(DRY_RUN_BAIL_TEXT$z)
2802
+ logger.logger.log(DRY_RUN_BAIL_TEXT$x)
2730
2803
  return
2731
2804
  }
2732
2805
  await handleDiffScan({
@@ -2766,8 +2839,13 @@ const { NPM: NPM$f } = constants
2766
2839
  function isTopLevel(tree, node) {
2767
2840
  return tree.children.get(node.name) === node
2768
2841
  }
2769
- async function npmFix(_pkgEnvDetails, cwd, options) {
2770
- const { spinner } = {
2842
+ async function npmFix(_pkgEnvDetails, options) {
2843
+ const {
2844
+ cwd = process.cwd(),
2845
+ spinner,
2846
+ test = false,
2847
+ testScript = 'test'
2848
+ } = {
2771
2849
  __proto__: null,
2772
2850
  ...options
2773
2851
  }
@@ -2783,7 +2861,8 @@ async function npmFix(_pkgEnvDetails, cwd, options) {
2783
2861
  existing: true,
2784
2862
  unfixable: false,
2785
2863
  upgradable: false
2786
- }
2864
+ },
2865
+ nothrow: true
2787
2866
  })
2788
2867
  const infoByPkg = shadowNpmInject.getCveInfoByAlertsMap(alertsMap)
2789
2868
  if (!infoByPkg) {
@@ -2828,11 +2907,13 @@ async function npmFix(_pkgEnvDetails, cwd, options) {
2828
2907
  shadowNpmInject.updateNode(node, packument, vulnerableVersionRange)
2829
2908
  ) {
2830
2909
  try {
2831
- // eslint-disable-next-line no-await-in-loop
2832
- await npm.runScript('test', [], {
2833
- spinner,
2834
- stdio: 'ignore'
2835
- })
2910
+ if (test) {
2911
+ // eslint-disable-next-line no-await-in-loop
2912
+ await npm.runScript(testScript, [], {
2913
+ spinner,
2914
+ stdio: 'ignore'
2915
+ })
2916
+ }
2836
2917
  spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)
2837
2918
  if (isTopLevel(tree, node)) {
2838
2919
  for (const depField of [
@@ -2868,16 +2949,24 @@ async function npmFix(_pkgEnvDetails, cwd, options) {
2868
2949
  spinner?.stop()
2869
2950
  }
2870
2951
 
2871
- async function getAlertsMapFromPnpmLockfile(lockfile, options) {
2872
- const { include: _include, spinner } = {
2952
+ async function getAlertsMapFromPnpmLockfile(lockfile, options_) {
2953
+ const options = {
2873
2954
  __proto__: null,
2874
- ...options
2955
+ consolidate: false,
2956
+ nothrow: false,
2957
+ ...options_
2875
2958
  }
2876
2959
  const include = {
2877
2960
  __proto__: null,
2961
+ blocked: true,
2962
+ critical: true,
2963
+ cve: true,
2964
+ existing: false,
2878
2965
  unfixable: true,
2879
- ..._include
2966
+ upgradable: false,
2967
+ ...options.include
2880
2968
  }
2969
+ const { spinner } = options
2881
2970
  const depTypes = lockfile_detectDepTypes.detectDepTypes(lockfile)
2882
2971
  const pkgIds = Object.keys(depTypes)
2883
2972
  let { length: remaining } = pkgIds
@@ -2892,9 +2981,11 @@ async function getAlertsMapFromPnpmLockfile(lockfile, options) {
2892
2981
  )
2893
2982
  const toAlertsMapOptions = {
2894
2983
  overrides: lockfile.overrides,
2895
- ...options
2984
+ consolidate: options.consolidate,
2985
+ include,
2986
+ spinner
2896
2987
  }
2897
- for await (const batchPackageFetchResult of sockSdk.batchPackageStream(
2988
+ for await (const batchResult of sockSdk.batchPackageStream(
2898
2989
  {
2899
2990
  alerts: 'true',
2900
2991
  compact: 'true',
@@ -2906,12 +2997,18 @@ async function getAlertsMapFromPnpmLockfile(lockfile, options) {
2906
2997
  }))
2907
2998
  }
2908
2999
  )) {
2909
- if (batchPackageFetchResult.success) {
3000
+ if (batchResult.success) {
2910
3001
  await shadowNpmInject.addArtifactToAlertsMap(
2911
- batchPackageFetchResult.data,
3002
+ batchResult.data,
2912
3003
  alertsByPkgId,
2913
3004
  toAlertsMapOptions
2914
3005
  )
3006
+ } else if (!options.nothrow) {
3007
+ const statusCode = batchResult.status ?? 'unknown'
3008
+ const statusMessage = batchResult.error ?? 'No status message'
3009
+ throw new Error(
3010
+ `Socket API server error (${statusCode}): ${statusMessage}`
3011
+ )
2915
3012
  }
2916
3013
  remaining -= 1
2917
3014
  if (spinner && remaining > 0) {
@@ -3039,12 +3136,99 @@ function runAgentInstall(pkgEnvDetails, options) {
3039
3136
  }
3040
3137
 
3041
3138
  const { NPM: NPM$c, OVERRIDES: OVERRIDES$2, PNPM: PNPM$9 } = constants
3042
- async function pnpmFix(pkgEnvDetails, cwd, options) {
3043
- const { spinner } = {
3139
+ async function branchExists(branchName, cwd) {
3140
+ try {
3141
+ await spawn.spawn('git', ['rev-parse', '--verify', branchName], {
3142
+ cwd,
3143
+ stdio: 'ignore'
3144
+ })
3145
+ return true
3146
+ } catch {
3147
+ return false
3148
+ }
3149
+ }
3150
+ async function remoteBranchExists(branchName, cwd) {
3151
+ try {
3152
+ const result = await spawn.spawn(
3153
+ 'git',
3154
+ ['ls-remote', '--heads', 'origin', branchName],
3155
+ {
3156
+ cwd,
3157
+ stdio: 'pipe'
3158
+ }
3159
+ )
3160
+ return !!result.stdout.trim()
3161
+ } catch {
3162
+ return false
3163
+ }
3164
+ }
3165
+ async function commitAndPushFix(branchName, commitMsg, cwd) {
3166
+ const localExists = await branchExists(branchName, cwd)
3167
+ const remoteExists = await remoteBranchExists(branchName, cwd)
3168
+ if (localExists || remoteExists) {
3169
+ logger.logger.warn(
3170
+ `Branch "${branchName}" already exists. Skipping creation.`
3171
+ )
3172
+ return
3173
+ }
3174
+ await spawn.spawn('git', ['checkout', '-b', branchName], {
3175
+ cwd
3176
+ })
3177
+ await spawn.spawn('git', ['add', 'package.json', 'pnpm-lock.yaml'], {
3178
+ cwd
3179
+ })
3180
+ await spawn.spawn('git', ['commit', '-m', commitMsg], {
3181
+ cwd
3182
+ })
3183
+ await spawn.spawn('git', ['push', '--set-upstream', 'origin', branchName], {
3184
+ cwd
3185
+ })
3186
+ }
3187
+ async function createPullRequest({
3188
+ base = 'main',
3189
+ body,
3190
+ head,
3191
+ owner,
3192
+ repo,
3193
+ title
3194
+ }) {
3195
+ const octokit = new rest.Octokit({
3196
+ auth: process.env['GITHUB_TOKEN']
3197
+ })
3198
+ await octokit.pulls.create({
3199
+ owner,
3200
+ repo,
3201
+ title,
3202
+ head,
3203
+ base,
3204
+ ...(body
3205
+ ? {
3206
+ body
3207
+ }
3208
+ : {})
3209
+ })
3210
+ }
3211
+ function getRepoInfo() {
3212
+ const repoString = process.env['GITHUB_REPOSITORY']
3213
+ if (!repoString || !repoString.includes('/')) {
3214
+ throw new Error('GITHUB_REPOSITORY is not set or invalid')
3215
+ }
3216
+ const { 0: owner, 1: repo } = repoString.split('/')
3217
+ return {
3218
+ owner,
3219
+ repo
3220
+ }
3221
+ }
3222
+ async function pnpmFix(pkgEnvDetails, options) {
3223
+ const {
3224
+ cwd = process.cwd(),
3225
+ spinner,
3226
+ test = false,
3227
+ testScript = 'test'
3228
+ } = {
3044
3229
  __proto__: null,
3045
3230
  ...options
3046
3231
  }
3047
- spinner?.start()
3048
3232
  const lockfile = await lockfile_fs.readWantedLockfile(cwd, {
3049
3233
  ignoreIncompatible: false
3050
3234
  })
@@ -3058,7 +3242,8 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3058
3242
  existing: true,
3059
3243
  unfixable: false,
3060
3244
  upgradable: false
3061
- }
3245
+ },
3246
+ nothrow: true
3062
3247
  })
3063
3248
  const infoByPkg = shadowNpmInject.getCveInfoByAlertsMap(alertsMap)
3064
3249
  if (!infoByPkg) {
@@ -3074,11 +3259,12 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3074
3259
  editable: true
3075
3260
  })
3076
3261
  const { content: pkgJson } = editablePkgJson
3262
+ spinner?.stop()
3077
3263
  for (const { 0: name, 1: infos } of infoByPkg) {
3078
3264
  const tree = arb.actualTree
3079
3265
  const hasUpgrade = !!registry.getManifestData(NPM$c, name)
3080
3266
  if (hasUpgrade) {
3081
- spinner?.info(`Skipping ${name}. Socket Optimize package exists.`)
3267
+ logger.logger.info(`Skipping ${name}. Socket Optimize package exists.`)
3082
3268
  continue
3083
3269
  }
3084
3270
  const nodes = shadowNpmInject.findPackageNodes(tree, name)
@@ -3100,6 +3286,7 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3100
3286
  const { firstPatchedVersionIdentifier, vulnerableVersionRange } =
3101
3287
  infos[j]
3102
3288
  const { version: oldVersion } = node
3289
+ const oldSpec = `${name}@${oldVersion}`
3103
3290
  const availableVersions = Object.keys(packument.versions)
3104
3291
  // Find the highest non-vulnerable version within the same major range
3105
3292
  const targetVersion = shadowNpmInject.findBestPatchVersion(
@@ -3110,20 +3297,28 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3110
3297
  const targetPackument = targetVersion
3111
3298
  ? packument.versions[targetVersion]
3112
3299
  : undefined
3113
- if (targetPackument) {
3300
+ spinner?.stop()
3301
+
3302
+ // Check targetVersion to make TypeScript happy.
3303
+ if (targetVersion && targetPackument) {
3114
3304
  const oldPnpm = pkgJson[PNPM$9]
3115
3305
  const oldOverrides = oldPnpm?.[OVERRIDES$2]
3116
- try {
3117
- editablePkgJson.update({
3118
- [PNPM$9]: {
3119
- ...oldPnpm,
3120
- [OVERRIDES$2]: {
3121
- [`${node.name}@${vulnerableVersionRange}`]: `^${targetVersion}`,
3122
- ...oldOverrides
3123
- }
3306
+ const overrideKey = `${node.name}@${vulnerableVersionRange}`
3307
+ const overrideRange = `^${targetVersion}`
3308
+ const fixSpec = `${name}@${overrideRange}`
3309
+ const data = {
3310
+ [PNPM$9]: {
3311
+ ...oldPnpm,
3312
+ [OVERRIDES$2]: {
3313
+ [overrideKey]: overrideRange,
3314
+ ...oldOverrides
3124
3315
  }
3125
- })
3126
- spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)
3316
+ }
3317
+ }
3318
+ try {
3319
+ editablePkgJson.update(data)
3320
+ spinner?.start()
3321
+ spinner?.info(`Installing ${fixSpec}`)
3127
3322
 
3128
3323
  // eslint-disable-next-line no-await-in-loop
3129
3324
  await editablePkgJson.save()
@@ -3131,11 +3326,70 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3131
3326
  await runAgentInstall(pkgEnvDetails, {
3132
3327
  spinner
3133
3328
  })
3329
+ if (test) {
3330
+ spinner?.info(`Testing ${fixSpec}`)
3331
+ // eslint-disable-next-line no-await-in-loop
3332
+ await npm.runScript(testScript, [], {
3333
+ spinner,
3334
+ stdio: 'ignore'
3335
+ })
3336
+ }
3337
+ try {
3338
+ const branchName = `fix-${name}-${targetVersion.replace(/\./g, '-')}`
3339
+ const commitMsg = `fix: upgrade ${name} to ${targetVersion}`
3340
+ const { owner, repo } = getRepoInfo()
3341
+ // eslint-disable-next-line no-await-in-loop
3342
+ await commitAndPushFix(branchName, commitMsg, cwd)
3343
+ // eslint-disable-next-line no-await-in-loop
3344
+ await createPullRequest({
3345
+ owner,
3346
+ repo,
3347
+ title: commitMsg,
3348
+ head: branchName,
3349
+ base: process.env['GITHUB_REF_NAME'] ?? 'master',
3350
+ body: `This PR fixes a security issue in \`${name}\` by upgrading to \`${targetVersion}\`.`
3351
+ })
3352
+ } catch (e) {
3353
+ console.log(e)
3354
+ }
3355
+ logger.logger.success(`Fixed ${name}`)
3134
3356
  } catch {
3135
- spinner?.error(`Reverting ${name} to ${oldVersion}`)
3357
+ spinner?.error(`Reverting ${fixSpec}`)
3358
+ const pnpmKeyCount = Object.keys(data[PNPM$9]).length
3359
+ const pnpmOverridesKeyCount = Object.keys(
3360
+ data[PNPM$9][OVERRIDES$2]
3361
+ ).length
3362
+ if (pnpmKeyCount === 1 && pnpmOverridesKeyCount === 1) {
3363
+ editablePkgJson.update({
3364
+ // Setting to `undefined` will remove the property.
3365
+ [PNPM$9]: undefined
3366
+ })
3367
+ } else {
3368
+ editablePkgJson.update({
3369
+ [PNPM$9]: {
3370
+ ...oldPnpm,
3371
+ [OVERRIDES$2]:
3372
+ pnpmOverridesKeyCount === 1
3373
+ ? undefined
3374
+ : {
3375
+ [overrideKey]: undefined,
3376
+ ...oldOverrides
3377
+ }
3378
+ }
3379
+ })
3380
+ }
3381
+ // eslint-disable-next-line no-await-in-loop
3382
+ await editablePkgJson.save()
3383
+ // eslint-disable-next-line no-await-in-loop
3384
+ await runAgentInstall(pkgEnvDetails, {
3385
+ spinner
3386
+ })
3387
+ spinner?.stop()
3388
+ logger.logger.error(`Failed to fix ${oldSpec}`)
3136
3389
  }
3137
3390
  } else {
3138
- spinner?.error(`Could not patch ${name} ${oldVersion}`)
3391
+ spinner?.stop()
3392
+ logger.logger.error(`Could not patch ${oldSpec}`)
3139
3393
  }
3140
3394
  }
3141
3395
  }
@@ -3541,39 +3795,59 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3541
3795
 
3542
3796
  const { NPM: NPM$a, PNPM: PNPM$7 } = constants
3543
3797
  const CMD_NAME$2 = 'socket fix'
3544
- async function runFix() {
3545
- // Lazily access constants.spinner.
3546
- const { spinner } = constants
3547
- spinner.start()
3548
- const cwd = process.cwd()
3798
+ async function runFix({
3799
+ cwd = process.cwd(),
3800
+ spinner,
3801
+ test = false,
3802
+ testScript = 'test'
3803
+ }) {
3549
3804
  const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {
3550
3805
  cmdName: CMD_NAME$2,
3551
3806
  logger: logger.logger
3552
3807
  })
3553
3808
  if (!pkgEnvDetails) {
3554
- spinner.stop()
3809
+ spinner?.stop()
3555
3810
  return
3556
3811
  }
3812
+ logger.logger.info(`Fixing packages for ${pkgEnvDetails.agent}`)
3557
3813
  switch (pkgEnvDetails.agent) {
3558
3814
  case NPM$a: {
3559
- await npmFix(pkgEnvDetails, cwd)
3815
+ await npmFix(pkgEnvDetails, {
3816
+ spinner,
3817
+ test,
3818
+ testScript
3819
+ })
3560
3820
  break
3561
3821
  }
3562
3822
  case PNPM$7: {
3563
- await pnpmFix(pkgEnvDetails, cwd)
3823
+ await pnpmFix(pkgEnvDetails, {
3824
+ spinner,
3825
+ test,
3826
+ testScript
3827
+ })
3564
3828
  break
3565
3829
  }
3566
3830
  }
3567
- spinner.successAndStop('Socket.dev fix successful')
3831
+ // spinner.successAndStop('Socket.dev fix successful')
3568
3832
  }
3569
3833
 
3570
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$y } = constants
3834
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w } = constants
3571
3835
  const config$z = {
3572
3836
  commandName: 'fix',
3573
3837
  description: 'Fix "fixable" Socket alerts',
3574
3838
  hidden: true,
3575
3839
  flags: {
3576
- ...commonFlags
3840
+ ...commonFlags,
3841
+ test: {
3842
+ type: 'boolean',
3843
+ default: true,
3844
+ description: 'Very the fix by running unit tests'
3845
+ },
3846
+ testScript: {
3847
+ type: 'string',
3848
+ default: 'test',
3849
+ description: 'The test script to run for each fix attempt'
3850
+ }
3577
3851
  },
3578
3852
  help: (command, config) => `
3579
3853
  Usage
@@ -3596,10 +3870,17 @@ async function run$z(argv, importMeta, { parentName }) {
3596
3870
  parentName
3597
3871
  })
3598
3872
  if (cli.flags['dryRun']) {
3599
- logger.logger.log(DRY_RUN_BAIL_TEXT$y)
3873
+ logger.logger.log(DRY_RUN_BAIL_TEXT$w)
3600
3874
  return
3601
3875
  }
3602
- await runFix()
3876
+
3877
+ // Lazily access constants.spinner.
3878
+ const { spinner } = constants
3879
+ await runFix({
3880
+ spinner,
3881
+ test: Boolean(cli.flags['test']),
3882
+ testScript: cli.flags['testScript']
3883
+ })
3603
3884
  }
3604
3885
 
3605
3886
  async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
@@ -3624,10 +3905,10 @@ async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
3624
3905
  )
3625
3906
  spinner.successAndStop('Data fetched')
3626
3907
  if (result.success === false) {
3627
- return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result)
3908
+ handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result)
3628
3909
  }
3629
3910
  if (scoreResult.success === false) {
3630
- return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult)
3911
+ handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult)
3631
3912
  }
3632
3913
  const severityCount = shadowNpmInject.getSeverityCount(
3633
3914
  result.data,
@@ -3788,7 +4069,7 @@ async function handlePackageInfo({
3788
4069
  }
3789
4070
  }
3790
4071
 
3791
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$x } = constants
4072
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v } = constants
3792
4073
  const config$y = {
3793
4074
  commandName: 'info',
3794
4075
  description: 'Look up info regarding a package',
@@ -3856,7 +4137,7 @@ async function run$y(argv, importMeta, { parentName }) {
3856
4137
  const pkgVersion =
3857
4138
  versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1)
3858
4139
  if (cli.flags['dryRun']) {
3859
- logger.logger.log(DRY_RUN_BAIL_TEXT$x)
4140
+ logger.logger.log(DRY_RUN_BAIL_TEXT$v)
3860
4141
  return
3861
4142
  }
3862
4143
  await handlePackageInfo({
@@ -3893,7 +4174,6 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3893
4174
  if (!result.success) {
3894
4175
  logger.logger.fail('Authentication failed...')
3895
4176
  handleUnsuccessfulApiResponse('getOrganizations', result)
3896
- return
3897
4177
  }
3898
4178
  logger.logger.success('API key verified')
3899
4179
  const orgs = result.data
@@ -3931,16 +4211,24 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3931
4211
  }
3932
4212
  }
3933
4213
  spinner.stop()
3934
- const oldToken = shadowNpmInject.getConfigValue('apiToken')
4214
+ const previousPersistedToken = shadowNpmInject.getConfigValue('apiToken')
3935
4215
  try {
3936
4216
  applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)
3937
- logger.logger.success(`API credentials ${oldToken ? 'updated' : 'set'}`)
4217
+ logger.logger.success(
4218
+ `API credentials ${previousPersistedToken === apiToken ? 'refreshed' : previousPersistedToken ? 'updated' : 'set'}`
4219
+ )
4220
+ if (!shadowNpmInject.isReadOnlyConfig()) {
4221
+ logger.logger.log('')
4222
+ logger.logger.warn(
4223
+ 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the login was not persisted!'
4224
+ )
4225
+ }
3938
4226
  } catch {
3939
4227
  logger.logger.fail(`API login failed`)
3940
4228
  }
3941
4229
  }
3942
4230
 
3943
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w } = constants
4231
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u } = constants
3944
4232
  const config$x = {
3945
4233
  commandName: 'login',
3946
4234
  description: 'Socket API login',
@@ -3960,6 +4248,9 @@ const config$x = {
3960
4248
  Usage
3961
4249
  $ ${command}
3962
4250
 
4251
+ API Token Requirements
4252
+ - Quota: 1 unit
4253
+
3963
4254
  Logs into the Socket API by prompting for an API key
3964
4255
 
3965
4256
  Options
@@ -3985,7 +4276,7 @@ async function run$x(argv, importMeta, { parentName }) {
3985
4276
  const apiBaseUrl = cli.flags['apiBaseUrl']
3986
4277
  const apiProxy = cli.flags['apiProxy']
3987
4278
  if (cli.flags['dryRun']) {
3988
- logger.logger.log(DRY_RUN_BAIL_TEXT$w)
4279
+ logger.logger.log(DRY_RUN_BAIL_TEXT$u)
3989
4280
  return
3990
4281
  }
3991
4282
  if (!isInteractive()) {
@@ -4007,12 +4298,18 @@ function attemptLogout() {
4007
4298
  try {
4008
4299
  applyLogout()
4009
4300
  logger.logger.success('Successfully logged out')
4301
+ if (!shadowNpmInject.isReadOnlyConfig()) {
4302
+ logger.logger.log('')
4303
+ logger.logger.warn(
4304
+ 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the logout was not persisted!'
4305
+ )
4306
+ }
4010
4307
  } catch {
4011
4308
  logger.logger.fail('Failed to complete logout steps')
4012
4309
  }
4013
4310
  }
4014
4311
 
4015
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v } = constants
4312
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t } = constants
4016
4313
  const config$w = {
4017
4314
  commandName: 'logout',
4018
4315
  description: 'Socket API logout',
@@ -4040,7 +4337,7 @@ async function run$w(argv, importMeta, { parentName }) {
4040
4337
  parentName
4041
4338
  })
4042
4339
  if (cli.flags['dryRun']) {
4043
- logger.logger.log(DRY_RUN_BAIL_TEXT$v)
4340
+ logger.logger.log(DRY_RUN_BAIL_TEXT$t)
4044
4341
  return
4045
4342
  }
4046
4343
  attemptLogout()
@@ -4148,7 +4445,7 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
4148
4445
  }
4149
4446
  }
4150
4447
 
4151
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u } = constants
4448
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s } = constants
4152
4449
  const config$v = {
4153
4450
  commandName: 'gradle',
4154
4451
  description:
@@ -4295,7 +4592,7 @@ async function run$v(argv, importMeta, { parentName }) {
4295
4592
  .filter(Boolean)
4296
4593
  }
4297
4594
  if (cli.flags['dryRun']) {
4298
- logger.logger.log(DRY_RUN_BAIL_TEXT$u)
4595
+ logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4299
4596
  return
4300
4597
  }
4301
4598
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts)
@@ -4404,7 +4701,7 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
4404
4701
  }
4405
4702
  }
4406
4703
 
4407
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t } = constants
4704
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r } = constants
4408
4705
  const config$u = {
4409
4706
  commandName: 'scala',
4410
4707
  description:
@@ -4549,13 +4846,13 @@ async function run$u(argv, importMeta, { parentName }) {
4549
4846
  .filter(Boolean)
4550
4847
  }
4551
4848
  if (cli.flags['dryRun']) {
4552
- logger.logger.log(DRY_RUN_BAIL_TEXT$t)
4849
+ logger.logger.log(DRY_RUN_BAIL_TEXT$r)
4553
4850
  return
4554
4851
  }
4555
4852
  await convertSbtToMaven(target, bin, out, verbose, sbtOpts)
4556
4853
  }
4557
4854
 
4558
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s } = constants
4855
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q } = constants
4559
4856
  const config$t = {
4560
4857
  commandName: 'auto',
4561
4858
  description: 'Auto-detect build and attempt to generate manifest file',
@@ -4621,7 +4918,7 @@ async function run$t(argv, importMeta, { parentName }) {
4621
4918
  }
4622
4919
  subArgs.push(dir)
4623
4920
  if (cli.flags['dryRun']) {
4624
- logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4921
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q)
4625
4922
  return
4626
4923
  }
4627
4924
  await cmdManifestScala.run(subArgs, importMeta, {
@@ -4638,7 +4935,7 @@ async function run$t(argv, importMeta, { parentName }) {
4638
4935
  subArgs.push(cwd)
4639
4936
  }
4640
4937
  if (cli.flags['dryRun']) {
4641
- logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4938
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q)
4642
4939
  return
4643
4940
  }
4644
4941
  await cmdManifestGradle.run(subArgs, importMeta, {
@@ -4647,7 +4944,7 @@ async function run$t(argv, importMeta, { parentName }) {
4647
4944
  return
4648
4945
  }
4649
4946
  if (cli.flags['dryRun']) {
4650
- logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4947
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q)
4651
4948
  return
4652
4949
  }
4653
4950
 
@@ -4674,7 +4971,7 @@ async function run$t(argv, importMeta, { parentName }) {
4674
4971
  ).showHelp()
4675
4972
  }
4676
4973
 
4677
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r } = constants
4974
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p } = constants
4678
4975
 
4679
4976
  // TODO: we may want to dedupe some pieces for all gradle languages. I think it
4680
4977
  // makes sense to have separate commands for them and I think it makes
@@ -4827,7 +5124,7 @@ async function run$s(argv, importMeta, { parentName }) {
4827
5124
  .filter(Boolean)
4828
5125
  }
4829
5126
  if (cli.flags['dryRun']) {
4830
- logger.logger.log(DRY_RUN_BAIL_TEXT$r)
5127
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p)
4831
5128
  return
4832
5129
  }
4833
5130
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts)
@@ -4878,7 +5175,7 @@ async function wrapNpm(argv) {
4878
5175
  await shadowBin(NPM$8, argv)
4879
5176
  }
4880
5177
 
4881
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q, NPM: NPM$7 } = constants
5178
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o, NPM: NPM$7 } = constants
4882
5179
  const config$q = {
4883
5180
  commandName: 'npm',
4884
5181
  description: `${NPM$7} wrapper functionality`,
@@ -4905,7 +5202,7 @@ async function run$q(argv, importMeta, { parentName }) {
4905
5202
  parentName
4906
5203
  })
4907
5204
  if (cli.flags['dryRun']) {
4908
- logger.logger.log(DRY_RUN_BAIL_TEXT$q)
5205
+ logger.logger.log(DRY_RUN_BAIL_TEXT$o)
4909
5206
  return
4910
5207
  }
4911
5208
  await wrapNpm(argv)
@@ -4918,7 +5215,7 @@ async function wrapNpx(argv) {
4918
5215
  await shadowBin(NPX$2, argv)
4919
5216
  }
4920
5217
 
4921
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p, NPX: NPX$1 } = constants
5218
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n, NPX: NPX$1 } = constants
4922
5219
  const config$p = {
4923
5220
  commandName: 'npx',
4924
5221
  description: `${NPX$1} wrapper functionality`,
@@ -4945,13 +5242,13 @@ async function run$p(argv, importMeta, { parentName }) {
4945
5242
  parentName
4946
5243
  })
4947
5244
  if (cli.flags['dryRun']) {
4948
- logger.logger.log(DRY_RUN_BAIL_TEXT$p)
5245
+ logger.logger.log(DRY_RUN_BAIL_TEXT$n)
4949
5246
  return
4950
5247
  }
4951
5248
  await wrapNpx(argv)
4952
5249
  }
4953
5250
 
4954
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o } = constants
5251
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m } = constants
4955
5252
  const config$o = {
4956
5253
  commandName: 'oops',
4957
5254
  description: 'Trigger an intentional error (for development)',
@@ -4979,7 +5276,7 @@ async function run$o(argv, importMeta, { parentName }) {
4979
5276
  parentName
4980
5277
  })
4981
5278
  if (cli.flags['dryRun']) {
4982
- logger.logger.log(DRY_RUN_BAIL_TEXT$o)
5279
+ logger.logger.log(DRY_RUN_BAIL_TEXT$m)
4983
5280
  return
4984
5281
  }
4985
5282
  throw new Error('This error was intentionally left blank')
@@ -5868,7 +6165,7 @@ async function applyOptimization(cwd, pin, prod) {
5868
6165
  }
5869
6166
  }
5870
6167
 
5871
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n } = constants
6168
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l } = constants
5872
6169
  const config$n = {
5873
6170
  commandName: 'optimize',
5874
6171
  description: 'Optimize dependencies with @socketregistry overrides',
@@ -5912,7 +6209,7 @@ async function run$n(argv, importMeta, { parentName }) {
5912
6209
  })
5913
6210
  const cwd = process.cwd()
5914
6211
  if (cli.flags['dryRun']) {
5915
- logger.logger.log(DRY_RUN_BAIL_TEXT$n)
6212
+ logger.logger.log(DRY_RUN_BAIL_TEXT$l)
5916
6213
  return
5917
6214
  }
5918
6215
  await applyOptimization(
@@ -5935,7 +6232,6 @@ async function fetchOrganization() {
5935
6232
  spinner.successAndStop('Received organization list response.')
5936
6233
  if (!result.success) {
5937
6234
  handleUnsuccessfulApiResponse('getOrganizations', result)
5938
- return
5939
6235
  }
5940
6236
  return result.data
5941
6237
  }
@@ -6014,7 +6310,7 @@ async function handleOrganizationList(outputKind = 'text') {
6014
6310
  await outputOrganizationList(data, outputKind)
6015
6311
  }
6016
6312
 
6017
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m } = constants
6313
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k } = constants
6018
6314
  const config$m = {
6019
6315
  commandName: 'list',
6020
6316
  description: 'List organizations associated with the API key used',
@@ -6027,6 +6323,10 @@ const config$m = {
6027
6323
  Usage
6028
6324
  $ ${command}
6029
6325
 
6326
+ API Token Requirements
6327
+ - Quota: 1 unit
6328
+ - Permissions: none (does need a token)
6329
+
6030
6330
  Options
6031
6331
  ${getFlagListOutput(config$m.flags, 6)}
6032
6332
  `
@@ -6067,7 +6367,7 @@ async function run$m(argv, importMeta, { parentName }) {
6067
6367
  return
6068
6368
  }
6069
6369
  if (cli.flags['dryRun']) {
6070
- logger.logger.log(DRY_RUN_BAIL_TEXT$m)
6370
+ logger.logger.log(DRY_RUN_BAIL_TEXT$k)
6071
6371
  return
6072
6372
  }
6073
6373
  await handleOrganizationList(json ? 'json' : markdown ? 'markdown' : 'text')
@@ -6086,7 +6386,6 @@ async function fetchLicensePolicy(orgSlug) {
6086
6386
  spinner.successAndStop('Received organization license policy response.')
6087
6387
  if (!result.success) {
6088
6388
  handleUnsuccessfulApiResponse('getOrgLicensePolicy', result)
6089
- return
6090
6389
  }
6091
6390
  return result.data
6092
6391
  }
@@ -6131,7 +6430,7 @@ async function handleLicensePolicy(orgSlug, outputKind) {
6131
6430
  await outputLicensePolicy(data, outputKind)
6132
6431
  }
6133
6432
 
6134
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l } = constants
6433
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j } = constants
6135
6434
 
6136
6435
  // TODO: secret toplevel alias `socket license policy`?
6137
6436
  const config$l = {
@@ -6146,6 +6445,10 @@ const config$l = {
6146
6445
  Usage
6147
6446
  $ ${command} <org slug>
6148
6447
 
6448
+ API Token Requirements
6449
+ - Quota: 1 unit
6450
+ - Permissions: license-policy:read
6451
+
6149
6452
  Options
6150
6453
  ${getFlagListOutput(config$l.flags, 6)}
6151
6454
 
@@ -6202,7 +6505,7 @@ async function run$l(argv, importMeta, { parentName }) {
6202
6505
  return
6203
6506
  }
6204
6507
  if (cli.flags['dryRun']) {
6205
- logger.logger.log(DRY_RUN_BAIL_TEXT$l)
6508
+ logger.logger.log(DRY_RUN_BAIL_TEXT$j)
6206
6509
  return
6207
6510
  }
6208
6511
  await handleLicensePolicy(
@@ -6224,7 +6527,6 @@ async function fetchSecurityPolicy(orgSlug) {
6224
6527
  spinner.successAndStop('Received organization security policy response.')
6225
6528
  if (!result.success) {
6226
6529
  handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result)
6227
- return
6228
6530
  }
6229
6531
  return result.data
6230
6532
  }
@@ -6270,7 +6572,7 @@ async function handleSecurityPolicy(orgSlug, outputKind) {
6270
6572
  await outputSecurityPolicy(data, outputKind)
6271
6573
  }
6272
6574
 
6273
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k } = constants
6575
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i } = constants
6274
6576
 
6275
6577
  // TODO: secret toplevel alias `socket security policy`?
6276
6578
  const config$k = {
@@ -6285,6 +6587,10 @@ const config$k = {
6285
6587
  Usage
6286
6588
  $ ${command} <org slug>
6287
6589
 
6590
+ API Token Requirements
6591
+ - Quota: 1 unit
6592
+ - Permissions: security-policy:read
6593
+
6288
6594
  Options
6289
6595
  ${getFlagListOutput(config$k.flags, 6)}
6290
6596
 
@@ -6341,7 +6647,7 @@ async function run$k(argv, importMeta, { parentName }) {
6341
6647
  return
6342
6648
  }
6343
6649
  if (cli.flags['dryRun']) {
6344
- logger.logger.log(DRY_RUN_BAIL_TEXT$k)
6650
+ logger.logger.log(DRY_RUN_BAIL_TEXT$i)
6345
6651
  return
6346
6652
  }
6347
6653
  await handleSecurityPolicy(
@@ -6389,7 +6695,6 @@ async function fetchQuota() {
6389
6695
  spinner.successAndStop('Received organization quota response.')
6390
6696
  if (!result.success) {
6391
6697
  handleUnsuccessfulApiResponse('getQuota', result)
6392
- return
6393
6698
  }
6394
6699
  return result.data
6395
6700
  }
@@ -6428,7 +6733,7 @@ async function handleQuota(outputKind = 'text') {
6428
6733
  await outputQuota(data, outputKind)
6429
6734
  }
6430
6735
 
6431
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j } = constants
6736
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h } = constants
6432
6737
  const config$j = {
6433
6738
  commandName: 'quota',
6434
6739
  description: 'List organizations associated with the API key used',
@@ -6481,7 +6786,7 @@ async function run$j(argv, importMeta, { parentName }) {
6481
6786
  return
6482
6787
  }
6483
6788
  if (cli.flags['dryRun']) {
6484
- logger.logger.log(DRY_RUN_BAIL_TEXT$j)
6789
+ logger.logger.log(DRY_RUN_BAIL_TEXT$h)
6485
6790
  return
6486
6791
  }
6487
6792
  await handleQuota(json ? 'json' : markdown ? 'markdown' : 'text')
@@ -6514,6 +6819,7 @@ const cmdOrganization = {
6514
6819
  }
6515
6820
  }
6516
6821
 
6822
+ const { SOCKET_CLI_ISSUES_URL } = constants
6517
6823
  async function fetchPurlDeepScore(purl) {
6518
6824
  const apiToken = shadowNpmInject.getDefaultToken()
6519
6825
  if (!apiToken) {
@@ -6555,7 +6861,7 @@ async function fetchPurlDeepScore(purl) {
6555
6861
  return JSON.parse(data)
6556
6862
  } catch (e) {
6557
6863
  throw new Error(
6558
- 'Was unable to JSON parse the input from the server. It may not have been a proper JSON response. Please report this problem.'
6864
+ `Unable to parse JSON response from the Socket API.\nPlease report to ${SOCKET_CLI_ISSUES_URL}`
6559
6865
  )
6560
6866
  }
6561
6867
  }
@@ -6748,7 +7054,7 @@ async function outputPurlScore(purl, data, outputKind) {
6748
7054
  )
6749
7055
  } else {
6750
7056
  logger.logger.log(
6751
- 'This package had no alerts and neither did any of its direct/transitive dependencies.'
7057
+ 'This package had no alerts and neither did any of its direct/transitive dependencies'
6752
7058
  )
6753
7059
  }
6754
7060
  logger.logger.log('')
@@ -6823,7 +7129,7 @@ function parsePackageSpecifiers(ecosystem, pkgs) {
6823
7129
  }
6824
7130
  }
6825
7131
 
6826
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i } = constants
7132
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g } = constants
6827
7133
  const config$i = {
6828
7134
  commandName: 'score',
6829
7135
  description:
@@ -6837,13 +7143,13 @@ const config$i = {
6837
7143
  Usage
6838
7144
  $ ${command} <<ecosystem> <name> | <purl>>
6839
7145
 
7146
+ API Token Requirements
7147
+ - Quota: 100 units
7148
+ - Permissions: packages:list
7149
+
6840
7150
  Options
6841
7151
  ${getFlagListOutput(config.flags, 6)}
6842
7152
 
6843
- Requirements
6844
- - quota: 100
6845
- - scope: \`packages:list\`
6846
-
6847
7153
  Show deep scoring details for one package. The score will reflect the package
6848
7154
  itself, any of its dependencies, and any of its transitive dependencies.
6849
7155
 
@@ -6915,7 +7221,7 @@ async function run$i(argv, importMeta, { parentName }) {
6915
7221
  return
6916
7222
  }
6917
7223
  if (cli.flags['dryRun']) {
6918
- logger.logger.log(DRY_RUN_BAIL_TEXT$i)
7224
+ logger.logger.log(DRY_RUN_BAIL_TEXT$g)
6919
7225
  return
6920
7226
  }
6921
7227
  await handlePurlDeepScore(
@@ -6939,10 +7245,6 @@ async function fetchPurlsShallowScore(purls) {
6939
7245
  sockSdk.batchPackageFetch(
6940
7246
  {
6941
7247
  alerts: 'true'
6942
- // compact: false,
6943
- // fixable: false,
6944
- // licenseattrib: false,
6945
- // licensedetails: false
6946
7248
  },
6947
7249
  {
6948
7250
  components: purls.map(purl => ({
@@ -6955,7 +7257,6 @@ async function fetchPurlsShallowScore(purls) {
6955
7257
  spinner.successAndStop('Request completed')
6956
7258
  if (!result.success) {
6957
7259
  handleUnsuccessfulApiResponse('batchPackageFetch', result)
6958
- return
6959
7260
  }
6960
7261
  return result
6961
7262
  }
@@ -7112,7 +7413,7 @@ async function handlePurlsShallowScore({ outputKind, purls }) {
7112
7413
  outputPurlsShallowScore(purls, packageData.data, outputKind)
7113
7414
  }
7114
7415
 
7115
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h } = constants
7416
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f } = constants
7116
7417
  const config$h = {
7117
7418
  commandName: 'shallow',
7118
7419
  description:
@@ -7126,13 +7427,13 @@ const config$h = {
7126
7427
  Usage
7127
7428
  $ ${command} <<ecosystem> <name> [<name> ...] | <purl> [<purl> ...]>
7128
7429
 
7430
+ API Token Requirements
7431
+ - Quota: 100 units
7432
+ - Permissions: packages:list
7433
+
7129
7434
  Options
7130
7435
  ${getFlagListOutput(config.flags, 6)}
7131
7436
 
7132
- Requirements
7133
- - quota: 100
7134
- - scope: \`packages:list\`
7135
-
7136
7437
  Show scoring details for one or more packages purely based on their own package.
7137
7438
  This means that any dependency scores are not reflected by the score. You can
7138
7439
  use the \`socket package score <pkg>\` command to get its full transitive score.
@@ -7203,7 +7504,7 @@ async function run$h(argv, importMeta, { parentName }) {
7203
7504
  return
7204
7505
  }
7205
7506
  if (cli.flags['dryRun']) {
7206
- logger.logger.log(DRY_RUN_BAIL_TEXT$h)
7507
+ logger.logger.log(DRY_RUN_BAIL_TEXT$f)
7207
7508
  return
7208
7509
  }
7209
7510
  await handlePurlsShallowScore({
@@ -7256,7 +7557,7 @@ async function runRawNpm(argv) {
7256
7557
  await spawnPromise
7257
7558
  }
7258
7559
 
7259
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g, NPM } = constants
7560
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e, NPM } = constants
7260
7561
  const config$g = {
7261
7562
  commandName: 'raw-npm',
7262
7563
  description: `Temporarily disable the Socket ${NPM} wrapper`,
@@ -7284,7 +7585,7 @@ async function run$g(argv, importMeta, { parentName }) {
7284
7585
  parentName
7285
7586
  })
7286
7587
  if (cli.flags['dryRun']) {
7287
- logger.logger.log(DRY_RUN_BAIL_TEXT$g)
7588
+ logger.logger.log(DRY_RUN_BAIL_TEXT$e)
7288
7589
  return
7289
7590
  }
7290
7591
  await runRawNpm(argv)
@@ -7306,7 +7607,7 @@ async function runRawNpx(argv) {
7306
7607
  await spawnPromise
7307
7608
  }
7308
7609
 
7309
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f, NPX } = constants
7610
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d, NPX } = constants
7310
7611
  const config$f = {
7311
7612
  commandName: 'raw-npx',
7312
7613
  description: `Temporarily disable the Socket ${NPX} wrapper`,
@@ -7334,242 +7635,12 @@ async function run$f(argv, importMeta, { parentName }) {
7334
7635
  parentName
7335
7636
  })
7336
7637
  if (cli.flags['dryRun']) {
7337
- logger.logger.log(DRY_RUN_BAIL_TEXT$f)
7638
+ logger.logger.log(DRY_RUN_BAIL_TEXT$d)
7338
7639
  return
7339
7640
  }
7340
7641
  await runRawNpx(argv)
7341
7642
  }
7342
7643
 
7343
- const { DRY_RUN_LABEL } = constants
7344
- async function createReport(socketConfig, inputPaths, { cwd, dryRun }) {
7345
- // Lazily access constants.spinner.
7346
- const { spinner } = constants
7347
- const sockSdk = await shadowNpmInject.setupSdk()
7348
- const supportedFiles = await sockSdk
7349
- .getReportSupportedFiles()
7350
- .then(res => {
7351
- if (!res.success) {
7352
- handleUnsuccessfulApiResponse('getReportSupportedFiles', res)
7353
- }
7354
- return res.data
7355
- })
7356
- .catch(cause => {
7357
- throw new Error('Failed getting supported files for report', {
7358
- cause
7359
- })
7360
- })
7361
- const packagePaths = await shadowNpmPaths.getPackageFilesForScan(
7362
- cwd,
7363
- inputPaths,
7364
- supportedFiles,
7365
- socketConfig
7366
- )
7367
- const packagePathsCount = packagePaths.length
7368
- if (packagePathsCount && debug.isDebug()) {
7369
- for (const pkgPath of packagePaths) {
7370
- debug.debugLog(`Uploading: ${pkgPath}`)
7371
- }
7372
- }
7373
- if (dryRun) {
7374
- debug.debugLog(`${DRY_RUN_LABEL}: Skipped actual upload`)
7375
- return undefined
7376
- }
7377
- spinner.start(
7378
- `Creating report with ${packagePathsCount} package ${words.pluralize('file', packagePathsCount)}`
7379
- )
7380
- const apiCall = sockSdk.createReportFromFilePaths(
7381
- packagePaths,
7382
- cwd,
7383
- socketConfig?.issueRules
7384
- )
7385
- const result = await handleApiCall(apiCall, 'creating report')
7386
- if (!result.success) {
7387
- handleUnsuccessfulApiResponse('createReport', result)
7388
- return undefined
7389
- }
7390
- spinner.successAndStop()
7391
- return result
7392
- }
7393
-
7394
- async function getSocketConfig(absoluteConfigPath) {
7395
- const socketConfig = await config$K
7396
- .readSocketConfig(absoluteConfigPath)
7397
- .catch(cause => {
7398
- if (
7399
- cause &&
7400
- typeof cause === 'object' &&
7401
- cause instanceof config$K.SocketValidationError
7402
- ) {
7403
- // Inspired by workbox-build:
7404
- // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71
7405
- const betterErrors = betterAjvErrors.betterAjvErrors({
7406
- basePath: 'config',
7407
- data: cause.data,
7408
- errors: cause.validationErrors,
7409
- schema: cause.schema
7410
- })
7411
- throw new shadowNpmInject.InputError(
7412
- 'The socket.yml config is not valid',
7413
- betterErrors
7414
- .map(
7415
- err =>
7416
- `[${err.path}] ${err.message}.${err.suggestion ? err.suggestion : ''}`
7417
- )
7418
- .join('\n')
7419
- )
7420
- } else {
7421
- throw new Error('Failed to read socket.yml config', {
7422
- cause
7423
- })
7424
- }
7425
- })
7426
- return socketConfig
7427
- }
7428
-
7429
- const MAX_TIMEOUT_RETRY = 5
7430
- const HTTP_CODE_TIMEOUT = 524
7431
- async function fetchReportData$1(reportId, includeAllIssues, strict) {
7432
- // Lazily access constants.spinner.
7433
- const { spinner } = constants
7434
- spinner.log('Fetching report with ID ${reportId} (this could take a while)')
7435
- spinner.start(`Fetch started... (this could take a while)`)
7436
- const sockSdk = await shadowNpmInject.setupSdk()
7437
- let result
7438
- for (let retry = 1; !result; ++retry) {
7439
- try {
7440
- // eslint-disable-next-line no-await-in-loop
7441
- result = await handleApiCall(
7442
- sockSdk.getReport(reportId),
7443
- 'fetching report'
7444
- )
7445
- } catch (err) {
7446
- if (
7447
- retry >= MAX_TIMEOUT_RETRY ||
7448
- !(err instanceof Error) ||
7449
- err.cause?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT
7450
- ) {
7451
- spinner.stop(`Failed to fetch report`)
7452
- throw err
7453
- }
7454
- spinner.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`)
7455
- }
7456
- }
7457
- if (!result.success) {
7458
- return handleUnsuccessfulApiResponse('getReport', result)
7459
- }
7460
-
7461
- // Conclude the status of the API call.
7462
- if (strict) {
7463
- if (result.data.healthy) {
7464
- spinner.success('Report result is healthy and great!')
7465
- } else {
7466
- spinner.error('Report result deemed unhealthy for project')
7467
- }
7468
- } else if (!result.data.healthy) {
7469
- const severityCount = shadowNpmInject.getSeverityCount(
7470
- result.data.issues,
7471
- includeAllIssues ? undefined : 'high'
7472
- )
7473
- const issueSummary = shadowNpmInject.formatSeverityCount(severityCount)
7474
- spinner.success(`Report has these issues: ${issueSummary}`)
7475
- } else {
7476
- spinner.success('Report has no issues')
7477
- }
7478
- spinner.stop()
7479
- return result.data
7480
- }
7481
-
7482
- function formatReportDataOutput(
7483
- reportId,
7484
- data,
7485
- commandName,
7486
- outputKind,
7487
- strict,
7488
- artifacts
7489
- ) {
7490
- if (outputKind === 'json') {
7491
- logger.logger.log(JSON.stringify(data, undefined, 2))
7492
- } else {
7493
- const format = new shadowNpmInject.ColorOrMarkdown(
7494
- outputKind === 'markdown'
7495
- )
7496
- logger.logger.log(commonTags.stripIndents`
7497
- Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {
7498
- fallbackToUrl: true
7499
- })}`)
7500
- if (outputKind === 'print') {
7501
- logger.logger.log(data)
7502
- logger.logger.log(
7503
- colors.dim(
7504
- `Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`
7505
- )
7506
- )
7507
- logger.logger.log('The scan:')
7508
- logger.logger.log(artifacts)
7509
- }
7510
- }
7511
- if (strict && !data.healthy) {
7512
-
7513
- process$1.exit(1)
7514
- }
7515
- }
7516
-
7517
- async function fetchScan(orgSlug, scanId) {
7518
- const apiToken = shadowNpmInject.getDefaultToken()
7519
- if (!apiToken) {
7520
- throw new shadowNpmInject.AuthError(
7521
- 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
7522
- )
7523
- }
7524
-
7525
- // Lazily access constants.spinner.
7526
- const { spinner } = constants
7527
- spinner.start('Fetching scan data...')
7528
- const response = await queryApi(
7529
- `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,
7530
- apiToken
7531
- )
7532
- spinner.successAndStop('Received response while fetching scan data.')
7533
- if (!response.ok) {
7534
- const err = await handleApiError(response.status)
7535
- logger.logger.fail(
7536
- failMsgWithBadge(response.statusText, `Fetch error: ${err}`)
7537
- )
7538
- return
7539
- }
7540
-
7541
- // This is nd-json; each line is a json object
7542
- const jsons = await response.text()
7543
- const lines = jsons.split('\n').filter(Boolean)
7544
- const data = lines.map(line => {
7545
- try {
7546
- return JSON.parse(line)
7547
- } catch {
7548
- console.error(
7549
- 'At least one line item was returned that could not be parsed as JSON...'
7550
- )
7551
- return {}
7552
- }
7553
- })
7554
- return data
7555
- }
7556
-
7557
- async function viewReport(reportId, { all, commandName, outputKind, strict }) {
7558
- const result = await fetchReportData$1(reportId, all, strict)
7559
- const artifacts = await fetchScan('socketdev', reportId)
7560
- if (result) {
7561
- formatReportDataOutput(
7562
- reportId,
7563
- result,
7564
- commandName,
7565
- outputKind,
7566
- strict,
7567
- artifacts
7568
- )
7569
- }
7570
- }
7571
-
7572
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e } = constants
7573
7644
  const config$e = {
7574
7645
  commandName: 'create',
7575
7646
  description: '[Deprecated] Create a project report',
@@ -7601,57 +7672,17 @@ const cmdReportCreate = {
7601
7672
  run: run$e
7602
7673
  }
7603
7674
  async function run$e(argv, importMeta, { parentName }) {
7604
- const cli = meowOrExit({
7675
+ meowOrExit({
7605
7676
  argv,
7606
7677
  config: config$e,
7607
7678
  importMeta,
7608
7679
  parentName
7609
7680
  })
7610
-
7611
- // TODO: Allow setting a custom cwd and/or configFile path?
7612
- const cwd = process.cwd()
7613
- const absoluteConfigPath = path$1.join(cwd, 'socket.yml')
7614
- const dryRun = Boolean(cli.flags['dryRun'])
7615
- const json = Boolean(cli.flags['json'])
7616
- const markdown = Boolean(cli.flags['markdown'])
7617
- const strict = Boolean(cli.flags['strict'])
7618
- const includeAllIssues = Boolean(cli.flags['all'])
7619
- const view = Boolean(cli.flags['view'])
7620
-
7621
- // Note exiting earlier to skirt a hidden auth requirement
7622
- if (cli.flags['dryRun']) {
7623
- logger.logger.log(DRY_RUN_BAIL_TEXT$e)
7624
- return
7625
- }
7626
- const socketConfig = await getSocketConfig(absoluteConfigPath)
7627
- const result = await createReport(socketConfig, cli.input, {
7628
- cwd,
7629
- dryRun
7630
- })
7631
- const commandName = `${parentName} ${config$e.commandName}`
7632
- if (result?.success) {
7633
- if (view) {
7634
- const reportId = result.data.id
7635
- await viewReport(reportId, {
7636
- all: includeAllIssues,
7637
- commandName,
7638
- outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
7639
- strict
7640
- })
7641
- } else if (json) {
7642
- logger.logger.log(JSON.stringify(result.data, undefined, 2))
7643
- } else {
7644
- const format = new shadowNpmInject.ColorOrMarkdown(markdown)
7645
- logger.logger.log(
7646
- `New report: ${format.hyperlink(result.data.id, result.data.url, {
7647
- fallbackToUrl: true
7648
- })}`
7649
- )
7650
- }
7651
- }
7681
+ logger.logger.fail(
7682
+ 'This command has been sunset. Instead, please look at `socket scan create` to create scans and `socket scan report` to view a report of your scans.'
7683
+ )
7652
7684
  }
7653
7685
 
7654
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d } = constants
7655
7686
  const config$d = {
7656
7687
  commandName: 'view',
7657
7688
  description: '[Deprecated] View a project report',
@@ -7672,49 +7703,15 @@ const cmdReportView = {
7672
7703
  run: run$d
7673
7704
  }
7674
7705
  async function run$d(argv, importMeta, { parentName }) {
7675
- const cli = meowOrExit({
7706
+ meowOrExit({
7676
7707
  argv,
7677
7708
  config: config$d,
7678
7709
  importMeta,
7679
7710
  parentName
7680
7711
  })
7681
- const { json, markdown } = cli.flags
7682
- const [reportId = '', ...extraInput] = cli.input
7683
- const wasBadInput = handleBadInput(
7684
- {
7685
- test: reportId,
7686
- message: 'Need at least one report ID',
7687
- pass: 'ok',
7688
- fail: 'missing'
7689
- },
7690
- {
7691
- nook: true,
7692
- test: extraInput.length === 0,
7693
- message: 'Can only handle a single report ID',
7694
- pass: 'ok',
7695
- fail: 'received ' + (extraInput.length + 1)
7696
- },
7697
- {
7698
- nook: true,
7699
- test: !json || !markdown,
7700
- message: 'The json and markdown flags cannot be both set, pick one',
7701
- pass: 'ok',
7702
- fail: 'omit one'
7703
- }
7712
+ logger.logger.fail(
7713
+ 'This command has been sunset. Instead, please look at `socket scan create` to create scans and `socket scan report` to view a report of your scans.'
7704
7714
  )
7705
- if (wasBadInput) {
7706
- return
7707
- }
7708
- if (cli.flags['dryRun']) {
7709
- logger.logger.log(DRY_RUN_BAIL_TEXT$d)
7710
- return
7711
- }
7712
- await viewReport(reportId, {
7713
- all: Boolean(cli.flags['all']),
7714
- commandName: `${parentName} ${config$d.commandName}`,
7715
- outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
7716
- strict: Boolean(cli.flags['strict'])
7717
- })
7718
7715
  }
7719
7716
 
7720
7717
  const description$2 = '[Deprecated] Project report related commands'
@@ -7764,7 +7761,6 @@ async function fetchCreateRepo({
7764
7761
  spinner.successAndStop('Received response requesting to create a repository.')
7765
7762
  if (!result.success) {
7766
7763
  handleUnsuccessfulApiResponse('createOrgRepo', result)
7767
- return
7768
7764
  }
7769
7765
  return result.data
7770
7766
  }
@@ -7837,6 +7833,10 @@ const config$c = {
7837
7833
  Usage
7838
7834
  $ ${command} <org slug>
7839
7835
 
7836
+ API Token Requirements
7837
+ - Quota: 1 unit
7838
+ - Permissions: repo:create
7839
+
7840
7840
  Options
7841
7841
  ${getFlagListOutput(config.flags, 6)}
7842
7842
 
@@ -7912,7 +7912,6 @@ async function handleDeleteRepo(orgSlug, repoName) {
7912
7912
  )
7913
7913
  if (!result.success) {
7914
7914
  handleUnsuccessfulApiResponse('deleteOrgRepo', result)
7915
- return
7916
7915
  }
7917
7916
  spinner.successAndStop('Repository deleted successfully')
7918
7917
  }
@@ -7929,6 +7928,10 @@ const config$b = {
7929
7928
  Usage
7930
7929
  $ ${command} <org slug> <repo slug>
7931
7930
 
7931
+ API Token Requirements
7932
+ - Quota: 1 unit
7933
+ - Permissions: repo:delete
7934
+
7932
7935
  Options
7933
7936
  ${getFlagListOutput(config.flags, 6)}
7934
7937
 
@@ -8003,7 +8006,6 @@ async function fetchListRepos({ direction, orgSlug, page, per_page, sort }) {
8003
8006
  spinner.successAndStop('Received response for repository list.')
8004
8007
  if (!result.success) {
8005
8008
  handleUnsuccessfulApiResponse('getOrgRepoList', result)
8006
- return
8007
8009
  }
8008
8010
  return result.data
8009
8011
  }
@@ -8105,6 +8107,10 @@ const config$a = {
8105
8107
  Usage
8106
8108
  $ ${command} <org slug>
8107
8109
 
8110
+ API Token Requirements
8111
+ - Quota: 1 unit
8112
+ - Permissions: repo:list
8113
+
8108
8114
  Options
8109
8115
  ${getFlagListOutput(config.flags, 6)}
8110
8116
 
@@ -8197,7 +8203,6 @@ async function fetchUpdateRepo({
8197
8203
  spinner.successAndStop('Received response trying to update a repository')
8198
8204
  if (!result.success) {
8199
8205
  handleUnsuccessfulApiResponse('updateOrgRepo', result)
8200
- return
8201
8206
  }
8202
8207
  return result.data
8203
8208
  }
@@ -8270,6 +8275,10 @@ const config$9 = {
8270
8275
  Usage
8271
8276
  $ ${command} <org slug>
8272
8277
 
8278
+ API Token Requirements
8279
+ - Quota: 1 unit
8280
+ - Permissions: repo:update
8281
+
8273
8282
  Options
8274
8283
  ${getFlagListOutput(config.flags, 6)}
8275
8284
 
@@ -8346,7 +8355,6 @@ async function fetchViewRepo(orgSlug, repoName) {
8346
8355
  spinner.successAndStop('Received response while fetched repository data.')
8347
8356
  if (!result.success) {
8348
8357
  handleUnsuccessfulApiResponse('getOrgRepo', result)
8349
- return
8350
8358
  }
8351
8359
  return result.data
8352
8360
  }
@@ -8441,6 +8449,10 @@ const config$8 = {
8441
8449
  Usage
8442
8450
  $ ${command} <org slug>
8443
8451
 
8452
+ API Token Requirements
8453
+ - Quota: 1 unit
8454
+ - Permissions: repo:list
8455
+
8444
8456
  Options
8445
8457
  ${getFlagListOutput(config.flags, 6)}
8446
8458
 
@@ -8546,7 +8558,9 @@ async function fetchCreateOrgFullScan(
8546
8558
 
8547
8559
  // Lazily access constants.spinner.
8548
8560
  const { spinner } = constants
8549
- spinner.start(`Creating a scan with ${packagePaths.length} packages...`)
8561
+ spinner.start(
8562
+ `Sending request to create a scan with ${packagePaths.length} packages...`
8563
+ )
8550
8564
  const result = await handleApiCall(
8551
8565
  sockSdk.createOrgFullScan(
8552
8566
  orgSlug,
@@ -8563,10 +8577,9 @@ async function fetchCreateOrgFullScan(
8563
8577
  ),
8564
8578
  'Creating scan'
8565
8579
  )
8566
- spinner.successAndStop('Scan created successfully')
8580
+ spinner.successAndStop('Completed request to create a new scan.')
8567
8581
  if (!result.success) {
8568
8582
  handleUnsuccessfulApiResponse('CreateOrgFullScan', result)
8569
- return
8570
8583
  }
8571
8584
  return result.data
8572
8585
  }
@@ -8581,12 +8594,12 @@ async function fetchSupportedScanFileNames() {
8581
8594
  sockSdk.getReportSupportedFiles(),
8582
8595
  'fetching supported scan file types'
8583
8596
  )
8584
- spinner.successAndStop(
8597
+ spinner.stop()
8598
+ logger.logger.success(
8585
8599
  'Received response while fetched supported scan file types.'
8586
8600
  )
8587
8601
  if (!result.success) {
8588
8602
  handleUnsuccessfulApiResponse('getReportSupportedFiles', result)
8589
- return
8590
8603
  }
8591
8604
  return result.data
8592
8605
  }
@@ -8624,7 +8637,6 @@ async function handleCreateNewScan({
8624
8637
  cwd,
8625
8638
  targets,
8626
8639
  supportedFileNames
8627
- // socketConfig
8628
8640
  )
8629
8641
  handleBadInput({
8630
8642
  nook: true,
@@ -8688,7 +8700,7 @@ async function suggestOrgSlug() {
8688
8700
  }
8689
8701
  } else {
8690
8702
  logger.logger.fail(
8691
- 'Failed to lookup organization list from API, unable to suggest.'
8703
+ 'Failed to lookup organization list from API, unable to suggest'
8692
8704
  )
8693
8705
  }
8694
8706
  }
@@ -8943,6 +8955,10 @@ const config$7 = {
8943
8955
  Usage
8944
8956
  $ ${command} [...options] <org> <TARGET> [TARGET...]
8945
8957
 
8958
+ API Token Requirements
8959
+ - Quota: 1 unit
8960
+ - Permissions: full-scans:create
8961
+
8946
8962
  Uploads the specified "package.json" and lock files for JavaScript, Python,
8947
8963
  Go, Scala, Gradle, and Kotlin dependency manifests.
8948
8964
  If any folder is specified, the ones found in there recursively are uploaded.
@@ -8984,7 +9000,7 @@ async function run$7(argv, importMeta, { parentName }) {
8984
9000
  cwdOverride && cwdOverride !== 'process.cwd()'
8985
9001
  ? String(cwdOverride)
8986
9002
  : process.cwd()
8987
- let { branch: branchName, repo: repoName } = cli.flags
9003
+ let { branch: branchName = '', repo: repoName = '' } = cli.flags
8988
9004
 
8989
9005
  // We're going to need an api token to suggest data because those suggestions
8990
9006
  // must come from data we already know. Don't error on missing api token yet.
@@ -9112,7 +9128,6 @@ async function fetchDeleteOrgFullScan(orgSlug, scanId) {
9112
9128
  spinner.successAndStop('Received response for deleting a scan.')
9113
9129
  if (!result.success) {
9114
9130
  handleUnsuccessfulApiResponse('deleteOrgFullScan', result)
9115
- return
9116
9131
  }
9117
9132
  return result.data
9118
9133
  }
@@ -9142,6 +9157,10 @@ const config$6 = {
9142
9157
  Usage
9143
9158
  $ ${command} <org slug> <scan ID>
9144
9159
 
9160
+ API Token Requirements
9161
+ - Quota: 1 unit
9162
+ - Permissions: full-scans:delete
9163
+
9145
9164
  Options
9146
9165
  ${getFlagListOutput(config.flags, 6)}
9147
9166
 
@@ -9224,7 +9243,6 @@ async function fetchListScans({
9224
9243
  spinner.successAndStop(`Received response for list of scans.`)
9225
9244
  if (!result.success) {
9226
9245
  handleUnsuccessfulApiResponse('getOrgFullScanList', result)
9227
- return
9228
9246
  }
9229
9247
  return result.data
9230
9248
  }
@@ -9345,6 +9363,10 @@ const config$5 = {
9345
9363
  Usage
9346
9364
  $ ${command} <org slug>
9347
9365
 
9366
+ API Token Requirements
9367
+ - Quota: 1 unit
9368
+ - Permissions: full-scans:list
9369
+
9348
9370
  Options
9349
9371
  ${getFlagListOutput(config.flags, 6)}
9350
9372
 
@@ -9423,7 +9445,6 @@ async function fetchScanMetadata(orgSlug, scanId) {
9423
9445
  spinner.successAndStop('Received response for scan meta data.')
9424
9446
  if (!result.success) {
9425
9447
  handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result)
9426
- return
9427
9448
  }
9428
9449
  return result.data
9429
9450
  }
@@ -9485,6 +9506,10 @@ const config$4 = {
9485
9506
  Usage
9486
9507
  $ ${command} <org slug> <scan id>
9487
9508
 
9509
+ API Token Requirements
9510
+ - Quota: 1 unit
9511
+ - Permissions: full-scans:list
9512
+
9488
9513
  Options
9489
9514
  ${getFlagListOutput(config.flags, 6)}
9490
9515
 
@@ -9556,14 +9581,8 @@ async function run$4(argv, importMeta, { parentName }) {
9556
9581
  /**
9557
9582
  * This fetches all the relevant pieces of data to generate a report, given a
9558
9583
  * full scan ID.
9559
- * It can optionally only fetch the security or license side of things.
9560
9584
  */
9561
- async function fetchReportData(
9562
- orgSlug,
9563
- scanId,
9564
- // includeLicensePolicy: boolean,
9565
- includeSecurityPolicy
9566
- ) {
9585
+ async function fetchReportData(orgSlug, scanId, includeLicensePolicy) {
9567
9586
  const apiToken = shadowNpmInject.getDefaultToken()
9568
9587
  if (!apiToken) {
9569
9588
  throw new shadowNpmInject.AuthError(
@@ -9572,7 +9591,6 @@ async function fetchReportData(
9572
9591
  }
9573
9592
  const sockSdk = await shadowNpmInject.setupSdk(apiToken)
9574
9593
  let haveScan = false
9575
- // let haveLicensePolicy = false
9576
9594
  let haveSecurityPolicy = false
9577
9595
 
9578
9596
  // Lazily access constants.spinner.
@@ -9580,48 +9598,26 @@ async function fetchReportData(
9580
9598
  function updateProgress() {
9581
9599
  const needs = [
9582
9600
  !haveScan ? 'scan' : undefined,
9583
- // includeLicensePolicy && !haveLicensePolicy ? 'license policy' : undefined,
9584
- includeSecurityPolicy && !haveSecurityPolicy
9585
- ? 'security policy'
9586
- : undefined
9601
+ !haveSecurityPolicy ? 'security policy' : undefined
9587
9602
  ].filter(Boolean)
9588
- if (needs.length > 2) {
9589
- // .toOxford()
9590
- needs[needs.length - 1] = `and ${needs[needs.length - 1]}`
9591
- }
9592
9603
  const haves = [
9593
9604
  haveScan ? 'scan' : undefined,
9594
- // includeLicensePolicy && haveLicensePolicy ? 'license policy' : undefined,
9595
- includeSecurityPolicy && haveSecurityPolicy
9596
- ? 'security policy'
9597
- : undefined
9605
+ haveSecurityPolicy ? 'security policy' : undefined
9598
9606
  ].filter(Boolean)
9599
- if (haves.length > 2) {
9600
- // .toOxford()
9601
- haves[haves.length - 1] = `and ${haves[haves.length - 1]}`
9602
- }
9603
9607
  if (needs.length) {
9604
9608
  spinner.start(
9605
- `Fetching ${needs.join(needs.length > 2 ? ', ' : ' and ')}...${haves.length ? ` Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` : ''}`
9609
+ `Fetching ${needs.join(' and ')}...${haves.length ? ` Completed fetching ${haves.join(' and ')}.` : ''}`
9606
9610
  )
9607
9611
  } else {
9608
- spinner.successAndStop(
9609
- `Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.`
9610
- )
9612
+ spinner.successAndStop(`Completed fetching ${haves.join(' and ')}.`)
9611
9613
  }
9612
9614
  }
9613
9615
  updateProgress()
9614
-
9615
- // @ts-ignore
9616
- const [
9617
- scan,
9618
- // licensePolicyMaybe,
9619
- securityPolicyMaybe
9620
- ] = await Promise.all([
9616
+ const [scan, securityPolicyMaybe] = await Promise.all([
9621
9617
  (async () => {
9622
9618
  try {
9623
9619
  const response = await queryApi(
9624
- `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,
9620
+ `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}${includeLicensePolicy ? '?include_license_details=true' : ''}`,
9625
9621
  apiToken
9626
9622
  )
9627
9623
  haveScan = true
@@ -9647,32 +9643,16 @@ async function fetchReportData(
9647
9643
  })
9648
9644
  return data
9649
9645
  } catch (e) {
9650
- spinner.errorAndStop(
9651
- 'There was an issue while fetching full scan data.'
9652
- )
9646
+ spinner.errorAndStop('There was an issue while fetching full scan data')
9653
9647
  throw e
9654
9648
  }
9655
9649
  })(),
9656
- // includeLicensePolicy &&
9657
- // (async () => {
9658
- // const r = await sockSdk.getOrgSecurityPolicy(orgSlug)
9659
- // haveLicensePolicy = true
9660
- // updateProgress()
9661
- // return await handleApiCall(
9662
- // r,
9663
- // "looking up organization's license policy"
9664
- // )
9665
- // })(),
9666
- includeSecurityPolicy &&
9667
- (async () => {
9668
- const r = await sockSdk.getOrgSecurityPolicy(orgSlug)
9669
- haveSecurityPolicy = true
9670
- updateProgress()
9671
- return await handleApiCall(
9672
- r,
9673
- "looking up organization's security policy"
9674
- )
9675
- })()
9650
+ (async () => {
9651
+ const r = await sockSdk.getOrgSecurityPolicy(orgSlug)
9652
+ haveSecurityPolicy = true
9653
+ updateProgress()
9654
+ return await handleApiCall(r, "looking up organization's security policy")
9655
+ })()
9676
9656
  ]).finally(() => spinner.stop())
9677
9657
  if (!Array.isArray(scan)) {
9678
9658
  logger.logger.error('Was unable to fetch scan, bailing')
@@ -9680,55 +9660,30 @@ async function fetchReportData(
9680
9660
  return {
9681
9661
  ok: false,
9682
9662
  scan: undefined,
9683
- // licensePolicy: undefined,
9684
9663
  securityPolicy: undefined
9685
9664
  }
9686
9665
  }
9687
-
9688
- // // Note: security->license once the api ships in the sdk
9689
- // let licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'> =
9690
- // undefined
9691
- // if (includeLicensePolicy) {
9692
- // if (licensePolicyMaybe && licensePolicyMaybe.success) {
9693
- // licensePolicy = licensePolicyMaybe
9694
- // } else {
9695
- // logger.error('Was unable to fetch license policy, bailing')
9696
- // process.exitCode = 1
9697
- // return {
9698
- // ok: false,
9699
- // scan: undefined,
9700
- // licensePolicy: undefined,
9701
- // securityPolicy: undefined
9702
- // }
9703
- // }
9704
- // }
9705
-
9706
9666
  let securityPolicy = undefined
9707
- if (includeSecurityPolicy) {
9708
- if (securityPolicyMaybe && securityPolicyMaybe.success) {
9709
- securityPolicy = securityPolicyMaybe
9710
- } else {
9711
- logger.logger.error('Was unable to fetch security policy, bailing')
9712
- process.exitCode = 1
9713
- return {
9714
- ok: false,
9715
- scan: undefined,
9716
- // licensePolicy: undefined,
9717
- securityPolicy: undefined
9718
- }
9667
+ if (securityPolicyMaybe && securityPolicyMaybe.success) {
9668
+ securityPolicy = securityPolicyMaybe
9669
+ } else {
9670
+ logger.logger.error('Was unable to fetch security policy, bailing')
9671
+ process.exitCode = 1
9672
+ return {
9673
+ ok: false,
9674
+ scan: undefined,
9675
+ securityPolicy: undefined
9719
9676
  }
9720
9677
  }
9721
9678
  return {
9722
9679
  ok: true,
9723
9680
  scan,
9724
- // licensePolicy,
9725
9681
  securityPolicy
9726
9682
  }
9727
9683
  }
9728
9684
 
9729
9685
  function generateReport(
9730
9686
  scan,
9731
- _licensePolicy,
9732
9687
  securityPolicy,
9733
9688
  { fold, orgSlug, reportLevel, scanId, short, spinner }
9734
9689
  ) {
@@ -9754,6 +9709,14 @@ function generateReport(
9754
9709
  // - monitor/ignore: no action
9755
9710
  // - defer: unknown (no action)
9756
9711
 
9712
+ // Note: the server will emit alerts for license policy violations but
9713
+ // those are only included if you set the flag when requesting the scan
9714
+ // data. The alerts map to a single security policy key that determines
9715
+ // what to do with any violation, regardless of the concrete license.
9716
+ // That rule is called "License Policy Violation".
9717
+ // The license policy part is implicitly handled here. Either they are
9718
+ // included and may show up, or they are not and won't show up.
9719
+
9757
9720
  const violations = new Map()
9758
9721
  let healthy = true
9759
9722
  const securityRules = securityPolicy?.data.securityPolicyRules
@@ -10000,13 +9963,11 @@ function* walkNestedMap(map, keys = []) {
10000
9963
 
10001
9964
  async function outputScanReport(
10002
9965
  scan,
10003
- // licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'>,
10004
9966
  securityPolicy,
10005
9967
  {
10006
9968
  filePath,
10007
9969
  fold,
10008
9970
  includeLicensePolicy,
10009
- includeSecurityPolicy,
10010
9971
  orgSlug,
10011
9972
  outputKind,
10012
9973
  reportLevel,
@@ -10014,25 +9975,15 @@ async function outputScanReport(
10014
9975
  short
10015
9976
  }
10016
9977
  ) {
10017
- if (!includeSecurityPolicy) {
10018
- process.exitCode = 1
10019
- return // caller should assert
10020
- }
10021
- const scanReport = generateReport(
10022
- scan,
10023
- undefined,
10024
- // licensePolicy,
10025
- securityPolicy,
10026
- {
10027
- orgSlug,
10028
- scanId,
10029
- fold,
10030
- reportLevel,
10031
- short,
10032
- // Lazily access constants.spinner.
10033
- spinner: constants.spinner
10034
- }
10035
- )
9978
+ const scanReport = generateReport(scan, securityPolicy, {
9979
+ orgSlug,
9980
+ scanId,
9981
+ fold,
9982
+ reportLevel,
9983
+ short,
9984
+ // Lazily access constants.spinner.
9985
+ spinner: constants.spinner
9986
+ })
10036
9987
  if (!scanReport.healthy) {
10037
9988
  process.exitCode = 1
10038
9989
  }
@@ -10040,7 +9991,9 @@ async function outputScanReport(
10040
9991
  outputKind === 'json' ||
10041
9992
  (outputKind === 'text' && filePath && filePath.endsWith('.json'))
10042
9993
  ) {
10043
- const json = short ? JSON.stringify(scanReport) : toJsonReport(scanReport)
9994
+ const json = short
9995
+ ? JSON.stringify(scanReport)
9996
+ : toJsonReport(scanReport, includeLicensePolicy)
10044
9997
  if (filePath !== '-') {
10045
9998
  logger.logger.log('Writing json report to', filePath)
10046
9999
  return await fs.writeFile(filePath, json)
@@ -10051,7 +10004,7 @@ async function outputScanReport(
10051
10004
  if (outputKind === 'markdown' || filePath.endsWith('.md')) {
10052
10005
  const md = short
10053
10006
  ? `healthy = ${scanReport.healthy}`
10054
- : toMarkdownReport(scanReport)
10007
+ : toMarkdownReport(scanReport, includeLicensePolicy)
10055
10008
  if (filePath !== '-') {
10056
10009
  logger.logger.log('Writing markdown report to', filePath)
10057
10010
  return await fs.writeFile(filePath, md)
@@ -10067,10 +10020,11 @@ async function outputScanReport(
10067
10020
  })
10068
10021
  }
10069
10022
  }
10070
- function toJsonReport(report) {
10023
+ function toJsonReport(report, includeLicensePolicy) {
10071
10024
  const obj = mapToObject(report.alerts)
10072
10025
  const json = JSON.stringify(
10073
10026
  {
10027
+ includeLicensePolicy,
10074
10028
  ...report,
10075
10029
  alerts: obj
10076
10030
  },
@@ -10079,7 +10033,7 @@ function toJsonReport(report) {
10079
10033
  )
10080
10034
  return json
10081
10035
  }
10082
- function toMarkdownReport(report) {
10036
+ function toMarkdownReport(report, includeLicensePolicy) {
10083
10037
  const flatData = Array.from(walkNestedMap(report.alerts)).map(
10084
10038
  ({ keys, value }) => {
10085
10039
  const { manifest, policy, type, url } = value
@@ -10098,11 +10052,11 @@ function toMarkdownReport(report) {
10098
10052
  # Scan Policy Report
10099
10053
 
10100
10054
  This report tells you whether the results of a Socket scan results violate the
10101
- security or license policy set by your organization.
10055
+ security${includeLicensePolicy ? ' or license' : ''} policy set by your organization.
10102
10056
 
10103
10057
  ## Health status
10104
10058
 
10105
- ${report.healthy ? 'The scan *PASSES* all requirements set by your security and license policy.' : 'The scan *VIOLATES* one or more policies set to the "error" level.'}
10059
+ ${report.healthy ? `The scan *PASSES* all requirements set by your security${includeLicensePolicy ? ' and license' : ''} policy.` : 'The scan *VIOLATES* one or more policies set to the "error" level.'}
10106
10060
 
10107
10061
  ## Settings
10108
10062
 
@@ -10112,6 +10066,7 @@ Configuration used to generate this report:
10112
10066
  - Scan ID: ${report.scanId}
10113
10067
  - Alert folding: ${report.options.fold === 'none' ? 'none' : `up to ${report.options.fold}`}
10114
10068
  - Minimal policy level for alert to be included in report: ${report.options.reportLevel === 'defer' ? 'everything' : report.options.reportLevel}
10069
+ - Include license alerts: ${includeLicensePolicy ? 'yes' : 'no'}
10115
10070
 
10116
10071
  ## Alerts
10117
10072
 
@@ -10126,27 +10081,16 @@ async function handleScanReport({
10126
10081
  filePath,
10127
10082
  fold,
10128
10083
  includeLicensePolicy,
10129
- includeSecurityPolicy,
10130
10084
  orgSlug,
10131
10085
  outputKind,
10132
10086
  reportLevel,
10133
10087
  scanId,
10134
10088
  short
10135
10089
  }) {
10136
- if (!includeSecurityPolicy) {
10137
- process.exitCode = 1
10138
- return // caller should assert
10139
- }
10140
- const {
10141
- // licensePolicy,
10142
- ok,
10143
- scan,
10144
- securityPolicy
10145
- } = await fetchReportData(
10090
+ const { ok, scan, securityPolicy } = await fetchReportData(
10146
10091
  orgSlug,
10147
10092
  scanId,
10148
- // includeLicensePolicy
10149
- includeSecurityPolicy
10093
+ includeLicensePolicy
10150
10094
  )
10151
10095
  if (!ok) {
10152
10096
  return
@@ -10156,7 +10100,6 @@ async function handleScanReport({
10156
10100
  fold,
10157
10101
  scanId: scanId,
10158
10102
  includeLicensePolicy,
10159
- includeSecurityPolicy,
10160
10103
  orgSlug,
10161
10104
  outputKind,
10162
10105
  reportLevel,
@@ -10169,8 +10112,7 @@ const config$3 = {
10169
10112
  commandName: 'report',
10170
10113
  description:
10171
10114
  'Check whether a scan result passes the organizational policies (security, license)',
10172
- hidden: true,
10173
- // [beta]
10115
+ hidden: false,
10174
10116
  flags: {
10175
10117
  ...commonFlags,
10176
10118
  ...outputFlags,
@@ -10189,31 +10131,23 @@ const config$3 = {
10189
10131
  default: false,
10190
10132
  description: 'Report only the healthy status'
10191
10133
  },
10192
- // license: {
10193
- // type: 'boolean',
10194
- // default: true,
10195
- // description: 'Report the license policy status. Default: true'
10196
- // },
10197
- security: {
10134
+ license: {
10198
10135
  type: 'boolean',
10199
- default: true,
10200
- description: 'Report the security policy status. Default: true'
10136
+ default: false,
10137
+ description: 'Also report the license policy status. Default: false'
10201
10138
  }
10202
10139
  },
10203
10140
  help: (command, config) => `
10204
10141
  Usage
10205
10142
  $ ${command} <org slug> <scan ID> [path to output file]
10206
10143
 
10144
+ API Token Requirements
10145
+ - Quota: 2 units
10146
+ - Permissions: full-scans:list security-policy:read
10147
+
10207
10148
  Options
10208
10149
  ${getFlagListOutput(config.flags, 6)}
10209
10150
 
10210
- This consumes 1 quota unit plus 1 for each of the requested policy types.
10211
-
10212
- Note: By default it reports both so by default it consumes 3 quota units.
10213
-
10214
- Your API token will need the \`full-scans:list\` scope regardless. Additionally
10215
- it needs \`security-policy:read\` to report on the security policy.
10216
-
10217
10151
  By default the result is a nested object that looks like this:
10218
10152
  \`{[ecosystem]: {[pkgName]: {[version]: {[file]: {[type:loc]: policy}}}}\`
10219
10153
  You can fold this up to given level: 'pkg', 'version', 'file', and 'none'.
@@ -10225,6 +10159,7 @@ const config$3 = {
10225
10159
 
10226
10160
  Examples
10227
10161
  $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version
10162
+ $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --license --markdown --short
10228
10163
  `
10229
10164
  }
10230
10165
  const cmdScanReport = {
@@ -10242,10 +10177,9 @@ async function run$3(argv, importMeta, { parentName }) {
10242
10177
  const {
10243
10178
  fold = 'none',
10244
10179
  json,
10245
- // license,
10180
+ license,
10246
10181
  markdown,
10247
- reportLevel = 'warn',
10248
- security
10182
+ reportLevel = 'warn'
10249
10183
  } = cli.flags
10250
10184
  const defaultOrgSlug = shadowNpmInject.getConfigValue('defaultOrg')
10251
10185
  const orgSlug = defaultOrgSlug || cli.input[0] || ''
@@ -10292,9 +10226,7 @@ async function run$3(argv, importMeta, { parentName }) {
10292
10226
  await handleScanReport({
10293
10227
  orgSlug,
10294
10228
  scanId: scanId,
10295
- includeLicensePolicy: false,
10296
- // !!license,
10297
- includeSecurityPolicy: typeof security === 'boolean' ? security : true,
10229
+ includeLicensePolicy: !!license,
10298
10230
  outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
10299
10231
  filePath: file,
10300
10232
  fold: fold,
@@ -10303,6 +10235,46 @@ async function run$3(argv, importMeta, { parentName }) {
10303
10235
  })
10304
10236
  }
10305
10237
 
10238
+ async function fetchScan(orgSlug, scanId) {
10239
+ const apiToken = shadowNpmInject.getDefaultToken()
10240
+ if (!apiToken) {
10241
+ throw new shadowNpmInject.AuthError(
10242
+ 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
10243
+ )
10244
+ }
10245
+
10246
+ // Lazily access constants.spinner.
10247
+ const { spinner } = constants
10248
+ spinner.start('Fetching scan data...')
10249
+ const response = await queryApi(
10250
+ `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,
10251
+ apiToken
10252
+ )
10253
+ spinner.successAndStop('Received response while fetching scan data.')
10254
+ if (!response.ok) {
10255
+ const err = await handleApiError(response.status)
10256
+ logger.logger.fail(
10257
+ failMsgWithBadge(response.statusText, `Fetch error: ${err}`)
10258
+ )
10259
+ return
10260
+ }
10261
+
10262
+ // This is nd-json; each line is a json object
10263
+ const jsons = await response.text()
10264
+ const lines = jsons.split('\n').filter(Boolean)
10265
+ const data = lines.map(line => {
10266
+ try {
10267
+ return JSON.parse(line)
10268
+ } catch {
10269
+ console.error(
10270
+ 'At least one line item was returned that could not be parsed as JSON...'
10271
+ )
10272
+ return {}
10273
+ }
10274
+ })
10275
+ return data
10276
+ }
10277
+
10306
10278
  async function outputScanView(artifacts, orgSlug, scanId, filePath) {
10307
10279
  const display = artifacts.map(art => {
10308
10280
  const author = Array.isArray(art.author)
@@ -10363,7 +10335,6 @@ async function streamScan(orgSlug, scanId, file) {
10363
10335
  spinner.successAndStop(`Full scan details written to ${file}`)
10364
10336
  if (!data?.success) {
10365
10337
  handleUnsuccessfulApiResponse('getOrgFullScan', data)
10366
- return
10367
10338
  }
10368
10339
  return data
10369
10340
  }
@@ -10381,6 +10352,10 @@ const config$2 = {
10381
10352
  Usage
10382
10353
  $ ${command} <org slug> <scan ID> [path to output file]
10383
10354
 
10355
+ API Token Requirements
10356
+ - Quota: 1 unit
10357
+ - Permissions: full-scans:list
10358
+
10384
10359
  When no output path is given the contents is sent to stdout.
10385
10360
 
10386
10361
  Options
@@ -10730,6 +10705,11 @@ const config$1 = {
10730
10705
  Usage
10731
10706
  $ ${command}
10732
10707
 
10708
+ API Token Requirements
10709
+ - Quota: 1 unit
10710
+ - Permissions: threat-feed:list
10711
+ - Special access
10712
+
10733
10713
  This feature requires a Threat Feed license. Please contact
10734
10714
  sales@socket.dev if you are interested in purchasing this access.
10735
10715
 
@@ -10848,7 +10828,7 @@ function checkSocketWrapperSetup(file) {
10848
10828
  )
10849
10829
  if (linesWithSocketAlias.length) {
10850
10830
  logger.logger.log(
10851
- `The Socket npm/npx wrapper is set up in your bash profile (${file}).`
10831
+ `The Socket npm/npx wrapper is set up in your bash profile (${file})`
10852
10832
  )
10853
10833
  return true
10854
10834
  }
@@ -11030,7 +11010,7 @@ void (async () => {
11030
11010
  await updateNotifier({
11031
11011
  name: SOCKET_CLI_BIN_NAME,
11032
11012
  // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION']".
11033
- version: '0.14.68',
11013
+ version: '0.14.69',
11034
11014
  ttl: 86_400_000 /* 24 hours in milliseconds */
11035
11015
  })
11036
11016
  try {
@@ -11101,5 +11081,5 @@ void (async () => {
11101
11081
  await shadowNpmInject.captureException(e)
11102
11082
  }
11103
11083
  })()
11104
- //# debugId=693f62d3-ee05-4925-886c-e64e0ec8a06f
11084
+ //# debugId=aabf095d-4c7e-4f8b-86c6-75307bc026c5
11105
11085
  //# sourceMappingURL=cli.js.map