@contrast/contrast 1.0.7 → 1.0.10

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 (196) hide show
  1. package/README.md +1 -1
  2. package/dist/audit/catalogueApplication/catalogueApplication.js +23 -5
  3. package/dist/audit/languageAnalysisEngine/getProjectRootFilenames.js +17 -26
  4. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +98 -37
  5. package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +2 -1
  6. package/dist/audit/languageAnalysisEngine/report/models/reportOutputModel.js +4 -3
  7. package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +3 -0
  8. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +87 -19
  9. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +40 -7
  10. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +6 -30
  11. package/dist/audit/save.js +37 -0
  12. package/dist/commands/audit/auditConfig.js +0 -16
  13. package/dist/commands/audit/auditController.js +18 -11
  14. package/dist/commands/audit/help.js +31 -25
  15. package/dist/commands/audit/processAudit.js +2 -2
  16. package/dist/commands/audit/saveFile.js +8 -4
  17. package/dist/commands/scan/sca/scaAnalysis.js +54 -16
  18. package/dist/common/HTTPClient.js +14 -8
  19. package/dist/common/errorHandling.js +2 -2
  20. package/dist/common/versionChecker.js +19 -4
  21. package/dist/constants/constants.js +7 -2
  22. package/dist/constants/locales.js +44 -44
  23. package/dist/constants.js +31 -14
  24. package/dist/index.js +55 -45
  25. package/dist/lambda/lambda.js +5 -2
  26. package/dist/sbom/generateSbom.js +5 -4
  27. package/dist/scaAnalysis/common/formatMessage.js +33 -6
  28. package/dist/scaAnalysis/common/treeUpload.js +4 -6
  29. package/dist/scaAnalysis/dotnet/analysis.js +43 -0
  30. package/dist/scaAnalysis/dotnet/index.js +10 -0
  31. package/dist/scaAnalysis/go/goReadDepFile.js +1 -3
  32. package/dist/scaAnalysis/java/analysis.js +5 -5
  33. package/dist/scaAnalysis/javascript/analysis.js +107 -0
  34. package/dist/scaAnalysis/javascript/index.js +53 -0
  35. package/dist/scaAnalysis/php/analysis.js +70 -0
  36. package/dist/scaAnalysis/php/index.js +17 -0
  37. package/dist/scaAnalysis/python/analysis.js +8 -7
  38. package/dist/scaAnalysis/ruby/analysis.js +8 -16
  39. package/dist/scaAnalysis/ruby/index.js +2 -2
  40. package/dist/scan/autoDetection.js +13 -24
  41. package/dist/scan/fileUtils.js +44 -14
  42. package/dist/scan/formatScanOutput.js +3 -3
  43. package/dist/scan/scanConfig.js +2 -2
  44. package/dist/utils/commonApi.js +1 -1
  45. package/dist/utils/filterProjectPath.js +7 -2
  46. package/dist/utils/getConfig.js +1 -6
  47. package/package.json +2 -3
  48. package/src/audit/catalogueApplication/catalogueApplication.js +28 -6
  49. package/src/audit/languageAnalysisEngine/getProjectRootFilenames.js +22 -58
  50. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +157 -47
  51. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +4 -1
  52. package/src/audit/languageAnalysisEngine/report/models/reportOutputModel.ts +11 -5
  53. package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +4 -0
  54. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +86 -32
  55. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +44 -5
  56. package/src/audit/languageAnalysisEngine/sendSnapshot.js +6 -32
  57. package/src/audit/save.js +48 -0
  58. package/src/commands/audit/auditConfig.ts +0 -25
  59. package/src/commands/audit/auditController.ts +18 -20
  60. package/src/commands/audit/help.ts +31 -25
  61. package/src/commands/audit/processAudit.ts +2 -5
  62. package/src/commands/audit/saveFile.ts +6 -2
  63. package/src/commands/scan/processScan.js +0 -1
  64. package/src/commands/scan/sca/scaAnalysis.js +87 -32
  65. package/src/common/HTTPClient.js +16 -9
  66. package/src/common/errorHandling.ts +2 -3
  67. package/src/common/versionChecker.ts +23 -4
  68. package/src/constants/constants.js +9 -3
  69. package/src/constants/locales.js +72 -50
  70. package/src/constants.js +32 -15
  71. package/src/index.ts +70 -58
  72. package/src/lambda/lambda.ts +5 -2
  73. package/src/lambda/types.ts +1 -0
  74. package/src/sbom/generateSbom.ts +2 -2
  75. package/src/scaAnalysis/common/formatMessage.js +35 -6
  76. package/src/scaAnalysis/common/treeUpload.js +4 -6
  77. package/src/scaAnalysis/dotnet/analysis.js +54 -0
  78. package/src/scaAnalysis/dotnet/index.js +11 -0
  79. package/src/scaAnalysis/go/goReadDepFile.js +1 -3
  80. package/src/scaAnalysis/java/analysis.js +5 -5
  81. package/src/scaAnalysis/javascript/analysis.js +126 -0
  82. package/src/scaAnalysis/javascript/index.js +75 -0
  83. package/src/scaAnalysis/php/analysis.js +78 -0
  84. package/src/scaAnalysis/php/index.js +22 -0
  85. package/src/scaAnalysis/python/analysis.js +8 -7
  86. package/src/scaAnalysis/ruby/analysis.js +8 -17
  87. package/src/scaAnalysis/ruby/index.js +2 -2
  88. package/src/scan/autoDetection.js +14 -27
  89. package/src/scan/fileUtils.js +46 -14
  90. package/src/scan/formatScanOutput.ts +3 -3
  91. package/src/scan/scanConfig.js +2 -4
  92. package/src/utils/commonApi.js +1 -1
  93. package/src/utils/filterProjectPath.js +6 -2
  94. package/src/utils/getConfig.ts +1 -12
  95. package/dist/audit/AnalysisEngine.js +0 -37
  96. package/dist/audit/autodetection/autoDetectLanguage.js +0 -32
  97. package/dist/audit/dotnetAnalysisEngine/index.js +0 -25
  98. package/dist/audit/dotnetAnalysisEngine/parseLockFileContents.js +0 -35
  99. package/dist/audit/dotnetAnalysisEngine/parseProjectFileContents.js +0 -15
  100. package/dist/audit/dotnetAnalysisEngine/readLockFileContents.js +0 -18
  101. package/dist/audit/dotnetAnalysisEngine/readProjectFileContents.js +0 -14
  102. package/dist/audit/dotnetAnalysisEngine/sanitizer.js +0 -9
  103. package/dist/audit/goAnalysisEngine/index.js +0 -17
  104. package/dist/audit/goAnalysisEngine/parseProjectFileContents.js +0 -164
  105. package/dist/audit/goAnalysisEngine/readProjectFileContents.js +0 -21
  106. package/dist/audit/goAnalysisEngine/sanitizer.js +0 -5
  107. package/dist/audit/javaAnalysisEngine/index.js +0 -34
  108. package/dist/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +0 -155
  109. package/dist/audit/javaAnalysisEngine/parseProjectFileContents.js +0 -353
  110. package/dist/audit/javaAnalysisEngine/readProjectFileContents.js +0 -98
  111. package/dist/audit/javaAnalysisEngine/sanitizer.js +0 -5
  112. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +0 -25
  113. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +0 -25
  114. package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +0 -35
  115. package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +0 -24
  116. package/dist/audit/languageAnalysisEngine/constants.js +0 -20
  117. package/dist/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +0 -25
  118. package/dist/audit/languageAnalysisEngine/index.js +0 -39
  119. package/dist/audit/languageAnalysisEngine/languageAnalysisFactory.js +0 -89
  120. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +0 -159
  121. package/dist/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +0 -40
  122. package/dist/audit/nodeAnalysisEngine/index.js +0 -31
  123. package/dist/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +0 -18
  124. package/dist/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +0 -18
  125. package/dist/audit/nodeAnalysisEngine/readNPMLockFileContents.js +0 -17
  126. package/dist/audit/nodeAnalysisEngine/readProjectFileContents.js +0 -14
  127. package/dist/audit/nodeAnalysisEngine/readYarnLockFileContents.js +0 -24
  128. package/dist/audit/nodeAnalysisEngine/sanitizer.js +0 -9
  129. package/dist/audit/phpAnalysisEngine/index.js +0 -23
  130. package/dist/audit/phpAnalysisEngine/parseLockFileContents.js +0 -52
  131. package/dist/audit/phpAnalysisEngine/readLockFileContents.js +0 -13
  132. package/dist/audit/phpAnalysisEngine/readProjectFileContents.js +0 -16
  133. package/dist/audit/phpAnalysisEngine/sanitizer.js +0 -5
  134. package/dist/audit/pythonAnalysisEngine/index.js +0 -25
  135. package/dist/audit/pythonAnalysisEngine/parsePipfileLockContents.js +0 -17
  136. package/dist/audit/pythonAnalysisEngine/parseProjectFileContents.js +0 -21
  137. package/dist/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +0 -13
  138. package/dist/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +0 -14
  139. package/dist/audit/pythonAnalysisEngine/sanitizer.js +0 -7
  140. package/dist/audit/rubyAnalysisEngine/index.js +0 -25
  141. package/dist/audit/rubyAnalysisEngine/parseGemfileLockContents.js +0 -176
  142. package/dist/audit/rubyAnalysisEngine/parsedGemfile.js +0 -22
  143. package/dist/audit/rubyAnalysisEngine/readGemfileContents.js +0 -14
  144. package/dist/audit/rubyAnalysisEngine/readGemfileLockContents.js +0 -14
  145. package/dist/audit/rubyAnalysisEngine/sanitizer.js +0 -6
  146. package/src/audit/AnalysisEngine.js +0 -103
  147. package/src/audit/autodetection/autoDetectLanguage.ts +0 -40
  148. package/src/audit/dotnetAnalysisEngine/index.js +0 -26
  149. package/src/audit/dotnetAnalysisEngine/parseLockFileContents.js +0 -47
  150. package/src/audit/dotnetAnalysisEngine/parseProjectFileContents.js +0 -29
  151. package/src/audit/dotnetAnalysisEngine/readLockFileContents.js +0 -30
  152. package/src/audit/dotnetAnalysisEngine/readProjectFileContents.js +0 -26
  153. package/src/audit/dotnetAnalysisEngine/sanitizer.js +0 -11
  154. package/src/audit/goAnalysisEngine/index.js +0 -18
  155. package/src/audit/goAnalysisEngine/parseProjectFileContents.js +0 -209
  156. package/src/audit/goAnalysisEngine/readProjectFileContents.js +0 -31
  157. package/src/audit/goAnalysisEngine/sanitizer.js +0 -7
  158. package/src/audit/javaAnalysisEngine/index.js +0 -41
  159. package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +0 -225
  160. package/src/audit/javaAnalysisEngine/parseProjectFileContents.js +0 -420
  161. package/src/audit/javaAnalysisEngine/readProjectFileContents.js +0 -141
  162. package/src/audit/javaAnalysisEngine/sanitizer.js +0 -6
  163. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +0 -36
  164. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +0 -42
  165. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +0 -54
  166. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +0 -33
  167. package/src/audit/languageAnalysisEngine/constants.js +0 -23
  168. package/src/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +0 -41
  169. package/src/audit/languageAnalysisEngine/index.js +0 -45
  170. package/src/audit/languageAnalysisEngine/languageAnalysisFactory.js +0 -124
  171. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +0 -250
  172. package/src/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +0 -49
  173. package/src/audit/nodeAnalysisEngine/index.js +0 -35
  174. package/src/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +0 -20
  175. package/src/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +0 -26
  176. package/src/audit/nodeAnalysisEngine/readNPMLockFileContents.js +0 -23
  177. package/src/audit/nodeAnalysisEngine/readProjectFileContents.js +0 -27
  178. package/src/audit/nodeAnalysisEngine/readYarnLockFileContents.js +0 -36
  179. package/src/audit/nodeAnalysisEngine/sanitizer.js +0 -11
  180. package/src/audit/phpAnalysisEngine/index.js +0 -27
  181. package/src/audit/phpAnalysisEngine/parseLockFileContents.js +0 -60
  182. package/src/audit/phpAnalysisEngine/readLockFileContents.js +0 -14
  183. package/src/audit/phpAnalysisEngine/readProjectFileContents.js +0 -25
  184. package/src/audit/phpAnalysisEngine/sanitizer.js +0 -4
  185. package/src/audit/pythonAnalysisEngine/index.js +0 -55
  186. package/src/audit/pythonAnalysisEngine/parsePipfileLockContents.js +0 -23
  187. package/src/audit/pythonAnalysisEngine/parseProjectFileContents.js +0 -33
  188. package/src/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +0 -16
  189. package/src/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +0 -22
  190. package/src/audit/pythonAnalysisEngine/sanitizer.js +0 -9
  191. package/src/audit/rubyAnalysisEngine/index.js +0 -30
  192. package/src/audit/rubyAnalysisEngine/parseGemfileLockContents.js +0 -215
  193. package/src/audit/rubyAnalysisEngine/parsedGemfile.js +0 -39
  194. package/src/audit/rubyAnalysisEngine/readGemfileContents.js +0 -18
  195. package/src/audit/rubyAnalysisEngine/readGemfileLockContents.js +0 -17
  196. package/src/audit/rubyAnalysisEngine/sanitizer.js +0 -8
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.setConfigValues = exports.createConfigFromYaml = exports.localConfig = void 0;
6
+ exports.setConfigValues = exports.localConfig = void 0;
7
7
  const conf_1 = __importDefault(require("conf"));
8
8
  const localConfig = (name, version) => {
9
9
  const config = new conf_1.default({
@@ -19,11 +19,6 @@ const localConfig = (name, version) => {
19
19
  return config;
20
20
  };
21
21
  exports.localConfig = localConfig;
22
- const createConfigFromYaml = (yamlPath) => {
23
- const yamlConfig = {};
24
- return yamlConfig;
25
- };
26
- exports.createConfigFromYaml = createConfigFromYaml;
27
22
  const setConfigValues = (config, values) => {
28
23
  config.set('apiKey', values.apiKey);
29
24
  config.set('organizationId', values.orgId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.7",
3
+ "version": "1.0.10",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "test": "jest --testPathIgnorePatterns=./test-integration/",
24
24
  "test-int": "jest ./test-integration/",
25
25
  "test-int-scan": "jest ./test-integration/scan",
26
- "test-int-audit": "jest ./test-integration/audit",
26
+ "test-int-audit": "jest test-integration/audit/audit-int.spec.js",
27
27
  "format": "prettier --write \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
28
28
  "check-format": "prettier --check \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
29
29
  "coverage-local": "nyc --reporter=text mocha './test/**/*.spec.js'",
@@ -53,7 +53,6 @@
53
53
  "fast-glob": "^3.2.11",
54
54
  "i18n": "^0.14.2",
55
55
  "js-yaml": "^4.1.0",
56
- "latest-version": "5.1.0",
57
56
  "lodash": "^4.17.21",
58
57
  "log-symbols": "^4.1.0",
59
58
  "open": "^8.4.0",
@@ -1,10 +1,5 @@
1
- const i18n = require('i18n')
2
1
  const { getHttpClient, handleResponseErrors } = require('../../utils/commonApi')
3
2
 
4
- const displaySuccessMessage = () => {
5
- console.log(i18n.__('catalogueSuccessCommand'))
6
- }
7
-
8
3
  const catalogueApplication = async config => {
9
4
  const client = getHttpClient(config)
10
5
  let appId
@@ -14,6 +9,8 @@ const catalogueApplication = async config => {
14
9
  if (res.statusCode === 201) {
15
10
  //displaySuccessMessage(config, res.body.application.app_id)
16
11
  appId = res.body.application.app_id
12
+ } else if (doesMessagesContainAppId(res)) {
13
+ appId = tryRetrieveAppIdFromMessages(res.body.messages)
17
14
  } else {
18
15
  handleResponseErrors(res, 'catalogue')
19
16
  }
@@ -24,6 +21,31 @@ const catalogueApplication = async config => {
24
21
  return appId
25
22
  }
26
23
 
24
+ const doesMessagesContainAppId = res => {
25
+ const regex = /(Application ID =)/
26
+ if (
27
+ res.statusCode === 400 &&
28
+ res.body.messages.filter(message => regex.exec(message))[0]
29
+ ) {
30
+ return true
31
+ }
32
+
33
+ return false
34
+ }
35
+
36
+ const tryRetrieveAppIdFromMessages = messages => {
37
+ let appId
38
+ messages.forEach(message => {
39
+ if (message.includes('Application ID')) {
40
+ appId = message.split('=')[1].replace(/\s+/g, '')
41
+ }
42
+ })
43
+
44
+ return appId
45
+ }
46
+
27
47
  module.exports = {
28
- catalogueApplication: catalogueApplication
48
+ catalogueApplication: catalogueApplication,
49
+ doesMessagesContainAppId,
50
+ tryRetrieveAppIdFromMessages
29
51
  }
@@ -1,72 +1,36 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
  const i18n = require('i18n')
4
- /**
5
- * Will get the filenames from the project path provided to the SCA CLI tool. If
6
- * the project path points to a file and not a directory will return the
7
- * filename in the same fashion as if a directory had been read.
8
- *
9
- * Will fail and throw for a manner of reasons when doing file/directory
10
- * inspection.
11
- *
12
- * @param {string} projectPath - The path to a projects root directory or a
13
- * specific project file
14
- *
15
- * @return {string[]} List of filenames associated with a projects root
16
- * directory or the name of the specific project file if that was provided to
17
- * the 'projectPath' parameter
18
- *
19
- * @throws {Error} If the project path doesn't exist
20
- * @throws {Error} If the project path information can't be collected
21
- * @throws {Error} If a non-file or non-directory inspected
22
- */
23
- module.exports = exports = (analysis, next) => {
24
- const { projectPath, languageAnalysis } = analysis
25
- try {
26
- languageAnalysis.projectRootFilenames = getProjectRootFilenames(projectPath)
27
- } catch (err) {
28
- next(err)
29
- return
4
+
5
+ const getDirectoryFromPathGiven = file => {
6
+ let projectStats = getProjectStats(file)
7
+
8
+ if (projectStats.isFile()) {
9
+ let newPath = path.resolve(file)
10
+ return path.dirname(newPath)
11
+ }
12
+
13
+ if (projectStats.isDirectory()) {
14
+ return file
30
15
  }
31
- next()
32
16
  }
33
17
 
34
- const getProjectRootFilenames = projectPath => {
35
- let projectStats = null
18
+ const getProjectStats = file => {
36
19
  try {
37
- projectStats = fs.statSync(projectPath)
20
+ //might not need this
21
+ if (file.endsWith('/')) {
22
+ file = file.slice(0, -1)
23
+ }
24
+ return fs.statSync(file)
38
25
  } catch (err) {
39
26
  throw new Error(
40
- i18n.__('languageAnalysisProjectRootFileNameFailure', projectPath) +
27
+ i18n.__('languageAnalysisProjectRootFileNameFailure', file) +
41
28
  `${err.message}`
42
29
  )
43
30
  }
44
-
45
- // Return the contents of a directory...
46
- if (projectStats.isDirectory()) {
47
- try {
48
- return fs.readdirSync(projectPath)
49
- } catch (err) {
50
- throw new Error(
51
- i18n.__('languageAnalysisProjectRootFileNameReadError', projectPath) +
52
- `${err.message}`
53
- )
54
- }
55
- }
56
-
57
- // If we are working with a file return it in a list as we do when we work
58
- // with a directory...
59
- if (projectStats.isFile()) {
60
- return [path.basename(projectPath)]
61
- }
62
-
63
- // Error out if we are working with something like a socket file or some
64
- // other craziness...
65
- throw new Error(
66
- i18n.__('languageAnalysisProjectRootFileNameMissingError'),
67
- projectPath
68
- )
69
31
  }
70
32
 
71
- //For testing purposes
72
- exports.getProjectRootFilenames = getProjectRootFilenames
33
+ module.exports = {
34
+ getProjectStats,
35
+ getDirectoryFromPathGiven: getDirectoryFromPathGiven
36
+ }
@@ -9,6 +9,7 @@ import { orderBy } from 'lodash'
9
9
  import chalk from 'chalk'
10
10
  import { ReportCVEModel, ReportLibraryModel } from './models/reportLibraryModel'
11
11
  import {
12
+ countVulnerableLibrariesBySeverity,
12
13
  findCVESeveritiesAndOrderByHighestPriority,
13
14
  findHighestSeverityCVE,
14
15
  findNameAndVersion,
@@ -20,18 +21,23 @@ import {
20
21
  ReportOutputHeaderModel,
21
22
  ReportOutputModel
22
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'
31
+ import Table from 'cli-table3'
23
32
 
24
- export const createLibraryHeader = (
25
- id: string,
33
+ export const createSummaryMessage = (
26
34
  numberOfVulnerableLibraries: number,
27
35
  numberOfCves: number
28
36
  ) => {
29
37
  numberOfVulnerableLibraries === 1
30
- ? console.log(
31
- ` Found 1 vulnerable library containing ${numberOfCves} CVE's`
32
- )
38
+ ? console.log(`Found 1 vulnerable library containing ${numberOfCves} CVE`)
33
39
  : console.log(
34
- ` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's `
40
+ `Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs`
35
41
  )
36
42
  }
37
43
 
@@ -43,10 +49,6 @@ export const getReport = async (config: any, reportId: string) => {
43
49
  if (res.statusCode === 200) {
44
50
  return res.body
45
51
  } else {
46
- console.log('config-------------------')
47
- console.log(config)
48
- console.log('reportId----------------')
49
- console.log(reportId)
50
52
  console.log(JSON.stringify(res))
51
53
  handleResponseErrors(res, 'report')
52
54
  }
@@ -57,21 +59,35 @@ export const getReport = async (config: any, reportId: string) => {
57
59
  }
58
60
 
59
61
  export const printVulnerabilityResponse = (
60
- vulnerabilities: ReportLibraryModel[],
61
- config: any
62
+ config: any,
63
+ vulnerableLibraries: ReportLibraryModel[],
64
+ numberOfVulnerableLibraries: number,
65
+ numberOfCves: number,
66
+ guidance: any
62
67
  ) => {
63
68
  let hasSomeVulnerabilitiesReported = false
64
- printFormattedOutput(vulnerabilities, config)
65
- if (Object.keys(vulnerabilities).length > 0) {
69
+ printFormattedOutput(
70
+ config,
71
+ vulnerableLibraries,
72
+ numberOfVulnerableLibraries,
73
+ numberOfCves,
74
+ guidance
75
+ )
76
+ if (Object.keys(vulnerableLibraries).length > 0) {
66
77
  hasSomeVulnerabilitiesReported = true
67
78
  }
68
79
  return hasSomeVulnerabilitiesReported
69
80
  }
70
81
 
71
82
  export const printFormattedOutput = (
83
+ config: any,
72
84
  libraries: ReportLibraryModel[],
73
- config: any
85
+ numberOfVulnerableLibraries: number,
86
+ numberOfCves: number,
87
+ guidance: any
74
88
  ) => {
89
+ createSummaryMessage(numberOfVulnerableLibraries, numberOfCves)
90
+ console.log()
75
91
  const report = new ReportList()
76
92
 
77
93
  for (const library of libraries) {
@@ -81,27 +97,61 @@ export const printFormattedOutput = (
81
97
  new ReportCompositeKey(
82
98
  name,
83
99
  version,
84
- findHighestSeverityCVE(library.cveArray) as ReportSeverityModel
100
+ findHighestSeverityCVE(library.cveArray) as ReportSeverityModel,
101
+ severityCountAllCVEs(
102
+ library.cveArray,
103
+ new SeverityCountModel()
104
+ ).getTotal
85
105
  ),
86
106
  library.cveArray
87
107
  )
88
-
89
108
  report.reportOutputList.push(newOutputModel)
90
109
  }
91
110
 
92
- const orderedOutputListLowestFirst = orderBy(
111
+ const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(
93
112
  report.reportOutputList,
94
- reportListItem => reportListItem.compositeKey.highestSeverity.priority,
95
- ['desc']
113
+ [
114
+ (reportListItem: ReportModelStructure) => {
115
+ return reportListItem.compositeKey.highestSeverity.priority
116
+ },
117
+ (reportListItem: ReportModelStructure) => {
118
+ return reportListItem.compositeKey.numberOfSeverities
119
+ }
120
+ ],
121
+ ['asc', 'desc']
96
122
  )
97
123
 
98
124
  let contrastHeaderNumCounter = 0
99
- for (const reportModel of orderedOutputListLowestFirst) {
125
+ for (const reportModel of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
100
126
  contrastHeaderNumCounter++
101
127
  const { libraryName, libraryVersion, highestSeverity } =
102
128
  reportModel.compositeKey
103
129
  const numOfCVEs = reportModel.cveArray.length
104
130
 
131
+ const table = new Table({
132
+ chars: {
133
+ top: '',
134
+ 'top-mid': '',
135
+ 'top-left': '',
136
+ 'top-right': '',
137
+ bottom: '',
138
+ 'bottom-mid': '',
139
+ 'bottom-left': '',
140
+ 'bottom-right': '',
141
+ left: '',
142
+ 'left-mid': '',
143
+ mid: '',
144
+ 'mid-mid': '',
145
+ right: '',
146
+ 'right-mid': '',
147
+ middle: ' '
148
+ },
149
+ style: { 'padding-left': 0, 'padding-right': 0 },
150
+ colAligns: ['right'],
151
+ wordWrap: true,
152
+ colWidths: [12, 1, 100]
153
+ })
154
+
105
155
  const header = buildHeader(
106
156
  highestSeverity,
107
157
  contrastHeaderNumCounter,
@@ -110,16 +160,36 @@ export const printFormattedOutput = (
110
160
  numOfCVEs
111
161
  )
112
162
 
113
- const body = buildBody(reportModel.cveArray)
163
+ const advice = gatherRemediationAdvice(guidance, reportModel)
164
+
165
+ const body = buildBody(reportModel.cveArray, advice)
114
166
 
115
167
  const reportOutputModel = new ReportOutputModel(header, body)
168
+
169
+ table.push(
170
+ reportOutputModel.body.issueMessage,
171
+ reportOutputModel.body.issueMessageCves,
172
+ reportOutputModel.body.adviceMessage
173
+ )
174
+
116
175
  console.log(
117
176
  reportOutputModel.header.vulnMessage,
118
177
  reportOutputModel.header.introducesMessage
119
178
  )
120
- console.log(reportOutputModel.body.issueMessage)
121
- console.log(reportOutputModel.body.adviceMessage)
179
+ console.log(table.toString() + '\n')
122
180
  }
181
+
182
+ createSummaryMessage(numberOfVulnerableLibraries, numberOfCves)
183
+ const {
184
+ criticalMessage,
185
+ highMessage,
186
+ mediumMessage,
187
+ lowMessage,
188
+ noteMessage
189
+ } = buildFooter(outputOrderedByLowestSeverityAndLowestNumOfCvesFirst)
190
+ console.log(
191
+ `${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`
192
+ )
123
193
  }
124
194
 
125
195
  export function buildHeader(
@@ -130,7 +200,7 @@ export function buildHeader(
130
200
  numOfCVEs: number
131
201
  ) {
132
202
  const vulnerabilityPluralised =
133
- numOfCVEs > 1 ? 'Vulnerabilities' : 'Vulnerability'
203
+ numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability'
134
204
  const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum)
135
205
 
136
206
  const vulnMessage = chalk
@@ -139,14 +209,12 @@ export function buildHeader(
139
209
  `${formattedHeaderNum} - [${highestSeverity.severity}] ${libraryName}-${version}`
140
210
  )
141
211
 
142
- const introducesMessage = chalk.bold(
143
- `introduces ${numOfCVEs} ${vulnerabilityPluralised}`
144
- )
212
+ const introducesMessage = `introduces ${numOfCVEs} ${vulnerabilityPluralised}`
145
213
 
146
214
  return new ReportOutputHeaderModel(vulnMessage, introducesMessage)
147
215
  }
148
216
 
149
- export function buildBody(cveArray: ReportCVEModel[]) {
217
+ export function buildBody(cveArray: ReportCVEModel[], advice: any) {
150
218
  const cveMessages: string[] = []
151
219
 
152
220
  findCVESeveritiesAndOrderByHighestPriority(cveArray).forEach(
@@ -158,36 +226,57 @@ export function buildBody(cveArray: ReportCVEModel[]) {
158
226
  .hex(outputColour)
159
227
  .bold(`[${severity.charAt(0).toUpperCase()}]`)
160
228
 
161
- const builtMessage = `${severityShorthand} ${cveName}`
229
+ const builtMessage = severityShorthand + cveName
162
230
  cveMessages.push(builtMessage)
163
231
  }
164
232
  )
165
233
 
166
234
  const numAndSeverityType = getNumOfAndSeverityType(cveArray)
167
235
 
168
- const issueMessage = ` ${chalk.bold(
169
- 'Issue'
170
- )} : ${numAndSeverityType} ${cveMessages.join(', ')}.`
236
+ const issueMessage = [chalk.bold('Issue'), ':', `${numAndSeverityType}`]
171
237
 
172
- const adviceMessage = ` ${chalk.bold('Advice')} : ${chalk.bold(
173
- 'Update to latest version'
174
- )}.`
238
+ const issueMessageCves = ['', '', cveMessages.join(', ')]
175
239
 
176
- return new ReportOutputBodyModel(issueMessage, adviceMessage)
240
+ //todo different advice based on remediationGuidance being available or now
241
+ // console.log(advice)
242
+
243
+ const displayAdvice = advice?.minimum
244
+ ? `Update to version ${chalk.bold(advice.minimum)}`
245
+ : `Update to latest version`
246
+
247
+ const adviceMessage = [chalk.bold('Advice'), ':', displayAdvice]
248
+
249
+ return new ReportOutputBodyModel(
250
+ issueMessage,
251
+ issueMessageCves,
252
+ adviceMessage
253
+ )
177
254
  }
178
255
 
179
- export function buildFormattedHeaderNum(contrastHeaderNum: number) {
180
- let formattedHeaderNum
181
-
182
- if (contrastHeaderNum < 10) {
183
- formattedHeaderNum = `00${contrastHeaderNum}`
184
- } else if (contrastHeaderNum >= 10 && contrastHeaderNum < 100) {
185
- formattedHeaderNum = `0${contrastHeaderNum}`
186
- } else if (contrastHeaderNum >= 100) {
187
- formattedHeaderNum = contrastHeaderNum
256
+ export function gatherRemediationAdvice(guidance: any, reportModel: any) {
257
+ const guidanceData = {
258
+ minimum: undefined,
259
+ maximum: undefined,
260
+ latest: undefined
261
+ }
262
+
263
+ const data =
264
+ guidance[
265
+ reportModel.compositeKey.libraryName +
266
+ '@' +
267
+ reportModel.compositeKey.libraryVersion
268
+ ]
269
+
270
+ if (data) {
271
+ guidanceData.minimum = data.minUpgradeVersion
272
+ guidanceData.maximum = data.maxUpgradeVersion
188
273
  }
189
274
 
190
- return `CONTRAST-${formattedHeaderNum}`
275
+ return guidanceData
276
+ }
277
+
278
+ export function buildFormattedHeaderNum(contrastHeaderNum: number) {
279
+ return `CONTRAST-${contrastHeaderNum.toString().padStart(3, '0')}`
191
280
  }
192
281
 
193
282
  export function getNumOfAndSeverityType(cveArray: ReportCVEModel[]) {
@@ -207,3 +296,24 @@ export function getNumOfAndSeverityType(cveArray: ReportCVEModel[]) {
207
296
  .replace(/\s+/g, ' ')
208
297
  .trim()
209
298
  }
299
+
300
+ const buildFooter = (reportModelStructure: ReportModelStructure[]) => {
301
+ const { critical, high, medium, low, note } =
302
+ countVulnerableLibrariesBySeverity(reportModelStructure)
303
+
304
+ const criticalMessage = chalk
305
+ .hex(CRITICAL_COLOUR)
306
+ .bold(`${critical} Critical`)
307
+ const highMessage = chalk.hex(HIGH_COLOUR).bold(`${high} High`)
308
+ const mediumMessage = chalk.hex(MEDIUM_COLOUR).bold(`${medium} Medium`)
309
+ const lowMessage = chalk.hex(LOW_COLOUR).bold(`${low} Low`)
310
+ const noteMessage = chalk.hex(NOTE_COLOUR).bold(`${note} Note`)
311
+
312
+ return {
313
+ criticalMessage,
314
+ highMessage,
315
+ mediumMessage,
316
+ lowMessage,
317
+ noteMessage
318
+ }
319
+ }
@@ -23,14 +23,17 @@ export class ReportCompositeKey {
23
23
  libraryName!: string
24
24
  libraryVersion!: string
25
25
  highestSeverity!: ReportSeverityModel
26
+ numberOfSeverities!: number
26
27
 
27
28
  constructor(
28
29
  libraryName: string,
29
30
  libraryVersion: string,
30
- highestSeverity: ReportSeverityModel
31
+ highestSeverity: ReportSeverityModel,
32
+ numberOfSeverities: number
31
33
  ) {
32
34
  this.libraryName = libraryName
33
35
  this.libraryVersion = libraryVersion
34
36
  this.highestSeverity = highestSeverity
37
+ this.numberOfSeverities = numberOfSeverities
35
38
  }
36
39
  }
@@ -19,11 +19,17 @@ export class ReportOutputHeaderModel {
19
19
  }
20
20
 
21
21
  export class ReportOutputBodyModel {
22
- issueMessage: string
23
- adviceMessage: string
22
+ issueMessage: string[]
23
+ issueMessageCves: string[]
24
+ adviceMessage: string[]
24
25
 
25
- constructor(bodyIssueMessage: string, bodyAdviceMessage: string) {
26
- this.issueMessage = bodyIssueMessage
27
- this.adviceMessage = bodyAdviceMessage
26
+ constructor(
27
+ issueMessage: string[],
28
+ issueMessageCves: string[],
29
+ adviceMessage: string[]
30
+ ) {
31
+ this.issueMessage = issueMessage
32
+ this.issueMessageCves = issueMessageCves
33
+ this.adviceMessage = adviceMessage
28
34
  }
29
35
  }
@@ -13,4 +13,8 @@ export class SeverityCountModel {
13
13
  this.low = 0
14
14
  this.note = 0
15
15
  }
16
+
17
+ get getTotal(): number {
18
+ return this.critical + this.high + this.medium + this.low + this.note
19
+ }
16
20
  }