@contrast/contrast 1.0.4 → 1.0.7

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 (115) hide show
  1. package/.prettierignore +0 -3
  2. package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
  3. package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
  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/languageAnalysisFactory.js +6 -2
  9. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +39 -1
  10. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +69 -30
  11. package/dist/audit/languageAnalysisEngine/report/models/reportOutputModel.js +24 -0
  12. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +3 -1
  13. package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +13 -0
  14. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +2 -2
  15. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +56 -45
  16. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +65 -17
  17. package/dist/commands/audit/auditConfig.js +8 -2
  18. package/dist/commands/audit/auditController.js +9 -3
  19. package/dist/commands/audit/processAudit.js +1 -1
  20. package/dist/commands/scan/processScan.js +7 -4
  21. package/dist/commands/scan/sca/scaAnalysis.js +60 -0
  22. package/dist/common/HTTPClient.js +50 -16
  23. package/dist/common/errorHandling.js +11 -16
  24. package/dist/common/versionChecker.js +1 -1
  25. package/dist/constants/constants.js +24 -2
  26. package/dist/constants/locales.js +31 -36
  27. package/dist/constants.js +20 -0
  28. package/dist/lambda/analytics.js +11 -0
  29. package/dist/lambda/lambda.js +35 -4
  30. package/dist/lambda/types.js +13 -0
  31. package/dist/scaAnalysis/common/formatMessage.js +35 -0
  32. package/dist/scaAnalysis/common/treeUpload.js +29 -0
  33. package/dist/scaAnalysis/go/goAnalysis.js +17 -0
  34. package/dist/scaAnalysis/go/goParseDeps.js +158 -0
  35. package/dist/scaAnalysis/go/goReadDepFile.js +23 -0
  36. package/dist/scaAnalysis/java/analysis.js +105 -0
  37. package/dist/scaAnalysis/java/index.js +18 -0
  38. package/dist/scaAnalysis/java/javaBuildDepsParser.js +339 -0
  39. package/dist/scaAnalysis/python/analysis.js +41 -0
  40. package/dist/scaAnalysis/python/index.js +10 -0
  41. package/dist/scaAnalysis/ruby/analysis.js +226 -0
  42. package/dist/scaAnalysis/ruby/index.js +10 -0
  43. package/dist/scan/autoDetection.js +50 -1
  44. package/dist/scan/fileUtils.js +80 -1
  45. package/dist/scan/formatScanOutput.js +213 -0
  46. package/dist/scan/help.js +3 -1
  47. package/dist/scan/models/groupedResultsModel.js +2 -1
  48. package/dist/scan/models/scanResultsModel.js +3 -1
  49. package/dist/scan/populateProjectIdAndProjectName.js +2 -1
  50. package/dist/scan/scan.js +6 -99
  51. package/dist/scan/scanConfig.js +6 -1
  52. package/dist/scan/scanController.js +26 -7
  53. package/dist/scan/scanResults.js +20 -20
  54. package/dist/utils/commonApi.js +4 -1
  55. package/dist/utils/oraWrapper.js +5 -1
  56. package/package.json +12 -7
  57. package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
  58. package/src/audit/catalogueApplication/catalogueApplication.js +3 -16
  59. package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +11 -8
  60. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
  61. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
  62. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
  63. package/src/audit/languageAnalysisEngine/languageAnalysisFactory.js +17 -5
  64. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +76 -3
  65. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +122 -40
  66. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +3 -3
  67. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +15 -11
  68. package/src/audit/languageAnalysisEngine/report/models/reportOutputModel.ts +29 -0
  69. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +12 -3
  70. package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +16 -0
  71. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +3 -3
  72. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +87 -65
  73. package/src/audit/languageAnalysisEngine/sendSnapshot.js +78 -25
  74. package/src/commands/audit/auditConfig.ts +12 -3
  75. package/src/commands/audit/auditController.ts +9 -3
  76. package/src/commands/audit/processAudit.ts +4 -1
  77. package/src/commands/scan/processScan.js +10 -4
  78. package/src/commands/scan/sca/scaAnalysis.js +83 -0
  79. package/src/common/HTTPClient.js +65 -25
  80. package/src/common/errorHandling.ts +14 -22
  81. package/src/common/versionChecker.ts +1 -1
  82. package/src/constants/constants.js +24 -2
  83. package/src/constants/locales.js +33 -50
  84. package/src/constants.js +22 -0
  85. package/src/lambda/analytics.ts +9 -0
  86. package/src/lambda/arn.ts +2 -1
  87. package/src/lambda/lambda.ts +37 -17
  88. package/src/lambda/types.ts +35 -0
  89. package/src/lambda/utils.ts +2 -7
  90. package/src/scaAnalysis/common/formatMessage.js +38 -0
  91. package/src/scaAnalysis/common/treeUpload.js +30 -0
  92. package/src/scaAnalysis/go/goAnalysis.js +19 -0
  93. package/src/scaAnalysis/go/goParseDeps.js +203 -0
  94. package/src/scaAnalysis/go/goReadDepFile.js +32 -0
  95. package/src/scaAnalysis/java/analysis.js +142 -0
  96. package/src/scaAnalysis/java/index.js +21 -0
  97. package/src/scaAnalysis/java/javaBuildDepsParser.js +404 -0
  98. package/src/scaAnalysis/python/analysis.js +48 -0
  99. package/src/scaAnalysis/python/index.js +11 -0
  100. package/src/scaAnalysis/ruby/analysis.js +282 -0
  101. package/src/scaAnalysis/ruby/index.js +11 -0
  102. package/src/scan/autoDetection.js +58 -1
  103. package/src/scan/fileUtils.js +99 -1
  104. package/src/scan/formatScanOutput.ts +249 -0
  105. package/src/scan/help.js +3 -1
  106. package/src/scan/models/groupedResultsModel.ts +7 -5
  107. package/src/scan/models/resultContentModel.ts +2 -2
  108. package/src/scan/models/scanResultsModel.ts +5 -2
  109. package/src/scan/populateProjectIdAndProjectName.js +3 -1
  110. package/src/scan/scan.ts +8 -136
  111. package/src/scan/scanConfig.js +5 -1
  112. package/src/scan/scanController.js +30 -10
  113. package/src/scan/scanResults.js +31 -18
  114. package/src/utils/commonApi.js +4 -1
  115. package/src/utils/oraWrapper.js +6 -1
@@ -4,6 +4,7 @@ const requestUtils = require('../../src/utils/requestUtils');
4
4
  const oraFunctions = require('../utils/oraWrapper');
5
5
  const _ = require('lodash');
6
6
  const i18n = require('i18n');
7
+ const oraWrapper = require('../utils/oraWrapper');
7
8
  const getScanId = async (config, codeArtifactId, client) => {
8
9
  return client
9
10
  .getScanId(config, codeArtifactId)
@@ -25,9 +26,13 @@ const pollScanResults = async (config, scanId, client) => {
25
26
  console.log(err);
26
27
  });
27
28
  };
28
- const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinner) => {
29
+ const returnScanResults = async (config, codeArtifactId, newProject, timeout, startScanSpinner) => {
29
30
  const client = commonApi.getHttpClient(config);
30
31
  let scanId = await getScanId(config, codeArtifactId, client);
32
+ if (process.env.CODESEC_INVOCATION_ENVIRONMENT &&
33
+ process.env.CODESEC_INVOCATION_ENVIRONMENT.toUpperCase() === 'GITHUB') {
34
+ await client.createNewEvent(config, scanId, newProject);
35
+ }
31
36
  let startTime = new Date();
32
37
  let complete = false;
33
38
  if (!_.isNil(scanId)) {
@@ -40,12 +45,16 @@ const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinn
40
45
  }
41
46
  if (result.body.status === 'FAILED') {
42
47
  complete = true;
43
- oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.');
44
- console.log(result.body.errorMessage);
45
- if (result.body.errorMessage ===
48
+ if (config.debug) {
49
+ oraFunctions.failSpinner(startScanSpinner, i18n.__('scanNotCompleted', 'https://docs.contrastsecurity.com/en/binary-package-preparation.html'));
50
+ }
51
+ if (result?.body?.errorMessage ===
46
52
  'Unable to determine language for code artifact') {
53
+ console.log(result.body.errorMessage);
47
54
  console.log('Try scanning again using --language param. ', i18n.__('scanOptionsLanguageSummary'));
48
55
  }
56
+ oraWrapper.stopSpinner(startScanSpinner);
57
+ console.log('Contrast Scan Finished');
49
58
  process.exit(1);
50
59
  }
51
60
  }
@@ -64,30 +73,21 @@ const returnScanResultsInstances = async (config, scanId) => {
64
73
  try {
65
74
  result = await client.getScanResultsInstances(config, scanId);
66
75
  if (JSON.stringify(result.statusCode) == 200) {
67
- return result.body;
76
+ return { body: result.body, statusCode: result.statusCode };
68
77
  }
69
- }
70
- catch (e) {
71
- console.log(e.message.toString());
72
- }
73
- };
74
- const returnScanProjectById = async (config) => {
75
- const client = commonApi.getHttpClient(config);
76
- let result;
77
- try {
78
- result = await client.getScanProjectById(config);
79
- if (JSON.stringify(result.statusCode) == 200) {
80
- return result.body;
78
+ if (JSON.stringify(result.statusCode) == 503) {
79
+ return { statusCode: result.statusCode };
81
80
  }
82
81
  }
83
82
  catch (e) {
84
- console.log(e.message.toString());
83
+ if (config.debug) {
84
+ console.log(e.message.toString());
85
+ }
85
86
  }
86
87
  };
87
88
  module.exports = {
88
89
  getScanId: getScanId,
89
90
  returnScanResults: returnScanResults,
90
91
  pollScanResults: pollScanResults,
91
- returnScanResultsInstances: returnScanResultsInstances,
92
- returnScanProjectById: returnScanProjectById
92
+ returnScanResultsInstances: returnScanResultsInstances
93
93
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  const HttpClient = require('./../common/HTTPClient');
3
- const { badRequestError, unauthenticatedError, forbiddenError, proxyError, genericError } = require('../common/errorHandling');
3
+ const { badRequestError, unauthenticatedError, forbiddenError, proxyError, genericError, maxAppError } = require('../common/errorHandling');
4
4
  const handleResponseErrors = (res, api) => {
5
5
  if (res.statusCode === 400) {
6
6
  api === 'catalogue' ? badRequestError(true) : badRequestError(false);
@@ -14,6 +14,9 @@ const handleResponseErrors = (res, api) => {
14
14
  else if (res.statusCode === 407) {
15
15
  proxyError();
16
16
  }
17
+ else if (res.statusCode === 412) {
18
+ maxAppError();
19
+ }
17
20
  else {
18
21
  genericError();
19
22
  }
@@ -6,6 +6,9 @@ const returnOra = text => {
6
6
  const startSpinner = spinner => {
7
7
  spinner.start();
8
8
  };
9
+ const stopSpinner = spinner => {
10
+ spinner.stop();
11
+ };
9
12
  const succeedSpinner = (spinner, text) => {
10
13
  spinner.succeed(text);
11
14
  };
@@ -16,5 +19,6 @@ module.exports = {
16
19
  returnOra,
17
20
  startSpinner,
18
21
  succeedSpinner,
19
- failSpinner
22
+ failSpinner,
23
+ stopSpinner
20
24
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -24,8 +24,8 @@
24
24
  "test-int": "jest ./test-integration/",
25
25
  "test-int-scan": "jest ./test-integration/scan",
26
26
  "test-int-audit": "jest ./test-integration/audit",
27
- "format": "prettier --write \"**/*.{ts,tsx,js,css,scss,json,md,yml}\" .eslintrc.* .babelrc",
28
- "check-format": "prettier --check \"**/*.{ts,tsx,js,css,scss,json,md,yml}\" .eslintrc.* .babelrc",
27
+ "format": "prettier --write \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
28
+ "check-format": "prettier --check \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
29
29
  "coverage-local": "nyc --reporter=text mocha './test/**/*.spec.js'",
30
30
  "coverage": "yarn test --coverage",
31
31
  "lint": "eslint --config .eslintrc.json . --ext .ts",
@@ -45,6 +45,7 @@
45
45
  "bluebird": "^3.7.2",
46
46
  "boxen": "5.1.2",
47
47
  "chalk": "4.1.2",
48
+ "cli-table3": "^0.6.2",
48
49
  "command-line-args": "^5.2.1",
49
50
  "command-line-usage": "^6.1.3",
50
51
  "conf": "^10.1.2",
@@ -73,12 +74,13 @@
73
74
  "@types/i18n": "^0.13.2",
74
75
  "@types/jest": "^27.4.1",
75
76
  "@types/lodash": "^4.14.182",
77
+ "@types/node": "*",
76
78
  "@typescript-eslint/eslint-plugin": "^5.21.0",
77
79
  "@typescript-eslint/parser": "^5.21.0",
78
80
  "csv-writer": "^1.6.0",
79
81
  "eslint": "^8.14.0",
80
82
  "eslint-config-prettier": "^8.5.0",
81
- "eslint-plugin-prettier": "^4.0.0",
83
+ "eslint-plugin-prettier": "^4.2.1",
82
84
  "husky": "^3.1.0",
83
85
  "jest": "^27.5.1",
84
86
  "jest-junit": "^13.2.0",
@@ -86,7 +88,7 @@
86
88
  "npm-license-crawler": "^0.2.1",
87
89
  "nyc": "^15.1.0",
88
90
  "pkg": "^5.6.0",
89
- "prettier": "^1.19.1",
91
+ "prettier": "^2.7.1",
90
92
  "tmp": "^0.2.1",
91
93
  "ts-jest": "^27.1.4",
92
94
  "ts-node": "^10.7.0",
@@ -102,12 +104,15 @@
102
104
  ],
103
105
  "prettier": {
104
106
  "semi": false,
107
+ "trailingComma": "none",
108
+ "arrowParens": "avoid",
109
+ "bracketSpacing": true,
105
110
  "singleQuote": true,
111
+ "bracketSameLine": true,
106
112
  "overrides": [
107
113
  {
108
114
  "files": [
109
- ".eslintrc.ng",
110
- ".babelrc"
115
+ ".eslintrc"
111
116
  ],
112
117
  "options": {
113
118
  "parser": "json"
@@ -0,0 +1,40 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import i18n from 'i18n'
3
+ import {
4
+ reduceIdentifiedLanguages,
5
+ deduceLanguage
6
+ } from '../languageAnalysisEngine/reduceIdentifiedLanguages'
7
+
8
+ import { getProjectRootFilenames } from '../languageAnalysisEngine/getProjectRootFilenames'
9
+
10
+ export function identifyLanguages(config: any) {
11
+ const { projectPath } = config
12
+ const projectRootFilenames = getProjectRootFilenames(projectPath)
13
+
14
+ const identifiedLanguages = projectRootFilenames.reduce(
15
+ (accumulator: any, filename: string) => {
16
+ const deducedLanguages = deduceLanguage(filename)
17
+ return [...accumulator, ...deducedLanguages]
18
+ },
19
+ []
20
+ )
21
+
22
+ if (Object.keys(identifiedLanguages).length === 0) {
23
+ throw new Error(i18n.__('languageAnalysisNoLanguage', projectPath))
24
+ }
25
+
26
+ return reduceIdentifiedLanguages(identifiedLanguages)
27
+ }
28
+
29
+ export function determineProjectLanguage(
30
+ reducedLanguages: Record<string, string>
31
+ ) {
32
+ const reducedLanguagesKeys = Object.keys(reducedLanguages)
33
+ if (reducedLanguagesKeys.length === 1) {
34
+ return reducedLanguagesKeys[0]
35
+ } else {
36
+ throw new Error(
37
+ 'Detected multiple languages. Please specify a single language using --language'
38
+ )
39
+ }
40
+ }
@@ -1,21 +1,8 @@
1
1
  const i18n = require('i18n')
2
2
  const { getHttpClient, handleResponseErrors } = require('../../utils/commonApi')
3
3
 
4
- const locationOfApp = (config, appId) => {
5
- return `${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${appId}`
6
- }
7
-
8
- const displaySuccessMessage = (config, appId) => {
9
- console.log(
10
- '\n **************************' +
11
- i18n.__('successHeader') +
12
- '************************** \n'
13
- )
14
- console.log('\n' + i18n.__('catalogueSuccessCommand') + appId + '\n')
15
- console.log(locationOfApp(config, appId))
16
- console.log(
17
- '\n *********************************************************** \n'
18
- )
4
+ const displaySuccessMessage = () => {
5
+ console.log(i18n.__('catalogueSuccessCommand'))
19
6
  }
20
7
 
21
8
  const catalogueApplication = async config => {
@@ -25,7 +12,7 @@ const catalogueApplication = async config => {
25
12
  .catalogueCommand(config)
26
13
  .then(res => {
27
14
  if (res.statusCode === 201) {
28
- displaySuccessMessage(config, res.body.application.app_id)
15
+ //displaySuccessMessage(config, res.body.application.app_id)
29
16
  appId = res.body.application.app_id
30
17
  } else {
31
18
  handleResponseErrors(res, 'catalogue')
@@ -34,12 +34,13 @@ const formatKeyName = value => {
34
34
  return tempArr[0] + '/' + tempArr[1] + '@' + tempArr[versionIndex]
35
35
  }
36
36
 
37
- const shaveConsoleOutputUntilItFindsFirsDigraphMention = mvnDependancyTreeOutput => {
38
- //shaves of the console output until it reaches the first digraph
39
- return mvnDependancyTreeOutput.substring(
40
- mvnDependancyTreeOutput.indexOf('digraph')
41
- )
42
- }
37
+ const shaveConsoleOutputUntilItFindsFirsDigraphMention =
38
+ mvnDependancyTreeOutput => {
39
+ //shaves of the console output until it reaches the first digraph
40
+ return mvnDependancyTreeOutput.substring(
41
+ mvnDependancyTreeOutput.indexOf('digraph')
42
+ )
43
+ }
43
44
 
44
45
  const getDigraphObjInfo = editedOutput => {
45
46
  //turns the output into an array of digraph information
@@ -211,10 +212,12 @@ const parseMvn = mvnDependancyTreeOutput => {
211
212
  }
212
213
 
213
214
  // testing purposes
214
- exports.shaveConsoleOutputUntilItFindsFirsDigraphMention = shaveConsoleOutputUntilItFindsFirsDigraphMention
215
+ exports.shaveConsoleOutputUntilItFindsFirsDigraphMention =
216
+ shaveConsoleOutputUntilItFindsFirsDigraphMention
215
217
  exports.getDigraphObjInfo = getDigraphObjInfo
216
218
  exports.createDigraphObjKey = createDigraphObjKey
217
- exports.turnDigraphDependanciesIntoArrOfInnerDep = turnDigraphDependanciesIntoArrOfInnerDep
219
+ exports.turnDigraphDependanciesIntoArrOfInnerDep =
220
+ turnDigraphDependanciesIntoArrOfInnerDep
218
221
  exports.hasVersion = hasVersion
219
222
  exports.formatKeyName = formatKeyName
220
223
  exports.createOuterDependanciesAndType = createOuterDependanciesAndType
@@ -32,4 +32,5 @@ const checkForMultipleIdentifiedLanguages = identifiedLanguages => {
32
32
  }
33
33
 
34
34
  //For testing purposes
35
- exports.checkForMultipleIdentifiedLanguages = checkForMultipleIdentifiedLanguages
35
+ exports.checkForMultipleIdentifiedLanguages =
36
+ checkForMultipleIdentifiedLanguages
@@ -38,4 +38,5 @@ const checkForMultipleIdentifiedProjectFiles = identifiedLanguages => {
38
38
  }
39
39
 
40
40
  //For testing purposes
41
- exports.checkForMultipleIdentifiedProjectFiles = checkForMultipleIdentifiedProjectFiles
41
+ exports.checkForMultipleIdentifiedProjectFiles =
42
+ checkForMultipleIdentifiedProjectFiles
@@ -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
@@ -15,6 +15,13 @@ const fs = require('fs')
15
15
  const chalk = require('chalk')
16
16
  const saveFile = require('../../commands/audit/saveFile').default
17
17
  const generateSbom = require('../../sbom/generateSbom').default
18
+ const {
19
+ failSpinner,
20
+ returnOra,
21
+ startSpinner,
22
+ succeedSpinner
23
+ } = require('../../utils/oraWrapper')
24
+ const { pollForSnapshotCompletition } = require('./sendSnapshot')
18
25
 
19
26
  module.exports = exports = (err, analysis) => {
20
27
  const { identifiedLanguageInfo } = analysis.languageAnalysis
@@ -45,17 +52,22 @@ module.exports = exports = (err, analysis) => {
45
52
  return process.exit(5)
46
53
  }
47
54
 
48
- console.log('\n **************CONTRAST OSS ANALYSIS BEGINS**************')
55
+ const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
56
+ startSpinner(reportSpinner)
49
57
  const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId)
50
58
 
59
+ //poll for completion
60
+ const pollResult = await pollForSnapshotCompletition(
61
+ analysis.config,
62
+ snapshotResponse.id,
63
+ reportSpinner
64
+ )
65
+ succeedSpinner(reportSpinner, 'Contrast SCA analysis complete')
66
+
51
67
  await vulnerabilityReport(analysis, catalogueAppId, snapshotResponse.id)
52
68
 
53
69
  //should be moved to processAudit.ts once promises implemented
54
70
  await auditSave(config)
55
-
56
- console.log(
57
- '\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n'
58
- )
59
71
  }
60
72
 
61
73
  if (identifiedLanguageInfo.language === DOTNET) {
@@ -28,6 +28,79 @@ const isRubyLockFilename = filename => filename === 'Gemfile.lock'
28
28
  const isPipfileLockLockFilename = filename => filename === 'Pipfile.lock'
29
29
  const isGoProjectFilename = filename => filename === 'go.mod'
30
30
 
31
+ const deduceLanguageScaAnalysis = filenames => {
32
+ const deducedLanguages = []
33
+ let language = ''
34
+
35
+ filenames.forEach(filename => {
36
+ // Check for project filenames...
37
+ if (isJavaMavenProjectFilename(filename)) {
38
+ deducedLanguages.push(filename)
39
+ language = JAVA
40
+ }
41
+
42
+ if (isJavaGradleProjectFilename(filename)) {
43
+ deducedLanguages.push(filename)
44
+ language = JAVA
45
+ }
46
+
47
+ if (isNodeProjectFilename(filename)) {
48
+ deducedLanguages.push(filename)
49
+ language = NODE
50
+ }
51
+ //
52
+ // if (isDotNetProjectFilename(filename)) {
53
+ // deducedLanguages.push({language: DOTNET, projectFilename: filename})
54
+ // }
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
+ // }
69
+ //
70
+ // // Check for lock filenames...
71
+ // if (isDotNetLockFilename(filename)) {
72
+ // deducedLanguages.push({language: DOTNET, lockFilename: filename})
73
+ // }
74
+ //
75
+ if (isNodeLockFilename(filename)) {
76
+ deducedLanguages.push(filename)
77
+ language = NODE
78
+ }
79
+ //
80
+ // if (isRubyLockFilename(filename)) {
81
+ // deducedLanguages.push({language: RUBY, lockFilename: filename})
82
+ // }
83
+ //
84
+ // // this is pipfileLock rather than python lock as there can be different python locks
85
+ // if (isPipfileLockLockFilename(filename)) {
86
+ // deducedLanguages.push({language: PYTHON, lockFilename: filename})
87
+ // }
88
+ //
89
+ // if (isPhpLockFilename(filename)) {
90
+ // deducedLanguages.push({language: PHP, lockFilename: filename})
91
+ // }
92
+ //
93
+ // go does not have a lockfile, it should have a go.mod file containing the modules
94
+ if (isGoProjectFilename(filename)) {
95
+ deducedLanguages.push({ language: GO, projectFilename: filename })
96
+ language = GO
97
+ }
98
+ })
99
+ let identifiedLanguages = { [language]: deducedLanguages }
100
+
101
+ return identifiedLanguages
102
+ }
103
+
31
104
  const deduceLanguage = filename => {
32
105
  const deducedLanguages = []
33
106
 
@@ -136,9 +209,8 @@ module.exports = exports = (analysis, next) => {
136
209
 
137
210
  let language = config.language
138
211
  if (language === undefined) {
139
- languageAnalysis.identifiedLanguages = reduceIdentifiedLanguages(
140
- identifiedLanguages
141
- )
212
+ languageAnalysis.identifiedLanguages =
213
+ reduceIdentifiedLanguages(identifiedLanguages)
142
214
  } else {
143
215
  let refinedIdentifiedLanguages = []
144
216
  for (let x in identifiedLanguages) {
@@ -175,3 +247,4 @@ exports.isPhpProjectFilename = isPhpProjectFilename
175
247
  exports.isPhpLockFilename = isPhpLockFilename
176
248
  exports.deduceLanguage = deduceLanguage
177
249
  exports.reduceIdentifiedLanguages = reduceIdentifiedLanguages
250
+ exports.deduceLanguageScaAnalysis = deduceLanguageScaAnalysis
@@ -1,4 +1,3 @@
1
- import i18n from 'i18n'
2
1
  import { getHttpClient, handleResponseErrors } from '../../../utils/commonApi'
3
2
  import {
4
3
  ReportCompositeKey,
@@ -8,52 +7,42 @@ import {
8
7
  import { ReportSeverityModel } from './models/reportSeverityModel'
9
8
  import { orderBy } from 'lodash'
10
9
  import chalk from 'chalk'
11
- import { ReportLibraryModel } from './models/reportLibraryModel'
12
- import { findHighestSeverityCVE, findNameAndVersion } from './utils/reportUtils'
10
+ import { ReportCVEModel, ReportLibraryModel } from './models/reportLibraryModel'
13
11
  import {
14
- failSpinner,
15
- returnOra,
16
- startSpinner,
17
- succeedSpinner
18
- } from '../../../utils/oraWrapper'
12
+ findCVESeveritiesAndOrderByHighestPriority,
13
+ findHighestSeverityCVE,
14
+ findNameAndVersion,
15
+ severityCountAllCVEs
16
+ } from './utils/reportUtils'
17
+ import { SeverityCountModel } from './models/severityCountModel'
18
+ import {
19
+ ReportOutputBodyModel,
20
+ ReportOutputHeaderModel,
21
+ ReportOutputModel
22
+ } from './models/reportOutputModel'
19
23
 
20
24
  export const createLibraryHeader = (
21
25
  id: string,
22
26
  numberOfVulnerableLibraries: number,
23
- numberOfCves: number,
24
- name: string
27
+ numberOfCves: number
25
28
  ) => {
26
- name
27
- ? console.log(`\n Application Name: ${name} | Application ID: ${id}`)
28
- : console.log(` Application ID: ${id}`)
29
-
30
29
  numberOfVulnerableLibraries === 1
31
30
  ? console.log(
32
- '\n **************************' +
33
- ` Found 1 vulnerable library containing ${numberOfCves} CVE's` +
34
- '************************** '
31
+ ` Found 1 vulnerable library containing ${numberOfCves} CVE's`
35
32
  )
36
33
  : console.log(
37
- '\n **************************' +
38
- ` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's ` +
39
- '************************** '
34
+ ` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's `
40
35
  )
41
36
  }
42
37
 
43
38
  export const getReport = async (config: any, reportId: string) => {
44
39
  const client = getHttpClient(config)
45
-
46
- const reportSpinner = returnOra(i18n.__('auditReportWaiting'))
47
- reportSpinner.indent = 1
48
- startSpinner(reportSpinner)
49
40
  return client
50
41
  .getReportById(config, reportId)
51
42
  .then((res: { statusCode: number; body: any }) => {
52
43
  if (res.statusCode === 200) {
53
- succeedSpinner(reportSpinner, i18n.__('auditReportSuccessMessage'))
54
44
  return res.body
55
45
  } else {
56
- failSpinner(reportSpinner, i18n.__('auditReportFail'))
57
46
  console.log('config-------------------')
58
47
  console.log(config)
59
48
  console.log('reportId----------------')
@@ -100,28 +89,121 @@ export const printFormattedOutput = (
100
89
  report.reportOutputList.push(newOutputModel)
101
90
  }
102
91
 
103
- const orderedOutputList = orderBy(
92
+ const orderedOutputListLowestFirst = orderBy(
104
93
  report.reportOutputList,
105
- reportListItem => reportListItem.compositeKey.highestSeverity.priority
94
+ reportListItem => reportListItem.compositeKey.highestSeverity.priority,
95
+ ['desc']
106
96
  )
107
97
 
108
- for (const reportModel of orderedOutputList) {
109
- const name = reportModel.compositeKey.libraryName
110
- const version = reportModel.compositeKey.libraryVersion
111
- const highestSeverity = reportModel.compositeKey.highestSeverity.severity
112
-
98
+ let contrastHeaderNumCounter = 0
99
+ for (const reportModel of orderedOutputListLowestFirst) {
100
+ contrastHeaderNumCounter++
101
+ const { libraryName, libraryVersion, highestSeverity } =
102
+ reportModel.compositeKey
113
103
  const numOfCVEs = reportModel.cveArray.length
114
104
 
115
- const cveNames: string[] = []
105
+ const header = buildHeader(
106
+ highestSeverity,
107
+ contrastHeaderNumCounter,
108
+ libraryName,
109
+ libraryVersion,
110
+ numOfCVEs
111
+ )
116
112
 
117
- reportModel.cveArray.forEach(cve => cveNames.push(cve.name as string))
113
+ const body = buildBody(reportModel.cveArray)
118
114
 
119
- const boldHeader = chalk.bold(`${highestSeverity} | Vulnerable Library`)
120
- const cvePluralised = numOfCVEs > 1 ? 'CVEs' : 'CVE'
115
+ const reportOutputModel = new ReportOutputModel(header, body)
121
116
  console.log(
122
- `\n ${boldHeader} ${name} (${version}) has ${numOfCVEs} known ${cvePluralised}`
117
+ reportOutputModel.header.vulnMessage,
118
+ reportOutputModel.header.introducesMessage
119
+ )
120
+ console.log(reportOutputModel.body.issueMessage)
121
+ console.log(reportOutputModel.body.adviceMessage)
122
+ }
123
+ }
124
+
125
+ export function buildHeader(
126
+ highestSeverity: ReportSeverityModel,
127
+ contrastHeaderNum: number,
128
+ libraryName: string,
129
+ version: string,
130
+ numOfCVEs: number
131
+ ) {
132
+ const vulnerabilityPluralised =
133
+ numOfCVEs > 1 ? 'Vulnerabilities' : 'Vulnerability'
134
+ const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum)
135
+
136
+ const vulnMessage = chalk
137
+ .hex(highestSeverity.outputColour)
138
+ .bold(
139
+ `${formattedHeaderNum} - [${highestSeverity.severity}] ${libraryName}-${version}`
123
140
  )
124
- console.log(` ${cveNames.join(', ')}`)
125
- console.log(chalk.bold(' How to fix: Update to latest version'))
141
+
142
+ const introducesMessage = chalk.bold(
143
+ `introduces ${numOfCVEs} ${vulnerabilityPluralised}`
144
+ )
145
+
146
+ return new ReportOutputHeaderModel(vulnMessage, introducesMessage)
147
+ }
148
+
149
+ export function buildBody(cveArray: ReportCVEModel[]) {
150
+ const cveMessages: string[] = []
151
+
152
+ findCVESeveritiesAndOrderByHighestPriority(cveArray).forEach(
153
+ reportSeverityModel => {
154
+ // @ts-ignore
155
+ const { outputColour, severity, cveName } = reportSeverityModel
156
+
157
+ const severityShorthand = chalk
158
+ .hex(outputColour)
159
+ .bold(`[${severity.charAt(0).toUpperCase()}]`)
160
+
161
+ const builtMessage = `${severityShorthand} ${cveName}`
162
+ cveMessages.push(builtMessage)
163
+ }
164
+ )
165
+
166
+ const numAndSeverityType = getNumOfAndSeverityType(cveArray)
167
+
168
+ const issueMessage = ` ${chalk.bold(
169
+ 'Issue'
170
+ )} : ${numAndSeverityType} ${cveMessages.join(', ')}.`
171
+
172
+ const adviceMessage = ` ${chalk.bold('Advice')} : ${chalk.bold(
173
+ 'Update to latest version'
174
+ )}.`
175
+
176
+ return new ReportOutputBodyModel(issueMessage, adviceMessage)
177
+ }
178
+
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
126
188
  }
189
+
190
+ return `CONTRAST-${formattedHeaderNum}`
191
+ }
192
+
193
+ export function getNumOfAndSeverityType(cveArray: ReportCVEModel[]) {
194
+ const { critical, high, medium, low, note } = severityCountAllCVEs(
195
+ cveArray,
196
+ new SeverityCountModel()
197
+ )
198
+
199
+ const criticalMessage = critical > 0 ? `${critical} Critical` : ''
200
+ const highMessage = high > 0 ? `${high} High` : ''
201
+ const mediumMessage = medium > 0 ? `${medium} Medium` : ''
202
+ const lowMessage = low > 0 ? `${low} Low` : ''
203
+ const noteMessage = note > 0 ? `${note} Note` : ''
204
+
205
+ //removes/trims whitespace to single spaces
206
+ return `${criticalMessage} ${highMessage} ${mediumMessage} ${lowMessage} ${noteMessage}`
207
+ .replace(/\s+/g, ' ')
208
+ .trim()
127
209
  }