@socketsecurity/cli-with-sentry 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:b5e74c63:pub' // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION_HASH']".
917
+ const cliVersion = '0.14.69:2aebac3:e4bf0eac: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) {
@@ -3043,12 +3140,99 @@ function runAgentInstall(pkgEnvDetails, options) {
3043
3140
  }
3044
3141
 
3045
3142
  const { NPM: NPM$c, OVERRIDES: OVERRIDES$2, PNPM: PNPM$9 } = constants
3046
- async function pnpmFix(pkgEnvDetails, cwd, options) {
3047
- const { spinner } = {
3143
+ async function branchExists(branchName, cwd) {
3144
+ try {
3145
+ await spawn.spawn('git', ['rev-parse', '--verify', branchName], {
3146
+ cwd,
3147
+ stdio: 'ignore'
3148
+ })
3149
+ return true
3150
+ } catch {
3151
+ return false
3152
+ }
3153
+ }
3154
+ async function remoteBranchExists(branchName, cwd) {
3155
+ try {
3156
+ const result = await spawn.spawn(
3157
+ 'git',
3158
+ ['ls-remote', '--heads', 'origin', branchName],
3159
+ {
3160
+ cwd,
3161
+ stdio: 'pipe'
3162
+ }
3163
+ )
3164
+ return !!result.stdout.trim()
3165
+ } catch {
3166
+ return false
3167
+ }
3168
+ }
3169
+ async function commitAndPushFix(branchName, commitMsg, cwd) {
3170
+ const localExists = await branchExists(branchName, cwd)
3171
+ const remoteExists = await remoteBranchExists(branchName, cwd)
3172
+ if (localExists || remoteExists) {
3173
+ logger.logger.warn(
3174
+ `Branch "${branchName}" already exists. Skipping creation.`
3175
+ )
3176
+ return
3177
+ }
3178
+ await spawn.spawn('git', ['checkout', '-b', branchName], {
3179
+ cwd
3180
+ })
3181
+ await spawn.spawn('git', ['add', 'package.json', 'pnpm-lock.yaml'], {
3182
+ cwd
3183
+ })
3184
+ await spawn.spawn('git', ['commit', '-m', commitMsg], {
3185
+ cwd
3186
+ })
3187
+ await spawn.spawn('git', ['push', '--set-upstream', 'origin', branchName], {
3188
+ cwd
3189
+ })
3190
+ }
3191
+ async function createPullRequest({
3192
+ base = 'main',
3193
+ body,
3194
+ head,
3195
+ owner,
3196
+ repo,
3197
+ title
3198
+ }) {
3199
+ const octokit = new rest.Octokit({
3200
+ auth: process.env['GITHUB_TOKEN']
3201
+ })
3202
+ await octokit.pulls.create({
3203
+ owner,
3204
+ repo,
3205
+ title,
3206
+ head,
3207
+ base,
3208
+ ...(body
3209
+ ? {
3210
+ body
3211
+ }
3212
+ : {})
3213
+ })
3214
+ }
3215
+ function getRepoInfo() {
3216
+ const repoString = process.env['GITHUB_REPOSITORY']
3217
+ if (!repoString || !repoString.includes('/')) {
3218
+ throw new Error('GITHUB_REPOSITORY is not set or invalid')
3219
+ }
3220
+ const { 0: owner, 1: repo } = repoString.split('/')
3221
+ return {
3222
+ owner,
3223
+ repo
3224
+ }
3225
+ }
3226
+ async function pnpmFix(pkgEnvDetails, options) {
3227
+ const {
3228
+ cwd = process.cwd(),
3229
+ spinner,
3230
+ test = false,
3231
+ testScript = 'test'
3232
+ } = {
3048
3233
  __proto__: null,
3049
3234
  ...options
3050
3235
  }
3051
- spinner?.start()
3052
3236
  const lockfile = await lockfile_fs.readWantedLockfile(cwd, {
3053
3237
  ignoreIncompatible: false
3054
3238
  })
@@ -3062,7 +3246,8 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3062
3246
  existing: true,
3063
3247
  unfixable: false,
3064
3248
  upgradable: false
3065
- }
3249
+ },
3250
+ nothrow: true
3066
3251
  })
3067
3252
  const infoByPkg = shadowNpmInject.getCveInfoByAlertsMap(alertsMap)
3068
3253
  if (!infoByPkg) {
@@ -3078,11 +3263,12 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3078
3263
  editable: true
3079
3264
  })
3080
3265
  const { content: pkgJson } = editablePkgJson
3266
+ spinner?.stop()
3081
3267
  for (const { 0: name, 1: infos } of infoByPkg) {
3082
3268
  const tree = arb.actualTree
3083
3269
  const hasUpgrade = !!registry.getManifestData(NPM$c, name)
3084
3270
  if (hasUpgrade) {
3085
- spinner?.info(`Skipping ${name}. Socket Optimize package exists.`)
3271
+ logger.logger.info(`Skipping ${name}. Socket Optimize package exists.`)
3086
3272
  continue
3087
3273
  }
3088
3274
  const nodes = shadowNpmInject.findPackageNodes(tree, name)
@@ -3104,6 +3290,7 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3104
3290
  const { firstPatchedVersionIdentifier, vulnerableVersionRange } =
3105
3291
  infos[j]
3106
3292
  const { version: oldVersion } = node
3293
+ const oldSpec = `${name}@${oldVersion}`
3107
3294
  const availableVersions = Object.keys(packument.versions)
3108
3295
  // Find the highest non-vulnerable version within the same major range
3109
3296
  const targetVersion = shadowNpmInject.findBestPatchVersion(
@@ -3114,20 +3301,28 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3114
3301
  const targetPackument = targetVersion
3115
3302
  ? packument.versions[targetVersion]
3116
3303
  : undefined
3117
- if (targetPackument) {
3304
+ spinner?.stop()
3305
+
3306
+ // Check targetVersion to make TypeScript happy.
3307
+ if (targetVersion && targetPackument) {
3118
3308
  const oldPnpm = pkgJson[PNPM$9]
3119
3309
  const oldOverrides = oldPnpm?.[OVERRIDES$2]
3120
- try {
3121
- editablePkgJson.update({
3122
- [PNPM$9]: {
3123
- ...oldPnpm,
3124
- [OVERRIDES$2]: {
3125
- [`${node.name}@${vulnerableVersionRange}`]: `^${targetVersion}`,
3126
- ...oldOverrides
3127
- }
3310
+ const overrideKey = `${node.name}@${vulnerableVersionRange}`
3311
+ const overrideRange = `^${targetVersion}`
3312
+ const fixSpec = `${name}@${overrideRange}`
3313
+ const data = {
3314
+ [PNPM$9]: {
3315
+ ...oldPnpm,
3316
+ [OVERRIDES$2]: {
3317
+ [overrideKey]: overrideRange,
3318
+ ...oldOverrides
3128
3319
  }
3129
- })
3130
- spinner?.info(`Patched ${name} ${oldVersion} -> ${node.version}`)
3320
+ }
3321
+ }
3322
+ try {
3323
+ editablePkgJson.update(data)
3324
+ spinner?.start()
3325
+ spinner?.info(`Installing ${fixSpec}`)
3131
3326
 
3132
3327
  // eslint-disable-next-line no-await-in-loop
3133
3328
  await editablePkgJson.save()
@@ -3135,11 +3330,70 @@ async function pnpmFix(pkgEnvDetails, cwd, options) {
3135
3330
  await runAgentInstall(pkgEnvDetails, {
3136
3331
  spinner
3137
3332
  })
3333
+ if (test) {
3334
+ spinner?.info(`Testing ${fixSpec}`)
3335
+ // eslint-disable-next-line no-await-in-loop
3336
+ await npm.runScript(testScript, [], {
3337
+ spinner,
3338
+ stdio: 'ignore'
3339
+ })
3340
+ }
3341
+ try {
3342
+ const branchName = `fix-${name}-${targetVersion.replace(/\./g, '-')}`
3343
+ const commitMsg = `fix: upgrade ${name} to ${targetVersion}`
3344
+ const { owner, repo } = getRepoInfo()
3345
+ // eslint-disable-next-line no-await-in-loop
3346
+ await commitAndPushFix(branchName, commitMsg, cwd)
3347
+ // eslint-disable-next-line no-await-in-loop
3348
+ await createPullRequest({
3349
+ owner,
3350
+ repo,
3351
+ title: commitMsg,
3352
+ head: branchName,
3353
+ base: process.env['GITHUB_REF_NAME'] ?? 'master',
3354
+ body: `This PR fixes a security issue in \`${name}\` by upgrading to \`${targetVersion}\`.`
3355
+ })
3356
+ } catch (e) {
3357
+ console.log(e)
3358
+ }
3359
+ logger.logger.success(`Fixed ${name}`)
3138
3360
  } catch {
3139
- spinner?.error(`Reverting ${name} to ${oldVersion}`)
3361
+ spinner?.error(`Reverting ${fixSpec}`)
3362
+ const pnpmKeyCount = Object.keys(data[PNPM$9]).length
3363
+ const pnpmOverridesKeyCount = Object.keys(
3364
+ data[PNPM$9][OVERRIDES$2]
3365
+ ).length
3366
+ if (pnpmKeyCount === 1 && pnpmOverridesKeyCount === 1) {
3367
+ editablePkgJson.update({
3368
+ // Setting to `undefined` will remove the property.
3369
+ [PNPM$9]: undefined
3370
+ })
3371
+ } else {
3372
+ editablePkgJson.update({
3373
+ [PNPM$9]: {
3374
+ ...oldPnpm,
3375
+ [OVERRIDES$2]:
3376
+ pnpmOverridesKeyCount === 1
3377
+ ? undefined
3378
+ : {
3379
+ [overrideKey]: undefined,
3380
+ ...oldOverrides
3381
+ }
3382
+ }
3383
+ })
3384
+ }
3385
+ // eslint-disable-next-line no-await-in-loop
3386
+ await editablePkgJson.save()
3387
+ // eslint-disable-next-line no-await-in-loop
3388
+ await runAgentInstall(pkgEnvDetails, {
3389
+ spinner
3390
+ })
3391
+ spinner?.stop()
3392
+ logger.logger.error(`Failed to fix ${oldSpec}`)
3140
3393
  }
3141
3394
  } else {
3142
- spinner?.error(`Could not patch ${name} ${oldVersion}`)
3395
+ spinner?.stop()
3396
+ logger.logger.error(`Could not patch ${oldSpec}`)
3143
3397
  }
3144
3398
  }
3145
3399
  }
@@ -3545,39 +3799,59 @@ async function detectAndValidatePackageEnvironment(cwd, options) {
3545
3799
 
3546
3800
  const { NPM: NPM$a, PNPM: PNPM$7 } = constants
3547
3801
  const CMD_NAME$2 = 'socket fix'
3548
- async function runFix() {
3549
- // Lazily access constants.spinner.
3550
- const { spinner } = constants
3551
- spinner.start()
3552
- const cwd = process.cwd()
3802
+ async function runFix({
3803
+ cwd = process.cwd(),
3804
+ spinner,
3805
+ test = false,
3806
+ testScript = 'test'
3807
+ }) {
3553
3808
  const pkgEnvDetails = await detectAndValidatePackageEnvironment(cwd, {
3554
3809
  cmdName: CMD_NAME$2,
3555
3810
  logger: logger.logger
3556
3811
  })
3557
3812
  if (!pkgEnvDetails) {
3558
- spinner.stop()
3813
+ spinner?.stop()
3559
3814
  return
3560
3815
  }
3816
+ logger.logger.info(`Fixing packages for ${pkgEnvDetails.agent}`)
3561
3817
  switch (pkgEnvDetails.agent) {
3562
3818
  case NPM$a: {
3563
- await npmFix(pkgEnvDetails, cwd)
3819
+ await npmFix(pkgEnvDetails, {
3820
+ spinner,
3821
+ test,
3822
+ testScript
3823
+ })
3564
3824
  break
3565
3825
  }
3566
3826
  case PNPM$7: {
3567
- await pnpmFix(pkgEnvDetails, cwd)
3827
+ await pnpmFix(pkgEnvDetails, {
3828
+ spinner,
3829
+ test,
3830
+ testScript
3831
+ })
3568
3832
  break
3569
3833
  }
3570
3834
  }
3571
- spinner.successAndStop('Socket.dev fix successful')
3835
+ // spinner.successAndStop('Socket.dev fix successful')
3572
3836
  }
3573
3837
 
3574
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$y } = constants
3838
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w } = constants
3575
3839
  const config$z = {
3576
3840
  commandName: 'fix',
3577
3841
  description: 'Fix "fixable" Socket alerts',
3578
3842
  hidden: true,
3579
3843
  flags: {
3580
- ...commonFlags
3844
+ ...commonFlags,
3845
+ test: {
3846
+ type: 'boolean',
3847
+ default: true,
3848
+ description: 'Very the fix by running unit tests'
3849
+ },
3850
+ testScript: {
3851
+ type: 'string',
3852
+ default: 'test',
3853
+ description: 'The test script to run for each fix attempt'
3854
+ }
3581
3855
  },
3582
3856
  help: (command, config) => `
3583
3857
  Usage
@@ -3600,10 +3874,17 @@ async function run$z(argv, importMeta, { parentName }) {
3600
3874
  parentName
3601
3875
  })
3602
3876
  if (cli.flags['dryRun']) {
3603
- logger.logger.log(DRY_RUN_BAIL_TEXT$y)
3877
+ logger.logger.log(DRY_RUN_BAIL_TEXT$w)
3604
3878
  return
3605
3879
  }
3606
- await runFix()
3880
+
3881
+ // Lazily access constants.spinner.
3882
+ const { spinner } = constants
3883
+ await runFix({
3884
+ spinner,
3885
+ test: Boolean(cli.flags['test']),
3886
+ testScript: cli.flags['testScript']
3887
+ })
3607
3888
  }
3608
3889
 
3609
3890
  async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
@@ -3628,10 +3909,10 @@ async function fetchPackageInfo(pkgName, pkgVersion, includeAllIssues) {
3628
3909
  )
3629
3910
  spinner.successAndStop('Data fetched')
3630
3911
  if (result.success === false) {
3631
- return handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result)
3912
+ handleUnsuccessfulApiResponse('getIssuesByNPMPackage', result)
3632
3913
  }
3633
3914
  if (scoreResult.success === false) {
3634
- return handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult)
3915
+ handleUnsuccessfulApiResponse('getScoreByNPMPackage', scoreResult)
3635
3916
  }
3636
3917
  const severityCount = shadowNpmInject.getSeverityCount(
3637
3918
  result.data,
@@ -3792,7 +4073,7 @@ async function handlePackageInfo({
3792
4073
  }
3793
4074
  }
3794
4075
 
3795
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$x } = constants
4076
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v } = constants
3796
4077
  const config$y = {
3797
4078
  commandName: 'info',
3798
4079
  description: 'Look up info regarding a package',
@@ -3860,7 +4141,7 @@ async function run$y(argv, importMeta, { parentName }) {
3860
4141
  const pkgVersion =
3861
4142
  versionSeparator < 1 ? 'latest' : rawPkgName.slice(versionSeparator + 1)
3862
4143
  if (cli.flags['dryRun']) {
3863
- logger.logger.log(DRY_RUN_BAIL_TEXT$x)
4144
+ logger.logger.log(DRY_RUN_BAIL_TEXT$v)
3864
4145
  return
3865
4146
  }
3866
4147
  await handlePackageInfo({
@@ -3897,7 +4178,6 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3897
4178
  if (!result.success) {
3898
4179
  logger.logger.fail('Authentication failed...')
3899
4180
  handleUnsuccessfulApiResponse('getOrganizations', result)
3900
- return
3901
4181
  }
3902
4182
  logger.logger.success('API key verified')
3903
4183
  const orgs = result.data
@@ -3935,16 +4215,24 @@ async function attemptLogin(apiBaseUrl, apiProxy) {
3935
4215
  }
3936
4216
  }
3937
4217
  spinner.stop()
3938
- const oldToken = shadowNpmInject.getConfigValue('apiToken')
4218
+ const previousPersistedToken = shadowNpmInject.getConfigValue('apiToken')
3939
4219
  try {
3940
4220
  applyLogin(apiToken, enforcedOrgs, apiBaseUrl, apiProxy)
3941
- logger.logger.success(`API credentials ${oldToken ? 'updated' : 'set'}`)
4221
+ logger.logger.success(
4222
+ `API credentials ${previousPersistedToken === apiToken ? 'refreshed' : previousPersistedToken ? 'updated' : 'set'}`
4223
+ )
4224
+ if (!shadowNpmInject.isReadOnlyConfig()) {
4225
+ logger.logger.log('')
4226
+ logger.logger.warn(
4227
+ 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the login was not persisted!'
4228
+ )
4229
+ }
3942
4230
  } catch {
3943
4231
  logger.logger.fail(`API login failed`)
3944
4232
  }
3945
4233
  }
3946
4234
 
3947
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$w } = constants
4235
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u } = constants
3948
4236
  const config$x = {
3949
4237
  commandName: 'login',
3950
4238
  description: 'Socket API login',
@@ -3964,6 +4252,9 @@ const config$x = {
3964
4252
  Usage
3965
4253
  $ ${command}
3966
4254
 
4255
+ API Token Requirements
4256
+ - Quota: 1 unit
4257
+
3967
4258
  Logs into the Socket API by prompting for an API key
3968
4259
 
3969
4260
  Options
@@ -3989,7 +4280,7 @@ async function run$x(argv, importMeta, { parentName }) {
3989
4280
  const apiBaseUrl = cli.flags['apiBaseUrl']
3990
4281
  const apiProxy = cli.flags['apiProxy']
3991
4282
  if (cli.flags['dryRun']) {
3992
- logger.logger.log(DRY_RUN_BAIL_TEXT$w)
4283
+ logger.logger.log(DRY_RUN_BAIL_TEXT$u)
3993
4284
  return
3994
4285
  }
3995
4286
  if (!isInteractive()) {
@@ -4011,12 +4302,18 @@ function attemptLogout() {
4011
4302
  try {
4012
4303
  applyLogout()
4013
4304
  logger.logger.success('Successfully logged out')
4305
+ if (!shadowNpmInject.isReadOnlyConfig()) {
4306
+ logger.logger.log('')
4307
+ logger.logger.warn(
4308
+ 'Note: config is in read-only mode, at least one key was overridden through flag/env, so the logout was not persisted!'
4309
+ )
4310
+ }
4014
4311
  } catch {
4015
4312
  logger.logger.fail('Failed to complete logout steps')
4016
4313
  }
4017
4314
  }
4018
4315
 
4019
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$v } = constants
4316
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t } = constants
4020
4317
  const config$w = {
4021
4318
  commandName: 'logout',
4022
4319
  description: 'Socket API logout',
@@ -4044,7 +4341,7 @@ async function run$w(argv, importMeta, { parentName }) {
4044
4341
  parentName
4045
4342
  })
4046
4343
  if (cli.flags['dryRun']) {
4047
- logger.logger.log(DRY_RUN_BAIL_TEXT$v)
4344
+ logger.logger.log(DRY_RUN_BAIL_TEXT$t)
4048
4345
  return
4049
4346
  }
4050
4347
  attemptLogout()
@@ -4152,7 +4449,7 @@ async function convertGradleToMaven(target, bin, _out, verbose, gradleOpts) {
4152
4449
  }
4153
4450
  }
4154
4451
 
4155
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$u } = constants
4452
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s } = constants
4156
4453
  const config$v = {
4157
4454
  commandName: 'gradle',
4158
4455
  description:
@@ -4299,7 +4596,7 @@ async function run$v(argv, importMeta, { parentName }) {
4299
4596
  .filter(Boolean)
4300
4597
  }
4301
4598
  if (cli.flags['dryRun']) {
4302
- logger.logger.log(DRY_RUN_BAIL_TEXT$u)
4599
+ logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4303
4600
  return
4304
4601
  }
4305
4602
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts)
@@ -4408,7 +4705,7 @@ async function convertSbtToMaven(target, bin, out, verbose, sbtOpts) {
4408
4705
  }
4409
4706
  }
4410
4707
 
4411
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$t } = constants
4708
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r } = constants
4412
4709
  const config$u = {
4413
4710
  commandName: 'scala',
4414
4711
  description:
@@ -4553,13 +4850,13 @@ async function run$u(argv, importMeta, { parentName }) {
4553
4850
  .filter(Boolean)
4554
4851
  }
4555
4852
  if (cli.flags['dryRun']) {
4556
- logger.logger.log(DRY_RUN_BAIL_TEXT$t)
4853
+ logger.logger.log(DRY_RUN_BAIL_TEXT$r)
4557
4854
  return
4558
4855
  }
4559
4856
  await convertSbtToMaven(target, bin, out, verbose, sbtOpts)
4560
4857
  }
4561
4858
 
4562
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$s } = constants
4859
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q } = constants
4563
4860
  const config$t = {
4564
4861
  commandName: 'auto',
4565
4862
  description: 'Auto-detect build and attempt to generate manifest file',
@@ -4625,7 +4922,7 @@ async function run$t(argv, importMeta, { parentName }) {
4625
4922
  }
4626
4923
  subArgs.push(dir)
4627
4924
  if (cli.flags['dryRun']) {
4628
- logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4925
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q)
4629
4926
  return
4630
4927
  }
4631
4928
  await cmdManifestScala.run(subArgs, importMeta, {
@@ -4642,7 +4939,7 @@ async function run$t(argv, importMeta, { parentName }) {
4642
4939
  subArgs.push(cwd)
4643
4940
  }
4644
4941
  if (cli.flags['dryRun']) {
4645
- logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4942
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q)
4646
4943
  return
4647
4944
  }
4648
4945
  await cmdManifestGradle.run(subArgs, importMeta, {
@@ -4651,7 +4948,7 @@ async function run$t(argv, importMeta, { parentName }) {
4651
4948
  return
4652
4949
  }
4653
4950
  if (cli.flags['dryRun']) {
4654
- logger.logger.log(DRY_RUN_BAIL_TEXT$s)
4951
+ logger.logger.log(DRY_RUN_BAIL_TEXT$q)
4655
4952
  return
4656
4953
  }
4657
4954
 
@@ -4678,7 +4975,7 @@ async function run$t(argv, importMeta, { parentName }) {
4678
4975
  ).showHelp()
4679
4976
  }
4680
4977
 
4681
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$r } = constants
4978
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p } = constants
4682
4979
 
4683
4980
  // TODO: we may want to dedupe some pieces for all gradle languages. I think it
4684
4981
  // makes sense to have separate commands for them and I think it makes
@@ -4831,7 +5128,7 @@ async function run$s(argv, importMeta, { parentName }) {
4831
5128
  .filter(Boolean)
4832
5129
  }
4833
5130
  if (cli.flags['dryRun']) {
4834
- logger.logger.log(DRY_RUN_BAIL_TEXT$r)
5131
+ logger.logger.log(DRY_RUN_BAIL_TEXT$p)
4835
5132
  return
4836
5133
  }
4837
5134
  await convertGradleToMaven(target, bin, out, verbose, gradleOpts)
@@ -4882,7 +5179,7 @@ async function wrapNpm(argv) {
4882
5179
  await shadowBin(NPM$8, argv)
4883
5180
  }
4884
5181
 
4885
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$q, NPM: NPM$7 } = constants
5182
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o, NPM: NPM$7 } = constants
4886
5183
  const config$q = {
4887
5184
  commandName: 'npm',
4888
5185
  description: `${NPM$7} wrapper functionality`,
@@ -4909,7 +5206,7 @@ async function run$q(argv, importMeta, { parentName }) {
4909
5206
  parentName
4910
5207
  })
4911
5208
  if (cli.flags['dryRun']) {
4912
- logger.logger.log(DRY_RUN_BAIL_TEXT$q)
5209
+ logger.logger.log(DRY_RUN_BAIL_TEXT$o)
4913
5210
  return
4914
5211
  }
4915
5212
  await wrapNpm(argv)
@@ -4922,7 +5219,7 @@ async function wrapNpx(argv) {
4922
5219
  await shadowBin(NPX$2, argv)
4923
5220
  }
4924
5221
 
4925
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$p, NPX: NPX$1 } = constants
5222
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n, NPX: NPX$1 } = constants
4926
5223
  const config$p = {
4927
5224
  commandName: 'npx',
4928
5225
  description: `${NPX$1} wrapper functionality`,
@@ -4949,13 +5246,13 @@ async function run$p(argv, importMeta, { parentName }) {
4949
5246
  parentName
4950
5247
  })
4951
5248
  if (cli.flags['dryRun']) {
4952
- logger.logger.log(DRY_RUN_BAIL_TEXT$p)
5249
+ logger.logger.log(DRY_RUN_BAIL_TEXT$n)
4953
5250
  return
4954
5251
  }
4955
5252
  await wrapNpx(argv)
4956
5253
  }
4957
5254
 
4958
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$o } = constants
5255
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m } = constants
4959
5256
  const config$o = {
4960
5257
  commandName: 'oops',
4961
5258
  description: 'Trigger an intentional error (for development)',
@@ -4983,7 +5280,7 @@ async function run$o(argv, importMeta, { parentName }) {
4983
5280
  parentName
4984
5281
  })
4985
5282
  if (cli.flags['dryRun']) {
4986
- logger.logger.log(DRY_RUN_BAIL_TEXT$o)
5283
+ logger.logger.log(DRY_RUN_BAIL_TEXT$m)
4987
5284
  return
4988
5285
  }
4989
5286
  throw new Error('This error was intentionally left blank')
@@ -5872,7 +6169,7 @@ async function applyOptimization(cwd, pin, prod) {
5872
6169
  }
5873
6170
  }
5874
6171
 
5875
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$n } = constants
6172
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l } = constants
5876
6173
  const config$n = {
5877
6174
  commandName: 'optimize',
5878
6175
  description: 'Optimize dependencies with @socketregistry overrides',
@@ -5916,7 +6213,7 @@ async function run$n(argv, importMeta, { parentName }) {
5916
6213
  })
5917
6214
  const cwd = process.cwd()
5918
6215
  if (cli.flags['dryRun']) {
5919
- logger.logger.log(DRY_RUN_BAIL_TEXT$n)
6216
+ logger.logger.log(DRY_RUN_BAIL_TEXT$l)
5920
6217
  return
5921
6218
  }
5922
6219
  await applyOptimization(
@@ -5939,7 +6236,6 @@ async function fetchOrganization() {
5939
6236
  spinner.successAndStop('Received organization list response.')
5940
6237
  if (!result.success) {
5941
6238
  handleUnsuccessfulApiResponse('getOrganizations', result)
5942
- return
5943
6239
  }
5944
6240
  return result.data
5945
6241
  }
@@ -6018,7 +6314,7 @@ async function handleOrganizationList(outputKind = 'text') {
6018
6314
  await outputOrganizationList(data, outputKind)
6019
6315
  }
6020
6316
 
6021
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$m } = constants
6317
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k } = constants
6022
6318
  const config$m = {
6023
6319
  commandName: 'list',
6024
6320
  description: 'List organizations associated with the API key used',
@@ -6031,6 +6327,10 @@ const config$m = {
6031
6327
  Usage
6032
6328
  $ ${command}
6033
6329
 
6330
+ API Token Requirements
6331
+ - Quota: 1 unit
6332
+ - Permissions: none (does need a token)
6333
+
6034
6334
  Options
6035
6335
  ${getFlagListOutput(config$m.flags, 6)}
6036
6336
  `
@@ -6071,7 +6371,7 @@ async function run$m(argv, importMeta, { parentName }) {
6071
6371
  return
6072
6372
  }
6073
6373
  if (cli.flags['dryRun']) {
6074
- logger.logger.log(DRY_RUN_BAIL_TEXT$m)
6374
+ logger.logger.log(DRY_RUN_BAIL_TEXT$k)
6075
6375
  return
6076
6376
  }
6077
6377
  await handleOrganizationList(json ? 'json' : markdown ? 'markdown' : 'text')
@@ -6090,7 +6390,6 @@ async function fetchLicensePolicy(orgSlug) {
6090
6390
  spinner.successAndStop('Received organization license policy response.')
6091
6391
  if (!result.success) {
6092
6392
  handleUnsuccessfulApiResponse('getOrgLicensePolicy', result)
6093
- return
6094
6393
  }
6095
6394
  return result.data
6096
6395
  }
@@ -6135,7 +6434,7 @@ async function handleLicensePolicy(orgSlug, outputKind) {
6135
6434
  await outputLicensePolicy(data, outputKind)
6136
6435
  }
6137
6436
 
6138
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$l } = constants
6437
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j } = constants
6139
6438
 
6140
6439
  // TODO: secret toplevel alias `socket license policy`?
6141
6440
  const config$l = {
@@ -6150,6 +6449,10 @@ const config$l = {
6150
6449
  Usage
6151
6450
  $ ${command} <org slug>
6152
6451
 
6452
+ API Token Requirements
6453
+ - Quota: 1 unit
6454
+ - Permissions: license-policy:read
6455
+
6153
6456
  Options
6154
6457
  ${getFlagListOutput(config$l.flags, 6)}
6155
6458
 
@@ -6206,7 +6509,7 @@ async function run$l(argv, importMeta, { parentName }) {
6206
6509
  return
6207
6510
  }
6208
6511
  if (cli.flags['dryRun']) {
6209
- logger.logger.log(DRY_RUN_BAIL_TEXT$l)
6512
+ logger.logger.log(DRY_RUN_BAIL_TEXT$j)
6210
6513
  return
6211
6514
  }
6212
6515
  await handleLicensePolicy(
@@ -6228,7 +6531,6 @@ async function fetchSecurityPolicy(orgSlug) {
6228
6531
  spinner.successAndStop('Received organization security policy response.')
6229
6532
  if (!result.success) {
6230
6533
  handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result)
6231
- return
6232
6534
  }
6233
6535
  return result.data
6234
6536
  }
@@ -6274,7 +6576,7 @@ async function handleSecurityPolicy(orgSlug, outputKind) {
6274
6576
  await outputSecurityPolicy(data, outputKind)
6275
6577
  }
6276
6578
 
6277
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$k } = constants
6579
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i } = constants
6278
6580
 
6279
6581
  // TODO: secret toplevel alias `socket security policy`?
6280
6582
  const config$k = {
@@ -6289,6 +6591,10 @@ const config$k = {
6289
6591
  Usage
6290
6592
  $ ${command} <org slug>
6291
6593
 
6594
+ API Token Requirements
6595
+ - Quota: 1 unit
6596
+ - Permissions: security-policy:read
6597
+
6292
6598
  Options
6293
6599
  ${getFlagListOutput(config$k.flags, 6)}
6294
6600
 
@@ -6345,7 +6651,7 @@ async function run$k(argv, importMeta, { parentName }) {
6345
6651
  return
6346
6652
  }
6347
6653
  if (cli.flags['dryRun']) {
6348
- logger.logger.log(DRY_RUN_BAIL_TEXT$k)
6654
+ logger.logger.log(DRY_RUN_BAIL_TEXT$i)
6349
6655
  return
6350
6656
  }
6351
6657
  await handleSecurityPolicy(
@@ -6393,7 +6699,6 @@ async function fetchQuota() {
6393
6699
  spinner.successAndStop('Received organization quota response.')
6394
6700
  if (!result.success) {
6395
6701
  handleUnsuccessfulApiResponse('getQuota', result)
6396
- return
6397
6702
  }
6398
6703
  return result.data
6399
6704
  }
@@ -6432,7 +6737,7 @@ async function handleQuota(outputKind = 'text') {
6432
6737
  await outputQuota(data, outputKind)
6433
6738
  }
6434
6739
 
6435
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$j } = constants
6740
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h } = constants
6436
6741
  const config$j = {
6437
6742
  commandName: 'quota',
6438
6743
  description: 'List organizations associated with the API key used',
@@ -6485,7 +6790,7 @@ async function run$j(argv, importMeta, { parentName }) {
6485
6790
  return
6486
6791
  }
6487
6792
  if (cli.flags['dryRun']) {
6488
- logger.logger.log(DRY_RUN_BAIL_TEXT$j)
6793
+ logger.logger.log(DRY_RUN_BAIL_TEXT$h)
6489
6794
  return
6490
6795
  }
6491
6796
  await handleQuota(json ? 'json' : markdown ? 'markdown' : 'text')
@@ -6518,6 +6823,7 @@ const cmdOrganization = {
6518
6823
  }
6519
6824
  }
6520
6825
 
6826
+ const { SOCKET_CLI_ISSUES_URL } = constants
6521
6827
  async function fetchPurlDeepScore(purl) {
6522
6828
  const apiToken = shadowNpmInject.getDefaultToken()
6523
6829
  if (!apiToken) {
@@ -6559,7 +6865,7 @@ async function fetchPurlDeepScore(purl) {
6559
6865
  return JSON.parse(data)
6560
6866
  } catch (e) {
6561
6867
  throw new Error(
6562
- 'Was unable to JSON parse the input from the server. It may not have been a proper JSON response. Please report this problem.'
6868
+ `Unable to parse JSON response from the Socket API.\nPlease report to ${SOCKET_CLI_ISSUES_URL}`
6563
6869
  )
6564
6870
  }
6565
6871
  }
@@ -6752,7 +7058,7 @@ async function outputPurlScore(purl, data, outputKind) {
6752
7058
  )
6753
7059
  } else {
6754
7060
  logger.logger.log(
6755
- 'This package had no alerts and neither did any of its direct/transitive dependencies.'
7061
+ 'This package had no alerts and neither did any of its direct/transitive dependencies'
6756
7062
  )
6757
7063
  }
6758
7064
  logger.logger.log('')
@@ -6827,7 +7133,7 @@ function parsePackageSpecifiers(ecosystem, pkgs) {
6827
7133
  }
6828
7134
  }
6829
7135
 
6830
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$i } = constants
7136
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g } = constants
6831
7137
  const config$i = {
6832
7138
  commandName: 'score',
6833
7139
  description:
@@ -6841,13 +7147,13 @@ const config$i = {
6841
7147
  Usage
6842
7148
  $ ${command} <<ecosystem> <name> | <purl>>
6843
7149
 
7150
+ API Token Requirements
7151
+ - Quota: 100 units
7152
+ - Permissions: packages:list
7153
+
6844
7154
  Options
6845
7155
  ${getFlagListOutput(config.flags, 6)}
6846
7156
 
6847
- Requirements
6848
- - quota: 100
6849
- - scope: \`packages:list\`
6850
-
6851
7157
  Show deep scoring details for one package. The score will reflect the package
6852
7158
  itself, any of its dependencies, and any of its transitive dependencies.
6853
7159
 
@@ -6919,7 +7225,7 @@ async function run$i(argv, importMeta, { parentName }) {
6919
7225
  return
6920
7226
  }
6921
7227
  if (cli.flags['dryRun']) {
6922
- logger.logger.log(DRY_RUN_BAIL_TEXT$i)
7228
+ logger.logger.log(DRY_RUN_BAIL_TEXT$g)
6923
7229
  return
6924
7230
  }
6925
7231
  await handlePurlDeepScore(
@@ -6943,10 +7249,6 @@ async function fetchPurlsShallowScore(purls) {
6943
7249
  sockSdk.batchPackageFetch(
6944
7250
  {
6945
7251
  alerts: 'true'
6946
- // compact: false,
6947
- // fixable: false,
6948
- // licenseattrib: false,
6949
- // licensedetails: false
6950
7252
  },
6951
7253
  {
6952
7254
  components: purls.map(purl => ({
@@ -6959,7 +7261,6 @@ async function fetchPurlsShallowScore(purls) {
6959
7261
  spinner.successAndStop('Request completed')
6960
7262
  if (!result.success) {
6961
7263
  handleUnsuccessfulApiResponse('batchPackageFetch', result)
6962
- return
6963
7264
  }
6964
7265
  return result
6965
7266
  }
@@ -7116,7 +7417,7 @@ async function handlePurlsShallowScore({ outputKind, purls }) {
7116
7417
  outputPurlsShallowScore(purls, packageData.data, outputKind)
7117
7418
  }
7118
7419
 
7119
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$h } = constants
7420
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f } = constants
7120
7421
  const config$h = {
7121
7422
  commandName: 'shallow',
7122
7423
  description:
@@ -7130,13 +7431,13 @@ const config$h = {
7130
7431
  Usage
7131
7432
  $ ${command} <<ecosystem> <name> [<name> ...] | <purl> [<purl> ...]>
7132
7433
 
7434
+ API Token Requirements
7435
+ - Quota: 100 units
7436
+ - Permissions: packages:list
7437
+
7133
7438
  Options
7134
7439
  ${getFlagListOutput(config.flags, 6)}
7135
7440
 
7136
- Requirements
7137
- - quota: 100
7138
- - scope: \`packages:list\`
7139
-
7140
7441
  Show scoring details for one or more packages purely based on their own package.
7141
7442
  This means that any dependency scores are not reflected by the score. You can
7142
7443
  use the \`socket package score <pkg>\` command to get its full transitive score.
@@ -7207,7 +7508,7 @@ async function run$h(argv, importMeta, { parentName }) {
7207
7508
  return
7208
7509
  }
7209
7510
  if (cli.flags['dryRun']) {
7210
- logger.logger.log(DRY_RUN_BAIL_TEXT$h)
7511
+ logger.logger.log(DRY_RUN_BAIL_TEXT$f)
7211
7512
  return
7212
7513
  }
7213
7514
  await handlePurlsShallowScore({
@@ -7260,7 +7561,7 @@ async function runRawNpm(argv) {
7260
7561
  await spawnPromise
7261
7562
  }
7262
7563
 
7263
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$g, NPM } = constants
7564
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e, NPM } = constants
7264
7565
  const config$g = {
7265
7566
  commandName: 'raw-npm',
7266
7567
  description: `Temporarily disable the Socket ${NPM} wrapper`,
@@ -7288,7 +7589,7 @@ async function run$g(argv, importMeta, { parentName }) {
7288
7589
  parentName
7289
7590
  })
7290
7591
  if (cli.flags['dryRun']) {
7291
- logger.logger.log(DRY_RUN_BAIL_TEXT$g)
7592
+ logger.logger.log(DRY_RUN_BAIL_TEXT$e)
7292
7593
  return
7293
7594
  }
7294
7595
  await runRawNpm(argv)
@@ -7310,7 +7611,7 @@ async function runRawNpx(argv) {
7310
7611
  await spawnPromise
7311
7612
  }
7312
7613
 
7313
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$f, NPX } = constants
7614
+ const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d, NPX } = constants
7314
7615
  const config$f = {
7315
7616
  commandName: 'raw-npx',
7316
7617
  description: `Temporarily disable the Socket ${NPX} wrapper`,
@@ -7338,242 +7639,12 @@ async function run$f(argv, importMeta, { parentName }) {
7338
7639
  parentName
7339
7640
  })
7340
7641
  if (cli.flags['dryRun']) {
7341
- logger.logger.log(DRY_RUN_BAIL_TEXT$f)
7642
+ logger.logger.log(DRY_RUN_BAIL_TEXT$d)
7342
7643
  return
7343
7644
  }
7344
7645
  await runRawNpx(argv)
7345
7646
  }
7346
7647
 
7347
- const { DRY_RUN_LABEL } = constants
7348
- async function createReport(socketConfig, inputPaths, { cwd, dryRun }) {
7349
- // Lazily access constants.spinner.
7350
- const { spinner } = constants
7351
- const sockSdk = await shadowNpmInject.setupSdk()
7352
- const supportedFiles = await sockSdk
7353
- .getReportSupportedFiles()
7354
- .then(res => {
7355
- if (!res.success) {
7356
- handleUnsuccessfulApiResponse('getReportSupportedFiles', res)
7357
- }
7358
- return res.data
7359
- })
7360
- .catch(cause => {
7361
- throw new Error('Failed getting supported files for report', {
7362
- cause
7363
- })
7364
- })
7365
- const packagePaths = await shadowNpmPaths.getPackageFilesForScan(
7366
- cwd,
7367
- inputPaths,
7368
- supportedFiles,
7369
- socketConfig
7370
- )
7371
- const packagePathsCount = packagePaths.length
7372
- if (packagePathsCount && debug.isDebug()) {
7373
- for (const pkgPath of packagePaths) {
7374
- debug.debugLog(`Uploading: ${pkgPath}`)
7375
- }
7376
- }
7377
- if (dryRun) {
7378
- debug.debugLog(`${DRY_RUN_LABEL}: Skipped actual upload`)
7379
- return undefined
7380
- }
7381
- spinner.start(
7382
- `Creating report with ${packagePathsCount} package ${words.pluralize('file', packagePathsCount)}`
7383
- )
7384
- const apiCall = sockSdk.createReportFromFilePaths(
7385
- packagePaths,
7386
- cwd,
7387
- socketConfig?.issueRules
7388
- )
7389
- const result = await handleApiCall(apiCall, 'creating report')
7390
- if (!result.success) {
7391
- handleUnsuccessfulApiResponse('createReport', result)
7392
- return undefined
7393
- }
7394
- spinner.successAndStop()
7395
- return result
7396
- }
7397
-
7398
- async function getSocketConfig(absoluteConfigPath) {
7399
- const socketConfig = await config$K
7400
- .readSocketConfig(absoluteConfigPath)
7401
- .catch(cause => {
7402
- if (
7403
- cause &&
7404
- typeof cause === 'object' &&
7405
- cause instanceof config$K.SocketValidationError
7406
- ) {
7407
- // Inspired by workbox-build:
7408
- // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71
7409
- const betterErrors = betterAjvErrors.betterAjvErrors({
7410
- basePath: 'config',
7411
- data: cause.data,
7412
- errors: cause.validationErrors,
7413
- schema: cause.schema
7414
- })
7415
- throw new shadowNpmInject.InputError(
7416
- 'The socket.yml config is not valid',
7417
- betterErrors
7418
- .map(
7419
- err =>
7420
- `[${err.path}] ${err.message}.${err.suggestion ? err.suggestion : ''}`
7421
- )
7422
- .join('\n')
7423
- )
7424
- } else {
7425
- throw new Error('Failed to read socket.yml config', {
7426
- cause
7427
- })
7428
- }
7429
- })
7430
- return socketConfig
7431
- }
7432
-
7433
- const MAX_TIMEOUT_RETRY = 5
7434
- const HTTP_CODE_TIMEOUT = 524
7435
- async function fetchReportData$1(reportId, includeAllIssues, strict) {
7436
- // Lazily access constants.spinner.
7437
- const { spinner } = constants
7438
- spinner.log('Fetching report with ID ${reportId} (this could take a while)')
7439
- spinner.start(`Fetch started... (this could take a while)`)
7440
- const sockSdk = await shadowNpmInject.setupSdk()
7441
- let result
7442
- for (let retry = 1; !result; ++retry) {
7443
- try {
7444
- // eslint-disable-next-line no-await-in-loop
7445
- result = await handleApiCall(
7446
- sockSdk.getReport(reportId),
7447
- 'fetching report'
7448
- )
7449
- } catch (err) {
7450
- if (
7451
- retry >= MAX_TIMEOUT_RETRY ||
7452
- !(err instanceof Error) ||
7453
- err.cause?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT
7454
- ) {
7455
- spinner.stop(`Failed to fetch report`)
7456
- throw err
7457
- }
7458
- spinner.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`)
7459
- }
7460
- }
7461
- if (!result.success) {
7462
- return handleUnsuccessfulApiResponse('getReport', result)
7463
- }
7464
-
7465
- // Conclude the status of the API call.
7466
- if (strict) {
7467
- if (result.data.healthy) {
7468
- spinner.success('Report result is healthy and great!')
7469
- } else {
7470
- spinner.error('Report result deemed unhealthy for project')
7471
- }
7472
- } else if (!result.data.healthy) {
7473
- const severityCount = shadowNpmInject.getSeverityCount(
7474
- result.data.issues,
7475
- includeAllIssues ? undefined : 'high'
7476
- )
7477
- const issueSummary = shadowNpmInject.formatSeverityCount(severityCount)
7478
- spinner.success(`Report has these issues: ${issueSummary}`)
7479
- } else {
7480
- spinner.success('Report has no issues')
7481
- }
7482
- spinner.stop()
7483
- return result.data
7484
- }
7485
-
7486
- function formatReportDataOutput(
7487
- reportId,
7488
- data,
7489
- commandName,
7490
- outputKind,
7491
- strict,
7492
- artifacts
7493
- ) {
7494
- if (outputKind === 'json') {
7495
- logger.logger.log(JSON.stringify(data, undefined, 2))
7496
- } else {
7497
- const format = new shadowNpmInject.ColorOrMarkdown(
7498
- outputKind === 'markdown'
7499
- )
7500
- logger.logger.log(commonTags.stripIndents`
7501
- Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, {
7502
- fallbackToUrl: true
7503
- })}`)
7504
- if (outputKind === 'print') {
7505
- logger.logger.log(data)
7506
- logger.logger.log(
7507
- colors.dim(
7508
- `Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output`
7509
- )
7510
- )
7511
- logger.logger.log('The scan:')
7512
- logger.logger.log(artifacts)
7513
- }
7514
- }
7515
- if (strict && !data.healthy) {
7516
-
7517
- process$1.exit(1)
7518
- }
7519
- }
7520
-
7521
- async function fetchScan(orgSlug, scanId) {
7522
- const apiToken = shadowNpmInject.getDefaultToken()
7523
- if (!apiToken) {
7524
- throw new shadowNpmInject.AuthError(
7525
- 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
7526
- )
7527
- }
7528
-
7529
- // Lazily access constants.spinner.
7530
- const { spinner } = constants
7531
- spinner.start('Fetching scan data...')
7532
- const response = await queryApi(
7533
- `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,
7534
- apiToken
7535
- )
7536
- spinner.successAndStop('Received response while fetching scan data.')
7537
- if (!response.ok) {
7538
- const err = await handleApiError(response.status)
7539
- logger.logger.fail(
7540
- failMsgWithBadge(response.statusText, `Fetch error: ${err}`)
7541
- )
7542
- return
7543
- }
7544
-
7545
- // This is nd-json; each line is a json object
7546
- const jsons = await response.text()
7547
- const lines = jsons.split('\n').filter(Boolean)
7548
- const data = lines.map(line => {
7549
- try {
7550
- return JSON.parse(line)
7551
- } catch {
7552
- console.error(
7553
- 'At least one line item was returned that could not be parsed as JSON...'
7554
- )
7555
- return {}
7556
- }
7557
- })
7558
- return data
7559
- }
7560
-
7561
- async function viewReport(reportId, { all, commandName, outputKind, strict }) {
7562
- const result = await fetchReportData$1(reportId, all, strict)
7563
- const artifacts = await fetchScan('socketdev', reportId)
7564
- if (result) {
7565
- formatReportDataOutput(
7566
- reportId,
7567
- result,
7568
- commandName,
7569
- outputKind,
7570
- strict,
7571
- artifacts
7572
- )
7573
- }
7574
- }
7575
-
7576
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$e } = constants
7577
7648
  const config$e = {
7578
7649
  commandName: 'create',
7579
7650
  description: '[Deprecated] Create a project report',
@@ -7605,57 +7676,17 @@ const cmdReportCreate = {
7605
7676
  run: run$e
7606
7677
  }
7607
7678
  async function run$e(argv, importMeta, { parentName }) {
7608
- const cli = meowOrExit({
7679
+ meowOrExit({
7609
7680
  argv,
7610
7681
  config: config$e,
7611
7682
  importMeta,
7612
7683
  parentName
7613
7684
  })
7614
-
7615
- // TODO: Allow setting a custom cwd and/or configFile path?
7616
- const cwd = process.cwd()
7617
- const absoluteConfigPath = path$1.join(cwd, 'socket.yml')
7618
- const dryRun = Boolean(cli.flags['dryRun'])
7619
- const json = Boolean(cli.flags['json'])
7620
- const markdown = Boolean(cli.flags['markdown'])
7621
- const strict = Boolean(cli.flags['strict'])
7622
- const includeAllIssues = Boolean(cli.flags['all'])
7623
- const view = Boolean(cli.flags['view'])
7624
-
7625
- // Note exiting earlier to skirt a hidden auth requirement
7626
- if (cli.flags['dryRun']) {
7627
- logger.logger.log(DRY_RUN_BAIL_TEXT$e)
7628
- return
7629
- }
7630
- const socketConfig = await getSocketConfig(absoluteConfigPath)
7631
- const result = await createReport(socketConfig, cli.input, {
7632
- cwd,
7633
- dryRun
7634
- })
7635
- const commandName = `${parentName} ${config$e.commandName}`
7636
- if (result?.success) {
7637
- if (view) {
7638
- const reportId = result.data.id
7639
- await viewReport(reportId, {
7640
- all: includeAllIssues,
7641
- commandName,
7642
- outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
7643
- strict
7644
- })
7645
- } else if (json) {
7646
- logger.logger.log(JSON.stringify(result.data, undefined, 2))
7647
- } else {
7648
- const format = new shadowNpmInject.ColorOrMarkdown(markdown)
7649
- logger.logger.log(
7650
- `New report: ${format.hyperlink(result.data.id, result.data.url, {
7651
- fallbackToUrl: true
7652
- })}`
7653
- )
7654
- }
7655
- }
7685
+ logger.logger.fail(
7686
+ '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.'
7687
+ )
7656
7688
  }
7657
7689
 
7658
- const { DRY_RUN_BAIL_TEXT: DRY_RUN_BAIL_TEXT$d } = constants
7659
7690
  const config$d = {
7660
7691
  commandName: 'view',
7661
7692
  description: '[Deprecated] View a project report',
@@ -7676,49 +7707,15 @@ const cmdReportView = {
7676
7707
  run: run$d
7677
7708
  }
7678
7709
  async function run$d(argv, importMeta, { parentName }) {
7679
- const cli = meowOrExit({
7710
+ meowOrExit({
7680
7711
  argv,
7681
7712
  config: config$d,
7682
7713
  importMeta,
7683
7714
  parentName
7684
7715
  })
7685
- const { json, markdown } = cli.flags
7686
- const [reportId = '', ...extraInput] = cli.input
7687
- const wasBadInput = handleBadInput(
7688
- {
7689
- test: reportId,
7690
- message: 'Need at least one report ID',
7691
- pass: 'ok',
7692
- fail: 'missing'
7693
- },
7694
- {
7695
- nook: true,
7696
- test: extraInput.length === 0,
7697
- message: 'Can only handle a single report ID',
7698
- pass: 'ok',
7699
- fail: 'received ' + (extraInput.length + 1)
7700
- },
7701
- {
7702
- nook: true,
7703
- test: !json || !markdown,
7704
- message: 'The json and markdown flags cannot be both set, pick one',
7705
- pass: 'ok',
7706
- fail: 'omit one'
7707
- }
7716
+ logger.logger.fail(
7717
+ '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.'
7708
7718
  )
7709
- if (wasBadInput) {
7710
- return
7711
- }
7712
- if (cli.flags['dryRun']) {
7713
- logger.logger.log(DRY_RUN_BAIL_TEXT$d)
7714
- return
7715
- }
7716
- await viewReport(reportId, {
7717
- all: Boolean(cli.flags['all']),
7718
- commandName: `${parentName} ${config$d.commandName}`,
7719
- outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
7720
- strict: Boolean(cli.flags['strict'])
7721
- })
7722
7719
  }
7723
7720
 
7724
7721
  const description$2 = '[Deprecated] Project report related commands'
@@ -7768,7 +7765,6 @@ async function fetchCreateRepo({
7768
7765
  spinner.successAndStop('Received response requesting to create a repository.')
7769
7766
  if (!result.success) {
7770
7767
  handleUnsuccessfulApiResponse('createOrgRepo', result)
7771
- return
7772
7768
  }
7773
7769
  return result.data
7774
7770
  }
@@ -7841,6 +7837,10 @@ const config$c = {
7841
7837
  Usage
7842
7838
  $ ${command} <org slug>
7843
7839
 
7840
+ API Token Requirements
7841
+ - Quota: 1 unit
7842
+ - Permissions: repo:create
7843
+
7844
7844
  Options
7845
7845
  ${getFlagListOutput(config.flags, 6)}
7846
7846
 
@@ -7916,7 +7916,6 @@ async function handleDeleteRepo(orgSlug, repoName) {
7916
7916
  )
7917
7917
  if (!result.success) {
7918
7918
  handleUnsuccessfulApiResponse('deleteOrgRepo', result)
7919
- return
7920
7919
  }
7921
7920
  spinner.successAndStop('Repository deleted successfully')
7922
7921
  }
@@ -7933,6 +7932,10 @@ const config$b = {
7933
7932
  Usage
7934
7933
  $ ${command} <org slug> <repo slug>
7935
7934
 
7935
+ API Token Requirements
7936
+ - Quota: 1 unit
7937
+ - Permissions: repo:delete
7938
+
7936
7939
  Options
7937
7940
  ${getFlagListOutput(config.flags, 6)}
7938
7941
 
@@ -8007,7 +8010,6 @@ async function fetchListRepos({ direction, orgSlug, page, per_page, sort }) {
8007
8010
  spinner.successAndStop('Received response for repository list.')
8008
8011
  if (!result.success) {
8009
8012
  handleUnsuccessfulApiResponse('getOrgRepoList', result)
8010
- return
8011
8013
  }
8012
8014
  return result.data
8013
8015
  }
@@ -8109,6 +8111,10 @@ const config$a = {
8109
8111
  Usage
8110
8112
  $ ${command} <org slug>
8111
8113
 
8114
+ API Token Requirements
8115
+ - Quota: 1 unit
8116
+ - Permissions: repo:list
8117
+
8112
8118
  Options
8113
8119
  ${getFlagListOutput(config.flags, 6)}
8114
8120
 
@@ -8201,7 +8207,6 @@ async function fetchUpdateRepo({
8201
8207
  spinner.successAndStop('Received response trying to update a repository')
8202
8208
  if (!result.success) {
8203
8209
  handleUnsuccessfulApiResponse('updateOrgRepo', result)
8204
- return
8205
8210
  }
8206
8211
  return result.data
8207
8212
  }
@@ -8274,6 +8279,10 @@ const config$9 = {
8274
8279
  Usage
8275
8280
  $ ${command} <org slug>
8276
8281
 
8282
+ API Token Requirements
8283
+ - Quota: 1 unit
8284
+ - Permissions: repo:update
8285
+
8277
8286
  Options
8278
8287
  ${getFlagListOutput(config.flags, 6)}
8279
8288
 
@@ -8350,7 +8359,6 @@ async function fetchViewRepo(orgSlug, repoName) {
8350
8359
  spinner.successAndStop('Received response while fetched repository data.')
8351
8360
  if (!result.success) {
8352
8361
  handleUnsuccessfulApiResponse('getOrgRepo', result)
8353
- return
8354
8362
  }
8355
8363
  return result.data
8356
8364
  }
@@ -8445,6 +8453,10 @@ const config$8 = {
8445
8453
  Usage
8446
8454
  $ ${command} <org slug>
8447
8455
 
8456
+ API Token Requirements
8457
+ - Quota: 1 unit
8458
+ - Permissions: repo:list
8459
+
8448
8460
  Options
8449
8461
  ${getFlagListOutput(config.flags, 6)}
8450
8462
 
@@ -8550,7 +8562,9 @@ async function fetchCreateOrgFullScan(
8550
8562
 
8551
8563
  // Lazily access constants.spinner.
8552
8564
  const { spinner } = constants
8553
- spinner.start(`Creating a scan with ${packagePaths.length} packages...`)
8565
+ spinner.start(
8566
+ `Sending request to create a scan with ${packagePaths.length} packages...`
8567
+ )
8554
8568
  const result = await handleApiCall(
8555
8569
  sockSdk.createOrgFullScan(
8556
8570
  orgSlug,
@@ -8567,10 +8581,9 @@ async function fetchCreateOrgFullScan(
8567
8581
  ),
8568
8582
  'Creating scan'
8569
8583
  )
8570
- spinner.successAndStop('Scan created successfully')
8584
+ spinner.successAndStop('Completed request to create a new scan.')
8571
8585
  if (!result.success) {
8572
8586
  handleUnsuccessfulApiResponse('CreateOrgFullScan', result)
8573
- return
8574
8587
  }
8575
8588
  return result.data
8576
8589
  }
@@ -8585,12 +8598,12 @@ async function fetchSupportedScanFileNames() {
8585
8598
  sockSdk.getReportSupportedFiles(),
8586
8599
  'fetching supported scan file types'
8587
8600
  )
8588
- spinner.successAndStop(
8601
+ spinner.stop()
8602
+ logger.logger.success(
8589
8603
  'Received response while fetched supported scan file types.'
8590
8604
  )
8591
8605
  if (!result.success) {
8592
8606
  handleUnsuccessfulApiResponse('getReportSupportedFiles', result)
8593
- return
8594
8607
  }
8595
8608
  return result.data
8596
8609
  }
@@ -8628,7 +8641,6 @@ async function handleCreateNewScan({
8628
8641
  cwd,
8629
8642
  targets,
8630
8643
  supportedFileNames
8631
- // socketConfig
8632
8644
  )
8633
8645
  handleBadInput({
8634
8646
  nook: true,
@@ -8692,7 +8704,7 @@ async function suggestOrgSlug() {
8692
8704
  }
8693
8705
  } else {
8694
8706
  logger.logger.fail(
8695
- 'Failed to lookup organization list from API, unable to suggest.'
8707
+ 'Failed to lookup organization list from API, unable to suggest'
8696
8708
  )
8697
8709
  }
8698
8710
  }
@@ -8947,6 +8959,10 @@ const config$7 = {
8947
8959
  Usage
8948
8960
  $ ${command} [...options] <org> <TARGET> [TARGET...]
8949
8961
 
8962
+ API Token Requirements
8963
+ - Quota: 1 unit
8964
+ - Permissions: full-scans:create
8965
+
8950
8966
  Uploads the specified "package.json" and lock files for JavaScript, Python,
8951
8967
  Go, Scala, Gradle, and Kotlin dependency manifests.
8952
8968
  If any folder is specified, the ones found in there recursively are uploaded.
@@ -8988,7 +9004,7 @@ async function run$7(argv, importMeta, { parentName }) {
8988
9004
  cwdOverride && cwdOverride !== 'process.cwd()'
8989
9005
  ? String(cwdOverride)
8990
9006
  : process.cwd()
8991
- let { branch: branchName, repo: repoName } = cli.flags
9007
+ let { branch: branchName = '', repo: repoName = '' } = cli.flags
8992
9008
 
8993
9009
  // We're going to need an api token to suggest data because those suggestions
8994
9010
  // must come from data we already know. Don't error on missing api token yet.
@@ -9116,7 +9132,6 @@ async function fetchDeleteOrgFullScan(orgSlug, scanId) {
9116
9132
  spinner.successAndStop('Received response for deleting a scan.')
9117
9133
  if (!result.success) {
9118
9134
  handleUnsuccessfulApiResponse('deleteOrgFullScan', result)
9119
- return
9120
9135
  }
9121
9136
  return result.data
9122
9137
  }
@@ -9146,6 +9161,10 @@ const config$6 = {
9146
9161
  Usage
9147
9162
  $ ${command} <org slug> <scan ID>
9148
9163
 
9164
+ API Token Requirements
9165
+ - Quota: 1 unit
9166
+ - Permissions: full-scans:delete
9167
+
9149
9168
  Options
9150
9169
  ${getFlagListOutput(config.flags, 6)}
9151
9170
 
@@ -9228,7 +9247,6 @@ async function fetchListScans({
9228
9247
  spinner.successAndStop(`Received response for list of scans.`)
9229
9248
  if (!result.success) {
9230
9249
  handleUnsuccessfulApiResponse('getOrgFullScanList', result)
9231
- return
9232
9250
  }
9233
9251
  return result.data
9234
9252
  }
@@ -9349,6 +9367,10 @@ const config$5 = {
9349
9367
  Usage
9350
9368
  $ ${command} <org slug>
9351
9369
 
9370
+ API Token Requirements
9371
+ - Quota: 1 unit
9372
+ - Permissions: full-scans:list
9373
+
9352
9374
  Options
9353
9375
  ${getFlagListOutput(config.flags, 6)}
9354
9376
 
@@ -9427,7 +9449,6 @@ async function fetchScanMetadata(orgSlug, scanId) {
9427
9449
  spinner.successAndStop('Received response for scan meta data.')
9428
9450
  if (!result.success) {
9429
9451
  handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result)
9430
- return
9431
9452
  }
9432
9453
  return result.data
9433
9454
  }
@@ -9489,6 +9510,10 @@ const config$4 = {
9489
9510
  Usage
9490
9511
  $ ${command} <org slug> <scan id>
9491
9512
 
9513
+ API Token Requirements
9514
+ - Quota: 1 unit
9515
+ - Permissions: full-scans:list
9516
+
9492
9517
  Options
9493
9518
  ${getFlagListOutput(config.flags, 6)}
9494
9519
 
@@ -9560,14 +9585,8 @@ async function run$4(argv, importMeta, { parentName }) {
9560
9585
  /**
9561
9586
  * This fetches all the relevant pieces of data to generate a report, given a
9562
9587
  * full scan ID.
9563
- * It can optionally only fetch the security or license side of things.
9564
9588
  */
9565
- async function fetchReportData(
9566
- orgSlug,
9567
- scanId,
9568
- // includeLicensePolicy: boolean,
9569
- includeSecurityPolicy
9570
- ) {
9589
+ async function fetchReportData(orgSlug, scanId, includeLicensePolicy) {
9571
9590
  const apiToken = shadowNpmInject.getDefaultToken()
9572
9591
  if (!apiToken) {
9573
9592
  throw new shadowNpmInject.AuthError(
@@ -9576,7 +9595,6 @@ async function fetchReportData(
9576
9595
  }
9577
9596
  const sockSdk = await shadowNpmInject.setupSdk(apiToken)
9578
9597
  let haveScan = false
9579
- // let haveLicensePolicy = false
9580
9598
  let haveSecurityPolicy = false
9581
9599
 
9582
9600
  // Lazily access constants.spinner.
@@ -9584,48 +9602,26 @@ async function fetchReportData(
9584
9602
  function updateProgress() {
9585
9603
  const needs = [
9586
9604
  !haveScan ? 'scan' : undefined,
9587
- // includeLicensePolicy && !haveLicensePolicy ? 'license policy' : undefined,
9588
- includeSecurityPolicy && !haveSecurityPolicy
9589
- ? 'security policy'
9590
- : undefined
9605
+ !haveSecurityPolicy ? 'security policy' : undefined
9591
9606
  ].filter(Boolean)
9592
- if (needs.length > 2) {
9593
- // .toOxford()
9594
- needs[needs.length - 1] = `and ${needs[needs.length - 1]}`
9595
- }
9596
9607
  const haves = [
9597
9608
  haveScan ? 'scan' : undefined,
9598
- // includeLicensePolicy && haveLicensePolicy ? 'license policy' : undefined,
9599
- includeSecurityPolicy && haveSecurityPolicy
9600
- ? 'security policy'
9601
- : undefined
9609
+ haveSecurityPolicy ? 'security policy' : undefined
9602
9610
  ].filter(Boolean)
9603
- if (haves.length > 2) {
9604
- // .toOxford()
9605
- haves[haves.length - 1] = `and ${haves[haves.length - 1]}`
9606
- }
9607
9611
  if (needs.length) {
9608
9612
  spinner.start(
9609
- `Fetching ${needs.join(needs.length > 2 ? ', ' : ' and ')}...${haves.length ? ` Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` : ''}`
9613
+ `Fetching ${needs.join(' and ')}...${haves.length ? ` Completed fetching ${haves.join(' and ')}.` : ''}`
9610
9614
  )
9611
9615
  } else {
9612
- spinner.successAndStop(
9613
- `Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.`
9614
- )
9616
+ spinner.successAndStop(`Completed fetching ${haves.join(' and ')}.`)
9615
9617
  }
9616
9618
  }
9617
9619
  updateProgress()
9618
-
9619
- // @ts-ignore
9620
- const [
9621
- scan,
9622
- // licensePolicyMaybe,
9623
- securityPolicyMaybe
9624
- ] = await Promise.all([
9620
+ const [scan, securityPolicyMaybe] = await Promise.all([
9625
9621
  (async () => {
9626
9622
  try {
9627
9623
  const response = await queryApi(
9628
- `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,
9624
+ `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}${includeLicensePolicy ? '?include_license_details=true' : ''}`,
9629
9625
  apiToken
9630
9626
  )
9631
9627
  haveScan = true
@@ -9651,32 +9647,16 @@ async function fetchReportData(
9651
9647
  })
9652
9648
  return data
9653
9649
  } catch (e) {
9654
- spinner.errorAndStop(
9655
- 'There was an issue while fetching full scan data.'
9656
- )
9650
+ spinner.errorAndStop('There was an issue while fetching full scan data')
9657
9651
  throw e
9658
9652
  }
9659
9653
  })(),
9660
- // includeLicensePolicy &&
9661
- // (async () => {
9662
- // const r = await sockSdk.getOrgSecurityPolicy(orgSlug)
9663
- // haveLicensePolicy = true
9664
- // updateProgress()
9665
- // return await handleApiCall(
9666
- // r,
9667
- // "looking up organization's license policy"
9668
- // )
9669
- // })(),
9670
- includeSecurityPolicy &&
9671
- (async () => {
9672
- const r = await sockSdk.getOrgSecurityPolicy(orgSlug)
9673
- haveSecurityPolicy = true
9674
- updateProgress()
9675
- return await handleApiCall(
9676
- r,
9677
- "looking up organization's security policy"
9678
- )
9679
- })()
9654
+ (async () => {
9655
+ const r = await sockSdk.getOrgSecurityPolicy(orgSlug)
9656
+ haveSecurityPolicy = true
9657
+ updateProgress()
9658
+ return await handleApiCall(r, "looking up organization's security policy")
9659
+ })()
9680
9660
  ]).finally(() => spinner.stop())
9681
9661
  if (!Array.isArray(scan)) {
9682
9662
  logger.logger.error('Was unable to fetch scan, bailing')
@@ -9684,55 +9664,30 @@ async function fetchReportData(
9684
9664
  return {
9685
9665
  ok: false,
9686
9666
  scan: undefined,
9687
- // licensePolicy: undefined,
9688
9667
  securityPolicy: undefined
9689
9668
  }
9690
9669
  }
9691
-
9692
- // // Note: security->license once the api ships in the sdk
9693
- // let licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'> =
9694
- // undefined
9695
- // if (includeLicensePolicy) {
9696
- // if (licensePolicyMaybe && licensePolicyMaybe.success) {
9697
- // licensePolicy = licensePolicyMaybe
9698
- // } else {
9699
- // logger.error('Was unable to fetch license policy, bailing')
9700
- // process.exitCode = 1
9701
- // return {
9702
- // ok: false,
9703
- // scan: undefined,
9704
- // licensePolicy: undefined,
9705
- // securityPolicy: undefined
9706
- // }
9707
- // }
9708
- // }
9709
-
9710
9670
  let securityPolicy = undefined
9711
- if (includeSecurityPolicy) {
9712
- if (securityPolicyMaybe && securityPolicyMaybe.success) {
9713
- securityPolicy = securityPolicyMaybe
9714
- } else {
9715
- logger.logger.error('Was unable to fetch security policy, bailing')
9716
- process.exitCode = 1
9717
- return {
9718
- ok: false,
9719
- scan: undefined,
9720
- // licensePolicy: undefined,
9721
- securityPolicy: undefined
9722
- }
9671
+ if (securityPolicyMaybe && securityPolicyMaybe.success) {
9672
+ securityPolicy = securityPolicyMaybe
9673
+ } else {
9674
+ logger.logger.error('Was unable to fetch security policy, bailing')
9675
+ process.exitCode = 1
9676
+ return {
9677
+ ok: false,
9678
+ scan: undefined,
9679
+ securityPolicy: undefined
9723
9680
  }
9724
9681
  }
9725
9682
  return {
9726
9683
  ok: true,
9727
9684
  scan,
9728
- // licensePolicy,
9729
9685
  securityPolicy
9730
9686
  }
9731
9687
  }
9732
9688
 
9733
9689
  function generateReport(
9734
9690
  scan,
9735
- _licensePolicy,
9736
9691
  securityPolicy,
9737
9692
  { fold, orgSlug, reportLevel, scanId, short, spinner }
9738
9693
  ) {
@@ -9758,6 +9713,14 @@ function generateReport(
9758
9713
  // - monitor/ignore: no action
9759
9714
  // - defer: unknown (no action)
9760
9715
 
9716
+ // Note: the server will emit alerts for license policy violations but
9717
+ // those are only included if you set the flag when requesting the scan
9718
+ // data. The alerts map to a single security policy key that determines
9719
+ // what to do with any violation, regardless of the concrete license.
9720
+ // That rule is called "License Policy Violation".
9721
+ // The license policy part is implicitly handled here. Either they are
9722
+ // included and may show up, or they are not and won't show up.
9723
+
9761
9724
  const violations = new Map()
9762
9725
  let healthy = true
9763
9726
  const securityRules = securityPolicy?.data.securityPolicyRules
@@ -10004,13 +9967,11 @@ function* walkNestedMap(map, keys = []) {
10004
9967
 
10005
9968
  async function outputScanReport(
10006
9969
  scan,
10007
- // licensePolicy: undefined | SocketSdkReturnType<'getOrgSecurityPolicy'>,
10008
9970
  securityPolicy,
10009
9971
  {
10010
9972
  filePath,
10011
9973
  fold,
10012
9974
  includeLicensePolicy,
10013
- includeSecurityPolicy,
10014
9975
  orgSlug,
10015
9976
  outputKind,
10016
9977
  reportLevel,
@@ -10018,25 +9979,15 @@ async function outputScanReport(
10018
9979
  short
10019
9980
  }
10020
9981
  ) {
10021
- if (!includeSecurityPolicy) {
10022
- process.exitCode = 1
10023
- return // caller should assert
10024
- }
10025
- const scanReport = generateReport(
10026
- scan,
10027
- undefined,
10028
- // licensePolicy,
10029
- securityPolicy,
10030
- {
10031
- orgSlug,
10032
- scanId,
10033
- fold,
10034
- reportLevel,
10035
- short,
10036
- // Lazily access constants.spinner.
10037
- spinner: constants.spinner
10038
- }
10039
- )
9982
+ const scanReport = generateReport(scan, securityPolicy, {
9983
+ orgSlug,
9984
+ scanId,
9985
+ fold,
9986
+ reportLevel,
9987
+ short,
9988
+ // Lazily access constants.spinner.
9989
+ spinner: constants.spinner
9990
+ })
10040
9991
  if (!scanReport.healthy) {
10041
9992
  process.exitCode = 1
10042
9993
  }
@@ -10044,7 +9995,9 @@ async function outputScanReport(
10044
9995
  outputKind === 'json' ||
10045
9996
  (outputKind === 'text' && filePath && filePath.endsWith('.json'))
10046
9997
  ) {
10047
- const json = short ? JSON.stringify(scanReport) : toJsonReport(scanReport)
9998
+ const json = short
9999
+ ? JSON.stringify(scanReport)
10000
+ : toJsonReport(scanReport, includeLicensePolicy)
10048
10001
  if (filePath !== '-') {
10049
10002
  logger.logger.log('Writing json report to', filePath)
10050
10003
  return await fs.writeFile(filePath, json)
@@ -10055,7 +10008,7 @@ async function outputScanReport(
10055
10008
  if (outputKind === 'markdown' || filePath.endsWith('.md')) {
10056
10009
  const md = short
10057
10010
  ? `healthy = ${scanReport.healthy}`
10058
- : toMarkdownReport(scanReport)
10011
+ : toMarkdownReport(scanReport, includeLicensePolicy)
10059
10012
  if (filePath !== '-') {
10060
10013
  logger.logger.log('Writing markdown report to', filePath)
10061
10014
  return await fs.writeFile(filePath, md)
@@ -10071,10 +10024,11 @@ async function outputScanReport(
10071
10024
  })
10072
10025
  }
10073
10026
  }
10074
- function toJsonReport(report) {
10027
+ function toJsonReport(report, includeLicensePolicy) {
10075
10028
  const obj = mapToObject(report.alerts)
10076
10029
  const json = JSON.stringify(
10077
10030
  {
10031
+ includeLicensePolicy,
10078
10032
  ...report,
10079
10033
  alerts: obj
10080
10034
  },
@@ -10083,7 +10037,7 @@ function toJsonReport(report) {
10083
10037
  )
10084
10038
  return json
10085
10039
  }
10086
- function toMarkdownReport(report) {
10040
+ function toMarkdownReport(report, includeLicensePolicy) {
10087
10041
  const flatData = Array.from(walkNestedMap(report.alerts)).map(
10088
10042
  ({ keys, value }) => {
10089
10043
  const { manifest, policy, type, url } = value
@@ -10102,11 +10056,11 @@ function toMarkdownReport(report) {
10102
10056
  # Scan Policy Report
10103
10057
 
10104
10058
  This report tells you whether the results of a Socket scan results violate the
10105
- security or license policy set by your organization.
10059
+ security${includeLicensePolicy ? ' or license' : ''} policy set by your organization.
10106
10060
 
10107
10061
  ## Health status
10108
10062
 
10109
- ${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.'}
10063
+ ${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.'}
10110
10064
 
10111
10065
  ## Settings
10112
10066
 
@@ -10116,6 +10070,7 @@ Configuration used to generate this report:
10116
10070
  - Scan ID: ${report.scanId}
10117
10071
  - Alert folding: ${report.options.fold === 'none' ? 'none' : `up to ${report.options.fold}`}
10118
10072
  - Minimal policy level for alert to be included in report: ${report.options.reportLevel === 'defer' ? 'everything' : report.options.reportLevel}
10073
+ - Include license alerts: ${includeLicensePolicy ? 'yes' : 'no'}
10119
10074
 
10120
10075
  ## Alerts
10121
10076
 
@@ -10130,27 +10085,16 @@ async function handleScanReport({
10130
10085
  filePath,
10131
10086
  fold,
10132
10087
  includeLicensePolicy,
10133
- includeSecurityPolicy,
10134
10088
  orgSlug,
10135
10089
  outputKind,
10136
10090
  reportLevel,
10137
10091
  scanId,
10138
10092
  short
10139
10093
  }) {
10140
- if (!includeSecurityPolicy) {
10141
- process.exitCode = 1
10142
- return // caller should assert
10143
- }
10144
- const {
10145
- // licensePolicy,
10146
- ok,
10147
- scan,
10148
- securityPolicy
10149
- } = await fetchReportData(
10094
+ const { ok, scan, securityPolicy } = await fetchReportData(
10150
10095
  orgSlug,
10151
10096
  scanId,
10152
- // includeLicensePolicy
10153
- includeSecurityPolicy
10097
+ includeLicensePolicy
10154
10098
  )
10155
10099
  if (!ok) {
10156
10100
  return
@@ -10160,7 +10104,6 @@ async function handleScanReport({
10160
10104
  fold,
10161
10105
  scanId: scanId,
10162
10106
  includeLicensePolicy,
10163
- includeSecurityPolicy,
10164
10107
  orgSlug,
10165
10108
  outputKind,
10166
10109
  reportLevel,
@@ -10173,8 +10116,7 @@ const config$3 = {
10173
10116
  commandName: 'report',
10174
10117
  description:
10175
10118
  'Check whether a scan result passes the organizational policies (security, license)',
10176
- hidden: true,
10177
- // [beta]
10119
+ hidden: false,
10178
10120
  flags: {
10179
10121
  ...commonFlags,
10180
10122
  ...outputFlags,
@@ -10193,31 +10135,23 @@ const config$3 = {
10193
10135
  default: false,
10194
10136
  description: 'Report only the healthy status'
10195
10137
  },
10196
- // license: {
10197
- // type: 'boolean',
10198
- // default: true,
10199
- // description: 'Report the license policy status. Default: true'
10200
- // },
10201
- security: {
10138
+ license: {
10202
10139
  type: 'boolean',
10203
- default: true,
10204
- description: 'Report the security policy status. Default: true'
10140
+ default: false,
10141
+ description: 'Also report the license policy status. Default: false'
10205
10142
  }
10206
10143
  },
10207
10144
  help: (command, config) => `
10208
10145
  Usage
10209
10146
  $ ${command} <org slug> <scan ID> [path to output file]
10210
10147
 
10148
+ API Token Requirements
10149
+ - Quota: 2 units
10150
+ - Permissions: full-scans:list security-policy:read
10151
+
10211
10152
  Options
10212
10153
  ${getFlagListOutput(config.flags, 6)}
10213
10154
 
10214
- This consumes 1 quota unit plus 1 for each of the requested policy types.
10215
-
10216
- Note: By default it reports both so by default it consumes 3 quota units.
10217
-
10218
- Your API token will need the \`full-scans:list\` scope regardless. Additionally
10219
- it needs \`security-policy:read\` to report on the security policy.
10220
-
10221
10155
  By default the result is a nested object that looks like this:
10222
10156
  \`{[ecosystem]: {[pkgName]: {[version]: {[file]: {[type:loc]: policy}}}}\`
10223
10157
  You can fold this up to given level: 'pkg', 'version', 'file', and 'none'.
@@ -10229,6 +10163,7 @@ const config$3 = {
10229
10163
 
10230
10164
  Examples
10231
10165
  $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --json --fold=version
10166
+ $ ${command} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 --license --markdown --short
10232
10167
  `
10233
10168
  }
10234
10169
  const cmdScanReport = {
@@ -10246,10 +10181,9 @@ async function run$3(argv, importMeta, { parentName }) {
10246
10181
  const {
10247
10182
  fold = 'none',
10248
10183
  json,
10249
- // license,
10184
+ license,
10250
10185
  markdown,
10251
- reportLevel = 'warn',
10252
- security
10186
+ reportLevel = 'warn'
10253
10187
  } = cli.flags
10254
10188
  const defaultOrgSlug = shadowNpmInject.getConfigValue('defaultOrg')
10255
10189
  const orgSlug = defaultOrgSlug || cli.input[0] || ''
@@ -10296,9 +10230,7 @@ async function run$3(argv, importMeta, { parentName }) {
10296
10230
  await handleScanReport({
10297
10231
  orgSlug,
10298
10232
  scanId: scanId,
10299
- includeLicensePolicy: false,
10300
- // !!license,
10301
- includeSecurityPolicy: typeof security === 'boolean' ? security : true,
10233
+ includeLicensePolicy: !!license,
10302
10234
  outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
10303
10235
  filePath: file,
10304
10236
  fold: fold,
@@ -10307,6 +10239,46 @@ async function run$3(argv, importMeta, { parentName }) {
10307
10239
  })
10308
10240
  }
10309
10241
 
10242
+ async function fetchScan(orgSlug, scanId) {
10243
+ const apiToken = shadowNpmInject.getDefaultToken()
10244
+ if (!apiToken) {
10245
+ throw new shadowNpmInject.AuthError(
10246
+ 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
10247
+ )
10248
+ }
10249
+
10250
+ // Lazily access constants.spinner.
10251
+ const { spinner } = constants
10252
+ spinner.start('Fetching scan data...')
10253
+ const response = await queryApi(
10254
+ `orgs/${orgSlug}/full-scans/${encodeURIComponent(scanId)}`,
10255
+ apiToken
10256
+ )
10257
+ spinner.successAndStop('Received response while fetching scan data.')
10258
+ if (!response.ok) {
10259
+ const err = await handleApiError(response.status)
10260
+ logger.logger.fail(
10261
+ failMsgWithBadge(response.statusText, `Fetch error: ${err}`)
10262
+ )
10263
+ return
10264
+ }
10265
+
10266
+ // This is nd-json; each line is a json object
10267
+ const jsons = await response.text()
10268
+ const lines = jsons.split('\n').filter(Boolean)
10269
+ const data = lines.map(line => {
10270
+ try {
10271
+ return JSON.parse(line)
10272
+ } catch {
10273
+ console.error(
10274
+ 'At least one line item was returned that could not be parsed as JSON...'
10275
+ )
10276
+ return {}
10277
+ }
10278
+ })
10279
+ return data
10280
+ }
10281
+
10310
10282
  async function outputScanView(artifacts, orgSlug, scanId, filePath) {
10311
10283
  const display = artifacts.map(art => {
10312
10284
  const author = Array.isArray(art.author)
@@ -10367,7 +10339,6 @@ async function streamScan(orgSlug, scanId, file) {
10367
10339
  spinner.successAndStop(`Full scan details written to ${file}`)
10368
10340
  if (!data?.success) {
10369
10341
  handleUnsuccessfulApiResponse('getOrgFullScan', data)
10370
- return
10371
10342
  }
10372
10343
  return data
10373
10344
  }
@@ -10385,6 +10356,10 @@ const config$2 = {
10385
10356
  Usage
10386
10357
  $ ${command} <org slug> <scan ID> [path to output file]
10387
10358
 
10359
+ API Token Requirements
10360
+ - Quota: 1 unit
10361
+ - Permissions: full-scans:list
10362
+
10388
10363
  When no output path is given the contents is sent to stdout.
10389
10364
 
10390
10365
  Options
@@ -10734,6 +10709,11 @@ const config$1 = {
10734
10709
  Usage
10735
10710
  $ ${command}
10736
10711
 
10712
+ API Token Requirements
10713
+ - Quota: 1 unit
10714
+ - Permissions: threat-feed:list
10715
+ - Special access
10716
+
10737
10717
  This feature requires a Threat Feed license. Please contact
10738
10718
  sales@socket.dev if you are interested in purchasing this access.
10739
10719
 
@@ -10852,7 +10832,7 @@ function checkSocketWrapperSetup(file) {
10852
10832
  )
10853
10833
  if (linesWithSocketAlias.length) {
10854
10834
  logger.logger.log(
10855
- `The Socket npm/npx wrapper is set up in your bash profile (${file}).`
10835
+ `The Socket npm/npx wrapper is set up in your bash profile (${file})`
10856
10836
  )
10857
10837
  return true
10858
10838
  }
@@ -11034,7 +11014,7 @@ void (async () => {
11034
11014
  await updateNotifier({
11035
11015
  name: SOCKET_CLI_BIN_NAME,
11036
11016
  // The '@rollup/plugin-replace' will replace "process.env['INLINED_SOCKET_CLI_VERSION']".
11037
- version: '0.14.68',
11017
+ version: '0.14.69',
11038
11018
  ttl: 86_400_000 /* 24 hours in milliseconds */
11039
11019
  })
11040
11020
  try {
@@ -11105,5 +11085,5 @@ void (async () => {
11105
11085
  await shadowNpmInject.captureException(e)
11106
11086
  }
11107
11087
  })()
11108
- //# debugId=5d7bf594-d56e-4593-84a1-a57f22b14f60
11088
+ //# debugId=a9c4fdbd-ffcd-4218-a422-d82749e51eff
11109
11089
  //# sourceMappingURL=cli.js.map