@contrast/contrast 1.0.5 → 1.0.8

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 (138) hide show
  1. package/.prettierignore +0 -5
  2. package/dist/audit/autodetection/autoDetectLanguage.js +3 -3
  3. package/dist/audit/catalogueApplication/catalogueApplication.js +23 -5
  4. package/dist/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +4 -2
  5. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
  6. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
  7. package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
  8. package/dist/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +5 -5
  9. package/dist/audit/languageAnalysisEngine/getProjectRootFilenames.js +9 -9
  10. package/dist/audit/languageAnalysisEngine/index.js +2 -2
  11. package/dist/audit/languageAnalysisEngine/languageAnalysisFactory.js +6 -27
  12. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +25 -5
  13. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +99 -20
  14. package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +2 -1
  15. package/dist/audit/languageAnalysisEngine/report/models/reportOutputModel.js +24 -0
  16. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +3 -1
  17. package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +16 -0
  18. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +35 -14
  19. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +58 -47
  20. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +65 -3
  21. package/dist/audit/save.js +29 -0
  22. package/dist/commands/audit/auditController.js +22 -6
  23. package/dist/commands/audit/help.js +24 -1
  24. package/dist/commands/audit/processAudit.js +8 -2
  25. package/dist/commands/audit/saveFile.js +7 -3
  26. package/dist/commands/scan/processScan.js +1 -1
  27. package/dist/commands/scan/sca/scaAnalysis.js +48 -11
  28. package/dist/common/HTTPClient.js +56 -15
  29. package/dist/common/errorHandling.js +6 -1
  30. package/dist/common/versionChecker.js +20 -5
  31. package/dist/constants/constants.js +13 -3
  32. package/dist/constants/locales.js +15 -12
  33. package/dist/constants.js +9 -4
  34. package/dist/index.js +4 -3
  35. package/dist/lambda/analytics.js +11 -0
  36. package/dist/lambda/lambda.js +35 -4
  37. package/dist/lambda/types.js +13 -0
  38. package/dist/sbom/generateSbom.js +4 -3
  39. package/dist/scaAnalysis/common/formatMessage.js +46 -1
  40. package/dist/scaAnalysis/common/treeUpload.js +1 -3
  41. package/dist/scaAnalysis/go/goAnalysis.js +17 -0
  42. package/dist/scaAnalysis/go/goParseDeps.js +158 -0
  43. package/dist/scaAnalysis/go/goReadDepFile.js +21 -0
  44. package/dist/scaAnalysis/java/analysis.js +11 -22
  45. package/dist/scaAnalysis/java/index.js +6 -6
  46. package/dist/scaAnalysis/java/javaBuildDepsParser.js +14 -1
  47. package/dist/scaAnalysis/javascript/analysis.js +110 -0
  48. package/dist/scaAnalysis/javascript/index.js +41 -0
  49. package/dist/scaAnalysis/php/analysis.js +89 -0
  50. package/dist/scaAnalysis/php/index.js +10 -0
  51. package/dist/scaAnalysis/python/analysis.js +42 -0
  52. package/dist/scaAnalysis/python/index.js +10 -0
  53. package/dist/scaAnalysis/ruby/analysis.js +226 -0
  54. package/dist/scaAnalysis/ruby/index.js +10 -0
  55. package/dist/scan/autoDetection.js +8 -4
  56. package/dist/scan/fileUtils.js +26 -8
  57. package/dist/scan/formatScanOutput.js +18 -17
  58. package/dist/scan/models/groupedResultsModel.js +1 -1
  59. package/dist/scan/models/scanResultsModel.js +3 -1
  60. package/dist/scan/populateProjectIdAndProjectName.js +2 -1
  61. package/dist/scan/scan.js +5 -3
  62. package/dist/scan/scanConfig.js +6 -1
  63. package/dist/scan/scanController.js +26 -6
  64. package/dist/scan/scanResults.js +20 -6
  65. package/dist/utils/commonApi.js +4 -1
  66. package/dist/utils/filterProjectPath.js +7 -2
  67. package/dist/utils/oraWrapper.js +5 -1
  68. package/package.json +13 -9
  69. package/src/audit/autodetection/autoDetectLanguage.ts +3 -3
  70. package/src/audit/catalogueApplication/catalogueApplication.js +28 -7
  71. package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +11 -8
  72. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
  73. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
  74. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
  75. package/src/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +5 -5
  76. package/src/audit/languageAnalysisEngine/getProjectRootFilenames.js +11 -11
  77. package/src/audit/languageAnalysisEngine/index.js +2 -2
  78. package/src/audit/languageAnalysisEngine/languageAnalysisFactory.js +11 -31
  79. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +35 -32
  80. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +179 -25
  81. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +3 -3
  82. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +18 -11
  83. package/src/audit/languageAnalysisEngine/report/models/reportOutputModel.ts +29 -0
  84. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +12 -3
  85. package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +20 -0
  86. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +50 -18
  87. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +88 -66
  88. package/src/audit/languageAnalysisEngine/sendSnapshot.js +78 -3
  89. package/src/audit/save.js +32 -0
  90. package/src/commands/audit/auditController.ts +23 -15
  91. package/src/commands/audit/help.ts +24 -1
  92. package/src/commands/audit/processAudit.ts +7 -4
  93. package/src/commands/audit/saveFile.ts +5 -1
  94. package/src/commands/scan/processScan.js +2 -1
  95. package/src/commands/scan/sca/scaAnalysis.js +70 -29
  96. package/src/common/HTTPClient.js +72 -25
  97. package/src/common/errorHandling.ts +10 -1
  98. package/src/common/versionChecker.ts +24 -5
  99. package/src/constants/constants.js +13 -3
  100. package/src/constants/locales.js +15 -12
  101. package/src/constants.js +9 -4
  102. package/src/index.ts +5 -3
  103. package/src/lambda/analytics.ts +9 -0
  104. package/src/lambda/arn.ts +2 -1
  105. package/src/lambda/lambda.ts +37 -17
  106. package/src/lambda/types.ts +35 -0
  107. package/src/lambda/utils.ts +2 -7
  108. package/src/sbom/generateSbom.ts +1 -1
  109. package/src/scaAnalysis/common/formatMessage.js +51 -1
  110. package/src/scaAnalysis/common/treeUpload.js +1 -6
  111. package/src/scaAnalysis/go/goAnalysis.js +19 -0
  112. package/src/scaAnalysis/go/goParseDeps.js +203 -0
  113. package/src/scaAnalysis/go/goReadDepFile.js +30 -0
  114. package/src/scaAnalysis/java/analysis.js +15 -32
  115. package/src/scaAnalysis/java/index.js +6 -6
  116. package/src/scaAnalysis/java/javaBuildDepsParser.js +15 -2
  117. package/src/scaAnalysis/javascript/analysis.js +127 -0
  118. package/src/scaAnalysis/javascript/index.js +56 -0
  119. package/src/scaAnalysis/php/analysis.js +98 -0
  120. package/src/scaAnalysis/php/index.js +11 -0
  121. package/src/scaAnalysis/python/analysis.js +49 -0
  122. package/src/scaAnalysis/python/index.js +11 -0
  123. package/src/scaAnalysis/ruby/analysis.js +282 -0
  124. package/src/scaAnalysis/ruby/index.js +11 -0
  125. package/src/scan/autoDetection.js +11 -7
  126. package/src/scan/fileUtils.js +27 -8
  127. package/src/scan/formatScanOutput.ts +26 -18
  128. package/src/scan/models/groupedResultsModel.ts +3 -3
  129. package/src/scan/models/resultContentModel.ts +1 -1
  130. package/src/scan/models/scanResultsModel.ts +5 -2
  131. package/src/scan/populateProjectIdAndProjectName.js +3 -1
  132. package/src/scan/scan.ts +8 -6
  133. package/src/scan/scanConfig.js +5 -1
  134. package/src/scan/scanController.js +30 -9
  135. package/src/scan/scanResults.js +31 -10
  136. package/src/utils/commonApi.js +4 -1
  137. package/src/utils/filterProjectPath.js +6 -2
  138. package/src/utils/oraWrapper.js +6 -1
@@ -1,20 +1,40 @@
1
1
  const autoDetection = require('../../../scan/autoDetection')
2
- const { javaAnalysis } = require('../../../scaAnalysis/java')
3
- const { commonSendSnapShot } = require('../../../scaAnalysis/common/treeUpload')
2
+ const javaAnalysis = require('../../../scaAnalysis/java')
3
+ const treeUpload = require('../../../scaAnalysis/common/treeUpload')
4
4
  const {
5
5
  manualDetectAuditFilesAndLanguages
6
6
  } = require('../../../scan/autoDetection')
7
- const { dealWithNoAppId } = require('../../audit/auditController')
7
+ const auditController = require('../../audit/auditController')
8
8
  const {
9
- supportedLanguages: { JAVA }
9
+ supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP }
10
10
  } = require('../../../audit/languageAnalysisEngine/constants')
11
+ const goAnalysis = require('../../../scaAnalysis/go/goAnalysis')
12
+ const phpAnalysis = require('../../../scaAnalysis/php/index')
13
+ const { rubyAnalysis } = require('../../../scaAnalysis/ruby')
14
+ const { pythonAnalysis } = require('../../../scaAnalysis/python')
15
+ const javascriptAnalysis = require('../../../scaAnalysis/javascript')
16
+ const {
17
+ pollForSnapshotCompletition
18
+ } = require('../../../audit/languageAnalysisEngine/sendSnapshot')
19
+ const {
20
+ returnOra,
21
+ startSpinner,
22
+ succeedSpinner
23
+ } = require('../../../utils/oraWrapper')
24
+ const i18n = require('i18n')
25
+ const {
26
+ vulnerabilityReportV2
27
+ } = require('../../../audit/languageAnalysisEngine/report/reportingFeature')
28
+ const auditSave = require('../../../audit/save')
11
29
 
12
30
  const processSca = async config => {
13
31
  let filesFound
14
- if (config.projectPath) {
15
- filesFound = await manualDetectAuditFilesAndLanguages(config.projectPath)
32
+ if (config.file) {
33
+ config.file = config.file.concat('/')
34
+ filesFound = await manualDetectAuditFilesAndLanguages(config.file)
16
35
  } else {
17
36
  filesFound = await autoDetection.autoDetectAuditFilesAndLanguages(config)
37
+ config.file = process.cwd().concat('/')
18
38
  }
19
39
 
20
40
  // files found looks like [ { javascript: [ Array ] } ]
@@ -24,27 +44,32 @@ const processSca = async config => {
24
44
  if (filesFound.length === 1) {
25
45
  switch (Object.keys(filesFound[0])[0]) {
26
46
  case JAVA:
27
- messageToSend = await javaAnalysis(config, filesFound[0])
47
+ messageToSend = javaAnalysis.javaAnalysis(config, filesFound[0])
28
48
  config.language = JAVA
29
49
  break
30
- // case 'javascript':
31
- // // code block
32
- // break;
33
- // case 'dotnet':
34
- // // code block
35
- // break;
36
- // case 'python':
37
- // // code block
38
- // break;
39
- // case 'ruby':
40
- // // code block
41
- // break;
42
- // case 'php':
43
- // // code block
44
- // break;
45
- // case 'go':
46
- // // code block
47
- // break;
50
+ case JAVASCRIPT:
51
+ messageToSend = await javascriptAnalysis.jsAnalysis(
52
+ config,
53
+ filesFound[0]
54
+ )
55
+ config.language = NODE
56
+ break
57
+ case PYTHON:
58
+ messageToSend = pythonAnalysis(config, filesFound[0])
59
+ config.language = PYTHON
60
+ break
61
+ case RUBY:
62
+ messageToSend = rubyAnalysis(config, filesFound[0])
63
+ config.language = RUBY
64
+ break
65
+ case 'PHP':
66
+ messageToSend = phpAnalysis.phpAnalysis(config, filesFound[0])
67
+ config.language = PHP
68
+ break
69
+ case GO:
70
+ messageToSend = goAnalysis.goAnalysis(config, filesFound[0])
71
+ config.language = GO
72
+ break
48
73
  default:
49
74
  //something is wrong
50
75
  console.log('language detected not supported')
@@ -52,17 +77,33 @@ const processSca = async config => {
52
77
  }
53
78
 
54
79
  if (!config.applicationId) {
55
- config.applicationId = await dealWithNoAppId(config)
80
+ config.applicationId = await auditController.dealWithNoAppId(config)
56
81
  }
57
82
  //send message to TS
58
- console.log('processing dependencies')
59
- const response = await commonSendSnapShot(messageToSend, config)
83
+ const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
84
+ startSpinner(reportSpinner)
85
+ const snapshotResponse = await treeUpload.commonSendSnapShot(
86
+ messageToSend,
87
+ config
88
+ )
89
+
90
+ //poll for completion
91
+ await pollForSnapshotCompletition(
92
+ config,
93
+ snapshotResponse.id,
94
+ reportSpinner
95
+ )
96
+ succeedSpinner(reportSpinner, 'Contrast SCA audit complete')
97
+
98
+ await vulnerabilityReportV2(config, snapshotResponse.id)
99
+
100
+ await auditSave.auditSave(config)
60
101
  } else {
61
102
  if (filesFound.length === 0) {
62
103
  console.log('no compatible dependency files detected. Continuing...')
63
104
  } else {
64
105
  console.log(
65
- 'multiple language files detected, please use --project-path to specify a directory or the file where dependencies are declared'
106
+ 'multiple language files detected, please use --file to specify a directory or the file where dependencies are declared'
66
107
  )
67
108
  }
68
109
  }
@@ -22,7 +22,8 @@ function HTTPClient(config) {
22
22
  Authorization: authToken,
23
23
  'API-Key': apiKey,
24
24
  SuperAuthorization: superAuthToken,
25
- 'Super-API-Key': superApiKey
25
+ 'Super-API-Key': superApiKey,
26
+ 'User-Agent': 'contrast-cli-v2'
26
27
  }
27
28
  }
28
29
 
@@ -33,7 +34,7 @@ function HTTPClient(config) {
33
34
  this.maybeAddCertsToRequest(config)
34
35
  }
35
36
 
36
- HTTPClient.prototype.maybeAddCertsToRequest = function(config) {
37
+ HTTPClient.prototype.maybeAddCertsToRequest = function (config) {
37
38
  // cacert
38
39
  const caCertFilePath = config.cacert
39
40
  if (caCertFilePath) {
@@ -91,13 +92,30 @@ HTTPClient.prototype.getSpecificScanResult = function getSpecificScanResult(
91
92
  return requestUtils.sendRequest({ method: 'get', options })
92
93
  }
93
94
 
94
- HTTPClient.prototype.getSpecificScanResultSarif = function getSpecificScanResultSarif(
95
+ HTTPClient.prototype.getSpecificScanResultSarif =
96
+ function getSpecificScanResultSarif(config, scanId) {
97
+ const options = _.cloneDeep(this.requestOptions)
98
+ options.url = createRawOutputURL(config, scanId)
99
+ return requestUtils.sendRequest({ method: 'get', options })
100
+ }
101
+
102
+ HTTPClient.prototype.createNewEvent = function createNewEvent(
95
103
  config,
96
- scanId
104
+ scanId,
105
+ newProject
97
106
  ) {
98
107
  const options = _.cloneDeep(this.requestOptions)
99
- options.url = createRawOutputURL(config, scanId)
100
- return requestUtils.sendRequest({ method: 'get', options })
108
+ options.url = createEventCollectorURL(config, scanId)
109
+
110
+ options.body = {
111
+ eventSource: process.env.CODESEC_INVOCATION_ENVIRONMENT,
112
+ trackingProperties: {
113
+ projectNameSource: config.projectNameSource,
114
+ waitedForResults: !config.ff,
115
+ newProject
116
+ }
117
+ }
118
+ return requestUtils.sendRequest({ method: 'post', options })
101
119
  }
102
120
 
103
121
  HTTPClient.prototype.getScanId = function getScanId(config, codeArtifactId) {
@@ -190,9 +208,6 @@ HTTPClient.prototype.catalogueCommand = function catalogueCommand(config) {
190
208
  }
191
209
 
192
210
  HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
193
- if (config.language.toUpperCase() === 'RUBY') {
194
- //console.log('sendSnapshot requestBody', requestBody.snapshot.ruby)
195
- }
196
211
  const options = _.cloneDeep(this.requestOptions)
197
212
  let url = createSnapshotURL(config)
198
213
  options.url = url
@@ -210,17 +225,24 @@ HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
210
225
  return requestUtils.sendRequest({ method: 'get', options })
211
226
  }
212
227
 
213
- HTTPClient.prototype.getLibraryVulnerabilities = function getLibraryVulnerabilities(
228
+ HTTPClient.prototype.getReportStatusById = function getReportStatusById(
214
229
  config,
215
- requestBody
230
+ snapshotId
216
231
  ) {
217
232
  const options = _.cloneDeep(this.requestOptions)
218
- options.url = createLibraryVulnerabilitiesUrl(config)
219
- options.body = requestBody
220
-
221
- return requestUtils.sendRequest({ method: 'put', options })
233
+ options.url = createSpecificReportStatusURL(config, snapshotId)
234
+ return requestUtils.sendRequest({ method: 'get', options })
222
235
  }
223
236
 
237
+ HTTPClient.prototype.getLibraryVulnerabilities =
238
+ function getLibraryVulnerabilities(config, requestBody) {
239
+ const options = _.cloneDeep(this.requestOptions)
240
+ options.url = createLibraryVulnerabilitiesUrl(config)
241
+ options.body = requestBody
242
+
243
+ return requestUtils.sendRequest({ method: 'put', options })
244
+ }
245
+
224
246
  HTTPClient.prototype.getAppId = function getAppId(config) {
225
247
  const options = _.cloneDeep(this.requestOptions)
226
248
  let url = createAppNameUrl(config)
@@ -295,17 +317,13 @@ HTTPClient.prototype.getScanResources = async function getScanResources(
295
317
  return requestUtils.sendRequest({ method: 'get', options })
296
318
  }
297
319
 
298
- HTTPClient.prototype.getFunctionScanResults = async function getFunctionScanResults(
299
- config,
300
- params,
301
- scanId,
302
- functionArn
303
- ) {
304
- const url = createScanResultsGetUrl(config, params, scanId, functionArn)
305
- const options = { ...this.requestOptions, url }
320
+ HTTPClient.prototype.getFunctionScanResults =
321
+ async function getFunctionScanResults(config, params, scanId, functionArn) {
322
+ const url = createScanResultsGetUrl(config, params, scanId, functionArn)
323
+ const options = { ...this.requestOptions, url }
306
324
 
307
- return requestUtils.sendRequest({ method: 'get', options })
308
- }
325
+ return requestUtils.sendRequest({ method: 'get', options })
326
+ }
309
327
 
310
328
  HTTPClient.prototype.checkLibrary = function checkLibrary(data) {
311
329
  const options = _.cloneDeep(this.requestOptions)
@@ -321,6 +339,27 @@ HTTPClient.prototype.getSbom = function getSbom(config) {
321
339
  return requestUtils.sendRequest({ method: 'get', options })
322
340
  }
323
341
 
342
+ HTTPClient.prototype.getLatestVersion = function getLatestVersion() {
343
+ const options = _.cloneDeep(this.requestOptions)
344
+ options.url =
345
+ 'https://pkg.contrastsecurity.com/artifactory/cli/latest-version.txt'
346
+ return requestUtils.sendRequest({ method: 'get', options })
347
+ }
348
+
349
+ // analytics
350
+
351
+ HTTPClient.prototype.postAnalyticsFunction = function (config, provider, body) {
352
+ const url = createAnalyticsFunctionPostUrl(config, provider)
353
+ const options = { ...this.requestOptions, body, url }
354
+
355
+ return requestUtils.sendRequest({ method: 'post', options })
356
+ }
357
+
358
+ const createAnalyticsFunctionPostUrl = (config, provider) => {
359
+ const url = getServerlessHost(config)
360
+ return `${url}/organizations/${config.organizationId}/providers/${provider}/analytics`
361
+ }
362
+
324
363
  // scan
325
364
  const createGetScanIdURL = config => {
326
365
  return `${config.host}/Contrast/api/sast/v1/organizations/${config.organizationId}/projects/${config.projectId}/scans/`
@@ -350,6 +389,10 @@ function createScanProjectUrl(config) {
350
389
  return `${config.host}/Contrast/api/sast/v1/organizations/${config.organizationId}/projects/${config.projectId}`
351
390
  }
352
391
 
392
+ const createEventCollectorURL = (config, scanId) => {
393
+ return `${config.host}/Contrast/api/sast/organizations/${config.organizationId}/projects/${config.projectId}/scans/${scanId}/events`
394
+ }
395
+
353
396
  const createGlobalPropertiesUrl = protocol => {
354
397
  return `${protocol}/Contrast/api/ng/global/properties`
355
398
  }
@@ -384,6 +427,10 @@ function createSpecificReportWithProdUrl(config, reportId) {
384
427
  )
385
428
  }
386
429
 
430
+ function createSpecificReportStatusURL(config, reportId) {
431
+ return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/snapshots/${reportId}/status`
432
+ }
433
+
387
434
  function createDataUrl() {
388
435
  return `https://ardy.contrastsecurity.com/production`
389
436
  }
@@ -64,6 +64,14 @@ const proxyError = () => {
64
64
  generalError('proxyErrorHeader', 'proxyErrorMessage')
65
65
  }
66
66
 
67
+ const maxAppError = () => {
68
+ generalError(
69
+ 'No applications remaining',
70
+ 'You have reached the maximum number of application you can create.'
71
+ )
72
+ process.exit(1)
73
+ }
74
+
67
75
  const failOptionError = () => {
68
76
  console.log(
69
77
  '\n ******************************** ' +
@@ -140,5 +148,6 @@ export {
140
148
  findCommandOnError,
141
149
  snapshotFailureError,
142
150
  vulnerabilitiesFailureError,
143
- reportFailureError
151
+ reportFailureError,
152
+ maxAppError
144
153
  }
@@ -1,12 +1,31 @@
1
- import latestVersion from 'latest-version'
2
1
  import { APP_VERSION } from '../constants/constants'
3
2
  import boxen from 'boxen'
4
3
  import chalk from 'chalk'
5
4
  import semver from 'semver'
5
+ import commonApi from '../utils/commonApi'
6
+ import { constants } from 'http2'
7
+ import { ContrastConf } from '../utils/getConfig'
6
8
 
7
- export async function findLatestCLIVersion(updateMessageHidden: boolean) {
8
- if (!updateMessageHidden) {
9
- const latestCLIVersion = await latestVersion('@contrast/contrast')
9
+ const getLatestVersion = async (config: any) => {
10
+ const client = commonApi.getHttpClient(config)
11
+ try {
12
+ const res = await client.getLatestVersion()
13
+ if (res.statusCode === constants.HTTP_STATUS_OK) {
14
+ return res.body
15
+ }
16
+ } catch (e) {
17
+ return
18
+ }
19
+ }
20
+
21
+ // @ts-ignore
22
+ export async function findLatestCLIVersion(config: ContrastConf) {
23
+ const messageHidden = config.get('updateMessageHidden') as boolean
24
+
25
+ if (!messageHidden) {
26
+ let latestCLIVersion: string = await getLatestVersion(config)
27
+ //strip key
28
+ latestCLIVersion = latestCLIVersion.substring(8)
10
29
 
11
30
  if (semver.lt(APP_VERSION, latestCLIVersion)) {
12
31
  const updateAvailableMessage = `Update available ${chalk.yellow(
@@ -37,5 +56,5 @@ export async function findLatestCLIVersion(updateMessageHidden: boolean) {
37
56
  }
38
57
 
39
58
  export async function isCorrectNodeVersion(currentVersion: string) {
40
- return semver.satisfies(currentVersion, '>=16.13.2 <17')
59
+ return semver.satisfies(currentVersion, '>=16')
41
60
  }
@@ -13,13 +13,18 @@ const MEDIUM = 'MEDIUM'
13
13
  const HIGH = 'HIGH'
14
14
  const CRITICAL = 'CRITICAL'
15
15
  const APP_NAME = 'contrast'
16
- const APP_VERSION = '1.0.5'
16
+ const APP_VERSION = '1.0.8'
17
17
  const TIMEOUT = 120000
18
18
  const HIGH_COLOUR = '#ff9900'
19
19
  const CRITICAL_COLOUR = '#e35858'
20
20
  const MEDIUM_COLOUR = '#f1c232'
21
- const LOW_COLOUR = '#ff9900'
21
+ const LOW_COLOUR = '#b7b7b7'
22
22
  const NOTE_COLOUR = '#999999'
23
+ const CRITICAL_PRIORITY = 1
24
+ const HIGH_PRIORITY = 2
25
+ const MEDIUM_PRIORITY = 3
26
+ const LOW_PRIORITY = 4
27
+ const NOTE_PRIORITY = 5
23
28
 
24
29
  const AUTH_UI_URL = 'https://cli-auth.contrastsecurity.com'
25
30
  const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com'
@@ -43,5 +48,10 @@ module.exports = {
43
48
  MEDIUM_COLOUR,
44
49
  LOW_COLOUR,
45
50
  NOTE_COLOUR,
46
- CE_URL
51
+ CE_URL,
52
+ CRITICAL_PRIORITY,
53
+ HIGH_PRIORITY,
54
+ MEDIUM_PRIORITY,
55
+ LOW_PRIORITY,
56
+ NOTE_PRIORITY
47
57
  }
@@ -15,7 +15,7 @@ const en_locales = () => {
15
15
  catchErrorMessage: 'Contrast UI error: ',
16
16
  dependenciesNote:
17
17
  'Please Note: We currently only support projects with one .csproj AND *.package.lock.json',
18
- languageAnalysisFailureMessage: 'SCA Analysis Failure',
18
+ languageAnalysisFailureMessage: 'SCA audit Failure',
19
19
  languageAnalysisFactoryFailureHeader: 'FAIL',
20
20
  libraryAnalysisError:
21
21
  'Please ensure the language parameter is set in accordance to the language specified on the project path.\nContrast CLI must be run in the same directory as the project manifest file OR the project_path parameter must be used to identify the directory containing the project manifest file.\n\nFor further information please read our usage guide, which can be accessed with the following command:\n\ncontrast-cli --help',
@@ -118,7 +118,7 @@ const en_locales = () => {
118
118
  'Provide this if you want to catalogue an application',
119
119
  constantsLanguage:
120
120
  'Valid values are JAVA, DOTNET, NODE, PYTHON and RUBY. If there are multiple project configuration files in the project_path, language is also required. Also, provide this when cataloguing an application',
121
- constantsProjectPath:
121
+ constantsFilePath:
122
122
  'The directory root of a project/application that you would like analyzed. Defaults to current directory.',
123
123
  constantsSilent: 'Silences JSON output.',
124
124
  constantsAppGroups:
@@ -137,16 +137,16 @@ const en_locales = () => {
137
137
  'The ID associated with a scan project. Replace <ProjectID> with the ID for the scan project. To find the ID, select a scan project in Contrast and locate the last number in the URL.',
138
138
  constantsReport: 'Display vulnerability information for this application',
139
139
  constantsFail:
140
- 'Set the process to fail if this option is set in combination with the --report and --cve_severity.',
140
+ 'Set the process to fail if this option is set in combination with --cve_severity.',
141
141
  failOptionErrorMessage:
142
- " FAIL - CVE's have been detected that match at least the cve_severity or cve_threshold option specified.",
142
+ ' FAIL - CVEs have been detected that match at least the cve_severity or cve_threshold option specified.',
143
143
  constantsSeverity:
144
- 'Combined with the --report command, allows the user to report libraries with vulnerabilities above a chosen severity level. For example, cve_severity medium only reports libraries with vulnerabilities at medium or higher severity. Values for level are high, medium or low.',
145
- constantsCount: "The number of CVE's that must be exceeded to fail a build",
144
+ 'Allows the user to report libraries with vulnerabilities above a chosen severity level. For example, cve_severity medium only reports libraries with vulnerabilities at medium or higher severity. Values for level are high, medium or low.',
145
+ constantsCount: 'The number of CVEs that must be exceeded to fail a build',
146
146
  constantsHeader: 'CodeSec by Contrast Security',
147
147
  constantsPrerequisitesContentScanLanguages: 'Java & JavaScript supported',
148
148
  constantsContrastContent:
149
- 'Use the Contrast CLI to run a scan (Java, JavaScript and .NET ) or lambda command (Java and Python) to find your vulnerabilities and start securing your code.',
149
+ "Use the 'contrast' command for fast and accurate security analysis of your applications and APIs (Java, JavaScript and .NET ) as well as serverless functions (AWS lambda, Java and Python).",
150
150
  constantsUsageGuideContentRecommendation:
151
151
  'Our recommendation is that this is invoked as part of a CI pipeline so that running the cli is automated as part of your build process.',
152
152
  constantsPrerequisitesHeader: 'Pre-requisites',
@@ -249,7 +249,7 @@ const en_locales = () => {
249
249
  scanLabel:
250
250
  "adds a label to the scan - defaults to 'Started by CLI tool at current date'",
251
251
  constantsIgnoreDev:
252
- 'Combined with the --report command excludes developer dependencies from the vulnerabilities report. By default all dependencies are included in a report.',
252
+ 'Excludes developer dependencies from the output. By default all dependencies are included.',
253
253
  constantsCommands: 'Commands',
254
254
  constantsScanOptions: 'Scan Options',
255
255
  sbomError: 'All required parameters are not present.',
@@ -356,6 +356,7 @@ const en_locales = () => {
356
356
  scanZipError:
357
357
  'A .zip archive can be used for Javascript Scan. Archive found %s does not contain .JS files for Scan.',
358
358
  fileNotExist: 'File specified does not exist, please check and try again.',
359
+ scanFileIsEmpty: 'File specified is empty. Please choose another.',
359
360
  fileHasWhiteSpacesError:
360
361
  'File cannot have spaces, please rename or choose another file to Scan.',
361
362
  zipFileException: 'Error reading zip file',
@@ -392,12 +393,13 @@ const en_locales = () => {
392
393
  auditOptionsIgnoreDevDependenciesDescription: 'ignores DevDependencies',
393
394
  auditOptionsSave: '-s, --save',
394
395
  auditOptionsSaveDescription:
395
- 'saves the output in specified format Txt text, sbom',
396
+ 'saves the output in specified format, options: sbom',
396
397
  scanNotCompleted:
397
398
  'Scan not completed. Check for framework and language support here: %s',
398
- scanNoVulnerabilitiesFound: '👏 No vulnerabilities found',
399
+ auditNotCompleted: 'audit not completed. Please try again',
400
+ scanNoVulnerabilitiesFound: '🎉 No vulnerabilities found.',
399
401
  scanNoVulnerabilitiesFoundSecureCode: '👍 Your code looks secure.',
400
- scanNoVulnerabilitiesFoundGoodWork: '👏 Keep up the good work.',
402
+ scanNoVulnerabilitiesFoundGoodWork: ' Keep up the good work.',
401
403
  scanNoFiletypeSpecifiedForSave:
402
404
  'Please specify file type to save results to, accepted value is SARIF',
403
405
  auditSBOMSaveSuccess:
@@ -412,7 +414,8 @@ const en_locales = () => {
412
414
  auditReportFail: 'Report Retrieval Failed, please try again',
413
415
  auditReportSuccessMessage: 'Report successfully retrieved',
414
416
  auditReportFailureMessage: 'Unable to generate library report',
415
- auditSCAAnalysisBegins: 'Contrast SCA analysis begins',
417
+ auditSCAAnalysisBegins: 'Contrast SCA audit started',
418
+ auditSCAAnalysisComplete: 'Contrast SCA audit complete',
416
419
  ...lambda
417
420
  }
418
421
  }
package/src/constants.js CHANGED
@@ -49,7 +49,6 @@ const scanOptionDefinitions = [
49
49
  },
50
50
  {
51
51
  name: 'project-path',
52
- alias: 'i',
53
52
  description:
54
53
  '{bold ' +
55
54
  i18n.__('constantsOptional') +
@@ -212,13 +211,14 @@ const auditOptionDefinitions = [
212
211
  i18n.__('constantsApplicationName')
213
212
  },
214
213
  {
215
- name: 'project-path',
216
- defaultValue: process.env.PWD,
214
+ name: 'file',
215
+ alias: 'f',
216
+ defaultValue: process.cwd(),
217
217
  description:
218
218
  '{bold ' +
219
219
  i18n.__('constantsOptional') +
220
220
  '}: ' +
221
- i18n.__('constantsProjectPath')
221
+ i18n.__('constantsFilePath')
222
222
  },
223
223
  {
224
224
  name: 'app-groups',
@@ -334,6 +334,11 @@ const auditOptionDefinitions = [
334
334
  i18n.__('constantsOptional') +
335
335
  '}: ' +
336
336
  i18n.__('auditOptionsSaveDescription')
337
+ },
338
+ {
339
+ name: 'experimental',
340
+ alias: 'e',
341
+ type: Boolean
337
342
  }
338
343
  ]
339
344
 
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env node
2
+
1
3
  import commandLineArgs from 'command-line-args'
2
4
  import { processAudit } from './commands/audit/processAudit'
3
5
  import { processAuth } from './commands/auth/auth'
@@ -44,7 +46,7 @@ const start = async () => {
44
46
  argvMain.includes('--version')
45
47
  ) {
46
48
  console.log(APP_VERSION)
47
- await findLatestCLIVersion(config.get('updateMessageHidden') as boolean)
49
+ await findLatestCLIVersion(config)
48
50
  return
49
51
  }
50
52
 
@@ -52,8 +54,8 @@ const start = async () => {
52
54
  config.set('numOfRuns', config.get('numOfRuns') + 1)
53
55
 
54
56
  // @ts-ignore
55
- if (config.get('numOfRuns') >= 5) {
56
- await findLatestCLIVersion(config.get('updateMessageHidden') as boolean)
57
+ if (config.get('numOfRuns') >= 1) {
58
+ await findLatestCLIVersion(config)
57
59
  config.set('numOfRuns', 0)
58
60
  }
59
61
 
@@ -0,0 +1,9 @@
1
+ import { getHttpClient } from '../utils/commonApi'
2
+ import { getAuth } from '../utils/paramsUtil/paramHandler'
3
+ import { AnalyticsOption } from './types'
4
+
5
+ export const postAnalytics = (data: AnalyticsOption, provider = 'aws') => {
6
+ const config = getAuth()
7
+ const client = getHttpClient(config)
8
+ return client.postAnalyticsFunction(config, provider, data)
9
+ }
package/src/lambda/arn.ts CHANGED
@@ -10,7 +10,8 @@ type ARN = {
10
10
  resourceId?: string
11
11
  }
12
12
 
13
- const ARN_REGEX = /arn:(?<partition>[^:\n]*):(?<service>[^:\n]*):(?<region>[^:\n]*):(?<accountId>[^:\n]*):(?<ignore>(?<resource>[^:/\n]*)[:/])?(?<resourceId>.*)/
13
+ const ARN_REGEX =
14
+ /arn:(?<partition>[^:\n]*):(?<service>[^:\n]*):(?<region>[^:\n]*):(?<accountId>[^:\n]*):(?<ignore>(?<resource>[^:/\n]*)[:/])?(?<resourceId>.*)/
14
15
 
15
16
  const parseARN = (arn: string | undefined) => {
16
17
  if (!arn) {
@@ -14,18 +14,8 @@ import { printResults } from './utils'
14
14
  import { getAllLambdas, printAvailableLambdas } from './lambdaUtils'
15
15
  import { sleep } from '../utils/requestUtils'
16
16
  import ora from '../utils/oraWrapper'
17
-
18
- type LambdaOptions = {
19
- functionName?: string
20
- listFunctions?: boolean
21
- region?: string
22
- endpointUrl?: string
23
- profile?: string
24
- help?: boolean
25
- verbose?: boolean
26
- jsonOutput?: boolean
27
- _unknown?: string[]
28
- }
17
+ import { postAnalytics } from './analytics'
18
+ import { LambdaOptions, AnalyticsOption, StatusType, EventType } from './types'
29
19
 
30
20
  type ApiParams = {
31
21
  organizationId: string
@@ -74,10 +64,20 @@ const getLambdaOptions = (argv: string[]) => {
74
64
  }
75
65
 
76
66
  const processLambda = async (argv: string[]) => {
67
+ let errorMsg
68
+ let scanInfo: { functionArn: string; scanId: string } | undefined
69
+ const commandSessionId = Date.now().toString(36)
77
70
  try {
78
71
  const lambdaOptions = getLambdaOptions(argv)
79
72
  const { help } = lambdaOptions
80
-
73
+ const startCommandAnalytics: AnalyticsOption = {
74
+ arguments: lambdaOptions,
75
+ sessionId: commandSessionId,
76
+ eventType: EventType.START
77
+ }
78
+ postAnalytics(startCommandAnalytics).catch((error: Error) => {
79
+ /* ignore */
80
+ })
81
81
  if (help) {
82
82
  return handleLambdaHelp()
83
83
  }
@@ -87,15 +87,33 @@ const processLambda = async (argv: string[]) => {
87
87
  if (lambdaOptions.listFunctions) {
88
88
  await getAvailableFunctions(lambdaOptions)
89
89
  } else {
90
- await actualProcessLambda(lambdaOptions)
90
+ scanInfo = await actualProcessLambda(lambdaOptions)
91
91
  }
92
92
  } catch (error) {
93
93
  if (error instanceof CliError) {
94
- console.error(error.getErrorMessage())
94
+ errorMsg = error.getErrorMessage()
95
95
  } else if (error instanceof Error) {
96
- console.error(error.message)
96
+ errorMsg = error.message
97
+ }
98
+ } finally {
99
+ const endCommandAnalytics: AnalyticsOption = {
100
+ sessionId: commandSessionId,
101
+ eventType: EventType.END,
102
+ status: errorMsg ? StatusType.FAILED : StatusType.SUCCESS
103
+ }
104
+ if (errorMsg) {
105
+ endCommandAnalytics.errorMsg = errorMsg
106
+ console.error(errorMsg)
107
+ }
108
+ if (scanInfo) {
109
+ endCommandAnalytics.scanFunctionData = scanInfo
110
+ }
111
+ await postAnalytics(endCommandAnalytics).catch((error: Error) => {
112
+ /* ignore */
113
+ })
114
+ if (errorMsg) {
115
+ process.exit(1)
97
116
  }
98
- process.exit(1)
99
117
  }
100
118
  }
101
119
 
@@ -162,6 +180,8 @@ const actualProcessLambda = async (lambdaOptions: LambdaOptions) => {
162
180
  if (results?.length) {
163
181
  printResults(results)
164
182
  }
183
+
184
+ return { functionArn, scanId }
165
185
  }
166
186
 
167
187
  const validateRequiredLambdaParams = (options: LambdaOptions) => {