@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.
- package/.prettierignore +4 -0
- package/README.md +24 -16
- package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
- package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
- package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +30 -13
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +25 -0
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +51 -237
- package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
- package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +2 -14
- package/dist/commands/audit/auditConfig.js +8 -2
- package/dist/commands/audit/auditController.js +14 -5
- package/dist/commands/audit/saveFile.js +11 -0
- package/dist/commands/auth/auth.js +19 -1
- package/dist/commands/config/config.js +19 -8
- package/dist/commands/scan/processScan.js +13 -27
- package/dist/commands/scan/sca/scaAnalysis.js +44 -0
- package/dist/common/HTTPClient.js +29 -26
- package/dist/common/errorHandling.js +15 -39
- package/dist/common/versionChecker.js +32 -0
- package/dist/constants/constants.js +16 -2
- package/dist/constants/lambda.js +3 -1
- package/dist/constants/locales.js +58 -48
- package/dist/constants.js +59 -3
- package/dist/index.js +48 -30
- package/dist/lambda/help.js +22 -14
- package/dist/lambda/lambda.js +6 -0
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scaAnalysis/common/formatMessage.js +11 -0
- package/dist/scaAnalysis/common/treeUpload.js +30 -0
- package/dist/scaAnalysis/java/analysis.js +116 -0
- package/dist/scaAnalysis/java/index.js +18 -0
- package/dist/scaAnalysis/java/javaBuildDepsParser.js +326 -0
- package/dist/scan/autoDetection.js +46 -1
- package/dist/scan/fileUtils.js +73 -1
- package/dist/scan/formatScanOutput.js +212 -0
- package/dist/scan/help.js +6 -2
- package/dist/scan/models/groupedResultsModel.js +11 -0
- package/dist/scan/models/resultContentModel.js +2 -0
- package/dist/scan/models/scanResultsModel.js +11 -0
- package/dist/scan/populateProjectIdAndProjectName.js +1 -0
- package/dist/scan/saveResults.js +9 -10
- package/dist/scan/scan.js +26 -101
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +8 -4
- package/dist/scan/scanResults.js +8 -17
- package/dist/utils/getConfig.js +3 -0
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +3 -2
- package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
- package/src/audit/catalogueApplication/catalogueApplication.js +4 -16
- package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +41 -19
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +71 -0
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +105 -0
- package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
- package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
- package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
- package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
- package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +2 -22
- package/src/commands/audit/auditConfig.ts +12 -3
- package/src/commands/audit/auditController.ts +21 -5
- package/src/commands/audit/processAudit.ts +3 -1
- package/src/commands/audit/saveFile.ts +6 -0
- package/src/commands/auth/auth.js +25 -1
- package/src/commands/config/config.js +22 -8
- package/src/commands/scan/processScan.js +15 -31
- package/src/commands/scan/sca/scaAnalysis.js +73 -0
- package/src/common/HTTPClient.js +42 -36
- package/src/common/errorHandling.ts +17 -48
- package/src/common/versionChecker.ts +41 -0
- package/src/constants/constants.js +17 -4
- package/src/constants/lambda.js +3 -1
- package/src/constants/locales.js +69 -63
- package/src/constants.js +66 -3
- package/src/index.ts +62 -36
- package/src/lambda/help.ts +22 -14
- package/src/lambda/lambda.ts +8 -0
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scaAnalysis/common/formatMessage.js +10 -0
- package/src/scaAnalysis/common/treeUpload.js +34 -0
- package/src/scaAnalysis/java/analysis.js +159 -0
- package/src/scaAnalysis/java/index.js +21 -0
- package/src/scaAnalysis/java/javaBuildDepsParser.js +391 -0
- package/src/scan/autoDetection.js +54 -1
- package/src/scan/fileUtils.js +91 -1
- package/src/scan/formatScanOutput.ts +241 -0
- package/src/scan/help.js +6 -2
- package/src/scan/models/groupedResultsModel.ts +20 -0
- package/src/scan/models/resultContentModel.ts +86 -0
- package/src/scan/models/scanResultsModel.ts +52 -0
- package/src/scan/populateProjectIdAndProjectName.js +1 -0
- package/src/scan/saveResults.js +8 -9
- package/src/scan/scan.ts +62 -0
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +12 -4
- package/src/scan/scanResults.js +19 -17
- package/src/utils/getConfig.ts +12 -0
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
- package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
- package/dist/common/findLatestCLIVersion.js +0 -23
- package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
- package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
- package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
- package/src/common/findLatestCLIVersion.ts +0 -27
- 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(
|
|
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
|
-
|
|
47
|
-
return {
|
|
50
|
+
console.log(`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
51
|
+
return { scanDetail, scanResultsInstances };
|
|
48
52
|
}
|
|
49
53
|
};
|
|
50
54
|
module.exports = {
|
package/dist/scan/scanResults.js
CHANGED
|
@@ -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, '
|
|
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
|
};
|
package/dist/utils/getConfig.js
CHANGED
|
@@ -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 (
|
|
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.
|
|
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
|
|
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
|
|
5
|
-
|
|
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
|
})
|
package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js}
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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,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
|
+
}
|