@contrast/contrast 1.0.1 → 1.0.4
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 +2 -0
- package/README.md +103 -133
- package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +26 -11
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +62 -234
- 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 +3 -1
- package/dist/commands/audit/auditController.js +6 -3
- 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 +8 -25
- package/dist/common/HTTPClient.js +30 -26
- package/dist/common/errorHandling.js +17 -1
- package/dist/common/versionChecker.js +32 -0
- package/dist/constants/constants.js +4 -2
- package/dist/constants/lambda.js +3 -1
- package/dist/constants/locales.js +41 -18
- package/dist/constants.js +39 -3
- package/dist/index.js +49 -28
- package/dist/lambda/help.js +22 -14
- package/dist/lambda/lambda.js +6 -0
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scan/help.js +4 -2
- package/dist/scan/models/groupedResultsModel.js +10 -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 +99 -74
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +7 -2
- package/dist/scan/scanResults.js +6 -0
- package/dist/utils/getConfig.js +3 -0
- package/dist/utils/paramsUtil/commandlineParams.js +1 -1
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +2 -2
- package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +33 -15
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +127 -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 +3 -1
- package/src/commands/audit/auditController.ts +12 -3
- package/src/commands/audit/processAudit.ts +0 -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 +8 -29
- package/src/common/HTTPClient.js +42 -36
- package/src/common/errorHandling.ts +29 -2
- package/src/common/versionChecker.ts +41 -0
- package/src/constants/constants.js +5 -4
- package/src/constants/lambda.js +3 -1
- package/src/constants/locales.js +51 -19
- package/src/constants.js +44 -3
- package/src/index.ts +63 -31
- package/src/lambda/help.ts +22 -14
- package/src/lambda/lambda.ts +8 -0
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scan/help.js +4 -2
- package/src/scan/models/groupedResultsModel.ts +18 -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 +192 -0
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +11 -2
- package/src/scan/scanResults.js +11 -0
- package/src/utils/getConfig.ts +12 -0
- package/src/utils/paramsUtil/commandlineParams.js +1 -1
- 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/dist/lambda/scanDetail.js +0 -30
- package/dist/scan/fileFinder.js +0 -15
- package/dist/utils/fileUtils.js +0 -31
- package/dist/utils/paramsUtil/genericCommandLineParams.js +0 -12
- package/dist/utils/paramsUtil/yamlParams.js +0 -6
- 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 -162
package/dist/scan/scan.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.stripMustacheTags = exports.getMessage = exports.getGroups = exports.formatLinks = exports.formatScanOutput = exports.sendScan = exports.isFileAllowed = exports.allowedFileTypes = void 0;
|
|
7
|
+
const commonApi_js_1 = __importDefault(require("../utils/commonApi.js"));
|
|
8
|
+
const fileUtils_1 = __importDefault(require("../scan/fileUtils"));
|
|
9
|
+
const i18n_1 = __importDefault(require("i18n"));
|
|
10
|
+
const oraWrapper_1 = __importDefault(require("../utils/oraWrapper"));
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const groupedResultsModel_1 = require("./models/groupedResultsModel");
|
|
13
|
+
exports.allowedFileTypes = ['.jar', '.war', '.js', '.zip', '.exe'];
|
|
14
|
+
const isFileAllowed = (scanOption) => {
|
|
9
15
|
let valid = false;
|
|
10
|
-
allowedFileTypes.forEach(fileType => {
|
|
16
|
+
exports.allowedFileTypes.forEach(fileType => {
|
|
11
17
|
if (scanOption.endsWith(fileType)) {
|
|
12
18
|
valid = true;
|
|
13
19
|
}
|
|
14
20
|
});
|
|
15
21
|
return valid;
|
|
16
22
|
};
|
|
17
|
-
|
|
18
|
-
return oldString
|
|
19
|
-
.replace(/\n/g, ' ')
|
|
20
|
-
.replace(/{{.*?}}/g, '\n')
|
|
21
|
-
.replace(/\s+/g, ' ')
|
|
22
|
-
.trim();
|
|
23
|
-
};
|
|
23
|
+
exports.isFileAllowed = isFileAllowed;
|
|
24
24
|
const sendScan = async (config) => {
|
|
25
|
-
if (!isFileAllowed(config.file)) {
|
|
26
|
-
console.log(
|
|
25
|
+
if (!(0, exports.isFileAllowed)(config.file)) {
|
|
26
|
+
console.log(i18n_1.default.__('scanErrorFileMessage'));
|
|
27
27
|
process.exit(9);
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
|
-
|
|
31
|
-
const client =
|
|
32
|
-
const startUploadSpinner =
|
|
33
|
-
|
|
30
|
+
fileUtils_1.default.checkFilePermissions(config.file);
|
|
31
|
+
const client = commonApi_js_1.default.getHttpClient(config);
|
|
32
|
+
const startUploadSpinner = oraWrapper_1.default.returnOra(i18n_1.default.__('uploadingScan'));
|
|
33
|
+
oraWrapper_1.default.startSpinner(startUploadSpinner);
|
|
34
34
|
return await client
|
|
35
35
|
.sendArtifact(config)
|
|
36
36
|
.then(res => {
|
|
37
37
|
if (res.statusCode === 201) {
|
|
38
|
-
|
|
38
|
+
oraWrapper_1.default.succeedSpinner(startUploadSpinner, i18n_1.default.__('uploadingScanSuccessful'));
|
|
39
39
|
if (config.verbose) {
|
|
40
|
-
console.log(
|
|
40
|
+
console.log(i18n_1.default.__('responseMessage', res.body));
|
|
41
41
|
}
|
|
42
42
|
return res.body.id;
|
|
43
43
|
}
|
|
@@ -46,10 +46,12 @@ const sendScan = async (config) => {
|
|
|
46
46
|
console.log(res.statusCode);
|
|
47
47
|
console.log(config);
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
oraWrapper_1.default.failSpinner(startUploadSpinner, i18n_1.default.__('uploadingScanFail'));
|
|
50
50
|
if (res.statusCode === 403) {
|
|
51
|
-
console.log(
|
|
51
|
+
console.log(i18n_1.default.__('permissionsError'));
|
|
52
|
+
process.exit(1);
|
|
52
53
|
}
|
|
54
|
+
console.log(i18n_1.default.__('genericServiceError', res.statusCode));
|
|
53
55
|
process.exit(1);
|
|
54
56
|
}
|
|
55
57
|
})
|
|
@@ -58,74 +60,97 @@ const sendScan = async (config) => {
|
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
exports.sendScan = sendScan;
|
|
64
|
+
function formatScanOutput(scanResults) {
|
|
65
|
+
const { projectOverview, scanResultsInstances } = scanResults;
|
|
66
|
+
if (scanResultsInstances.content.length === 0) {
|
|
67
|
+
console.log(i18n_1.default.__('scanNoVulnerabilitiesFound'));
|
|
65
68
|
}
|
|
66
69
|
else {
|
|
67
|
-
|
|
70
|
+
const message = projectOverview.critical || projectOverview.high
|
|
71
|
+
? 'Here are your top priorities to fix'
|
|
72
|
+
: "No major issues, here's what we found";
|
|
73
|
+
console.log(chalk_1.default.bold(message));
|
|
68
74
|
console.log();
|
|
69
|
-
const groups = getGroups(
|
|
75
|
+
const groups = getGroups(scanResultsInstances.content);
|
|
70
76
|
groups.forEach(entry => {
|
|
71
|
-
console.log(
|
|
77
|
+
console.log(chalk_1.default.bold(`[ ${entry.severity} ] | ${entry.ruleId} (${entry.lineInfoSet.size}) - ` +
|
|
78
|
+
`${entry.message}`));
|
|
72
79
|
let count = 1;
|
|
73
80
|
entry.lineInfoSet.forEach(lineInfo => {
|
|
74
81
|
console.log(`\t ${count}. ${lineInfo}`);
|
|
75
82
|
count++;
|
|
76
83
|
});
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
if (entry?.issue) {
|
|
85
|
+
console.log(chalk_1.default.bold('Issue' + ': ') + entry.issue);
|
|
86
|
+
}
|
|
87
|
+
if (entry?.advice) {
|
|
88
|
+
console.log(chalk_1.default.bold('Advice' + ': ') + entry.advice);
|
|
89
|
+
}
|
|
90
|
+
if (entry?.learn && entry?.learn.length > 0) {
|
|
91
|
+
formatLinks('Learn', entry.learn);
|
|
92
|
+
}
|
|
79
93
|
console.log();
|
|
80
94
|
});
|
|
81
|
-
|
|
82
|
-
overview.high +
|
|
83
|
-
overview.medium +
|
|
84
|
-
overview.low +
|
|
85
|
-
overview.note;
|
|
86
|
-
console.log(chalk.bold(`Found ${totalVulnerabilities} vulnerabilities`));
|
|
87
|
-
console.log(i18n.__('foundDetailedVulnerabilities', overview.critical, overview.high, overview.medium, overview.low, overview.note));
|
|
95
|
+
printVulnInfo(projectOverview);
|
|
88
96
|
}
|
|
89
|
-
}
|
|
90
|
-
|
|
97
|
+
}
|
|
98
|
+
exports.formatScanOutput = formatScanOutput;
|
|
99
|
+
function printVulnInfo(projectOverview) {
|
|
100
|
+
const totalVulnerabilities = getTotalVulns(projectOverview);
|
|
101
|
+
const vulMessage = totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`;
|
|
102
|
+
console.log(chalk_1.default.bold(`Found ${totalVulnerabilities} ${vulMessage}`));
|
|
103
|
+
console.log(i18n_1.default.__('foundDetailedVulnerabilities', String(projectOverview.critical), String(projectOverview.high), String(projectOverview.medium), String(projectOverview.low), String(projectOverview.note)));
|
|
104
|
+
}
|
|
105
|
+
function getTotalVulns(projectOverview) {
|
|
106
|
+
return (projectOverview.critical +
|
|
107
|
+
projectOverview.high +
|
|
108
|
+
projectOverview.medium +
|
|
109
|
+
projectOverview.low +
|
|
110
|
+
projectOverview.note);
|
|
111
|
+
}
|
|
112
|
+
function formatLinks(objName, entry) {
|
|
113
|
+
console.log(chalk_1.default.bold(objName + ':'));
|
|
114
|
+
entry.forEach(link => {
|
|
115
|
+
console.log(link);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
exports.formatLinks = formatLinks;
|
|
119
|
+
function getGroups(content) {
|
|
91
120
|
const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId));
|
|
92
|
-
|
|
121
|
+
const groupTypeResults = [];
|
|
93
122
|
groupTypeSet.forEach(groupName => {
|
|
94
|
-
|
|
95
|
-
ruleId: groupName,
|
|
96
|
-
lineInfoSet: new Set(),
|
|
97
|
-
recommendation: '',
|
|
98
|
-
severity: ''
|
|
99
|
-
};
|
|
123
|
+
const groupResultsObj = new groupedResultsModel_1.GroupedResultsModel(groupName);
|
|
100
124
|
content.forEach(resultEntry => {
|
|
101
125
|
if (resultEntry.ruleId === groupName) {
|
|
102
126
|
groupResultsObj.severity = resultEntry.severity;
|
|
103
|
-
groupResultsObj.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
groupResultsObj.
|
|
127
|
+
groupResultsObj.issue = stripMustacheTags(resultEntry.issue);
|
|
128
|
+
groupResultsObj.advice = resultEntry.advice;
|
|
129
|
+
groupResultsObj.learn = resultEntry.learn;
|
|
130
|
+
groupResultsObj.message = resultEntry.message?.text;
|
|
131
|
+
groupResultsObj.lineInfoSet.add(getMessage(resultEntry.locations));
|
|
107
132
|
}
|
|
108
133
|
});
|
|
109
134
|
groupTypeResults.push(groupResultsObj);
|
|
110
135
|
});
|
|
111
136
|
return groupTypeResults;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
}
|
|
138
|
+
exports.getGroups = getGroups;
|
|
139
|
+
function getMessage(locations) {
|
|
140
|
+
const message = locations[0]?.physicalLocation?.artifactLocation?.uri || '';
|
|
141
|
+
const lineNumber = locations[0]?.physicalLocation?.region?.startLine || '';
|
|
142
|
+
if (!lineNumber) {
|
|
143
|
+
return '@' + message;
|
|
144
|
+
}
|
|
145
|
+
return '@' + message + ':' + lineNumber;
|
|
146
|
+
}
|
|
147
|
+
exports.getMessage = getMessage;
|
|
148
|
+
function stripMustacheTags(oldString) {
|
|
149
|
+
return oldString
|
|
150
|
+
.replace(/\n/g, ' ')
|
|
151
|
+
.replace(/{{.*?}}/g, '\n')
|
|
152
|
+
.replace(/\$\$LINK_DELIM\$\$/g, '\n')
|
|
153
|
+
.replace(/\s+/g, ' ')
|
|
154
|
+
.trim();
|
|
155
|
+
}
|
|
156
|
+
exports.stripMustacheTags = stripMustacheTags;
|
package/dist/scan/scanConfig.js
CHANGED
|
@@ -3,9 +3,24 @@ const paramHandler = require('../utils/paramsUtil/paramHandler');
|
|
|
3
3
|
const constants = require('../../src/constants.js');
|
|
4
4
|
const parsedCLIOptions = require('../../src/utils/parsedCLIOptions');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { supportedLanguages } = require('../audit/languageAnalysisEngine/constants');
|
|
7
|
+
const i18n = require('i18n');
|
|
8
|
+
const { scanUsageGuide } = require('./help');
|
|
6
9
|
const getScanConfig = argv => {
|
|
7
10
|
let scanParams = parsedCLIOptions.getCommandLineArgsCustom(argv, constants.commandLineDefinitions.scanOptionDefinitions);
|
|
11
|
+
if (scanParams.help) {
|
|
12
|
+
printHelpMessage();
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
8
15
|
const paramsAuth = paramHandler.getAuth(scanParams);
|
|
16
|
+
if (scanParams.language) {
|
|
17
|
+
scanParams.language = scanParams.language.toUpperCase();
|
|
18
|
+
if (!Object.values(supportedLanguages).includes(scanParams.language)) {
|
|
19
|
+
console.log(`Did not recognise --language ${scanParams.language}`);
|
|
20
|
+
console.log(i18n.__('constantsHowToRunDev3'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
9
24
|
if (!scanParams.name && scanParams.file) {
|
|
10
25
|
scanParams.name = getFileName(scanParams.file);
|
|
11
26
|
}
|
|
@@ -14,7 +29,11 @@ const getScanConfig = argv => {
|
|
|
14
29
|
const getFileName = file => {
|
|
15
30
|
return file.split(path.sep).pop();
|
|
16
31
|
};
|
|
32
|
+
const printHelpMessage = () => {
|
|
33
|
+
console.log(scanUsageGuide);
|
|
34
|
+
};
|
|
17
35
|
module.exports = {
|
|
18
36
|
getScanConfig,
|
|
19
|
-
getFileName
|
|
37
|
+
getFileName,
|
|
38
|
+
printHelpMessage
|
|
20
39
|
};
|
|
@@ -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,17 +33,21 @@ 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');
|
|
50
|
+
console.log(`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
46
51
|
const projectOverview = await scanResults.returnScanProjectById(configToUse);
|
|
47
52
|
return { projectOverview, scanDetail, scanResultsInstances };
|
|
48
53
|
}
|
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)
|
|
@@ -40,6 +41,11 @@ const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinn
|
|
|
40
41
|
if (result.body.status === 'FAILED') {
|
|
41
42
|
complete = true;
|
|
42
43
|
oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.');
|
|
44
|
+
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
|
+
}
|
|
43
49
|
process.exit(1);
|
|
44
50
|
}
|
|
45
51
|
}
|
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.4",
|
|
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",
|
package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js}
RENAMED
|
@@ -10,9 +10,11 @@ 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
|
|
16
18
|
|
|
17
19
|
module.exports = exports = (err, analysis) => {
|
|
18
20
|
const { identifiedLanguageInfo } = analysis.languageAnalysis
|
|
@@ -46,19 +48,11 @@ module.exports = exports = (err, analysis) => {
|
|
|
46
48
|
console.log('\n **************CONTRAST OSS ANALYSIS BEGINS**************')
|
|
47
49
|
const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId)
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
catalogueAppId,
|
|
55
|
-
snapshotResponse.id,
|
|
56
|
-
config
|
|
57
|
-
)
|
|
58
|
-
} else {
|
|
59
|
-
await vulnerabilityReport(analysis, catalogueAppId, config)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
51
|
+
await vulnerabilityReport(analysis, catalogueAppId, snapshotResponse.id)
|
|
52
|
+
|
|
53
|
+
//should be moved to processAudit.ts once promises implemented
|
|
54
|
+
await auditSave(config)
|
|
55
|
+
|
|
62
56
|
console.log(
|
|
63
57
|
'\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n'
|
|
64
58
|
)
|
|
@@ -92,3 +86,27 @@ module.exports = exports = (err, analysis) => {
|
|
|
92
86
|
goAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
93
87
|
}
|
|
94
88
|
}
|
|
89
|
+
|
|
90
|
+
async function auditSave(config) {
|
|
91
|
+
//should be moved to processAudit.ts once promises implemented
|
|
92
|
+
if (config.save) {
|
|
93
|
+
if (config.save.toLowerCase() === 'sbom') {
|
|
94
|
+
saveFile(config, await generateSbom(config))
|
|
95
|
+
|
|
96
|
+
const filename = `${config.applicationId}-sbom-cyclonedx.json`
|
|
97
|
+
if (fs.existsSync(filename)) {
|
|
98
|
+
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`)
|
|
99
|
+
} else {
|
|
100
|
+
console.log(
|
|
101
|
+
chalk.yellow.bold(
|
|
102
|
+
`\n Unable to save ${filename} Software Bill of Materials (SBOM)`
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
console.log(i18n.__('auditBadFiletypeSpecifiedForSave'))
|
|
108
|
+
}
|
|
109
|
+
} else if (config.save === null) {
|
|
110
|
+
console.log(i18n.__('auditNoFiletypeSpecifiedForSave'))
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import i18n from 'i18n'
|
|
2
|
+
import { getHttpClient, handleResponseErrors } from '../../../utils/commonApi'
|
|
3
|
+
import {
|
|
4
|
+
ReportCompositeKey,
|
|
5
|
+
ReportList,
|
|
6
|
+
ReportModelStructure
|
|
7
|
+
} from './models/reportListModel'
|
|
8
|
+
import { ReportSeverityModel } from './models/reportSeverityModel'
|
|
9
|
+
import { orderBy } from 'lodash'
|
|
10
|
+
import chalk from 'chalk'
|
|
11
|
+
import { ReportLibraryModel } from './models/reportLibraryModel'
|
|
12
|
+
import { findHighestSeverityCVE, findNameAndVersion } from './utils/reportUtils'
|
|
13
|
+
import {
|
|
14
|
+
failSpinner,
|
|
15
|
+
returnOra,
|
|
16
|
+
startSpinner,
|
|
17
|
+
succeedSpinner
|
|
18
|
+
} from '../../../utils/oraWrapper'
|
|
19
|
+
|
|
20
|
+
export const createLibraryHeader = (
|
|
21
|
+
id: string,
|
|
22
|
+
numberOfVulnerableLibraries: number,
|
|
23
|
+
numberOfCves: number,
|
|
24
|
+
name: string
|
|
25
|
+
) => {
|
|
26
|
+
name
|
|
27
|
+
? console.log(`\n Application Name: ${name} | Application ID: ${id}`)
|
|
28
|
+
: console.log(` Application ID: ${id}`)
|
|
29
|
+
|
|
30
|
+
numberOfVulnerableLibraries === 1
|
|
31
|
+
? console.log(
|
|
32
|
+
'\n **************************' +
|
|
33
|
+
` Found 1 vulnerable library containing ${numberOfCves} CVE's` +
|
|
34
|
+
'************************** '
|
|
35
|
+
)
|
|
36
|
+
: console.log(
|
|
37
|
+
'\n **************************' +
|
|
38
|
+
` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's ` +
|
|
39
|
+
'************************** '
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const getReport = async (config: any, reportId: string) => {
|
|
44
|
+
const client = getHttpClient(config)
|
|
45
|
+
|
|
46
|
+
const reportSpinner = returnOra(i18n.__('auditReportWaiting'))
|
|
47
|
+
reportSpinner.indent = 1
|
|
48
|
+
startSpinner(reportSpinner)
|
|
49
|
+
return client
|
|
50
|
+
.getReportById(config, reportId)
|
|
51
|
+
.then((res: { statusCode: number; body: any }) => {
|
|
52
|
+
if (res.statusCode === 200) {
|
|
53
|
+
succeedSpinner(reportSpinner, i18n.__('auditReportSuccessMessage'))
|
|
54
|
+
return res.body
|
|
55
|
+
} else {
|
|
56
|
+
failSpinner(reportSpinner, i18n.__('auditReportFail'))
|
|
57
|
+
console.log('config-------------------')
|
|
58
|
+
console.log(config)
|
|
59
|
+
console.log('reportId----------------')
|
|
60
|
+
console.log(reportId)
|
|
61
|
+
console.log(JSON.stringify(res))
|
|
62
|
+
handleResponseErrors(res, 'report')
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.catch((err: any) => {
|
|
66
|
+
console.log(err)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const printVulnerabilityResponse = (
|
|
71
|
+
vulnerabilities: ReportLibraryModel[],
|
|
72
|
+
config: any
|
|
73
|
+
) => {
|
|
74
|
+
let hasSomeVulnerabilitiesReported = false
|
|
75
|
+
printFormattedOutput(vulnerabilities, config)
|
|
76
|
+
if (Object.keys(vulnerabilities).length > 0) {
|
|
77
|
+
hasSomeVulnerabilitiesReported = true
|
|
78
|
+
}
|
|
79
|
+
return hasSomeVulnerabilitiesReported
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const printFormattedOutput = (
|
|
83
|
+
libraries: ReportLibraryModel[],
|
|
84
|
+
config: any
|
|
85
|
+
) => {
|
|
86
|
+
const report = new ReportList()
|
|
87
|
+
|
|
88
|
+
for (const library of libraries) {
|
|
89
|
+
const { name, version } = findNameAndVersion(library, config)
|
|
90
|
+
|
|
91
|
+
const newOutputModel = new ReportModelStructure(
|
|
92
|
+
new ReportCompositeKey(
|
|
93
|
+
name,
|
|
94
|
+
version,
|
|
95
|
+
findHighestSeverityCVE(library.cveArray) as ReportSeverityModel
|
|
96
|
+
),
|
|
97
|
+
library.cveArray
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
report.reportOutputList.push(newOutputModel)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const orderedOutputList = orderBy(
|
|
104
|
+
report.reportOutputList,
|
|
105
|
+
reportListItem => reportListItem.compositeKey.highestSeverity.priority
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
for (const reportModel of orderedOutputList) {
|
|
109
|
+
const name = reportModel.compositeKey.libraryName
|
|
110
|
+
const version = reportModel.compositeKey.libraryVersion
|
|
111
|
+
const highestSeverity = reportModel.compositeKey.highestSeverity.severity
|
|
112
|
+
|
|
113
|
+
const numOfCVEs = reportModel.cveArray.length
|
|
114
|
+
|
|
115
|
+
const cveNames: string[] = []
|
|
116
|
+
|
|
117
|
+
reportModel.cveArray.forEach(cve => cveNames.push(cve.name as string))
|
|
118
|
+
|
|
119
|
+
const boldHeader = chalk.bold(`${highestSeverity} | Vulnerable Library`)
|
|
120
|
+
const cvePluralised = numOfCVEs > 1 ? 'CVEs' : 'CVE'
|
|
121
|
+
console.log(
|
|
122
|
+
`\n ${boldHeader} ${name} (${version}) has ${numOfCVEs} known ${cvePluralised}`
|
|
123
|
+
)
|
|
124
|
+
console.log(` ${cveNames.join(', ')}`)
|
|
125
|
+
console.log(chalk.bold(' How to fix: Update to latest version'))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -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
|
+
}
|