@contrast/contrast 1.0.2 → 1.0.5

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 (113) hide show
  1. package/.prettierignore +4 -0
  2. package/README.md +24 -16
  3. package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
  4. package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
  5. package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +30 -13
  6. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +25 -0
  7. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +51 -237
  8. package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
  9. package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
  10. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
  11. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
  12. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
  13. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +2 -14
  14. package/dist/commands/audit/auditConfig.js +8 -2
  15. package/dist/commands/audit/auditController.js +14 -5
  16. package/dist/commands/audit/saveFile.js +11 -0
  17. package/dist/commands/auth/auth.js +19 -1
  18. package/dist/commands/config/config.js +19 -8
  19. package/dist/commands/scan/processScan.js +13 -27
  20. package/dist/commands/scan/sca/scaAnalysis.js +44 -0
  21. package/dist/common/HTTPClient.js +29 -26
  22. package/dist/common/errorHandling.js +15 -39
  23. package/dist/common/versionChecker.js +32 -0
  24. package/dist/constants/constants.js +16 -2
  25. package/dist/constants/lambda.js +3 -1
  26. package/dist/constants/locales.js +58 -48
  27. package/dist/constants.js +59 -3
  28. package/dist/index.js +48 -30
  29. package/dist/lambda/help.js +22 -14
  30. package/dist/lambda/lambda.js +6 -0
  31. package/dist/sbom/generateSbom.js +20 -0
  32. package/dist/scaAnalysis/common/formatMessage.js +11 -0
  33. package/dist/scaAnalysis/common/treeUpload.js +30 -0
  34. package/dist/scaAnalysis/java/analysis.js +116 -0
  35. package/dist/scaAnalysis/java/index.js +18 -0
  36. package/dist/scaAnalysis/java/javaBuildDepsParser.js +326 -0
  37. package/dist/scan/autoDetection.js +46 -1
  38. package/dist/scan/fileUtils.js +73 -1
  39. package/dist/scan/formatScanOutput.js +212 -0
  40. package/dist/scan/help.js +6 -2
  41. package/dist/scan/models/groupedResultsModel.js +11 -0
  42. package/dist/scan/models/resultContentModel.js +2 -0
  43. package/dist/scan/models/scanResultsModel.js +11 -0
  44. package/dist/scan/populateProjectIdAndProjectName.js +1 -0
  45. package/dist/scan/saveResults.js +9 -10
  46. package/dist/scan/scan.js +26 -101
  47. package/dist/scan/scanConfig.js +20 -1
  48. package/dist/scan/scanController.js +8 -4
  49. package/dist/scan/scanResults.js +8 -17
  50. package/dist/utils/getConfig.js +3 -0
  51. package/dist/utils/requestUtils.js +1 -1
  52. package/dist/utils/saveFile.js +19 -0
  53. package/package.json +3 -2
  54. package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
  55. package/src/audit/catalogueApplication/catalogueApplication.js +4 -16
  56. package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +41 -19
  57. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +71 -0
  58. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +105 -0
  59. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
  60. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
  61. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
  62. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
  63. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
  64. package/src/audit/languageAnalysisEngine/sendSnapshot.js +2 -22
  65. package/src/commands/audit/auditConfig.ts +12 -3
  66. package/src/commands/audit/auditController.ts +21 -5
  67. package/src/commands/audit/processAudit.ts +3 -1
  68. package/src/commands/audit/saveFile.ts +6 -0
  69. package/src/commands/auth/auth.js +25 -1
  70. package/src/commands/config/config.js +22 -8
  71. package/src/commands/scan/processScan.js +15 -31
  72. package/src/commands/scan/sca/scaAnalysis.js +73 -0
  73. package/src/common/HTTPClient.js +42 -36
  74. package/src/common/errorHandling.ts +17 -48
  75. package/src/common/versionChecker.ts +41 -0
  76. package/src/constants/constants.js +17 -4
  77. package/src/constants/lambda.js +3 -1
  78. package/src/constants/locales.js +69 -63
  79. package/src/constants.js +66 -3
  80. package/src/index.ts +62 -36
  81. package/src/lambda/help.ts +22 -14
  82. package/src/lambda/lambda.ts +8 -0
  83. package/src/sbom/generateSbom.ts +17 -0
  84. package/src/scaAnalysis/common/formatMessage.js +10 -0
  85. package/src/scaAnalysis/common/treeUpload.js +34 -0
  86. package/src/scaAnalysis/java/analysis.js +159 -0
  87. package/src/scaAnalysis/java/index.js +21 -0
  88. package/src/scaAnalysis/java/javaBuildDepsParser.js +391 -0
  89. package/src/scan/autoDetection.js +54 -1
  90. package/src/scan/fileUtils.js +91 -1
  91. package/src/scan/formatScanOutput.ts +241 -0
  92. package/src/scan/help.js +6 -2
  93. package/src/scan/models/groupedResultsModel.ts +20 -0
  94. package/src/scan/models/resultContentModel.ts +86 -0
  95. package/src/scan/models/scanResultsModel.ts +52 -0
  96. package/src/scan/populateProjectIdAndProjectName.js +1 -0
  97. package/src/scan/saveResults.js +8 -9
  98. package/src/scan/scan.ts +62 -0
  99. package/src/scan/scanConfig.js +26 -1
  100. package/src/scan/scanController.js +12 -4
  101. package/src/scan/scanResults.js +19 -17
  102. package/src/utils/getConfig.ts +12 -0
  103. package/src/utils/requestUtils.js +1 -1
  104. package/src/utils/saveFile.js +19 -0
  105. package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
  106. package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
  107. package/dist/common/findLatestCLIVersion.js +0 -23
  108. package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
  109. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
  110. package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
  111. package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
  112. package/src/common/findLatestCLIVersion.ts +0 -27
  113. package/src/scan/scan.js +0 -167
@@ -6,6 +6,7 @@ const scan = require('./scan');
6
6
  const scanResults = require('./scanResults');
7
7
  const autoDetection = require('./autoDetection');
8
8
  const fileFunctions = require('./fileUtils');
9
+ const { performance } = require('perf_hooks');
9
10
  const getTimeout = config => {
10
11
  if (config.timeout) {
11
12
  return config.timeout;
@@ -21,7 +22,7 @@ const fileAndLanguageLogic = async (configToUse) => {
21
22
  if (configToUse.file) {
22
23
  if (!fileFunctions.fileExists(configToUse.file)) {
23
24
  console.log(i18n.__('fileNotExist'));
24
- process.exit(0);
25
+ process.exit(1);
25
26
  }
26
27
  return configToUse;
27
28
  }
@@ -32,19 +33,22 @@ const fileAndLanguageLogic = async (configToUse) => {
32
33
  }
33
34
  };
34
35
  const startScan = async (configToUse) => {
36
+ const startTime = performance.now();
35
37
  await fileAndLanguageLogic(configToUse);
36
38
  if (!configToUse.projectId) {
37
39
  configToUse.projectId = await populateProjectIdAndProjectName.populateProjectId(configToUse);
38
40
  }
39
41
  const codeArtifactId = await scan.sendScan(configToUse);
40
42
  if (!configToUse.ff) {
41
- const startScanSpinner = returnOra('Contrast Scan started');
43
+ const startScanSpinner = returnOra('🚀 Contrast Scan started');
42
44
  startSpinner(startScanSpinner);
43
45
  const scanDetail = await scanResults.returnScanResults(configToUse, codeArtifactId, getTimeout(configToUse), startScanSpinner);
44
46
  const scanResultsInstances = await scanResults.returnScanResultsInstances(configToUse, scanDetail.id);
47
+ const endTime = performance.now();
48
+ const scanDurationMs = endTime - startTime;
45
49
  succeedSpinner(startScanSpinner, 'Contrast Scan complete');
46
- const projectOverview = await scanResults.returnScanProjectById(configToUse);
47
- return { projectOverview, scanDetail, scanResultsInstances };
50
+ console.log(`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
51
+ return { scanDetail, scanResultsInstances };
48
52
  }
49
53
  };
50
54
  module.exports = {
@@ -3,6 +3,7 @@ const commonApi = require('../utils/commonApi');
3
3
  const requestUtils = require('../../src/utils/requestUtils');
4
4
  const oraFunctions = require('../utils/oraWrapper');
5
5
  const _ = require('lodash');
6
+ const i18n = require('i18n');
6
7
  const getScanId = async (config, codeArtifactId, client) => {
7
8
  return client
8
9
  .getScanId(config, codeArtifactId)
@@ -39,8 +40,12 @@ const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinn
39
40
  }
40
41
  if (result.body.status === 'FAILED') {
41
42
  complete = true;
42
- oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.');
43
- console.log(result.body.errorMessage);
43
+ oraFunctions.failSpinner(startScanSpinner, i18n.__('scanNotCompleted', 'https://docs.contrastsecurity.com/en/binary-package-preparation.html'));
44
+ result.body.errorMessage ? console.log(result.body.errorMessage) : '';
45
+ if (result.body.errorMessage ===
46
+ 'Unable to determine language for code artifact') {
47
+ console.log('Try scanning again using --language param. ', i18n.__('scanOptionsLanguageSummary'));
48
+ }
44
49
  process.exit(1);
45
50
  }
46
51
  }
@@ -66,23 +71,9 @@ const returnScanResultsInstances = async (config, scanId) => {
66
71
  console.log(e.message.toString());
67
72
  }
68
73
  };
69
- const returnScanProjectById = async (config) => {
70
- const client = commonApi.getHttpClient(config);
71
- let result;
72
- try {
73
- result = await client.getScanProjectById(config);
74
- if (JSON.stringify(result.statusCode) == 200) {
75
- return result.body;
76
- }
77
- }
78
- catch (e) {
79
- console.log(e.message.toString());
80
- }
81
- };
82
74
  module.exports = {
83
75
  getScanId: getScanId,
84
76
  returnScanResults: returnScanResults,
85
77
  pollScanResults: pollScanResults,
86
- returnScanResultsInstances: returnScanResultsInstances,
87
- returnScanProjectById: returnScanProjectById
78
+ returnScanResultsInstances: returnScanResultsInstances
88
79
  };
@@ -10,6 +10,9 @@ const localConfig = (name, version) => {
10
10
  configName: name
11
11
  });
12
12
  config.set('version', version);
13
+ if (process.env.CONTRAST_CODSEC_DISABLE_UPDATE_MESSAGE) {
14
+ config.set('updateMessageHidden', JSON.parse(process.env.CONTRAST_CODSEC_DISABLE_UPDATE_MESSAGE.toLowerCase()));
15
+ }
13
16
  if (!config.has('host')) {
14
17
  config.set('host', 'https://ce.contrastsecurity.com/');
15
18
  }
@@ -6,7 +6,7 @@ function sendRequest({ options, method = 'put' }) {
6
6
  return request[`${method}Async`](options.url, options);
7
7
  }
8
8
  const millisToSeconds = millis => {
9
- return ((millis % 60000) / 1000).toFixed(0);
9
+ return (millis / 1000).toFixed(0);
10
10
  };
11
11
  const sleep = ms => {
12
12
  return new Promise(resolve => setTimeout(resolve, ms));
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ const { SARIF_FILE } = require('../constants/constants');
3
+ const commonApi = require('./commonApi');
4
+ const saveResults = require('../scan/saveResults');
5
+ const i18n = require('i18n');
6
+ const saveScanFile = async (config, scanResults) => {
7
+ if (config.save === null || config.save.toUpperCase() === SARIF_FILE) {
8
+ const scanId = scanResults.scanDetail.id;
9
+ const client = commonApi.getHttpClient(config);
10
+ const rawResults = await client.getSpecificScanResultSarif(config, scanId);
11
+ await saveResults.writeResultsToFile(rawResults?.body);
12
+ }
13
+ else {
14
+ console.log(i18n.__('scanNoFiletypeSpecifiedForSave'));
15
+ }
16
+ };
17
+ module.exports = {
18
+ saveScanFile: saveScanFile
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -35,7 +35,7 @@
35
35
  "dev": "npx ts-node src/index.ts"
36
36
  },
37
37
  "engines": {
38
- "node": ">=16.13.2 <17"
38
+ "node": ">=16"
39
39
  },
40
40
  "dependencies": {
41
41
  "@aws-sdk/client-iam": "^3.78.0",
@@ -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",
@@ -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,9 +12,10 @@ 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 {
18
+ // console.log(res.statusCode)
31
19
  handleResponseErrors(res, 'catalogue')
32
20
  }
33
21
  })
@@ -10,9 +10,17 @@ const pythonAE = require('../pythonAnalysisEngine')
10
10
  const phpAE = require('../phpAnalysisEngine')
11
11
  const goAE = require('../goAnalysisEngine')
12
12
  const { vulnerabilityReport } = require('./report/reportingFeature')
13
- const { vulnReportWithoutDevDep } = require('./report/newReportingFeature')
14
- const { checkDevDeps } = require('./report/checkIgnoreDevDep')
15
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
+ const {
19
+ failSpinner,
20
+ returnOra,
21
+ startSpinner,
22
+ succeedSpinner
23
+ } = require('../../utils/oraWrapper')
16
24
 
17
25
  module.exports = exports = (err, analysis) => {
18
26
  const { identifiedLanguageInfo } = analysis.languageAnalysis
@@ -43,25 +51,15 @@ module.exports = exports = (err, analysis) => {
43
51
  return process.exit(5)
44
52
  }
45
53
 
46
- console.log('\n **************CONTRAST OSS ANALYSIS BEGINS**************')
54
+ const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
55
+ startSpinner(reportSpinner)
47
56
  const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId)
57
+ succeedSpinner(reportSpinner, 'Contrast SCA analysis complete')
48
58
 
49
- if (config.report) {
50
- const ignoreDevUrl = await checkDevDeps(config)
51
- if (ignoreDevUrl) {
52
- await vulnReportWithoutDevDep(
53
- analysis,
54
- catalogueAppId,
55
- snapshotResponse.id,
56
- config
57
- )
58
- } else {
59
- await vulnerabilityReport(analysis, catalogueAppId, config)
60
- }
61
- }
62
- console.log(
63
- '\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n'
64
- )
59
+ await vulnerabilityReport(analysis, catalogueAppId, snapshotResponse.id)
60
+
61
+ //should be moved to processAudit.ts once promises implemented
62
+ await auditSave(config)
65
63
  }
66
64
 
67
65
  if (identifiedLanguageInfo.language === DOTNET) {
@@ -92,3 +90,27 @@ module.exports = exports = (err, analysis) => {
92
90
  goAE(identifiedLanguageInfo, analysis.config, langCallback)
93
91
  }
94
92
  }
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
+ }
@@ -28,6 +28,76 @@ 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({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
+ //
68
+ // // Check for lock filenames...
69
+ // if (isDotNetLockFilename(filename)) {
70
+ // deducedLanguages.push({language: DOTNET, lockFilename: filename})
71
+ // }
72
+ //
73
+ if (isNodeLockFilename(filename)) {
74
+ deducedLanguages.push(filename)
75
+ language = node
76
+ }
77
+ //
78
+ // if (isRubyLockFilename(filename)) {
79
+ // deducedLanguages.push({language: RUBY, lockFilename: filename})
80
+ // }
81
+ //
82
+ // // this is pipfileLock rather than python lock as there can be different python locks
83
+ // if (isPipfileLockLockFilename(filename)) {
84
+ // deducedLanguages.push({language: PYTHON, lockFilename: filename})
85
+ // }
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
+ // }
95
+ })
96
+ let identifiedLanguages = { [language]: deducedLanguages }
97
+
98
+ return identifiedLanguages
99
+ }
100
+
31
101
  const deduceLanguage = filename => {
32
102
  const deducedLanguages = []
33
103
 
@@ -175,3 +245,4 @@ exports.isPhpProjectFilename = isPhpProjectFilename
175
245
  exports.isPhpLockFilename = isPhpLockFilename
176
246
  exports.deduceLanguage = deduceLanguage
177
247
  exports.reduceIdentifiedLanguages = reduceIdentifiedLanguages
248
+ exports.deduceLanguageScaAnalysis = deduceLanguageScaAnalysis
@@ -0,0 +1,105 @@
1
+ import { getHttpClient, handleResponseErrors } from '../../../utils/commonApi'
2
+ import {
3
+ ReportCompositeKey,
4
+ ReportList,
5
+ ReportModelStructure
6
+ } from './models/reportListModel'
7
+ import { ReportSeverityModel } from './models/reportSeverityModel'
8
+ import { orderBy } from 'lodash'
9
+ import chalk from 'chalk'
10
+ import { ReportLibraryModel } from './models/reportLibraryModel'
11
+ import { findHighestSeverityCVE, findNameAndVersion } from './utils/reportUtils'
12
+
13
+ export const createLibraryHeader = (
14
+ id: string,
15
+ numberOfVulnerableLibraries: number,
16
+ numberOfCves: number
17
+ ) => {
18
+ numberOfVulnerableLibraries === 1
19
+ ? console.log(
20
+ ` Found 1 vulnerable library containing ${numberOfCves} CVE's`
21
+ )
22
+ : console.log(
23
+ ` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's `
24
+ )
25
+ }
26
+
27
+ export const getReport = async (config: any, reportId: string) => {
28
+ const client = getHttpClient(config)
29
+ return client
30
+ .getReportById(config, reportId)
31
+ .then((res: { statusCode: number; body: any }) => {
32
+ if (res.statusCode === 200) {
33
+ return res.body
34
+ } else {
35
+ console.log('config-------------------')
36
+ console.log(config)
37
+ console.log('reportId----------------')
38
+ console.log(reportId)
39
+ console.log(JSON.stringify(res))
40
+ handleResponseErrors(res, 'report')
41
+ }
42
+ })
43
+ .catch((err: any) => {
44
+ console.log(err)
45
+ })
46
+ }
47
+
48
+ export const printVulnerabilityResponse = (
49
+ vulnerabilities: ReportLibraryModel[],
50
+ config: any
51
+ ) => {
52
+ let hasSomeVulnerabilitiesReported = false
53
+ printFormattedOutput(vulnerabilities, config)
54
+ if (Object.keys(vulnerabilities).length > 0) {
55
+ hasSomeVulnerabilitiesReported = true
56
+ }
57
+ return hasSomeVulnerabilitiesReported
58
+ }
59
+
60
+ export const printFormattedOutput = (
61
+ libraries: ReportLibraryModel[],
62
+ config: any
63
+ ) => {
64
+ const report = new ReportList()
65
+
66
+ for (const library of libraries) {
67
+ const { name, version } = findNameAndVersion(library, config)
68
+
69
+ const newOutputModel = new ReportModelStructure(
70
+ new ReportCompositeKey(
71
+ name,
72
+ version,
73
+ findHighestSeverityCVE(library.cveArray) as ReportSeverityModel
74
+ ),
75
+ library.cveArray
76
+ )
77
+
78
+ report.reportOutputList.push(newOutputModel)
79
+ }
80
+
81
+ const orderedOutputList = orderBy(
82
+ report.reportOutputList,
83
+ reportListItem => reportListItem.compositeKey.highestSeverity.priority
84
+ )
85
+
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
+
91
+ const numOfCVEs = reportModel.cveArray.length
92
+
93
+ const cveNames: string[] = []
94
+
95
+ reportModel.cveArray.forEach(cve => cveNames.push(cve.name as string))
96
+
97
+ const boldHeader = chalk.bold(`${highestSeverity} | Vulnerable Library`)
98
+ const cvePluralised = numOfCVEs > 1 ? 'CVEs' : 'CVE'
99
+ console.log(
100
+ `\n ${boldHeader} ${name} (${version}) has ${numOfCVEs} known ${cvePluralised}`
101
+ )
102
+ console.log(` ${cveNames.join(', ')}`)
103
+ console.log(chalk.bold(' How to fix: Update to latest version'))
104
+ }
105
+ }
@@ -0,0 +1,30 @@
1
+ export class ReportLibraryModel {
2
+ name: string
3
+ cveArray: ReportCVEModel[]
4
+
5
+ constructor (name: string, cveArray: ReportCVEModel[]){
6
+ this.name = name
7
+ this.cveArray = cveArray
8
+ }
9
+ }
10
+
11
+ export class ReportCVEModel {
12
+ name?: string
13
+ description?: string
14
+ authentication?: string
15
+ references?: []
16
+ severityCode?: string
17
+ cvss3SeverityCode?: string
18
+
19
+ constructor (
20
+ name: string,
21
+ description: string,
22
+ severityCode: string,
23
+ cvss3SeverityCode: string
24
+ ){
25
+ this.name = name
26
+ this.description = description
27
+ this.severityCode = severityCode
28
+ this.cvss3SeverityCode = cvss3SeverityCode
29
+ }
30
+ }
@@ -0,0 +1,32 @@
1
+ import {ReportSeverityModel} from "./reportSeverityModel";
2
+ import {ReportCVEModel} from "./reportLibraryModel";
3
+
4
+ export class ReportList {
5
+ reportOutputList: ReportModelStructure[]
6
+
7
+ constructor (){
8
+ this.reportOutputList = []
9
+ }
10
+ }
11
+
12
+ export class ReportModelStructure {
13
+ compositeKey: ReportCompositeKey;
14
+ cveArray: ReportCVEModel[];
15
+
16
+ constructor (compositeKey: ReportCompositeKey, cveArray: ReportCVEModel[]){
17
+ this.compositeKey = compositeKey
18
+ this.cveArray = cveArray
19
+ }
20
+ }
21
+
22
+ export class ReportCompositeKey {
23
+ libraryName!: string;
24
+ libraryVersion!: string;
25
+ highestSeverity!: ReportSeverityModel;
26
+
27
+ constructor (libraryName: string, libraryVersion: string, highestSeverity: ReportSeverityModel){
28
+ this.libraryName = libraryName
29
+ this.libraryVersion = libraryVersion
30
+ this.highestSeverity = highestSeverity
31
+ }
32
+ }
@@ -0,0 +1,9 @@
1
+ export class ReportSeverityModel {
2
+ severity!: string
3
+ priority!: number
4
+
5
+ constructor(severity: string, priority: number) {
6
+ this.severity = severity
7
+ this.priority = priority
8
+ }
9
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ createLibraryHeader,
3
+ getReport,
4
+ printVulnerabilityResponse
5
+ } from './commonReportingFunctions'
6
+ import {
7
+ convertGenericToTypedLibraries,
8
+ severityCount
9
+ } from './utils/reportUtils'
10
+
11
+ export async function vulnerabilityReport(
12
+ analysis: any,
13
+ applicationId: string,
14
+ reportId: string
15
+ ) {
16
+ const reportResponse = await getReport(analysis.config, reportId)
17
+
18
+ if (reportResponse !== undefined) {
19
+ const id = applicationId
20
+ const name = analysis.config.applicationName
21
+ formatVulnerabilityOutput(
22
+ reportResponse.vulnerabilities,
23
+ id,
24
+ name,
25
+ analysis.config
26
+ )
27
+ }
28
+ }
29
+
30
+ export function formatVulnerabilityOutput(
31
+ libraryVulnerabilityResponse: any,
32
+ id: string,
33
+ name: string,
34
+ config: any
35
+ ) {
36
+ const vulnerableLibraries = convertGenericToTypedLibraries(
37
+ libraryVulnerabilityResponse
38
+ )
39
+
40
+ const numberOfVulnerableLibraries = vulnerableLibraries.length
41
+ let numberOfCves = 0
42
+ vulnerableLibraries.forEach(lib => (numberOfCves += lib.cveArray.length))
43
+
44
+ createLibraryHeader(id, numberOfVulnerableLibraries, numberOfCves)
45
+
46
+ const hasSomeVulnerabilitiesReported = printVulnerabilityResponse(
47
+ vulnerableLibraries,
48
+ config
49
+ )
50
+
51
+ return [
52
+ hasSomeVulnerabilitiesReported,
53
+ numberOfCves,
54
+ severityCount(vulnerableLibraries)
55
+ ]
56
+ }