@socketsecurity/cli 0.14.68 → 0.14.70

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