@contrast/contrast 1.0.19 → 1.0.20
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/dist/audit/report/commonReportingFunctions.js +3 -4
- package/dist/audit/report/models/reportListModel.js +2 -1
- package/dist/audit/report/reportingFeature.js +1 -1
- package/dist/audit/report/utils/reportUtils.js +30 -11
- package/dist/commands/audit/auditConfig.js +1 -2
- package/dist/commands/scan/sca/scaAnalysis.js +4 -2
- package/dist/constants/constants.js +1 -1
- package/dist/constants/locales.js +6 -2
- package/dist/scaAnalysis/common/auditReport.js +16 -60
- package/dist/scaAnalysis/common/commonReportingFunctionsSca.js +154 -0
- package/dist/scaAnalysis/common/models/ScaReportModel.js +45 -0
- package/dist/scaAnalysis/common/scaServicesUpload.js +4 -3
- package/dist/scaAnalysis/common/utils/reportUtilsSca.js +76 -0
- package/dist/scaAnalysis/java/analysis.js +1 -28
- package/dist/scaAnalysis/java/index.js +1 -13
- package/dist/scan/formatScanOutput.js +19 -13
- package/dist/utils/paramsUtil/configStoreParams.js +1 -12
- package/dist/utils/paramsUtil/paramHandler.js +1 -7
- package/package.json +5 -1
- package/src/audit/report/commonReportingFunctions.js +7 -5
- package/src/audit/report/models/reportListModel.ts +12 -2
- package/src/audit/report/reportingFeature.ts +1 -1
- package/src/audit/report/utils/reportUtils.ts +4 -4
- package/src/commands/audit/auditConfig.js +1 -2
- package/src/commands/scan/sca/scaAnalysis.js +7 -2
- package/src/constants/constants.js +1 -1
- package/src/constants/locales.js +6 -2
- package/src/scaAnalysis/common/auditReport.js +25 -80
- package/src/scaAnalysis/common/commonReportingFunctionsSca.js +276 -0
- package/src/scaAnalysis/common/models/ScaReportModel.ts +81 -0
- package/src/scaAnalysis/common/scaServicesUpload.js +5 -3
- package/src/scaAnalysis/common/utils/reportUtilsSca.ts +123 -0
- package/src/scaAnalysis/java/analysis.js +1 -28
- package/src/scaAnalysis/java/index.js +1 -18
- package/src/scan/formatScanOutput.ts +28 -17
- package/src/utils/getConfig.ts +0 -1
- package/src/utils/paramsUtil/configStoreParams.js +1 -14
- package/src/utils/paramsUtil/paramHandler.js +1 -9
|
@@ -51,7 +51,7 @@ const printFormattedOutput = (config, libraries, numberOfVulnerableLibraries, nu
|
|
|
51
51
|
const report = new ReportList();
|
|
52
52
|
for (const library of libraries) {
|
|
53
53
|
const { name, version } = findNameAndVersion(library, config);
|
|
54
|
-
const newOutputModel = new ReportModelStructure(new ReportCompositeKey(name, version, findHighestSeverityCVE(library.cveArray), severityCountAllCVEs(library.cveArray, new SeverityCountModel()).getTotal), library.cveArray);
|
|
54
|
+
const newOutputModel = new ReportModelStructure(new ReportCompositeKey(name, version, findHighestSeverityCVE(library.cveArray), severityCountAllCVEs(library.cveArray, new SeverityCountModel()).getTotal), library.cveArray, null);
|
|
55
55
|
report.reportOutputList.push(newOutputModel);
|
|
56
56
|
}
|
|
57
57
|
const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(report.reportOutputList, [
|
|
@@ -120,8 +120,8 @@ function buildHeader(highestSeverity, contrastHeaderNum, libraryName, version, n
|
|
|
120
120
|
return new ReportOutputHeaderModel(vulnMessage, introducesMessage);
|
|
121
121
|
}
|
|
122
122
|
function buildBody(cveArray, advice) {
|
|
123
|
-
|
|
124
|
-
const issueMessage = getIssueRow(
|
|
123
|
+
const orderedCvesWithSeverityAssigned = orderByHighestPriority(cveArray.map(cve => findCVESeverity(cve)));
|
|
124
|
+
const issueMessage = getIssueRow(orderedCvesWithSeverityAssigned);
|
|
125
125
|
const minOrMax = advice.maximum ? advice.maximum : advice.minimum;
|
|
126
126
|
const displayAdvice = minOrMax
|
|
127
127
|
? `Change to version ${chalk.bold(minOrMax)}`
|
|
@@ -130,7 +130,6 @@ function buildBody(cveArray, advice) {
|
|
|
130
130
|
return new ReportOutputBodyModel(issueMessage, adviceMessage);
|
|
131
131
|
}
|
|
132
132
|
function getIssueRow(cveArray) {
|
|
133
|
-
orderByHighestPriority(cveArray);
|
|
134
133
|
const cveMessagesList = getIssueCveMsgList(cveArray);
|
|
135
134
|
return [chalk.bold('Issue'), ':', `${cveMessagesList.join(', ')}`];
|
|
136
135
|
}
|
|
@@ -8,9 +8,10 @@ class ReportList {
|
|
|
8
8
|
}
|
|
9
9
|
exports.ReportList = ReportList;
|
|
10
10
|
class ReportModelStructure {
|
|
11
|
-
constructor(compositeKey, cveArray) {
|
|
11
|
+
constructor(compositeKey, cveArray, remediationAdvice) {
|
|
12
12
|
this.compositeKey = compositeKey;
|
|
13
13
|
this.cveArray = cveArray;
|
|
14
|
+
this.remediationAdvice = remediationAdvice;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
exports.ReportModelStructure = ReportModelStructure;
|
|
@@ -80,7 +80,7 @@ async function vulnerabilityReportV2(config, reportId) {
|
|
|
80
80
|
console.log();
|
|
81
81
|
const reportResponse = await (0, commonReportingFunctions_1.getReport)(config, reportId);
|
|
82
82
|
if (reportResponse !== undefined) {
|
|
83
|
-
|
|
83
|
+
const output = formatVulnerabilityOutput(reportResponse.vulnerabilities, config.applicationId, config, reportResponse.remediationGuidance
|
|
84
84
|
? reportResponse.remediationGuidance
|
|
85
85
|
: {});
|
|
86
86
|
if (config.fail) {
|
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
4
24
|
};
|
|
5
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
26
|
exports.countVulnerableLibrariesBySeverity = exports.findNameAndVersion = exports.severityCountSingleCVE = exports.severityCountAllCVEs = exports.severityCountAllLibraries = exports.convertGenericToTypedLibraryVulns = exports.findCVESeverity = exports.orderByHighestPriority = exports.findHighestSeverityCVE = void 0;
|
|
7
27
|
const reportLibraryModel_1 = require("../models/reportLibraryModel");
|
|
8
28
|
const reportSeverityModel_1 = require("../models/reportSeverityModel");
|
|
9
|
-
const constants_1 =
|
|
10
|
-
const constants_2 = require("../../../constants/constants");
|
|
29
|
+
const constants_1 = __importStar(require("../../../constants/constants"));
|
|
11
30
|
const lodash_1 = require("lodash");
|
|
12
31
|
const severityCountModel_1 = require("../models/severityCountModel");
|
|
13
32
|
const { supportedLanguages: { GO } } = constants_1.default;
|
|
@@ -16,27 +35,27 @@ function findHighestSeverityCVE(cveArray) {
|
|
|
16
35
|
return (0, lodash_1.orderBy)(mappedToReportSeverityModels, cve => cve?.priority)[0];
|
|
17
36
|
}
|
|
18
37
|
exports.findHighestSeverityCVE = findHighestSeverityCVE;
|
|
19
|
-
function orderByHighestPriority(
|
|
20
|
-
return (0, lodash_1.orderBy)(
|
|
38
|
+
function orderByHighestPriority(severityModels) {
|
|
39
|
+
return (0, lodash_1.orderBy)(severityModels, ['priority'], ['asc']);
|
|
21
40
|
}
|
|
22
41
|
exports.orderByHighestPriority = orderByHighestPriority;
|
|
23
42
|
function findCVESeverity(cve) {
|
|
24
43
|
const cveName = cve.name;
|
|
25
44
|
if (cve.cvss3SeverityCode === 'CRITICAL' || cve.severityCode === 'CRITICAL') {
|
|
26
|
-
return new reportSeverityModel_1.ReportSeverityModel('CRITICAL',
|
|
45
|
+
return new reportSeverityModel_1.ReportSeverityModel('CRITICAL', constants_1.CRITICAL_PRIORITY, constants_1.CRITICAL_COLOUR, cveName);
|
|
27
46
|
}
|
|
28
47
|
else if (cve.cvss3SeverityCode === 'HIGH' || cve.severityCode === 'HIGH') {
|
|
29
|
-
return new reportSeverityModel_1.ReportSeverityModel('HIGH',
|
|
48
|
+
return new reportSeverityModel_1.ReportSeverityModel('HIGH', constants_1.HIGH_PRIORITY, constants_1.HIGH_COLOUR, cveName);
|
|
30
49
|
}
|
|
31
50
|
else if (cve.cvss3SeverityCode === 'MEDIUM' ||
|
|
32
51
|
cve.severityCode === 'MEDIUM') {
|
|
33
|
-
return new reportSeverityModel_1.ReportSeverityModel('MEDIUM',
|
|
52
|
+
return new reportSeverityModel_1.ReportSeverityModel('MEDIUM', constants_1.MEDIUM_PRIORITY, constants_1.MEDIUM_COLOUR, cveName);
|
|
34
53
|
}
|
|
35
54
|
else if (cve.cvss3SeverityCode === 'LOW' || cve.severityCode === 'LOW') {
|
|
36
|
-
return new reportSeverityModel_1.ReportSeverityModel('LOW',
|
|
55
|
+
return new reportSeverityModel_1.ReportSeverityModel('LOW', constants_1.LOW_PRIORITY, constants_1.LOW_COLOUR, cveName);
|
|
37
56
|
}
|
|
38
57
|
else if (cve.cvss3SeverityCode === 'NOTE' || cve.severityCode === 'NOTE') {
|
|
39
|
-
return new reportSeverityModel_1.ReportSeverityModel('NOTE',
|
|
58
|
+
return new reportSeverityModel_1.ReportSeverityModel('NOTE', constants_1.NOTE_PRIORITY, constants_1.NOTE_COLOUR, cveName);
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
exports.findCVESeverity = findCVESeverity;
|
|
@@ -5,8 +5,7 @@ const paramHandler = require('../../utils/paramsUtil/paramHandler');
|
|
|
5
5
|
const getAuditConfig = async (contrastConf, command, argv) => {
|
|
6
6
|
const auditParameters = await getCommandLineArgsCustom(contrastConf, command, argv, constants.commandLineDefinitions.auditOptionDefinitions);
|
|
7
7
|
const paramsAuth = paramHandler.getAuth(auditParameters);
|
|
8
|
-
|
|
9
|
-
return { ...paramsAuth, ...auditParameters, ...javaAgreement };
|
|
8
|
+
return { ...paramsAuth, ...auditParameters };
|
|
10
9
|
};
|
|
11
10
|
module.exports = {
|
|
12
11
|
getAuditConfig
|
|
@@ -24,6 +24,7 @@ const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload');
|
|
|
24
24
|
const settingsHelper = require('../../../utils/settingsHelper');
|
|
25
25
|
const chalk = require('chalk');
|
|
26
26
|
const saveResults = require('../../../scan/saveResults');
|
|
27
|
+
const { convertGenericToTypedReportModelSca } = require('../../../scaAnalysis/common/utils/reportUtilsSca');
|
|
27
28
|
const processSca = async (config) => {
|
|
28
29
|
config = await settingsHelper.getSettings(config);
|
|
29
30
|
const startTime = performance.now();
|
|
@@ -102,8 +103,9 @@ const processSca = async (config) => {
|
|
|
102
103
|
console.log('');
|
|
103
104
|
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'));
|
|
104
105
|
startSpinner(reportSpinner);
|
|
105
|
-
const
|
|
106
|
-
|
|
106
|
+
const { reportArray, reportId } = await scaUpload.scaTreeUpload(messageToSend, config);
|
|
107
|
+
const reportModelLibraryList = convertGenericToTypedReportModelSca(reportArray);
|
|
108
|
+
auditReport.processAuditReport(config, reportModelLibraryList);
|
|
107
109
|
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'));
|
|
108
110
|
if (config.save !== undefined) {
|
|
109
111
|
await auditSave.auditSave(config, reportId);
|
|
@@ -12,7 +12,7 @@ const MEDIUM = 'MEDIUM';
|
|
|
12
12
|
const HIGH = 'HIGH';
|
|
13
13
|
const CRITICAL = 'CRITICAL';
|
|
14
14
|
const APP_NAME = 'contrast';
|
|
15
|
-
const APP_VERSION = '1.0.
|
|
15
|
+
const APP_VERSION = '1.0.20';
|
|
16
16
|
const TIMEOUT = 120000;
|
|
17
17
|
const HIGH_COLOUR = '#ff9900';
|
|
18
18
|
const CRITICAL_COLOUR = '#e35858';
|
|
@@ -134,7 +134,10 @@ const en_locales = () => {
|
|
|
134
134
|
chalk.bold('\ncontrast scan') +
|
|
135
135
|
" to run Contrast's industry leading SAST scanner. \nSupports Java, JavaScript and .Net \n" +
|
|
136
136
|
chalk.bold('\ncontrast audit') +
|
|
137
|
-
' to find vulnerabilities in your open source dependencies
|
|
137
|
+
' to find vulnerabilities in your open source dependencies.' +
|
|
138
|
+
'\nSupports Java, .NET, Node, Ruby, Python, Go and PHP.' +
|
|
139
|
+
'\nOur CLI runs native build tools to generate a complete dependency tree.' +
|
|
140
|
+
'\nIf you are running on untrusted code, consider running in a sandbox.\n' +
|
|
138
141
|
chalk.bold('\ncontrast lambda') +
|
|
139
142
|
' to secure your AWS serverless functions. \nSupports Java and Python \n' +
|
|
140
143
|
chalk.bold('\ncontrast help') +
|
|
@@ -181,7 +184,8 @@ const en_locales = () => {
|
|
|
181
184
|
constantsAuditPrerequisitesContentSupportedLanguages: 'Supported languages and their requirements are:',
|
|
182
185
|
constantsAuditPrerequisitesJavaContentMessage: `
|
|
183
186
|
${chalk.bold('Java:')} pom.xml ${chalk.bold('and')} Maven build platform including the dependency plugin.
|
|
184
|
-
${chalk.bold('Or')} build.gradle ${chalk.bold('and')} gradle dependencies or ./gradlew dependencies must be supported
|
|
187
|
+
${chalk.bold('Or')} build.gradle ${chalk.bold('and')} gradle dependencies or ./gradlew dependencies must be supported
|
|
188
|
+
If you are running on untrusted code, consider running in a sandbox.`,
|
|
185
189
|
constantsAuditPrerequisitesContentDotNetMessage: `
|
|
186
190
|
${chalk.bold('.NET framework and .NET core:')} MSBuild 15.0 or greater and a packages.lock.json file.
|
|
187
191
|
Note: If the packages.lock.json file is unavailable it can be generated by setting RestorePackagesWithLockFile to true within each *.csproj file and running dotnet build.\n`,
|
|
@@ -1,77 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const { getSeverityCounts,
|
|
3
|
-
const { orderBy } = require('lodash');
|
|
4
|
-
const { assignBySeverity } = require('../../scan/formatScanOutput');
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
const { CE_URL } = require('../../constants/constants');
|
|
2
|
+
const { getSeverityCounts, printNoVulnFoundMsg } = require('../../audit/report/commonReportingFunctions');
|
|
7
3
|
const common = require('../../common/fail');
|
|
8
|
-
const
|
|
9
|
-
const processAuditReport = (config,
|
|
4
|
+
const { printFormattedOutputSca } = require('./commonReportingFunctionsSca');
|
|
5
|
+
const processAuditReport = (config, reportModelList) => {
|
|
10
6
|
let severityCounts = {};
|
|
11
|
-
if (
|
|
12
|
-
severityCounts = formatScaServicesReport(config,
|
|
7
|
+
if (reportModelList !== undefined) {
|
|
8
|
+
severityCounts = formatScaServicesReport(config, reportModelList);
|
|
13
9
|
}
|
|
14
10
|
if (config.fail) {
|
|
15
11
|
common.processFail(config, severityCounts);
|
|
16
12
|
}
|
|
17
13
|
};
|
|
18
|
-
const formatScaServicesReport = (config,
|
|
19
|
-
const projectOverviewCount = getSeverityCounts(
|
|
14
|
+
const formatScaServicesReport = (config, reportModelList) => {
|
|
15
|
+
const projectOverviewCount = getSeverityCounts(reportModelList);
|
|
20
16
|
if (projectOverviewCount.total === 0) {
|
|
21
17
|
printNoVulnFoundMsg();
|
|
22
|
-
return projectOverviewCount;
|
|
23
18
|
}
|
|
24
19
|
else {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let assignPriorityToResults = results.map(result => assignBySeverity(result, result));
|
|
30
|
-
const numberOfVulns = results
|
|
31
|
-
.map(result => result.vulnerabilities)
|
|
32
|
-
.reduce((a, b) => {
|
|
33
|
-
return (total += b.length);
|
|
34
|
-
}, 0);
|
|
35
|
-
const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(assignPriorityToResults, [
|
|
36
|
-
reportListItem => {
|
|
37
|
-
return reportListItem.priority;
|
|
38
|
-
},
|
|
39
|
-
reportListItem => {
|
|
40
|
-
return reportListItem.vulnerabilities.length;
|
|
20
|
+
const numberOfVulnerableLibraries = reportModelList.map(library => {
|
|
21
|
+
let count = 0;
|
|
22
|
+
if (library.vulnerabilities.length > 0) {
|
|
23
|
+
count++;
|
|
41
24
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const grammaticallyCorrectVul = result.vulnerabilities.length > 1 ? 'vulnerabilities' : 'vulnerability';
|
|
47
|
-
const headerColour = chalk.hex(result.colour);
|
|
48
|
-
const headerRow = [
|
|
49
|
-
headerColour(`CONTRAST-${contrastHeaderNumCounter.toString().padStart(3, '0')}`),
|
|
50
|
-
headerColour(`-`),
|
|
51
|
-
headerColour(`[${result.severity}] `) +
|
|
52
|
-
headerColour.bold(`${result.artifactName}`) +
|
|
53
|
-
` introduces ${cvesNum} ${grammaticallyCorrectVul}`
|
|
54
|
-
];
|
|
55
|
-
const adviceRow = [
|
|
56
|
-
chalk.bold(`Advice`),
|
|
57
|
-
chalk.bold(`:`),
|
|
58
|
-
`Change to version ${result.remediationAdvice.latestStableVersion}`
|
|
59
|
-
];
|
|
60
|
-
let assignPriorityToVulns = result.vulnerabilities.map(result => assignBySeverity(result, result));
|
|
61
|
-
const issueRow = getIssueRow(assignPriorityToVulns);
|
|
62
|
-
table.push(headerRow, issueRow, adviceRow);
|
|
63
|
-
console.log();
|
|
64
|
-
}
|
|
65
|
-
console.log();
|
|
66
|
-
createSummaryMessageTop(numberOfCves, numberOfVulns);
|
|
67
|
-
console.log(table.toString() + '\n');
|
|
68
|
-
printVulnInfo(projectOverviewCount);
|
|
69
|
-
if (config.host !== CE_URL) {
|
|
70
|
-
console.log('\n' + chalk.bold(i18n.__('auditServicesMessageForTS')));
|
|
71
|
-
console.log(`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs`);
|
|
72
|
-
}
|
|
73
|
-
return projectOverviewCount;
|
|
25
|
+
return count;
|
|
26
|
+
}).length;
|
|
27
|
+
let numberOfCves = reportModelList.reduce((count, current) => count + current.vulnerabilities.length, 0);
|
|
28
|
+
printFormattedOutputSca(config, reportModelList, numberOfVulnerableLibraries, numberOfCves);
|
|
74
29
|
}
|
|
30
|
+
return projectOverviewCount;
|
|
75
31
|
};
|
|
76
32
|
module.exports = {
|
|
77
33
|
formatScaServicesReport,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { ReportList, ReportModelStructure, ReportCompositeKey } = require('../../audit/report/models/reportListModel');
|
|
3
|
+
const { countVulnerableLibrariesBySeverity } = require('../../audit/report/utils/reportUtils');
|
|
4
|
+
const { SeverityCountModel } = require('../../audit/report/models/severityCountModel');
|
|
5
|
+
const { orderBy } = require('lodash');
|
|
6
|
+
const { ReportOutputModel, ReportOutputHeaderModel, ReportOutputBodyModel } = require('../../audit/report/models/reportOutputModel');
|
|
7
|
+
const { CE_URL, CRITICAL_COLOUR, HIGH_COLOUR, MEDIUM_COLOUR, LOW_COLOUR, NOTE_COLOUR } = require('../../constants/constants');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const Table = require('cli-table3');
|
|
10
|
+
const { findHighestSeverityCVESca, severityCountAllCVEsSca, findCVESeveritySca, orderByHighestPrioritySca } = require('./utils/reportUtilsSca');
|
|
11
|
+
const { buildFormattedHeaderNum } = require('../../audit/report/commonReportingFunctions');
|
|
12
|
+
const createSummaryMessageTop = (numberOfVulnerableLibraries, numberOfCves) => {
|
|
13
|
+
numberOfVulnerableLibraries === 1
|
|
14
|
+
? console.log(`\n\nFound 1 vulnerable library containing ${numberOfCves} CVE`)
|
|
15
|
+
: console.log(`\n\nFound ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs`);
|
|
16
|
+
};
|
|
17
|
+
const createSummaryMessageBottom = numberOfVulnerableLibraries => {
|
|
18
|
+
numberOfVulnerableLibraries === 1
|
|
19
|
+
? console.log(`Found 1 vulnerability`)
|
|
20
|
+
: console.log(`Found ${numberOfVulnerableLibraries} vulnerabilities`);
|
|
21
|
+
};
|
|
22
|
+
const printFormattedOutputSca = (config, reportModelList, numberOfVulnerableLibraries, numberOfCves) => {
|
|
23
|
+
createSummaryMessageTop(numberOfVulnerableLibraries, numberOfCves);
|
|
24
|
+
console.log();
|
|
25
|
+
const report = new ReportList();
|
|
26
|
+
for (const library of reportModelList) {
|
|
27
|
+
const { artifactName, version, vulnerabilities, remediationAdvice } = library;
|
|
28
|
+
const newOutputModel = new ReportModelStructure(new ReportCompositeKey(artifactName, version, findHighestSeverityCVESca(vulnerabilities), severityCountAllCVEsSca(vulnerabilities, new SeverityCountModel()).getTotal), vulnerabilities, remediationAdvice);
|
|
29
|
+
report.reportOutputList.push(newOutputModel);
|
|
30
|
+
}
|
|
31
|
+
const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(report.reportOutputList, [
|
|
32
|
+
reportListItem => {
|
|
33
|
+
return reportListItem.compositeKey.highestSeverity.priority;
|
|
34
|
+
},
|
|
35
|
+
reportListItem => {
|
|
36
|
+
return reportListItem.compositeKey.numberOfSeverities;
|
|
37
|
+
}
|
|
38
|
+
], ['asc', 'desc']);
|
|
39
|
+
let contrastHeaderNumCounter = 0;
|
|
40
|
+
for (const reportModel of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
|
|
41
|
+
contrastHeaderNumCounter++;
|
|
42
|
+
const { libraryName, libraryVersion, highestSeverity } = reportModel.compositeKey;
|
|
43
|
+
const { cveArray, remediationAdvice } = reportModel;
|
|
44
|
+
const numOfCVEs = reportModel.cveArray.length;
|
|
45
|
+
const table = getReportTable();
|
|
46
|
+
const header = buildHeader(highestSeverity, contrastHeaderNumCounter, libraryName, libraryVersion, numOfCVEs);
|
|
47
|
+
const body = buildBody(cveArray, remediationAdvice);
|
|
48
|
+
const reportOutputModel = new ReportOutputModel(header, body);
|
|
49
|
+
table.push(reportOutputModel.body.issueMessage, reportOutputModel.body.adviceMessage);
|
|
50
|
+
console.log(reportOutputModel.header.vulnMessage, reportOutputModel.header.introducesMessage);
|
|
51
|
+
console.log(table.toString() + '\n');
|
|
52
|
+
}
|
|
53
|
+
createSummaryMessageBottom(numberOfVulnerableLibraries);
|
|
54
|
+
const { criticalMessage, highMessage, mediumMessage, lowMessage, noteMessage } = buildFooter(outputOrderedByLowestSeverityAndLowestNumOfCvesFirst);
|
|
55
|
+
console.log(`${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`);
|
|
56
|
+
if (config.host !== CE_URL) {
|
|
57
|
+
console.log('\n' + chalk.bold('View your full dependency tree in Contrast:'));
|
|
58
|
+
console.log(`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
function getReportTable() {
|
|
62
|
+
return new Table({
|
|
63
|
+
chars: {
|
|
64
|
+
top: '',
|
|
65
|
+
'top-mid': '',
|
|
66
|
+
'top-left': '',
|
|
67
|
+
'top-right': '',
|
|
68
|
+
bottom: '',
|
|
69
|
+
'bottom-mid': '',
|
|
70
|
+
'bottom-left': '',
|
|
71
|
+
'bottom-right': '',
|
|
72
|
+
left: '',
|
|
73
|
+
'left-mid': '',
|
|
74
|
+
mid: '',
|
|
75
|
+
'mid-mid': '',
|
|
76
|
+
right: '',
|
|
77
|
+
'right-mid': '',
|
|
78
|
+
middle: ' '
|
|
79
|
+
},
|
|
80
|
+
style: { 'padding-left': 0, 'padding-right': 0 },
|
|
81
|
+
colAligns: ['right'],
|
|
82
|
+
wordWrap: true,
|
|
83
|
+
colWidths: [12, 1, 100]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function buildHeader(highestSeverity, contrastHeaderNum, libraryName, version, numOfCVEs) {
|
|
87
|
+
const vulnerabilityPluralised = numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability';
|
|
88
|
+
const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum);
|
|
89
|
+
const headerColour = chalk.hex(highestSeverity.colour);
|
|
90
|
+
const headerNumAndSeverity = headerColour(`${formattedHeaderNum} - [${highestSeverity.severity}]`);
|
|
91
|
+
const libraryNameAndVersion = headerColour.bold(`${libraryName}-${version}`);
|
|
92
|
+
const vulnMessage = `${headerNumAndSeverity} ${libraryNameAndVersion}`;
|
|
93
|
+
const introducesMessage = `introduces ${numOfCVEs} ${vulnerabilityPluralised}`;
|
|
94
|
+
return new ReportOutputHeaderModel(vulnMessage, introducesMessage);
|
|
95
|
+
}
|
|
96
|
+
function buildBody(cveArray, advice) {
|
|
97
|
+
const orderedCvesWithSeverityAssigned = orderByHighestPrioritySca(cveArray.map(cve => findCVESeveritySca(cve)));
|
|
98
|
+
const issueMessage = getIssueRow(orderedCvesWithSeverityAssigned);
|
|
99
|
+
const adviceMessage = getAdviceRow(advice);
|
|
100
|
+
return new ReportOutputBodyModel(issueMessage, adviceMessage);
|
|
101
|
+
}
|
|
102
|
+
function getIssueRow(cveArray) {
|
|
103
|
+
const cveMessagesList = getIssueCveMsgList(cveArray);
|
|
104
|
+
return [chalk.bold('Issue'), ':', `${cveMessagesList.join(', ')}`];
|
|
105
|
+
}
|
|
106
|
+
function getAdviceRow(advice) {
|
|
107
|
+
const latestOrClosest = advice.latestStableVersion
|
|
108
|
+
? advice.latestStableVersion
|
|
109
|
+
: advice.closestStableVersion;
|
|
110
|
+
const displayAdvice = latestOrClosest
|
|
111
|
+
? `Change to version ${chalk.bold(latestOrClosest)}`
|
|
112
|
+
: 'No recommendation is available according to our data. Upgrade to the latest stable is the best advice we can give.';
|
|
113
|
+
return [chalk.bold(`Advice`), chalk.bold(`:`), `${displayAdvice}`];
|
|
114
|
+
}
|
|
115
|
+
const buildFooter = reportModelStructure => {
|
|
116
|
+
const { critical, high, medium, low, note } = countVulnerableLibrariesBySeverity(reportModelStructure);
|
|
117
|
+
const criticalMessage = chalk
|
|
118
|
+
.hex(CRITICAL_COLOUR)
|
|
119
|
+
.bold(`${critical} Critical`);
|
|
120
|
+
const highMessage = chalk.hex(HIGH_COLOUR).bold(`${high} High`);
|
|
121
|
+
const mediumMessage = chalk.hex(MEDIUM_COLOUR).bold(`${medium} Medium`);
|
|
122
|
+
const lowMessage = chalk.hex(LOW_COLOUR).bold(`${low} Low`);
|
|
123
|
+
const noteMessage = chalk.hex(NOTE_COLOUR).bold(`${note} Note`);
|
|
124
|
+
return {
|
|
125
|
+
criticalMessage,
|
|
126
|
+
highMessage,
|
|
127
|
+
mediumMessage,
|
|
128
|
+
lowMessage,
|
|
129
|
+
noteMessage
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
const getIssueCveMsgList = reportSeverityModels => {
|
|
133
|
+
const cveMessages = [];
|
|
134
|
+
reportSeverityModels.forEach(reportSeverityModel => {
|
|
135
|
+
const { colour, severity, name } = reportSeverityModel;
|
|
136
|
+
const severityShorthand = chalk
|
|
137
|
+
.hex(colour)
|
|
138
|
+
.bold(`[${severity.charAt(0).toUpperCase()}]`);
|
|
139
|
+
const builtMessage = severityShorthand + name;
|
|
140
|
+
cveMessages.push(builtMessage);
|
|
141
|
+
});
|
|
142
|
+
return cveMessages;
|
|
143
|
+
};
|
|
144
|
+
module.exports = {
|
|
145
|
+
createSummaryMessageTop,
|
|
146
|
+
createSummaryMessageBottom,
|
|
147
|
+
printFormattedOutputSca,
|
|
148
|
+
getReportTable,
|
|
149
|
+
buildHeader,
|
|
150
|
+
buildBody,
|
|
151
|
+
getIssueRow,
|
|
152
|
+
buildFormattedHeaderNum,
|
|
153
|
+
getIssueCveMsgList
|
|
154
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScaReportRemediationAdviceModel = exports.ScaReportVulnerabilityModel = exports.ScaReportModel = void 0;
|
|
4
|
+
class ScaReportModel {
|
|
5
|
+
constructor(library) {
|
|
6
|
+
this.uuid = library.uuid;
|
|
7
|
+
this.groupName = library.groupName;
|
|
8
|
+
this.artifactName = library.artifactName;
|
|
9
|
+
this.version = library.version;
|
|
10
|
+
this.hash = library.hash;
|
|
11
|
+
this.fileName = library.fileName;
|
|
12
|
+
this.libraryLanguage = library.libraryLanguage;
|
|
13
|
+
this.vulnerable = library.vulnerable;
|
|
14
|
+
this.privateLibrary = library.privateLibrary;
|
|
15
|
+
this.severity = library.severity;
|
|
16
|
+
this.releaseDate = library.releaseDate;
|
|
17
|
+
this.latestVersionReleaseDate = library.latestVersionReleaseDate;
|
|
18
|
+
this.latestVersion = library.latestVersion;
|
|
19
|
+
this.versionsBehind = library.versionsBehind;
|
|
20
|
+
this.vulnerabilities = library.vulnerabilities;
|
|
21
|
+
this.remediationAdvice = library.remediationAdvice;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.ScaReportModel = ScaReportModel;
|
|
25
|
+
class ScaReportVulnerabilityModel {
|
|
26
|
+
constructor(name, description, cvss2Vector, severityValue, severity, cvss3Vector, cvss3SeverityValue, cvss3Severity, hasCvss3) {
|
|
27
|
+
this.name = name;
|
|
28
|
+
this.description = description;
|
|
29
|
+
this.cvss2Vector = cvss2Vector;
|
|
30
|
+
this.severityValue = severityValue;
|
|
31
|
+
this.severity = severity;
|
|
32
|
+
this.cvss3Vector = cvss3Vector;
|
|
33
|
+
this.cvss3SeverityValue = cvss3SeverityValue;
|
|
34
|
+
this.cvss3Severity = cvss3Severity;
|
|
35
|
+
this.hasCvss3 = hasCvss3;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.ScaReportVulnerabilityModel = ScaReportVulnerabilityModel;
|
|
39
|
+
class ScaReportRemediationAdviceModel {
|
|
40
|
+
constructor(closestStableVersion, latestStableVersion) {
|
|
41
|
+
this.closestStableVersion = closestStableVersion;
|
|
42
|
+
this.latestStableVersion = latestStableVersion;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.ScaReportRemediationAdviceModel = ScaReportRemediationAdviceModel;
|
|
@@ -44,16 +44,17 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
44
44
|
if (res.body.status === 'COMPLETED') {
|
|
45
45
|
keepChecking = false;
|
|
46
46
|
return client.scaServiceReport(config, reportID).then(res => {
|
|
47
|
-
|
|
47
|
+
const reportBody = res.body;
|
|
48
|
+
return { reportBody, reportID };
|
|
48
49
|
});
|
|
49
50
|
}
|
|
50
51
|
});
|
|
51
52
|
if (!keepChecking) {
|
|
52
|
-
return
|
|
53
|
+
return { reportArray: res.reportBody, reportID };
|
|
53
54
|
}
|
|
54
55
|
await requestUtils.sleep(5000);
|
|
55
56
|
}
|
|
56
|
-
return
|
|
57
|
+
return { reportArray: res, reportID };
|
|
57
58
|
};
|
|
58
59
|
module.exports = {
|
|
59
60
|
scaTreeUpload
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.severityCountSingleCVESca = exports.severityCountAllCVEsSca = exports.severityCountAllLibrariesSca = exports.convertGenericToTypedReportModelSca = exports.findCVESeveritySca = exports.orderByHighestPrioritySca = exports.findHighestSeverityCVESca = void 0;
|
|
4
|
+
const lodash_1 = require("lodash");
|
|
5
|
+
const constants_1 = require("../../../constants/constants");
|
|
6
|
+
const reportSeverityModel_1 = require("../../../audit/report/models/reportSeverityModel");
|
|
7
|
+
const ScaReportModel_1 = require("../models/ScaReportModel");
|
|
8
|
+
function findHighestSeverityCVESca(cveArray) {
|
|
9
|
+
const mappedToReportSeverityModels = cveArray.map(cve => findCVESeveritySca(cve));
|
|
10
|
+
return (0, lodash_1.orderBy)(mappedToReportSeverityModels, cve => cve?.priority)[0];
|
|
11
|
+
}
|
|
12
|
+
exports.findHighestSeverityCVESca = findHighestSeverityCVESca;
|
|
13
|
+
function orderByHighestPrioritySca(reportSeverityModel) {
|
|
14
|
+
return (0, lodash_1.orderBy)(reportSeverityModel, ['priority'], ['asc']);
|
|
15
|
+
}
|
|
16
|
+
exports.orderByHighestPrioritySca = orderByHighestPrioritySca;
|
|
17
|
+
function findCVESeveritySca(vulnerabilityModel) {
|
|
18
|
+
const { name } = vulnerabilityModel;
|
|
19
|
+
if (vulnerabilityModel.cvss3Severity === 'CRITICAL' ||
|
|
20
|
+
vulnerabilityModel.severity === 'CRITICAL') {
|
|
21
|
+
return new reportSeverityModel_1.ReportSeverityModel('CRITICAL', constants_1.CRITICAL_PRIORITY, constants_1.CRITICAL_COLOUR, name);
|
|
22
|
+
}
|
|
23
|
+
else if (vulnerabilityModel.cvss3Severity === 'HIGH' ||
|
|
24
|
+
vulnerabilityModel.severity === 'HIGH') {
|
|
25
|
+
return new reportSeverityModel_1.ReportSeverityModel('HIGH', constants_1.HIGH_PRIORITY, constants_1.HIGH_COLOUR, name);
|
|
26
|
+
}
|
|
27
|
+
else if (vulnerabilityModel.cvss3Severity === 'MEDIUM' ||
|
|
28
|
+
vulnerabilityModel.severity === 'MEDIUM') {
|
|
29
|
+
return new reportSeverityModel_1.ReportSeverityModel('MEDIUM', constants_1.MEDIUM_PRIORITY, constants_1.MEDIUM_COLOUR, name);
|
|
30
|
+
}
|
|
31
|
+
else if (vulnerabilityModel.cvss3Severity === 'LOW' ||
|
|
32
|
+
vulnerabilityModel.severity === 'LOW') {
|
|
33
|
+
return new reportSeverityModel_1.ReportSeverityModel('LOW', constants_1.LOW_PRIORITY, constants_1.LOW_COLOUR, name);
|
|
34
|
+
}
|
|
35
|
+
else if (vulnerabilityModel.cvss3Severity === 'NOTE' ||
|
|
36
|
+
vulnerabilityModel.severity === 'NOTE') {
|
|
37
|
+
return new reportSeverityModel_1.ReportSeverityModel('NOTE', constants_1.NOTE_PRIORITY, constants_1.NOTE_COLOUR, name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.findCVESeveritySca = findCVESeveritySca;
|
|
41
|
+
function convertGenericToTypedReportModelSca(reportArray) {
|
|
42
|
+
return reportArray.map((library) => {
|
|
43
|
+
return new ScaReportModel_1.ScaReportModel(library);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
exports.convertGenericToTypedReportModelSca = convertGenericToTypedReportModelSca;
|
|
47
|
+
function severityCountAllLibrariesSca(vulnerableLibraries, severityCount) {
|
|
48
|
+
vulnerableLibraries.forEach(lib => severityCountAllCVEsSca(lib.vulnerabilities, severityCount));
|
|
49
|
+
return severityCount;
|
|
50
|
+
}
|
|
51
|
+
exports.severityCountAllLibrariesSca = severityCountAllLibrariesSca;
|
|
52
|
+
function severityCountAllCVEsSca(cveArray, severityCount) {
|
|
53
|
+
const severityCountInner = severityCount;
|
|
54
|
+
cveArray.forEach(cve => severityCountSingleCVESca(cve, severityCountInner));
|
|
55
|
+
return severityCountInner;
|
|
56
|
+
}
|
|
57
|
+
exports.severityCountAllCVEsSca = severityCountAllCVEsSca;
|
|
58
|
+
function severityCountSingleCVESca(cve, severityCount) {
|
|
59
|
+
if (cve.cvss3Severity === 'CRITICAL' || cve.severity === 'CRITICAL') {
|
|
60
|
+
severityCount.critical += 1;
|
|
61
|
+
}
|
|
62
|
+
else if (cve.cvss3Severity === 'HIGH' || cve.severity === 'HIGH') {
|
|
63
|
+
severityCount.high += 1;
|
|
64
|
+
}
|
|
65
|
+
else if (cve.cvss3Severity === 'MEDIUM' || cve.severity === 'MEDIUM') {
|
|
66
|
+
severityCount.medium += 1;
|
|
67
|
+
}
|
|
68
|
+
else if (cve.cvss3Severity === 'LOW' || cve.severity === 'LOW') {
|
|
69
|
+
severityCount.low += 1;
|
|
70
|
+
}
|
|
71
|
+
else if (cve.cvss3Severity === 'NOTE' || cve.severity === 'NOTE') {
|
|
72
|
+
severityCount.note += 1;
|
|
73
|
+
}
|
|
74
|
+
return severityCount;
|
|
75
|
+
}
|
|
76
|
+
exports.severityCountSingleCVESca = severityCountSingleCVESca;
|
|
@@ -111,34 +111,7 @@ const getJavaBuildDeps = (config, files) => {
|
|
|
111
111
|
console.log(err.message.toString());
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
|
-
const agreementPrompt = async (config) => {
|
|
115
|
-
const rl = readLine.createInterface({
|
|
116
|
-
input: process.stdin,
|
|
117
|
-
output: process.stdout
|
|
118
|
-
});
|
|
119
|
-
return new Promise((resolve, reject) => {
|
|
120
|
-
rl.question('❔ Do you want to continue? Type Y or N', async (input) => {
|
|
121
|
-
if (input.toLowerCase() === 'yes' || input.toLowerCase() === 'y') {
|
|
122
|
-
config.javaAgreement = paramHandler.setAgreement(true);
|
|
123
|
-
rl.close();
|
|
124
|
-
resolve(config);
|
|
125
|
-
}
|
|
126
|
-
else if (input.toLowerCase() === 'no' || input.toLowerCase() === 'n') {
|
|
127
|
-
rl.close();
|
|
128
|
-
resolve(process.exit(1));
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
rl.close();
|
|
132
|
-
console.log('Invalid Input: Exiting');
|
|
133
|
-
resolve(process.exit(1));
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}).catch(e => {
|
|
137
|
-
throw e;
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
114
|
module.exports = {
|
|
141
115
|
getJavaBuildDeps,
|
|
142
|
-
determineProjectTypeAndCwd
|
|
143
|
-
agreementPrompt
|
|
116
|
+
determineProjectTypeAndCwd
|
|
144
117
|
};
|