@contrast/contrast 1.0.18 → 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/cliConstants.js +1 -0
- package/dist/commands/audit/auditConfig.js +1 -2
- package/dist/commands/auth/auth.js +43 -7
- package/dist/commands/scan/sca/scaAnalysis.js +4 -2
- package/dist/common/HTTPClient.js +4 -4
- package/dist/common/errorHandling.js +13 -1
- package/dist/constants/constants.js +1 -1
- package/dist/constants/locales.js +18 -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/getConfig.js +1 -1
- 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/cliConstants.js +1 -0
- package/src/commands/audit/auditConfig.js +1 -2
- package/src/commands/auth/auth.js +49 -7
- package/src/commands/scan/sca/scaAnalysis.js +7 -2
- package/src/common/HTTPClient.js +5 -4
- package/src/common/errorHandling.js +14 -1
- package/src/constants/constants.js +1 -1
- package/src/constants/locales.js +19 -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 +1 -2
- 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;
|
package/dist/cliConstants.js
CHANGED
|
@@ -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
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const { v4: uuidv4 } = require('uuid');
|
|
3
|
-
const
|
|
4
|
-
const open = require('open');
|
|
3
|
+
const configFunctions = require('../../utils/getConfig');
|
|
5
4
|
const commonApi = require('../../utils/commonApi');
|
|
6
|
-
const
|
|
5
|
+
const requestUtils = require('../../utils/requestUtils');
|
|
7
6
|
const i18n = require('i18n');
|
|
8
7
|
const { returnOra, startSpinner, failSpinner, succeedSpinner } = require('../../utils/oraWrapper');
|
|
9
8
|
const { TIMEOUT, AUTH_UI_URL } = require('../../constants/constants');
|
|
10
9
|
const parsedCLIOptions = require('../../utils/parsedCLIOptions');
|
|
11
10
|
const constants = require('../../cliConstants');
|
|
12
11
|
const commandLineUsage = require('command-line-usage');
|
|
12
|
+
const { commonMessageFormatter } = require('../../common/errorHandling');
|
|
13
|
+
const open = require('open');
|
|
14
|
+
const messages = require('../../constants/locales').en_locales();
|
|
13
15
|
const processAuth = async (argv, config) => {
|
|
14
16
|
let authParams = await parsedCLIOptions.getCommandLineArgsCustom(config, 'auth', argv, constants.commandLineDefinitions.authOptionDefinitions);
|
|
15
17
|
if (authParams.help) {
|
|
16
18
|
console.log(authUsageGuide);
|
|
17
19
|
process.exit(0);
|
|
18
20
|
}
|
|
21
|
+
if (checkForCustomCredentials(authParams)) {
|
|
22
|
+
processCustomCredentials(authParams, config);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
await startAuthProcess(config);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const startAuthProcess = async (config) => {
|
|
19
29
|
const token = uuidv4();
|
|
20
30
|
const url = `${AUTH_UI_URL}/?token=${token}`;
|
|
21
31
|
console.log(i18n.__('redirectAuth', url));
|
|
@@ -25,9 +35,8 @@ const processAuth = async (argv, config) => {
|
|
|
25
35
|
}, 0);
|
|
26
36
|
const result = await isAuthComplete(token, TIMEOUT, config);
|
|
27
37
|
if (result) {
|
|
28
|
-
setConfigValues(config, result);
|
|
38
|
+
configFunctions.setConfigValues(config, result);
|
|
29
39
|
}
|
|
30
|
-
return;
|
|
31
40
|
}
|
|
32
41
|
finally {
|
|
33
42
|
}
|
|
@@ -54,7 +63,7 @@ const isAuthComplete = async (token, timeout, config) => {
|
|
|
54
63
|
}
|
|
55
64
|
};
|
|
56
65
|
const pollAuthResult = async (token, client) => {
|
|
57
|
-
await sleep(5000);
|
|
66
|
+
await requestUtils.sleep(5000);
|
|
58
67
|
return client
|
|
59
68
|
.pollForAuth(token)
|
|
60
69
|
.then(res => {
|
|
@@ -74,6 +83,33 @@ const authUsageGuide = commandLineUsage([
|
|
|
74
83
|
content: [i18n.__('constantsAuthUsageContents')]
|
|
75
84
|
}
|
|
76
85
|
]);
|
|
86
|
+
const checkForCustomCredentials = authParams => {
|
|
87
|
+
const hasSomeKeys = authParams.apiKey ||
|
|
88
|
+
authParams.organizationId ||
|
|
89
|
+
authParams.host ||
|
|
90
|
+
authParams.authorization;
|
|
91
|
+
const hasAllKeys = authParams.apiKey &&
|
|
92
|
+
authParams.organizationId &&
|
|
93
|
+
authParams.host &&
|
|
94
|
+
authParams.authorization;
|
|
95
|
+
if (hasAllKeys) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (hasSomeKeys) {
|
|
99
|
+
commonMessageFormatter(messages.authCommand.credentialsMissing, true);
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
};
|
|
103
|
+
const processCustomCredentials = (authParams, config) => {
|
|
104
|
+
const valuesToSet = {
|
|
105
|
+
apiKey: authParams.apiKey,
|
|
106
|
+
orgId: authParams.organizationId,
|
|
107
|
+
authHeader: authParams.authorization,
|
|
108
|
+
host: authParams.host
|
|
109
|
+
};
|
|
110
|
+
configFunctions.setConfigValues(config, valuesToSet);
|
|
111
|
+
commonMessageFormatter(messages.authCommand.credentialsAccepted, false);
|
|
112
|
+
};
|
|
77
113
|
module.exports = {
|
|
78
|
-
processAuth
|
|
114
|
+
processAuth
|
|
79
115
|
};
|
|
@@ -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);
|
|
@@ -332,18 +332,18 @@ function createSnapshotURL(config) {
|
|
|
332
332
|
return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/snapshots`;
|
|
333
333
|
}
|
|
334
334
|
function createScaServiceReportURL(config, reportId) {
|
|
335
|
-
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/
|
|
335
|
+
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/reports/${reportId}`;
|
|
336
336
|
baseUrl = config.ignoreDev ? baseUrl.concat('?nodesToInclude=PROD') : baseUrl;
|
|
337
337
|
return baseUrl;
|
|
338
338
|
}
|
|
339
339
|
function createScaServiceReportStatusURL(config, reportId) {
|
|
340
|
-
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/${reportId}/status`;
|
|
340
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/ingests/${reportId}/status`;
|
|
341
341
|
}
|
|
342
342
|
function createScaServiceIngestsURL(config) {
|
|
343
|
-
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests`;
|
|
343
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/ingests`;
|
|
344
344
|
}
|
|
345
345
|
function createScaServiceIngestURL(config) {
|
|
346
|
-
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/tree`;
|
|
346
|
+
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/ingests/tree`;
|
|
347
347
|
baseUrl = config.track ? baseUrl.concat('?persist=true') : baseUrl;
|
|
348
348
|
return baseUrl;
|
|
349
349
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const i18n = require('i18n');
|
|
3
|
+
const chalk = require('chalk');
|
|
3
4
|
const libraryAnalysisError = () => {
|
|
4
5
|
console.log(i18n.__('libraryAnalysisError'));
|
|
5
6
|
};
|
|
@@ -79,6 +80,16 @@ const findCommandOnError = unknownOptions => {
|
|
|
79
80
|
return foundCommands[0];
|
|
80
81
|
}
|
|
81
82
|
};
|
|
83
|
+
const commonMessageFormatter = (message, fail) => {
|
|
84
|
+
console.log(chalk.bold(i18n.__(message.title)));
|
|
85
|
+
console.log(i18n.__(message.body));
|
|
86
|
+
if (message.extra) {
|
|
87
|
+
console.log(i18n.__(message.extra));
|
|
88
|
+
}
|
|
89
|
+
if (fail) {
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
82
93
|
module.exports = {
|
|
83
94
|
genericError,
|
|
84
95
|
unauthenticatedError,
|
|
@@ -95,5 +106,6 @@ module.exports = {
|
|
|
95
106
|
reportFailureError,
|
|
96
107
|
maxAppError,
|
|
97
108
|
parametersError,
|
|
98
|
-
invalidHostNameError
|
|
109
|
+
invalidHostNameError,
|
|
110
|
+
commonMessageFormatter
|
|
99
111
|
};
|
|
@@ -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`,
|
|
@@ -216,6 +220,18 @@ const en_locales = () => {
|
|
|
216
220
|
commonHelpLearnMoreEnterpriseText: ' https://docs.contrastsecurity.com/en/run-contrast-cli.html ',
|
|
217
221
|
commonHelpJoinDiscussionHeader: chalk.hex('#9DC184')('Join the discussion:'),
|
|
218
222
|
commonHelpJoinDiscussionText: ' https://dev.to/codesec',
|
|
223
|
+
authCommand: {
|
|
224
|
+
credentialsAccepted: {
|
|
225
|
+
title: 'Credentials accepted',
|
|
226
|
+
body: 'Now run contrast audit, lambda or scan',
|
|
227
|
+
extra: 'Or contrast help for full list of commands'
|
|
228
|
+
},
|
|
229
|
+
credentialsMissing: {
|
|
230
|
+
title: 'Credentials missing',
|
|
231
|
+
body: 'You have not entered the right parameters or enough information',
|
|
232
|
+
extra: 'Please check and try again e.g. contrast auth --api-key yourApiKey--organization-id yourOrg --authorization yourAuth --host https://yourHost\nOr contrast help for full list of commands'
|
|
233
|
+
}
|
|
234
|
+
},
|
|
219
235
|
...lambda
|
|
220
236
|
};
|
|
221
237
|
};
|
|
@@ -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
|
+
};
|