@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
@@ -29,4 +29,5 @@ const checkIdentifiedLanguageHasProjectFile = identifiedLanguages => {
29
29
  }
30
30
 
31
31
  //For testing purposes
32
- exports.checkIdentifiedLanguageHasProjectFile = checkIdentifiedLanguageHasProjectFile
32
+ exports.checkIdentifiedLanguageHasProjectFile =
33
+ checkIdentifiedLanguageHasProjectFile
@@ -5,15 +5,15 @@ const path = require('path')
5
5
  * language, project file name and paths
6
6
  */
7
7
  module.exports = exports = (analysis, next) => {
8
- const { projectPath, languageAnalysis } = analysis
8
+ const { file, languageAnalysis } = analysis
9
9
  languageAnalysis.identifiedLanguageInfo = getIdentifiedLanguageInfo(
10
- projectPath,
10
+ file,
11
11
  languageAnalysis.identifiedLanguages
12
12
  )
13
13
  next()
14
14
  }
15
15
 
16
- const getIdentifiedLanguageInfo = (projectPath, identifiedLanguages) => {
16
+ const getIdentifiedLanguageInfo = (file, identifiedLanguages) => {
17
17
  const [language] = Object.keys(identifiedLanguages)
18
18
  const {
19
19
  projectFilenames: [projectFilename],
@@ -23,14 +23,14 @@ const getIdentifiedLanguageInfo = (projectPath, identifiedLanguages) => {
23
23
  let identifiedLanguageInfo = {
24
24
  language,
25
25
  projectFilename,
26
- projectFilePath: path.join(projectPath, projectFilename)
26
+ projectFilePath: path.join(file, projectFilename)
27
27
  }
28
28
 
29
29
  if (lockFilename) {
30
30
  identifiedLanguageInfo = {
31
31
  ...identifiedLanguageInfo,
32
32
  lockFilename,
33
- lockFilePath: path.join(projectPath, lockFilename)
33
+ lockFilePath: path.join(file, lockFilename)
34
34
  }
35
35
  }
36
36
 
@@ -9,21 +9,21 @@ const i18n = require('i18n')
9
9
  * Will fail and throw for a manner of reasons when doing file/directory
10
10
  * inspection.
11
11
  *
12
- * @param {string} projectPath - The path to a projects root directory or a
12
+ * @param {string} file - The path to a projects root directory or a
13
13
  * specific project file
14
14
  *
15
15
  * @return {string[]} List of filenames associated with a projects root
16
16
  * directory or the name of the specific project file if that was provided to
17
- * the 'projectPath' parameter
17
+ * the 'file' parameter
18
18
  *
19
19
  * @throws {Error} If the project path doesn't exist
20
20
  * @throws {Error} If the project path information can't be collected
21
21
  * @throws {Error} If a non-file or non-directory inspected
22
22
  */
23
23
  module.exports = exports = (analysis, next) => {
24
- const { projectPath, languageAnalysis } = analysis
24
+ const { file, languageAnalysis } = analysis
25
25
  try {
26
- languageAnalysis.projectRootFilenames = getProjectRootFilenames(projectPath)
26
+ languageAnalysis.projectRootFilenames = getProjectRootFilenames(file)
27
27
  } catch (err) {
28
28
  next(err)
29
29
  return
@@ -31,13 +31,13 @@ module.exports = exports = (analysis, next) => {
31
31
  next()
32
32
  }
33
33
 
34
- const getProjectRootFilenames = projectPath => {
34
+ const getProjectRootFilenames = file => {
35
35
  let projectStats = null
36
36
  try {
37
- projectStats = fs.statSync(projectPath)
37
+ projectStats = fs.statSync(file)
38
38
  } catch (err) {
39
39
  throw new Error(
40
- i18n.__('languageAnalysisProjectRootFileNameFailure', projectPath) +
40
+ i18n.__('languageAnalysisProjectRootFileNameFailure', file) +
41
41
  `${err.message}`
42
42
  )
43
43
  }
@@ -45,10 +45,10 @@ const getProjectRootFilenames = projectPath => {
45
45
  // Return the contents of a directory...
46
46
  if (projectStats.isDirectory()) {
47
47
  try {
48
- return fs.readdirSync(projectPath)
48
+ return fs.readdirSync(file)
49
49
  } catch (err) {
50
50
  throw new Error(
51
- i18n.__('languageAnalysisProjectRootFileNameReadError', projectPath) +
51
+ i18n.__('languageAnalysisProjectRootFileNameReadError', file) +
52
52
  `${err.message}`
53
53
  )
54
54
  }
@@ -57,14 +57,14 @@ const getProjectRootFilenames = projectPath => {
57
57
  // If we are working with a file return it in a list as we do when we work
58
58
  // with a directory...
59
59
  if (projectStats.isFile()) {
60
- return [path.basename(projectPath)]
60
+ return [path.basename(file)]
61
61
  }
62
62
 
63
63
  // Error out if we are working with something like a socket file or some
64
64
  // other craziness...
65
65
  throw new Error(
66
66
  i18n.__('languageAnalysisProjectRootFileNameMissingError'),
67
- projectPath
67
+ file
68
68
  )
69
69
  }
70
70
 
@@ -10,10 +10,10 @@ const checkIdentifiedLanguageHasLockFile = require('./checkIdentifiedLanguageHas
10
10
  const getIdentifiedLanguageInfo = require('./getIdentifiedLanguageInfo')
11
11
  const { libraryAnalysisError } = require('../../common/errorHandling')
12
12
 
13
- module.exports = exports = (projectPath, callback, appId, config) => {
13
+ module.exports = exports = (file, callback, appId, config) => {
14
14
  // Create an analysis engine to identify the project language
15
15
  const ae = new AnalysisEngine({
16
- projectPath,
16
+ file,
17
17
  appId,
18
18
  languageAnalysis: { appId: appId },
19
19
  config
@@ -11,16 +11,13 @@ const phpAE = require('../phpAnalysisEngine')
11
11
  const goAE = require('../goAnalysisEngine')
12
12
  const { vulnerabilityReport } = require('./report/reportingFeature')
13
13
  const { newSendSnapShot } = require('../languageAnalysisEngine/sendSnapshot')
14
- const fs = require('fs')
15
- const chalk = require('chalk')
16
- const saveFile = require('../../commands/audit/saveFile').default
17
- const generateSbom = require('../../sbom/generateSbom').default
18
14
  const {
19
- failSpinner,
20
15
  returnOra,
21
16
  startSpinner,
22
17
  succeedSpinner
23
18
  } = require('../../utils/oraWrapper')
19
+ const { pollForSnapshotCompletition } = require('./sendSnapshot')
20
+ const auditSave = require('../save')
24
21
 
25
22
  module.exports = exports = (err, analysis) => {
26
23
  const { identifiedLanguageInfo } = analysis.languageAnalysis
@@ -54,12 +51,19 @@ module.exports = exports = (err, analysis) => {
54
51
  const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
55
52
  startSpinner(reportSpinner)
56
53
  const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId)
57
- succeedSpinner(reportSpinner, 'Contrast SCA analysis complete')
54
+
55
+ //poll for completion
56
+ await pollForSnapshotCompletition(
57
+ analysis.config,
58
+ snapshotResponse.id,
59
+ reportSpinner
60
+ )
61
+ succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
58
62
 
59
63
  await vulnerabilityReport(analysis, catalogueAppId, snapshotResponse.id)
60
64
 
61
65
  //should be moved to processAudit.ts once promises implemented
62
- await auditSave(config)
66
+ await auditSave.auditSave(config)
63
67
  }
64
68
 
65
69
  if (identifiedLanguageInfo.language === DOTNET) {
@@ -90,27 +94,3 @@ module.exports = exports = (err, analysis) => {
90
94
  goAE(identifiedLanguageInfo, analysis.config, langCallback)
91
95
  }
92
96
  }
93
-
94
- async function auditSave(config) {
95
- //should be moved to processAudit.ts once promises implemented
96
- if (config.save) {
97
- if (config.save.toLowerCase() === 'sbom') {
98
- saveFile(config, await generateSbom(config))
99
-
100
- const filename = `${config.applicationId}-sbom-cyclonedx.json`
101
- if (fs.existsSync(filename)) {
102
- console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`)
103
- } else {
104
- console.log(
105
- chalk.yellow.bold(
106
- `\n Unable to save ${filename} Software Bill of Materials (SBOM)`
107
- )
108
- )
109
- }
110
- } else {
111
- console.log(i18n.__('auditBadFiletypeSpecifiedForSave'))
112
- }
113
- } else if (config.save === null) {
114
- console.log(i18n.__('auditNoFiletypeSpecifiedForSave'))
115
- }
116
- }
@@ -46,35 +46,38 @@ const deduceLanguageScaAnalysis = filenames => {
46
46
 
47
47
  if (isNodeProjectFilename(filename)) {
48
48
  deducedLanguages.push(filename)
49
- language = NODE
49
+ language = JAVASCRIPT
50
50
  }
51
- //
51
+
52
52
  // if (isDotNetProjectFilename(filename)) {
53
53
  // deducedLanguages.push({language: DOTNET, projectFilename: filename})
54
54
  // }
55
- //
56
- // if (isRubyProjectFilename(filename)) {
57
- // deducedLanguages.push({language: RUBY, projectFilename: filename})
58
- // }
59
- //
60
- // if (isPythonProjectFilename(filename)) {
61
- // deducedLanguages.push({language: PYTHON, projectFilename: filename})
62
- // }
63
- //
64
- // if (isPhpProjectFilename(filename)) {
65
- // deducedLanguages.push({language: PHP, projectFilename: filename})
66
- // }
67
- //
55
+
56
+ if (isRubyProjectFilename(filename)) {
57
+ deducedLanguages.push(filename)
58
+ language = RUBY
59
+ }
60
+
61
+ if (isPythonProjectFilename(filename)) {
62
+ deducedLanguages.push(filename)
63
+ language = PYTHON
64
+ }
65
+
66
+ if (isPhpProjectFilename(filename)) {
67
+ deducedLanguages.push({ language: PHP, projectFilename: filename })
68
+ language = PHP
69
+ }
70
+
68
71
  // // Check for lock filenames...
69
72
  // if (isDotNetLockFilename(filename)) {
70
73
  // deducedLanguages.push({language: DOTNET, lockFilename: filename})
71
74
  // }
72
- //
75
+
73
76
  if (isNodeLockFilename(filename)) {
74
77
  deducedLanguages.push(filename)
75
- language = node
78
+ language = JAVASCRIPT
76
79
  }
77
- //
80
+
78
81
  // if (isRubyLockFilename(filename)) {
79
82
  // deducedLanguages.push({language: RUBY, lockFilename: filename})
80
83
  // }
@@ -83,15 +86,16 @@ const deduceLanguageScaAnalysis = filenames => {
83
86
  // if (isPipfileLockLockFilename(filename)) {
84
87
  // deducedLanguages.push({language: PYTHON, lockFilename: filename})
85
88
  // }
86
- //
87
- // if (isPhpLockFilename(filename)) {
88
- // deducedLanguages.push({language: PHP, lockFilename: filename})
89
- // }
90
- //
91
- // // go does not have a lockfile, it should have a go.mod file containing the modules
92
- // if (isGoProjectFilename(filename)) {
93
- // deducedLanguages.push({language: GO, projectFilename: filename})
94
- // }
89
+
90
+ if (isPhpLockFilename(filename)) {
91
+ deducedLanguages.push({ language: PHP, lockFilename: filename })
92
+ }
93
+
94
+ // go does not have a lockfile, it should have a go.mod file containing the modules
95
+ if (isGoProjectFilename(filename)) {
96
+ deducedLanguages.push({ language: GO, projectFilename: filename })
97
+ language = GO
98
+ }
95
99
  })
96
100
  let identifiedLanguages = { [language]: deducedLanguages }
97
101
 
@@ -189,7 +193,7 @@ const reduceIdentifiedLanguages = identifiedLanguages =>
189
193
  * specifies a specific language
190
194
  */
191
195
  module.exports = exports = (analysis, next) => {
192
- const { projectPath, languageAnalysis, config } = analysis
196
+ const { file, languageAnalysis, config } = analysis
193
197
 
194
198
  let identifiedLanguages = languageAnalysis.projectRootFilenames.reduce(
195
199
  (accumulator, filename) => {
@@ -200,15 +204,14 @@ module.exports = exports = (analysis, next) => {
200
204
  )
201
205
 
202
206
  if (Object.keys(identifiedLanguages).length === 0) {
203
- next(new Error(i18n.__('languageAnalysisNoLanguage', projectPath)))
207
+ next(new Error(i18n.__('languageAnalysisNoLanguage', file)))
204
208
  return
205
209
  }
206
210
 
207
211
  let language = config.language
208
212
  if (language === undefined) {
209
- languageAnalysis.identifiedLanguages = reduceIdentifiedLanguages(
210
- identifiedLanguages
211
- )
213
+ languageAnalysis.identifiedLanguages =
214
+ reduceIdentifiedLanguages(identifiedLanguages)
212
215
  } else {
213
216
  let refinedIdentifiedLanguages = []
214
217
  for (let x in identifiedLanguages) {
@@ -7,8 +7,27 @@ import {
7
7
  import { ReportSeverityModel } from './models/reportSeverityModel'
8
8
  import { orderBy } from 'lodash'
9
9
  import chalk from 'chalk'
10
- import { ReportLibraryModel } from './models/reportLibraryModel'
11
- import { findHighestSeverityCVE, findNameAndVersion } from './utils/reportUtils'
10
+ import { ReportCVEModel, ReportLibraryModel } from './models/reportLibraryModel'
11
+ import {
12
+ findCVESeveritiesAndOrderByHighestPriority,
13
+ findHighestSeverityCVE,
14
+ findNameAndVersion,
15
+ severityCountAllCVEs,
16
+ severityCountAllLibraries
17
+ } from './utils/reportUtils'
18
+ import { SeverityCountModel } from './models/severityCountModel'
19
+ import {
20
+ ReportOutputBodyModel,
21
+ ReportOutputHeaderModel,
22
+ ReportOutputModel
23
+ } from './models/reportOutputModel'
24
+ import {
25
+ CRITICAL_COLOUR,
26
+ HIGH_COLOUR,
27
+ LOW_COLOUR,
28
+ MEDIUM_COLOUR,
29
+ NOTE_COLOUR
30
+ } from '../../../constants/constants'
12
31
 
13
32
  export const createLibraryHeader = (
14
33
  id: string,
@@ -16,11 +35,9 @@ export const createLibraryHeader = (
16
35
  numberOfCves: number
17
36
  ) => {
18
37
  numberOfVulnerableLibraries === 1
19
- ? console.log(
20
- ` Found 1 vulnerable library containing ${numberOfCves} CVE's`
21
- )
38
+ ? console.log(`Found 1 vulnerable library containing ${numberOfCves} CVEs`)
22
39
  : console.log(
23
- ` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's `
40
+ `Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs `
24
41
  )
25
42
  }
26
43
 
@@ -32,10 +49,6 @@ export const getReport = async (config: any, reportId: string) => {
32
49
  if (res.statusCode === 200) {
33
50
  return res.body
34
51
  } else {
35
- console.log('config-------------------')
36
- console.log(config)
37
- console.log('reportId----------------')
38
- console.log(reportId)
39
52
  console.log(JSON.stringify(res))
40
53
  handleResponseErrors(res, 'report')
41
54
  }
@@ -70,7 +83,11 @@ export const printFormattedOutput = (
70
83
  new ReportCompositeKey(
71
84
  name,
72
85
  version,
73
- findHighestSeverityCVE(library.cveArray) as ReportSeverityModel
86
+ findHighestSeverityCVE(library.cveArray) as ReportSeverityModel,
87
+ severityCountAllCVEs(
88
+ library.cveArray,
89
+ new SeverityCountModel()
90
+ ).getTotal
74
91
  ),
75
92
  library.cveArray
76
93
  )
@@ -78,28 +95,165 @@ export const printFormattedOutput = (
78
95
  report.reportOutputList.push(newOutputModel)
79
96
  }
80
97
 
81
- const orderedOutputList = orderBy(
98
+ const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(
82
99
  report.reportOutputList,
83
- reportListItem => reportListItem.compositeKey.highestSeverity.priority
100
+ [
101
+ (reportListItem: ReportModelStructure) => {
102
+ return reportListItem.compositeKey.highestSeverity.priority
103
+ },
104
+ (reportListItem: ReportModelStructure) => {
105
+ return reportListItem.compositeKey.numberOfSeverities
106
+ }
107
+ ],
108
+ ['desc']
84
109
  )
85
110
 
86
- for (const reportModel of orderedOutputList) {
87
- const name = reportModel.compositeKey.libraryName
88
- const version = reportModel.compositeKey.libraryVersion
89
- const highestSeverity = reportModel.compositeKey.highestSeverity.severity
90
-
111
+ let contrastHeaderNumCounter =
112
+ outputOrderedByLowestSeverityAndLowestNumOfCvesFirst.length + 1
113
+ for (const reportModel of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
114
+ contrastHeaderNumCounter--
115
+ const { libraryName, libraryVersion, highestSeverity } =
116
+ reportModel.compositeKey
91
117
  const numOfCVEs = reportModel.cveArray.length
92
118
 
93
- const cveNames: string[] = []
119
+ const header = buildHeader(
120
+ highestSeverity,
121
+ contrastHeaderNumCounter,
122
+ libraryName,
123
+ libraryVersion,
124
+ numOfCVEs
125
+ )
126
+
127
+ const body = buildBody(reportModel.cveArray)
128
+
129
+ const reportOutputModel = new ReportOutputModel(header, body)
130
+ console.log(
131
+ reportOutputModel.header.vulnMessage,
132
+ reportOutputModel.header.introducesMessage
133
+ )
134
+ console.log(reportOutputModel.body.issueMessage)
135
+ console.log(reportOutputModel.body.adviceMessage + '\n')
136
+ }
94
137
 
95
- reportModel.cveArray.forEach(cve => cveNames.push(cve.name as string))
138
+ const {
139
+ criticalMessage,
140
+ highMessage,
141
+ mediumMessage,
142
+ lowMessage,
143
+ noteMessage,
144
+ total
145
+ } = buildFooter(libraries)
96
146
 
97
- const boldHeader = chalk.bold(`${highestSeverity} | Vulnerable Library`)
98
- const cvePluralised = numOfCVEs > 1 ? 'CVEs' : 'CVE'
147
+ if (total > 1) {
99
148
  console.log(
100
- `\n ${boldHeader} ${name} (${version}) has ${numOfCVEs} known ${cvePluralised}`
149
+ `${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`
101
150
  )
102
- console.log(` ${cveNames.join(', ')}`)
103
- console.log(chalk.bold(' How to fix: Update to latest version'))
104
151
  }
105
152
  }
153
+
154
+ export function buildHeader(
155
+ highestSeverity: ReportSeverityModel,
156
+ contrastHeaderNum: number,
157
+ libraryName: string,
158
+ version: string,
159
+ numOfCVEs: number
160
+ ) {
161
+ const vulnerabilityPluralised =
162
+ numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability'
163
+ const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum)
164
+
165
+ const vulnMessage = chalk
166
+ .hex(highestSeverity.outputColour)
167
+ .bold(
168
+ `${formattedHeaderNum} - [${highestSeverity.severity}] ${libraryName}-${version}`
169
+ )
170
+
171
+ const introducesMessage = chalk.bold(
172
+ `introduces ${numOfCVEs} ${vulnerabilityPluralised}`
173
+ )
174
+
175
+ return new ReportOutputHeaderModel(vulnMessage, introducesMessage)
176
+ }
177
+
178
+ export function buildBody(cveArray: ReportCVEModel[]) {
179
+ const cveMessages: string[] = []
180
+
181
+ findCVESeveritiesAndOrderByHighestPriority(cveArray).forEach(
182
+ reportSeverityModel => {
183
+ // @ts-ignore
184
+ const { outputColour, severity, cveName } = reportSeverityModel
185
+
186
+ const severityShorthand = chalk
187
+ .hex(outputColour)
188
+ .bold(`[${severity.charAt(0).toUpperCase()}]`)
189
+
190
+ const builtMessage = `${severityShorthand} ${cveName}`
191
+ cveMessages.push(builtMessage)
192
+ }
193
+ )
194
+
195
+ const numAndSeverityType = getNumOfAndSeverityType(cveArray)
196
+
197
+ const issueMessage = ` ${chalk.bold(
198
+ 'Issue'
199
+ )} : ${numAndSeverityType} ${cveMessages.join(', ')}.`
200
+
201
+ const adviceMessage = ` ${chalk.bold('Advice')} : ${chalk.bold(
202
+ 'Update to latest version'
203
+ )}.`
204
+
205
+ return new ReportOutputBodyModel(issueMessage, adviceMessage)
206
+ }
207
+
208
+ const buildFooter = (libraries: ReportLibraryModel[]) => {
209
+ const { critical, high, medium, low, note, getTotal } =
210
+ severityCountAllLibraries(libraries)
211
+ const criticalMessage = chalk
212
+ .hex(CRITICAL_COLOUR)
213
+ .bold(`${critical} Critical`)
214
+ const highMessage = chalk.hex(HIGH_COLOUR).bold(`${high} High`)
215
+ const mediumMessage = chalk.hex(MEDIUM_COLOUR).bold(`${medium} Medium`)
216
+ const lowMessage = chalk.hex(LOW_COLOUR).bold(`${low} Low`)
217
+ const noteMessage = chalk.hex(NOTE_COLOUR).bold(`${note} Note`)
218
+
219
+ return {
220
+ criticalMessage,
221
+ highMessage,
222
+ mediumMessage,
223
+ lowMessage,
224
+ noteMessage,
225
+ total: getTotal
226
+ }
227
+ }
228
+
229
+ export function buildFormattedHeaderNum(contrastHeaderNum: number) {
230
+ let formattedHeaderNum
231
+
232
+ if (contrastHeaderNum < 10) {
233
+ formattedHeaderNum = `00${contrastHeaderNum}`
234
+ } else if (contrastHeaderNum >= 10 && contrastHeaderNum < 100) {
235
+ formattedHeaderNum = `0${contrastHeaderNum}`
236
+ } else if (contrastHeaderNum >= 100) {
237
+ formattedHeaderNum = contrastHeaderNum
238
+ }
239
+
240
+ return `CONTRAST-${formattedHeaderNum}`
241
+ }
242
+
243
+ export function getNumOfAndSeverityType(cveArray: ReportCVEModel[]) {
244
+ const { critical, high, medium, low, note } = severityCountAllCVEs(
245
+ cveArray,
246
+ new SeverityCountModel()
247
+ )
248
+
249
+ const criticalMessage = critical > 0 ? `${critical} Critical` : ''
250
+ const highMessage = high > 0 ? `${high} High` : ''
251
+ const mediumMessage = medium > 0 ? `${medium} Medium` : ''
252
+ const lowMessage = low > 0 ? `${low} Low` : ''
253
+ const noteMessage = note > 0 ? `${note} Note` : ''
254
+
255
+ //removes/trims whitespace to single spaces
256
+ return `${criticalMessage} ${highMessage} ${mediumMessage} ${lowMessage} ${noteMessage}`
257
+ .replace(/\s+/g, ' ')
258
+ .trim()
259
+ }
@@ -2,7 +2,7 @@ export class ReportLibraryModel {
2
2
  name: string
3
3
  cveArray: ReportCVEModel[]
4
4
 
5
- constructor (name: string, cveArray: ReportCVEModel[]){
5
+ constructor(name: string, cveArray: ReportCVEModel[]) {
6
6
  this.name = name
7
7
  this.cveArray = cveArray
8
8
  }
@@ -16,12 +16,12 @@ export class ReportCVEModel {
16
16
  severityCode?: string
17
17
  cvss3SeverityCode?: string
18
18
 
19
- constructor (
19
+ constructor(
20
20
  name: string,
21
21
  description: string,
22
22
  severityCode: string,
23
23
  cvss3SeverityCode: string
24
- ){
24
+ ) {
25
25
  this.name = name
26
26
  this.description = description
27
27
  this.severityCode = severityCode
@@ -1,32 +1,39 @@
1
- import {ReportSeverityModel} from "./reportSeverityModel";
2
- import {ReportCVEModel} from "./reportLibraryModel";
1
+ import { ReportSeverityModel } from './reportSeverityModel'
2
+ import { ReportCVEModel } from './reportLibraryModel'
3
3
 
4
4
  export class ReportList {
5
5
  reportOutputList: ReportModelStructure[]
6
6
 
7
- constructor (){
7
+ constructor() {
8
8
  this.reportOutputList = []
9
9
  }
10
10
  }
11
11
 
12
12
  export class ReportModelStructure {
13
- compositeKey: ReportCompositeKey;
14
- cveArray: ReportCVEModel[];
13
+ compositeKey: ReportCompositeKey
14
+ cveArray: ReportCVEModel[]
15
15
 
16
- constructor (compositeKey: ReportCompositeKey, cveArray: ReportCVEModel[]){
16
+ constructor(compositeKey: ReportCompositeKey, cveArray: ReportCVEModel[]) {
17
17
  this.compositeKey = compositeKey
18
18
  this.cveArray = cveArray
19
19
  }
20
20
  }
21
21
 
22
22
  export class ReportCompositeKey {
23
- libraryName!: string;
24
- libraryVersion!: string;
25
- highestSeverity!: ReportSeverityModel;
23
+ libraryName!: string
24
+ libraryVersion!: string
25
+ highestSeverity!: ReportSeverityModel
26
+ numberOfSeverities!: number
26
27
 
27
- constructor (libraryName: string, libraryVersion: string, highestSeverity: ReportSeverityModel){
28
+ constructor(
29
+ libraryName: string,
30
+ libraryVersion: string,
31
+ highestSeverity: ReportSeverityModel,
32
+ numberOfSeverities: number
33
+ ) {
28
34
  this.libraryName = libraryName
29
35
  this.libraryVersion = libraryVersion
30
36
  this.highestSeverity = highestSeverity
37
+ this.numberOfSeverities = numberOfSeverities
31
38
  }
32
- }
39
+ }
@@ -0,0 +1,29 @@
1
+ export class ReportOutputModel {
2
+ header: ReportOutputHeaderModel
3
+ body: ReportOutputBodyModel
4
+
5
+ constructor(header: ReportOutputHeaderModel, body: ReportOutputBodyModel) {
6
+ this.header = header
7
+ this.body = body
8
+ }
9
+ }
10
+
11
+ export class ReportOutputHeaderModel {
12
+ vulnMessage: string
13
+ introducesMessage: string
14
+
15
+ constructor(vulnMessage: string, introducesMessage: string) {
16
+ this.vulnMessage = vulnMessage
17
+ this.introducesMessage = introducesMessage
18
+ }
19
+ }
20
+
21
+ export class ReportOutputBodyModel {
22
+ issueMessage: string
23
+ adviceMessage: string
24
+
25
+ constructor(bodyIssueMessage: string, bodyAdviceMessage: string) {
26
+ this.issueMessage = bodyIssueMessage
27
+ this.adviceMessage = bodyAdviceMessage
28
+ }
29
+ }
@@ -1,9 +1,18 @@
1
1
  export class ReportSeverityModel {
2
- severity!: string
3
- priority!: number
2
+ severity: string
3
+ priority: number
4
+ outputColour: string
5
+ cveName: string
4
6
 
5
- constructor(severity: string, priority: number) {
7
+ constructor(
8
+ severity: string,
9
+ priority: number,
10
+ outputColour: string,
11
+ cveName: string
12
+ ) {
6
13
  this.severity = severity
7
14
  this.priority = priority
15
+ this.outputColour = outputColour
16
+ this.cveName = cveName
8
17
  }
9
18
  }