@contrast/contrast 1.0.1 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.prettierignore +2 -0
  2. package/README.md +103 -133
  3. package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +26 -11
  4. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +62 -234
  5. package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
  6. package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
  7. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
  8. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
  9. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
  10. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
  11. package/dist/commands/audit/auditController.js +6 -3
  12. package/dist/commands/audit/saveFile.js +11 -0
  13. package/dist/commands/auth/auth.js +19 -1
  14. package/dist/commands/config/config.js +19 -8
  15. package/dist/commands/scan/processScan.js +8 -25
  16. package/dist/common/HTTPClient.js +30 -26
  17. package/dist/common/errorHandling.js +17 -1
  18. package/dist/common/versionChecker.js +32 -0
  19. package/dist/constants/constants.js +4 -2
  20. package/dist/constants/lambda.js +3 -1
  21. package/dist/constants/locales.js +41 -18
  22. package/dist/constants.js +39 -3
  23. package/dist/index.js +49 -28
  24. package/dist/lambda/help.js +22 -14
  25. package/dist/lambda/lambda.js +6 -0
  26. package/dist/sbom/generateSbom.js +20 -0
  27. package/dist/scan/help.js +4 -2
  28. package/dist/scan/models/groupedResultsModel.js +10 -0
  29. package/dist/scan/models/resultContentModel.js +2 -0
  30. package/dist/scan/models/scanResultsModel.js +11 -0
  31. package/dist/scan/populateProjectIdAndProjectName.js +1 -0
  32. package/dist/scan/saveResults.js +9 -10
  33. package/dist/scan/scan.js +99 -74
  34. package/dist/scan/scanConfig.js +20 -1
  35. package/dist/scan/scanController.js +7 -2
  36. package/dist/scan/scanResults.js +6 -0
  37. package/dist/utils/getConfig.js +3 -0
  38. package/dist/utils/paramsUtil/commandlineParams.js +1 -1
  39. package/dist/utils/requestUtils.js +1 -1
  40. package/dist/utils/saveFile.js +19 -0
  41. package/package.json +2 -2
  42. package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +33 -15
  43. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +127 -0
  44. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
  45. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
  46. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
  47. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
  48. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
  49. package/src/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
  50. package/src/commands/audit/auditController.ts +12 -3
  51. package/src/commands/audit/processAudit.ts +0 -1
  52. package/src/commands/audit/saveFile.ts +6 -0
  53. package/src/commands/auth/auth.js +25 -1
  54. package/src/commands/config/config.js +22 -8
  55. package/src/commands/scan/processScan.js +8 -29
  56. package/src/common/HTTPClient.js +42 -36
  57. package/src/common/errorHandling.ts +29 -2
  58. package/src/common/versionChecker.ts +41 -0
  59. package/src/constants/constants.js +5 -4
  60. package/src/constants/lambda.js +3 -1
  61. package/src/constants/locales.js +51 -19
  62. package/src/constants.js +44 -3
  63. package/src/index.ts +63 -31
  64. package/src/lambda/help.ts +22 -14
  65. package/src/lambda/lambda.ts +8 -0
  66. package/src/sbom/generateSbom.ts +17 -0
  67. package/src/scan/help.js +4 -2
  68. package/src/scan/models/groupedResultsModel.ts +18 -0
  69. package/src/scan/models/resultContentModel.ts +86 -0
  70. package/src/scan/models/scanResultsModel.ts +52 -0
  71. package/src/scan/populateProjectIdAndProjectName.js +1 -0
  72. package/src/scan/saveResults.js +8 -9
  73. package/src/scan/scan.ts +192 -0
  74. package/src/scan/scanConfig.js +26 -1
  75. package/src/scan/scanController.js +11 -2
  76. package/src/scan/scanResults.js +11 -0
  77. package/src/utils/getConfig.ts +12 -0
  78. package/src/utils/paramsUtil/commandlineParams.js +1 -1
  79. package/src/utils/requestUtils.js +1 -1
  80. package/src/utils/saveFile.js +19 -0
  81. package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
  82. package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
  83. package/dist/common/findLatestCLIVersion.js +0 -23
  84. package/dist/lambda/scanDetail.js +0 -30
  85. package/dist/scan/fileFinder.js +0 -15
  86. package/dist/utils/fileUtils.js +0 -31
  87. package/dist/utils/paramsUtil/genericCommandLineParams.js +0 -12
  88. package/dist/utils/paramsUtil/yamlParams.js +0 -6
  89. package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
  90. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
  91. package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
  92. package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
  93. package/src/common/findLatestCLIVersion.ts +0 -27
  94. package/src/scan/scan.js +0 -162
@@ -0,0 +1,56 @@
1
+ import {
2
+ createLibraryHeader,
3
+ getReport,
4
+ printVulnerabilityResponse
5
+ } from './commonReportingFunctions'
6
+ import {
7
+ convertGenericToTypedLibraries,
8
+ severityCount
9
+ } from './utils/reportUtils'
10
+
11
+ export async function vulnerabilityReport(
12
+ analysis: any,
13
+ applicationId: string,
14
+ reportId: string
15
+ ) {
16
+ const reportResponse = await getReport(analysis.config, reportId)
17
+
18
+ if (reportResponse !== undefined) {
19
+ const id = applicationId
20
+ const name = analysis.config.applicationName
21
+ formatVulnerabilityOutput(
22
+ reportResponse.vulnerabilities,
23
+ id,
24
+ name,
25
+ analysis.config
26
+ )
27
+ }
28
+ }
29
+
30
+ export function formatVulnerabilityOutput(
31
+ libraryVulnerabilityResponse: any,
32
+ id: string,
33
+ name: string,
34
+ config: any
35
+ ) {
36
+ const vulnerableLibraries = convertGenericToTypedLibraries(
37
+ libraryVulnerabilityResponse
38
+ )
39
+
40
+ const numberOfVulnerableLibraries = vulnerableLibraries.length
41
+ let numberOfCves = 0
42
+ vulnerableLibraries.forEach(lib => (numberOfCves += lib.cveArray.length))
43
+
44
+ createLibraryHeader(id, numberOfVulnerableLibraries, numberOfCves, name)
45
+
46
+ const hasSomeVulnerabilitiesReported = printVulnerabilityResponse(
47
+ vulnerableLibraries,
48
+ config
49
+ )
50
+
51
+ return [
52
+ hasSomeVulnerabilitiesReported,
53
+ numberOfCves,
54
+ severityCount(vulnerableLibraries)
55
+ ]
56
+ }
@@ -0,0 +1,110 @@
1
+ import {
2
+ ReportCVEModel,
3
+ ReportLibraryModel
4
+ } from '../models/reportLibraryModel'
5
+ import { ReportSeverityModel } from '../models/reportSeverityModel'
6
+ import languageAnalysisEngine from '../../../languageAnalysisEngine/constants'
7
+ const {
8
+ supportedLanguages: { GO }
9
+ } = languageAnalysisEngine
10
+
11
+ export function findHighestSeverityCVE(cveArray: ReportCVEModel[]) {
12
+ if (
13
+ cveArray.find(
14
+ cve =>
15
+ cve.cvss3SeverityCode === 'CRITICAL' || cve.severityCode === 'CRITICAL'
16
+ )
17
+ ) {
18
+ return new ReportSeverityModel('CRITICAL', 1)
19
+ } else if (
20
+ cveArray.find(
21
+ cve => cve.cvss3SeverityCode === 'HIGH' || cve.severityCode === 'HIGH'
22
+ )
23
+ ) {
24
+ return new ReportSeverityModel('HIGH', 2)
25
+ } else if (
26
+ cveArray.find(
27
+ cve => cve.cvss3SeverityCode === 'MEDIUM' || cve.severityCode === 'MEDIUM'
28
+ )
29
+ ) {
30
+ return new ReportSeverityModel('MEDIUM', 3)
31
+ } else if (
32
+ cveArray.find(
33
+ cve => cve.cvss3SeverityCode === 'LOW' || cve.severityCode === 'LOW'
34
+ )
35
+ ) {
36
+ return new ReportSeverityModel('LOW', 4)
37
+ } else if (
38
+ cveArray.find(
39
+ cve => cve.cvss3SeverityCode === 'NOTE' || cve.severityCode === 'NOTE'
40
+ )
41
+ ) {
42
+ return new ReportSeverityModel('NOTE', 5)
43
+ }
44
+ }
45
+
46
+ export function convertGenericToTypedLibraries(libraries: any) {
47
+ return Object.entries(libraries).map(([name, cveArray]) => {
48
+ return new ReportLibraryModel(name, cveArray as ReportCVEModel[])
49
+ })
50
+ }
51
+
52
+ export function severityCount(vulnerableLibraries: ReportLibraryModel[]) {
53
+ const severityCount = {
54
+ critical: 0,
55
+ high: 0,
56
+ medium: 0,
57
+ low: 0,
58
+ note: 0
59
+ }
60
+
61
+ vulnerableLibraries.forEach(lib => {
62
+ lib.cveArray.forEach(cve => {
63
+ if (
64
+ cve.cvss3SeverityCode === 'CRITICAL' ||
65
+ cve.severityCode === 'CRITICAL'
66
+ ) {
67
+ severityCount['critical'] += 1
68
+ } else if (
69
+ cve.cvss3SeverityCode === 'HIGH' ||
70
+ cve.severityCode === 'HIGH'
71
+ ) {
72
+ severityCount['high'] += 1
73
+ } else if (
74
+ cve.cvss3SeverityCode === 'MEDIUM' ||
75
+ cve.severityCode === 'MEDIUM'
76
+ ) {
77
+ severityCount['medium'] += 1
78
+ } else if (
79
+ cve.cvss3SeverityCode === 'LOW' ||
80
+ cve.severityCode === 'LOW'
81
+ ) {
82
+ severityCount['low'] += 1
83
+ } else if (
84
+ cve.cvss3SeverityCode === 'NOTE' ||
85
+ cve.severityCode === 'NOTE'
86
+ ) {
87
+ severityCount['note'] += 1
88
+ }
89
+ })
90
+ })
91
+
92
+ return severityCount
93
+ }
94
+
95
+ export function findNameAndVersion(library: ReportLibraryModel, config: any) {
96
+ if (config.language.toUpperCase() === GO) {
97
+ const nameVersion = library.name.split('@')
98
+ const name = nameVersion[0]
99
+ const version = nameVersion[1]
100
+
101
+ return { name, version }
102
+ } else {
103
+ const splitLibraryName = library.name.split('/')
104
+ const nameVersion = splitLibraryName[1].split('@')
105
+ const name = nameVersion[0]
106
+ const version = nameVersion[1]
107
+
108
+ return { name, version }
109
+ }
110
+ }
@@ -34,7 +34,9 @@ const newSendSnapShot = async (analysis, applicationId) => {
34
34
  // console.log(prettyjson.render(requestBody))
35
35
  // }
36
36
  if (res.statusCode === 201) {
37
- displaySnapshotSuccessMessage(analysis.config)
37
+ if (analysis.config.host !== 'https://ce.contrastsecurity.com/') {
38
+ displaySnapshotSuccessMessage(analysis.config)
39
+ }
38
40
  return res.body
39
41
  } else {
40
42
  handleResponseErrors(res, 'snapshot')
@@ -1,7 +1,8 @@
1
1
  import { catalogueApplication } from '../../audit/catalogueApplication/catalogueApplication'
2
2
  import commonApi from '../../audit/languageAnalysisEngine/commonApi'
3
+
3
4
  const identifyLanguageAE = require('./../../audit/languageAnalysisEngine')
4
- const languageFactory = require('./../../audit/languageAnalysisEngine/langugageAnalysisFactory')
5
+ const languageFactory = require('../../audit/languageAnalysisEngine/languageAnalysisFactory')
5
6
 
6
7
  const dealWithNoAppId = async (config: { [x: string]: string }) => {
7
8
  let appID
@@ -10,10 +11,18 @@ const dealWithNoAppId = async (config: { [x: string]: string }) => {
10
11
  if (!appID && config.applicationName) {
11
12
  return await catalogueApplication(config)
12
13
  }
14
+ // @ts-ignore
13
15
  } catch (e) {
14
- console.log(e)
16
+ // @ts-ignore
17
+ if (e.toString().includes('tunneling socket could not be established')) {
18
+ // @ts-ignore
19
+ console.log(e.message)
20
+ console.log(
21
+ 'There seems to be an issue with your proxy, please check and try again'
22
+ )
23
+ }
24
+ process.exit(1)
15
25
  }
16
- console.log(appID)
17
26
  return appID
18
27
  }
19
28
 
@@ -11,7 +11,6 @@ export const processAudit = async (argv: parameterInput) => {
11
11
  }
12
12
  const config = getAuditConfig(argv)
13
13
  const auditResults = await startAudit(config)
14
- //report here
15
14
  }
16
15
 
17
16
  const printHelpMessage = () => {
@@ -0,0 +1,6 @@
1
+ import fs from 'fs'
2
+
3
+ export default function saveFile(config: any, rawResults: any) {
4
+ const fileName = `${config.applicationId}-sbom-cyclonedx.json`
5
+ fs.writeFileSync(fileName, JSON.stringify(rawResults))
6
+ }
@@ -11,8 +11,21 @@ const {
11
11
  succeedSpinner
12
12
  } = require('../../utils/oraWrapper')
13
13
  const { TIMEOUT, AUTH_UI_URL } = require('../../constants/constants')
14
+ const parsedCLIOptions = require('../../utils/parsedCLIOptions')
15
+ const constants = require('../../constants')
16
+ const commandLineUsage = require('command-line-usage')
17
+
18
+ const processAuth = async (argv, config) => {
19
+ let authParams = parsedCLIOptions.getCommandLineArgsCustom(
20
+ argv,
21
+ constants.commandLineDefinitions.authOptionDefinitions
22
+ )
23
+
24
+ if (authParams.help) {
25
+ console.log(authUsageGuide)
26
+ process.exit(0)
27
+ }
14
28
 
15
- const processAuth = async config => {
16
29
  const token = uuidv4()
17
30
  const url = `${AUTH_UI_URL}/?token=${token}`
18
31
 
@@ -68,6 +81,17 @@ const pollAuthResult = async (token, client) => {
68
81
  })
69
82
  }
70
83
 
84
+ const authUsageGuide = commandLineUsage([
85
+ {
86
+ header: i18n.__('authHeader'),
87
+ content: [i18n.__('constantsAuthHeaderContents')]
88
+ },
89
+ {
90
+ header: i18n.__('constantsAuthUsageHeader'),
91
+ content: [i18n.__('constantsAuthUsageContents')]
92
+ }
93
+ ])
94
+
71
95
  module.exports = {
72
96
  processAuth: processAuth
73
97
  }
@@ -1,15 +1,19 @@
1
- const commandLineArgs = require('command-line-args')
1
+ const parsedCLIOptions = require('../../utils/parsedCLIOptions')
2
+ const constants = require('../../constants')
3
+ const commandLineUsage = require('command-line-usage')
4
+ const i18n = require('i18n')
2
5
 
3
6
  const processConfig = (argv, config) => {
4
- const options = [{ name: 'clear', alias: 'c', type: Boolean }]
5
-
6
7
  try {
7
- const configOptions = commandLineArgs(options, {
8
+ let configParams = parsedCLIOptions.getCommandLineArgsCustom(
8
9
  argv,
9
- caseInsensitive: true,
10
- camelCase: true
11
- })
12
- if (configOptions.clear) {
10
+ constants.commandLineDefinitions.configOptionDefinitions
11
+ )
12
+ if (configParams.help) {
13
+ console.log(configUsageGuide)
14
+ process.exit(0)
15
+ }
16
+ if (configParams.clear) {
13
17
  config.clear()
14
18
  } else {
15
19
  console.log(JSON.parse(JSON.stringify(config.store)))
@@ -20,6 +24,16 @@ const processConfig = (argv, config) => {
20
24
  }
21
25
  }
22
26
 
27
+ const configUsageGuide = commandLineUsage([
28
+ {
29
+ header: i18n.__('configHeader')
30
+ },
31
+ {
32
+ content: [i18n.__('constantsConfigUsageContents')],
33
+ optionList: constants.commandLineDefinitions.configOptionDefinitions
34
+ }
35
+ ])
36
+
23
37
  module.exports = {
24
38
  processConfig: processConfig
25
39
  }
@@ -1,44 +1,23 @@
1
1
  const { startScan } = require('../../scan/scanController')
2
- const { formatScanOutput } = require('../../scan/scan')
3
2
  const { scanUsageGuide } = require('../../scan/help')
4
3
  const scanConfig = require('../../scan/scanConfig')
5
- const saveResults = require('../../scan/saveResults')
6
- const commonApi = require('../../utils/commonApi')
7
- const i18n = require('i18n')
4
+ const { saveScanFile } = require('../../utils/saveFile')
5
+ const { ScanResultsModel } = require('../../scan/models/scanResultsModel')
6
+ const { formatScanOutput } = require('../../scan/scan')
8
7
 
9
8
  const processScan = async argvMain => {
10
- if (argvMain.indexOf('--help') !== -1) {
11
- printHelpMessage()
12
- process.exit(1)
13
- }
14
-
15
9
  let config = scanConfig.getScanConfig(argvMain)
16
10
 
17
- let scanResults = await startScan(config)
11
+ let scanResults = new ScanResultsModel(await startScan(config))
18
12
  if (scanResults) {
19
- formatScanOutput(
20
- scanResults?.projectOverview,
21
- scanResults?.scanResultsInstances
22
- )
13
+ formatScanOutput(scanResults)
23
14
  }
24
15
 
25
- if (config.save) {
26
- if (config.save.toLowerCase() === 'sarif') {
27
- const scanId = scanResults.scanDetail.id
28
- const client = commonApi.getHttpClient(config)
29
- const rawResults = await client.getSpecificScanResultSarif(config, scanId)
30
- saveResults.writeResultsToFile(rawResults?.body)
31
- } else {
32
- console.log(i18n.__('scanNoFiletypeSpecifiedForSave'))
33
- }
16
+ if (config.save !== undefined) {
17
+ await saveScanFile(config, scanResults)
34
18
  }
35
19
  }
36
20
 
37
- const printHelpMessage = () => {
38
- console.log(scanUsageGuide)
39
- }
40
-
41
21
  module.exports = {
42
- processScan,
43
- printHelpMessage
22
+ processScan
44
23
  }
@@ -106,7 +106,9 @@ HTTPClient.prototype.getScanId = function getScanId(config, codeArtifactId) {
106
106
  options.url = url
107
107
  options.body = {
108
108
  codeArtifactId: codeArtifactId,
109
- label: `Started by CLI tool at ${new Date().toString()}`
109
+ label: config.label
110
+ ? config.label
111
+ : `Started by CLI tool at ${new Date().toString()}`
110
112
  }
111
113
  return requestUtils.sendRequest({ method: 'post', options })
112
114
  }
@@ -130,6 +132,9 @@ HTTPClient.prototype.createProjectId = function createProjectId(config) {
130
132
  name: config.name,
131
133
  archived: 'false'
132
134
  }
135
+ if (config.language) {
136
+ options.body.language = config.language
137
+ }
133
138
  options.url = createHarmonyProjectsUrl(config)
134
139
  return requestUtils.sendRequest({ method: 'post', options })
135
140
  }
@@ -185,6 +190,9 @@ HTTPClient.prototype.catalogueCommand = function catalogueCommand(config) {
185
190
  }
186
191
 
187
192
  HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
193
+ if (config.language.toUpperCase() === 'RUBY') {
194
+ console.log('sendSnapshot requestBody', requestBody.snapshot.ruby)
195
+ }
188
196
  const options = _.cloneDeep(this.requestOptions)
189
197
  let url = createSnapshotURL(config)
190
198
  options.url = url
@@ -192,32 +200,22 @@ HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
192
200
  return requestUtils.sendRequest({ method: 'post', options })
193
201
  }
194
202
 
195
- HTTPClient.prototype.getReport = function getReport(config) {
203
+ HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
196
204
  const options = _.cloneDeep(this.requestOptions)
197
- let url = createReportUrl(config)
198
- options.url = url
199
-
200
- return requestUtils.sendRequest({ method: 'get', options })
201
- }
202
-
203
- HTTPClient.prototype.getSpecificReport = function getSpecificReport(
204
- config,
205
- reportId
206
- ) {
207
- const options = _.cloneDeep(this.requestOptions)
208
- let url = createSpecificReportUrl(config, reportId)
209
- options.url = url
210
-
205
+ if (config.ignoreDev) {
206
+ options.url = createSpecificReportWithProdUrl(config, reportId)
207
+ } else {
208
+ options.url = createSpecificReportUrl(config, reportId)
209
+ }
211
210
  return requestUtils.sendRequest({ method: 'get', options })
212
211
  }
213
212
 
214
213
  HTTPClient.prototype.getLibraryVulnerabilities = function getLibraryVulnerabilities(
215
- requestBody,
216
- config
214
+ config,
215
+ requestBody
217
216
  ) {
218
217
  const options = _.cloneDeep(this.requestOptions)
219
- let url = createLibraryVulnerabilitiesUrl(config)
220
- options.url = url
218
+ options.url = createLibraryVulnerabilitiesUrl(config)
221
219
  options.body = requestBody
222
220
 
223
221
  return requestUtils.sendRequest({ method: 'put', options })
@@ -230,16 +228,16 @@ HTTPClient.prototype.getAppId = function getAppId(config) {
230
228
  return requestUtils.sendRequest({ method: 'get', options })
231
229
  }
232
230
 
233
- HTTPClient.prototype.getDependencyTree = function getReport(
234
- orgUuid,
235
- appId,
236
- reportId
237
- ) {
238
- const options = _.cloneDeep(this.requestOptions)
239
- let url = createGetDependencyTree(options.uri, orgUuid, appId, reportId)
240
- options.url = url
241
- return requestUtils.sendRequest({ method: 'get', options })
242
- }
231
+ // HTTPClient.prototype.getDependencyTree = function getReport(
232
+ // orgUuid,
233
+ // appId,
234
+ // reportId
235
+ // ) {
236
+ // const options = _.cloneDeep(this.requestOptions)
237
+ // let url = createGetDependencyTree(options.uri, orgUuid, appId, reportId)
238
+ // options.url = url
239
+ // return requestUtils.sendRequest({ method: 'get', options })
240
+ // }
243
241
 
244
242
  // serverless - lambda
245
243
  function getServerlessHost(config = {}) {
@@ -317,6 +315,12 @@ HTTPClient.prototype.checkLibrary = function checkLibrary(data) {
317
315
  return requestUtils.sendRequest({ method: 'post', options })
318
316
  }
319
317
 
318
+ HTTPClient.prototype.getSbom = function getSbom(config) {
319
+ const options = _.cloneDeep(this.requestOptions)
320
+ options.url = createSbomCycloneDXUrl(config)
321
+ return requestUtils.sendRequest({ method: 'get', options })
322
+ }
323
+
320
324
  // scan
321
325
  const createGetScanIdURL = config => {
322
326
  return `${config.host}/Contrast/api/sast/v1/organizations/${config.organizationId}/projects/${config.projectId}/scans/`
@@ -370,20 +374,22 @@ function createLibraryVulnerabilitiesUrl(config) {
370
374
  return `${config.host}/Contrast/api/ng/${config.organizationId}/libraries/artifactsByGroupNameVersion`
371
375
  }
372
376
 
373
- function createReportUrl(config) {
374
- return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/reports`
377
+ function createSpecificReportUrl(config, reportId) {
378
+ return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/reports/${reportId}`
375
379
  }
376
380
 
377
- function createSpecificReportUrl(config, reportId) {
378
- return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/reports/${reportId}?nodesToInclude=PROD`
381
+ function createSpecificReportWithProdUrl(config, reportId) {
382
+ return createSpecificReportUrl(config, reportId).concat(
383
+ `?nodesToInclude=PROD`
384
+ )
379
385
  }
380
386
 
381
387
  function createDataUrl() {
382
388
  return `https://ardy.contrastsecurity.com/production`
383
389
  }
384
390
 
385
- const createGetDependencyTree = (protocol, orgUuid, appId, reportId) => {
386
- return `${protocol}/Contrast/api/ng/sca/organizations/${orgUuid}/applications/${appId}/reports/${reportId}`
391
+ function createSbomCycloneDXUrl(config) {
392
+ return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/cyclonedx`
387
393
  }
388
394
 
389
395
  module.exports = HTTPClient
@@ -72,6 +72,7 @@ const badRequestError = (catalogue: boolean) => {
72
72
 
73
73
  const forbiddenError = () => {
74
74
  generalError('forbiddenRequestErrorHeader', 'forbiddenRequestErrorMessage')
75
+ process.exit(1)
75
76
  }
76
77
 
77
78
  const proxyError = () => {
@@ -104,7 +105,7 @@ const getErrorMessage = (header: string, message?: string) => {
104
105
  const multiLine = message?.includes('\n')
105
106
  let finalMessage = ''
106
107
 
107
- // i18n split the line if it includes "\n"
108
+ // i18n split the line if it includes '\n'
108
109
  if (multiLine) {
109
110
  finalMessage = `\n${message}`
110
111
  } else if (message) {
@@ -119,6 +120,31 @@ const generalError = (header: string, message?: string) => {
119
120
  console.log(finalMessage)
120
121
  }
121
122
 
123
+ const findCommandOnError = (unknownOptions: string[]) => {
124
+ const commandKeywords = {
125
+ auth: 'auth',
126
+ audit: 'audit',
127
+ scan: 'scan',
128
+ lambda: 'lambda',
129
+ config: 'config'
130
+ }
131
+
132
+ const containsCommandKeyword = unknownOptions.some(
133
+ // @ts-ignore
134
+ command => commandKeywords[command]
135
+ )
136
+
137
+ if (containsCommandKeyword) {
138
+ const foundCommands = unknownOptions.filter(
139
+ // @ts-ignore
140
+ command => commandKeywords[command]
141
+ )
142
+
143
+ //return the first command found
144
+ return foundCommands[0]
145
+ }
146
+ }
147
+
122
148
  export {
123
149
  genericError,
124
150
  unauthenticatedError,
@@ -130,5 +156,6 @@ export {
130
156
  generalError,
131
157
  getErrorMessage,
132
158
  handleResponseErrors,
133
- libraryAnalysisError
159
+ libraryAnalysisError,
160
+ findCommandOnError
134
161
  }
@@ -0,0 +1,41 @@
1
+ import latestVersion from 'latest-version'
2
+ import { APP_VERSION } from '../constants/constants'
3
+ import boxen from 'boxen'
4
+ import chalk from 'chalk'
5
+ import semver from 'semver'
6
+
7
+ export async function findLatestCLIVersion(updateMessageHidden: boolean) {
8
+ if (!updateMessageHidden) {
9
+ const latestCLIVersion = await latestVersion('@contrast/contrast')
10
+
11
+ if (semver.lt(APP_VERSION, latestCLIVersion)) {
12
+ const updateAvailableMessage = `Update available ${chalk.yellow(
13
+ APP_VERSION
14
+ )} → ${chalk.green(latestCLIVersion)}`
15
+
16
+ const npmUpdateAvailableCommand = `Run ${chalk.cyan(
17
+ 'npm i @contrast/contrast -g'
18
+ )} to update via npm`
19
+
20
+ const homebrewUpdateAvailableCommand = `Run ${chalk.cyan(
21
+ 'brew install contrastsecurity/tap/contrast'
22
+ )} to update via brew`
23
+
24
+ console.log(
25
+ boxen(
26
+ `${updateAvailableMessage}\n${npmUpdateAvailableCommand}\n\n${homebrewUpdateAvailableCommand}`,
27
+ {
28
+ titleAlignment: 'center',
29
+ margin: 1,
30
+ padding: 1,
31
+ align: 'center'
32
+ }
33
+ )
34
+ )
35
+ }
36
+ }
37
+ }
38
+
39
+ export async function isCorrectNodeVersion(currentVersion: string) {
40
+ return semver.satisfies(currentVersion, '>=16.13.2 <17')
41
+ }
@@ -8,17 +8,17 @@ const GO = 'GO'
8
8
  // we set the langauge as Node instead of PHP since we're using the Node engine in TS
9
9
  const PHP = 'PHP'
10
10
  const JAVASCRIPT = 'JAVASCRIPT'
11
-
12
11
  const LOW = 'LOW'
13
12
  const MEDIUM = 'MEDIUM'
14
13
  const HIGH = 'HIGH'
15
14
  const CRITICAL = 'CRITICAL'
16
-
17
15
  const APP_NAME = 'contrast'
18
- const APP_VERSION = '1.0.1'
16
+ const APP_VERSION = '1.0.4'
19
17
  const TIMEOUT = 120000
18
+
20
19
  const AUTH_UI_URL = 'https://cli-auth.contrastsecurity.com'
21
20
  const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com'
21
+ const SARIF_FILE = 'SARIF'
22
22
 
23
23
  module.exports = {
24
24
  supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
@@ -30,5 +30,6 @@ module.exports = {
30
30
  APP_NAME,
31
31
  TIMEOUT,
32
32
  AUTH_UI_URL,
33
- AUTH_CALLBACK_URL
33
+ AUTH_CALLBACK_URL,
34
+ SARIF_FILE
34
35
  }
@@ -36,11 +36,13 @@ const lambda = {
36
36
  loadingFunctionList: 'Loading lambda function list',
37
37
  functionsFound: '{{count}} functions found',
38
38
  noFunctionsFound: 'No functions found',
39
- failedToLoadFunctions: 'Faled to load lambda functions',
39
+ failedToLoadFunctions: 'Failed to load lambda functions',
40
40
  availableForScan: '{{icon}} {{count}} available for scan',
41
41
  runtimeCount: '----- {{runtime}} ({{count}}) -----',
42
42
 
43
43
  // ====== print vulnerabilities ===== //
44
+ gatherResults: 'Gathering results...',
45
+ doneGatherResults: 'Done gathering results',
44
46
  whatHappenedTitle: 'What happened:',
45
47
  whatHappenedItem: '{{policy}} have:\n{{comments}}\n',
46
48
  recommendation: 'Recommendation:',