@contrast/contrast 1.0.5 → 1.0.6
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 +1 -0
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +5 -1
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +66 -13
- package/dist/audit/languageAnalysisEngine/report/models/reportOutputModel.js +24 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +3 -1
- package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +13 -0
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +1 -1
- package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +59 -45
- package/dist/commands/audit/auditController.js +1 -1
- package/dist/commands/scan/processScan.js +1 -1
- package/dist/commands/scan/sca/scaAnalysis.js +12 -7
- package/dist/constants/constants.js +13 -3
- package/dist/scaAnalysis/common/formatMessage.js +9 -1
- package/dist/scaAnalysis/common/treeUpload.js +2 -3
- package/dist/scaAnalysis/go/goAnalysis.js +17 -0
- package/dist/scaAnalysis/go/goParseDeps.js +158 -0
- package/dist/scaAnalysis/go/goReadDepFile.js +23 -0
- package/dist/scaAnalysis/java/analysis.js +6 -14
- package/dist/scaAnalysis/java/index.js +5 -5
- package/dist/scaAnalysis/java/javaBuildDepsParser.js +14 -1
- package/dist/scan/formatScanOutput.js +10 -7
- package/dist/scan/scan.js +4 -3
- package/dist/scan/scanController.js +11 -4
- package/dist/scan/scanResults.js +15 -5
- package/dist/utils/oraWrapper.js +5 -1
- package/package.json +1 -1
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +6 -5
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +117 -18
- package/src/audit/languageAnalysisEngine/report/models/reportOutputModel.ts +29 -0
- package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +7 -3
- package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +16 -0
- package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +2 -2
- package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +74 -68
- package/src/commands/audit/auditController.ts +1 -2
- package/src/commands/scan/processScan.js +2 -1
- package/src/commands/scan/sca/scaAnalysis.js +12 -10
- package/src/constants/constants.js +13 -3
- package/src/scaAnalysis/common/formatMessage.js +11 -1
- package/src/scaAnalysis/common/treeUpload.js +2 -6
- package/src/scaAnalysis/go/goAnalysis.js +20 -0
- package/src/scaAnalysis/go/goParseDeps.js +203 -0
- package/src/scaAnalysis/go/goReadDepFile.js +32 -0
- package/src/scaAnalysis/java/analysis.js +8 -24
- package/src/scaAnalysis/java/index.js +5 -5
- package/src/scaAnalysis/java/javaBuildDepsParser.js +15 -2
- package/src/scan/formatScanOutput.ts +17 -8
- package/src/scan/scan.ts +7 -6
- package/src/scan/scanController.js +13 -6
- package/src/scan/scanResults.js +21 -10
- package/src/utils/oraWrapper.js +6 -1
|
@@ -21,7 +21,7 @@ const determineProjectTypeAndCwd = (files, projectPath) => {
|
|
|
21
21
|
}
|
|
22
22
|
return projectData;
|
|
23
23
|
};
|
|
24
|
-
const buildMaven =
|
|
24
|
+
const buildMaven = (config, projectData, timeout) => {
|
|
25
25
|
let cmdStdout;
|
|
26
26
|
let mvn_settings = '';
|
|
27
27
|
try {
|
|
@@ -35,16 +35,7 @@ const buildMaven = async (config, projectData, timeout) => {
|
|
|
35
35
|
return cmdStdout.toString();
|
|
36
36
|
}
|
|
37
37
|
catch (err) {
|
|
38
|
-
|
|
39
|
-
child_process.execSync('mvn --version', {
|
|
40
|
-
cwd: projectData.cwd,
|
|
41
|
-
timeout
|
|
42
|
-
});
|
|
43
|
-
throw new Error(i18n.__('mavenDependencyTreeNonZero', projectData.cwd, `${err.message}`));
|
|
44
|
-
}
|
|
45
|
-
catch (mvnErr) {
|
|
46
|
-
throw new Error(i18n.__('mavenNotInstalledError', projectData.cwd, `${mvnErr.message}`));
|
|
47
|
-
}
|
|
38
|
+
throw new Error(i18n.__('mavenDependencyTreeNonZero', projectData.cwd, `${err.message}`));
|
|
48
39
|
}
|
|
49
40
|
};
|
|
50
41
|
const buildGradle = (config, projectData, timeout) => {
|
|
@@ -78,7 +69,7 @@ const buildGradle = (config, projectData, timeout) => {
|
|
|
78
69
|
timeout
|
|
79
70
|
});
|
|
80
71
|
}
|
|
81
|
-
output
|
|
72
|
+
output = cmdStdout.toString();
|
|
82
73
|
return output;
|
|
83
74
|
}
|
|
84
75
|
catch (err) {
|
|
@@ -91,7 +82,7 @@ const buildGradle = (config, projectData, timeout) => {
|
|
|
91
82
|
}
|
|
92
83
|
}
|
|
93
84
|
};
|
|
94
|
-
const getJavaBuildDeps =
|
|
85
|
+
const getJavaBuildDeps = (config, files) => {
|
|
95
86
|
const timeout = 960000;
|
|
96
87
|
let output = {
|
|
97
88
|
mvnDependancyTreeOutput: undefined,
|
|
@@ -100,7 +91,7 @@ const getJavaBuildDeps = async (config, files) => {
|
|
|
100
91
|
try {
|
|
101
92
|
const projectData = determineProjectTypeAndCwd(files, config.projectPath);
|
|
102
93
|
if (projectData.projectType === MAVEN) {
|
|
103
|
-
output.mvnDependancyTreeOutput =
|
|
94
|
+
output.mvnDependancyTreeOutput = buildMaven(config, projectData, timeout);
|
|
104
95
|
}
|
|
105
96
|
else if (projectData.projectType === GRADLE) {
|
|
106
97
|
output.mvnDependancyTreeOutput = buildGradle(config, projectData, timeout);
|
|
@@ -109,6 +100,7 @@ const getJavaBuildDeps = async (config, files) => {
|
|
|
109
100
|
return output;
|
|
110
101
|
}
|
|
111
102
|
catch (err) {
|
|
103
|
+
console.log(err.message.toString());
|
|
112
104
|
}
|
|
113
105
|
};
|
|
114
106
|
module.exports = {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
2
|
+
const analysis = require('./analysis');
|
|
3
3
|
const { parseBuildDeps } = require('./javaBuildDepsParser');
|
|
4
4
|
const { createJavaTSMessage } = require('../common/formatMessage');
|
|
5
|
-
const javaAnalysis =
|
|
5
|
+
const javaAnalysis = (config, languageFiles) => {
|
|
6
6
|
languageFiles.java.forEach(file => {
|
|
7
7
|
file.replace('build.gradle.kts', 'build.gradle');
|
|
8
8
|
});
|
|
9
|
-
const javaDeps =
|
|
9
|
+
const javaDeps = buildJavaTree(config, languageFiles.java);
|
|
10
10
|
return createJavaTSMessage(javaDeps);
|
|
11
11
|
};
|
|
12
|
-
const buildJavaTree =
|
|
13
|
-
const javaBuildDeps =
|
|
12
|
+
const buildJavaTree = (config, files) => {
|
|
13
|
+
const javaBuildDeps = analysis.getJavaBuildDeps(config, files);
|
|
14
14
|
return parseBuildDeps(config, javaBuildDeps);
|
|
15
15
|
};
|
|
16
16
|
module.exports = {
|
|
@@ -322,5 +322,18 @@ const parseGradle = (gradleDependencyTreeOutput, config, projectType) => {
|
|
|
322
322
|
}
|
|
323
323
|
};
|
|
324
324
|
module.exports = {
|
|
325
|
-
parseBuildDeps
|
|
325
|
+
parseBuildDeps,
|
|
326
|
+
shaveOutput,
|
|
327
|
+
validateIndentation,
|
|
328
|
+
calculateLevels,
|
|
329
|
+
lastChild,
|
|
330
|
+
hasChildren,
|
|
331
|
+
getElementHeader,
|
|
332
|
+
createElement,
|
|
333
|
+
stripElement,
|
|
334
|
+
checkVersion,
|
|
335
|
+
computeRelationToLastElement,
|
|
336
|
+
addIndentation,
|
|
337
|
+
computeLevel,
|
|
338
|
+
computeIndentation
|
|
326
339
|
};
|
|
@@ -12,7 +12,7 @@ const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
|
12
12
|
const constants_1 = require("../constants/constants");
|
|
13
13
|
function formatScanOutput(scanResults) {
|
|
14
14
|
const { scanResultsInstances } = scanResults;
|
|
15
|
-
let projectOverview = getProjectOverview(scanResultsInstances
|
|
15
|
+
let projectOverview = getProjectOverview(scanResultsInstances);
|
|
16
16
|
if (scanResultsInstances.content.length === 0) {
|
|
17
17
|
console.log(i18n_1.default.__('scanNoVulnerabilitiesFound'));
|
|
18
18
|
console.log(i18n_1.default.__('scanNoVulnerabilitiesFoundSecureCode'));
|
|
@@ -97,7 +97,7 @@ function printVulnInfo(projectOverview) {
|
|
|
97
97
|
console.log(chalk_1.default.bold(`Found ${totalVulnerabilities} ${vulMessage}`));
|
|
98
98
|
console.log(i18n_1.default.__('foundDetailedVulnerabilities', String(projectOverview.critical), String(projectOverview.high), String(projectOverview.medium), String(projectOverview.low), String(projectOverview.note)));
|
|
99
99
|
}
|
|
100
|
-
function getProjectOverview(
|
|
100
|
+
function getProjectOverview(scanResultsInstances) {
|
|
101
101
|
let acc = {
|
|
102
102
|
critical: 0,
|
|
103
103
|
high: 0,
|
|
@@ -106,11 +106,14 @@ function getProjectOverview(content) {
|
|
|
106
106
|
note: 0,
|
|
107
107
|
total: 0
|
|
108
108
|
};
|
|
109
|
-
content
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
if (scanResultsInstances?.content &&
|
|
110
|
+
scanResultsInstances.content.length > 0) {
|
|
111
|
+
scanResultsInstances.content.forEach((i) => {
|
|
112
|
+
acc[i.severity.toLowerCase()] += 1;
|
|
113
|
+
acc.total += 1;
|
|
114
|
+
return acc;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
114
117
|
return acc;
|
|
115
118
|
}
|
|
116
119
|
exports.getProjectOverview = getProjectOverview;
|
package/dist/scan/scan.js
CHANGED
|
@@ -41,15 +41,16 @@ const sendScan = async (config) => {
|
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
43
|
if (config.debug) {
|
|
44
|
-
console.log(res.statusCode);
|
|
45
44
|
console.log(config);
|
|
45
|
+
oraWrapper_1.default.failSpinner(startUploadSpinner, i18n_1.default.__('uploadingScanFail'));
|
|
46
|
+
console.log(i18n_1.default.__('genericServiceError', res.statusCode));
|
|
46
47
|
}
|
|
47
|
-
oraWrapper_1.default.failSpinner(startUploadSpinner, i18n_1.default.__('uploadingScanFail'));
|
|
48
48
|
if (res.statusCode === 403) {
|
|
49
49
|
console.log(i18n_1.default.__('permissionsError'));
|
|
50
50
|
process.exit(1);
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
oraWrapper_1.default.stopSpinner(startUploadSpinner);
|
|
53
|
+
console.log('Contrast Scan Finished');
|
|
53
54
|
process.exit(1);
|
|
54
55
|
}
|
|
55
56
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const i18n = require('i18n');
|
|
3
|
-
const { returnOra, startSpinner, succeedSpinner } = require('../utils/oraWrapper');
|
|
3
|
+
const { returnOra, startSpinner, succeedSpinner, stopSpinner } = require('../utils/oraWrapper');
|
|
4
4
|
const populateProjectIdAndProjectName = require('./populateProjectIdAndProjectName');
|
|
5
5
|
const scan = require('./scan');
|
|
6
6
|
const scanResults = require('./scanResults');
|
|
@@ -46,9 +46,16 @@ const startScan = async (configToUse) => {
|
|
|
46
46
|
const scanResultsInstances = await scanResults.returnScanResultsInstances(configToUse, scanDetail.id);
|
|
47
47
|
const endTime = performance.now();
|
|
48
48
|
const scanDurationMs = endTime - startTime;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (scanResultsInstances.statusCode !== 200) {
|
|
50
|
+
stopSpinner(startScanSpinner);
|
|
51
|
+
console.log('Result Service is unavailable, please try again later');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
succeedSpinner(startScanSpinner, 'Contrast Scan complete');
|
|
56
|
+
console.log(`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
57
|
+
return { scanDetail, scanResultsInstances: scanResultsInstances.body };
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
60
|
};
|
|
54
61
|
module.exports = {
|
package/dist/scan/scanResults.js
CHANGED
|
@@ -4,6 +4,7 @@ const requestUtils = require('../../src/utils/requestUtils');
|
|
|
4
4
|
const oraFunctions = require('../utils/oraWrapper');
|
|
5
5
|
const _ = require('lodash');
|
|
6
6
|
const i18n = require('i18n');
|
|
7
|
+
const oraWrapper = require('../utils/oraWrapper');
|
|
7
8
|
const getScanId = async (config, codeArtifactId, client) => {
|
|
8
9
|
return client
|
|
9
10
|
.getScanId(config, codeArtifactId)
|
|
@@ -40,12 +41,16 @@ const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinn
|
|
|
40
41
|
}
|
|
41
42
|
if (result.body.status === 'FAILED') {
|
|
42
43
|
complete = true;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if (config.debug) {
|
|
45
|
+
oraFunctions.failSpinner(startScanSpinner, i18n.__('scanNotCompleted', 'https://docs.contrastsecurity.com/en/binary-package-preparation.html'));
|
|
46
|
+
}
|
|
47
|
+
if (result?.body?.errorMessage ===
|
|
46
48
|
'Unable to determine language for code artifact') {
|
|
49
|
+
console.log(result.body.errorMessage);
|
|
47
50
|
console.log('Try scanning again using --language param. ', i18n.__('scanOptionsLanguageSummary'));
|
|
48
51
|
}
|
|
52
|
+
oraWrapper.stopSpinner(startScanSpinner);
|
|
53
|
+
console.log('Contrast Scan Finished');
|
|
49
54
|
process.exit(1);
|
|
50
55
|
}
|
|
51
56
|
}
|
|
@@ -64,11 +69,16 @@ const returnScanResultsInstances = async (config, scanId) => {
|
|
|
64
69
|
try {
|
|
65
70
|
result = await client.getScanResultsInstances(config, scanId);
|
|
66
71
|
if (JSON.stringify(result.statusCode) == 200) {
|
|
67
|
-
return result.body;
|
|
72
|
+
return { body: result.body, statusCode: result.statusCode };
|
|
73
|
+
}
|
|
74
|
+
if (JSON.stringify(result.statusCode) == 503) {
|
|
75
|
+
return { statusCode: result.statusCode };
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
catch (e) {
|
|
71
|
-
|
|
79
|
+
if (config.debug) {
|
|
80
|
+
console.log(e.message.toString());
|
|
81
|
+
}
|
|
72
82
|
}
|
|
73
83
|
};
|
|
74
84
|
module.exports = {
|
package/dist/utils/oraWrapper.js
CHANGED
|
@@ -6,6 +6,9 @@ const returnOra = text => {
|
|
|
6
6
|
const startSpinner = spinner => {
|
|
7
7
|
spinner.start();
|
|
8
8
|
};
|
|
9
|
+
const stopSpinner = spinner => {
|
|
10
|
+
spinner.stop();
|
|
11
|
+
};
|
|
9
12
|
const succeedSpinner = (spinner, text) => {
|
|
10
13
|
spinner.succeed(text);
|
|
11
14
|
};
|
|
@@ -16,5 +19,6 @@ module.exports = {
|
|
|
16
19
|
returnOra,
|
|
17
20
|
startSpinner,
|
|
18
21
|
succeedSpinner,
|
|
19
|
-
failSpinner
|
|
22
|
+
failSpinner,
|
|
23
|
+
stopSpinner
|
|
20
24
|
};
|
package/package.json
CHANGED
|
@@ -72,7 +72,7 @@ const deduceLanguageScaAnalysis = filenames => {
|
|
|
72
72
|
//
|
|
73
73
|
if (isNodeLockFilename(filename)) {
|
|
74
74
|
deducedLanguages.push(filename)
|
|
75
|
-
language =
|
|
75
|
+
language = NODE
|
|
76
76
|
}
|
|
77
77
|
//
|
|
78
78
|
// if (isRubyLockFilename(filename)) {
|
|
@@ -88,10 +88,11 @@ const deduceLanguageScaAnalysis = filenames => {
|
|
|
88
88
|
// deducedLanguages.push({language: PHP, lockFilename: filename})
|
|
89
89
|
// }
|
|
90
90
|
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
// go does not have a lockfile, it should have a go.mod file containing the modules
|
|
92
|
+
if (isGoProjectFilename(filename)) {
|
|
93
|
+
deducedLanguages.push({ language: GO, projectFilename: filename })
|
|
94
|
+
language = GO
|
|
95
|
+
}
|
|
95
96
|
})
|
|
96
97
|
let identifiedLanguages = { [language]: deducedLanguages }
|
|
97
98
|
|
|
@@ -7,8 +7,19 @@ import {
|
|
|
7
7
|
import { ReportSeverityModel } from './models/reportSeverityModel'
|
|
8
8
|
import { orderBy } from 'lodash'
|
|
9
9
|
import chalk from 'chalk'
|
|
10
|
-
import { ReportLibraryModel } from './models/reportLibraryModel'
|
|
11
|
-
import {
|
|
10
|
+
import { ReportCVEModel, ReportLibraryModel } from './models/reportLibraryModel'
|
|
11
|
+
import {
|
|
12
|
+
findCVESeveritiesAndOrderByHighestPriority,
|
|
13
|
+
findHighestSeverityCVE,
|
|
14
|
+
findNameAndVersion,
|
|
15
|
+
severityCountAllCVEs
|
|
16
|
+
} from './utils/reportUtils'
|
|
17
|
+
import {SeverityCountModel} from "./models/severityCountModel";
|
|
18
|
+
import {
|
|
19
|
+
ReportOutputBodyModel,
|
|
20
|
+
ReportOutputHeaderModel,
|
|
21
|
+
ReportOutputModel
|
|
22
|
+
} from "./models/reportOutputModel";
|
|
12
23
|
|
|
13
24
|
export const createLibraryHeader = (
|
|
14
25
|
id: string,
|
|
@@ -78,28 +89,116 @@ export const printFormattedOutput = (
|
|
|
78
89
|
report.reportOutputList.push(newOutputModel)
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
const
|
|
92
|
+
const orderedOutputListLowestFirst = orderBy(
|
|
82
93
|
report.reportOutputList,
|
|
83
|
-
reportListItem => reportListItem.compositeKey.highestSeverity.priority
|
|
94
|
+
reportListItem => reportListItem.compositeKey.highestSeverity.priority,
|
|
95
|
+
['desc']
|
|
84
96
|
)
|
|
85
97
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const highestSeverity = reportModel.compositeKey
|
|
90
|
-
|
|
98
|
+
let contrastHeaderNumCounter = 0
|
|
99
|
+
for (const reportModel of orderedOutputListLowestFirst) {
|
|
100
|
+
contrastHeaderNumCounter++
|
|
101
|
+
const {libraryName, libraryVersion, highestSeverity} = reportModel.compositeKey
|
|
91
102
|
const numOfCVEs = reportModel.cveArray.length
|
|
92
103
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
const header = buildHeader(
|
|
105
|
+
highestSeverity,
|
|
106
|
+
contrastHeaderNumCounter,
|
|
107
|
+
libraryName,
|
|
108
|
+
libraryVersion,
|
|
109
|
+
numOfCVEs
|
|
110
|
+
)
|
|
96
111
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
console.log(
|
|
100
|
-
`\n ${boldHeader} ${name} (${version}) has ${numOfCVEs} known ${cvePluralised}`
|
|
112
|
+
const body = buildBody(
|
|
113
|
+
reportModel.cveArray
|
|
101
114
|
)
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
|
|
116
|
+
const reportOutputModel = new ReportOutputModel(header, body)
|
|
117
|
+
console.log(reportOutputModel.header.vulnMessage, reportOutputModel.header.introducesMessage)
|
|
118
|
+
console.log(reportOutputModel.body.issueMessage)
|
|
119
|
+
console.log(reportOutputModel.body.adviceMessage)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function buildHeader(
|
|
124
|
+
highestSeverity: ReportSeverityModel,
|
|
125
|
+
contrastHeaderNum: number,
|
|
126
|
+
libraryName: string,
|
|
127
|
+
version: string,
|
|
128
|
+
numOfCVEs: number,
|
|
129
|
+
) {
|
|
130
|
+
const vulnerabilityPluralised = numOfCVEs > 1 ? 'Vulnerabilities' : 'Vulnerability'
|
|
131
|
+
const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum)
|
|
132
|
+
|
|
133
|
+
const vulnMessage = chalk
|
|
134
|
+
.hex(highestSeverity.outputColour)
|
|
135
|
+
.bold(`${formattedHeaderNum} - [${highestSeverity.severity}] ${libraryName}-${version}`)
|
|
136
|
+
|
|
137
|
+
const introducesMessage = chalk.bold(
|
|
138
|
+
`introduces ${numOfCVEs} ${vulnerabilityPluralised}`
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return new ReportOutputHeaderModel(vulnMessage, introducesMessage)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function buildBody(cveArray: ReportCVEModel[]) {
|
|
145
|
+
const cveMessages: string[] = []
|
|
146
|
+
|
|
147
|
+
findCVESeveritiesAndOrderByHighestPriority(cveArray).forEach(reportSeverityModel => {
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
const {outputColour, severity, cveName} = reportSeverityModel
|
|
150
|
+
|
|
151
|
+
const severityShorthand = chalk
|
|
152
|
+
.hex(outputColour)
|
|
153
|
+
.bold(`[${severity.charAt(0).toUpperCase()}]`)
|
|
154
|
+
|
|
155
|
+
const builtMessage = `${severityShorthand} ${cveName}`
|
|
156
|
+
cveMessages.push(builtMessage)
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const numAndSeverityType = getNumOfAndSeverityType(cveArray)
|
|
161
|
+
|
|
162
|
+
const issueMessage =
|
|
163
|
+
` ${chalk.bold('Issue')} : ${numAndSeverityType} ${cveMessages.join(', ')}.`
|
|
164
|
+
|
|
165
|
+
const adviceMessage =
|
|
166
|
+
` ${chalk.bold('Advice')} : ${chalk.bold('Update to latest version')}.`
|
|
167
|
+
|
|
168
|
+
return new ReportOutputBodyModel(issueMessage, adviceMessage)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function buildFormattedHeaderNum(contrastHeaderNum: number) {
|
|
172
|
+
let formattedHeaderNum
|
|
173
|
+
|
|
174
|
+
if (contrastHeaderNum < 10) {
|
|
175
|
+
formattedHeaderNum = `00${contrastHeaderNum}`
|
|
176
|
+
} else if (contrastHeaderNum >= 10 && contrastHeaderNum < 100) {
|
|
177
|
+
formattedHeaderNum = `0${contrastHeaderNum}`
|
|
178
|
+
} else if (contrastHeaderNum >= 100) {
|
|
179
|
+
formattedHeaderNum = contrastHeaderNum
|
|
104
180
|
}
|
|
181
|
+
|
|
182
|
+
return `CONTRAST-${formattedHeaderNum}`
|
|
105
183
|
}
|
|
184
|
+
|
|
185
|
+
export function getNumOfAndSeverityType(cveArray: ReportCVEModel[]) {
|
|
186
|
+
const {
|
|
187
|
+
critical,
|
|
188
|
+
high,
|
|
189
|
+
medium,
|
|
190
|
+
low,
|
|
191
|
+
note
|
|
192
|
+
} = severityCountAllCVEs(cveArray, new SeverityCountModel())
|
|
193
|
+
|
|
194
|
+
const criticalMessage = critical > 0 ? `${critical} Critical` : ''
|
|
195
|
+
const highMessage = high > 0 ? `${high} High` : ''
|
|
196
|
+
const mediumMessage = medium > 0 ? `${medium} Medium` : ''
|
|
197
|
+
const lowMessage = low > 0 ? `${low} Low` : ''
|
|
198
|
+
const noteMessage = note > 0 ? `${note} Note` : ''
|
|
199
|
+
|
|
200
|
+
//removes/trims whitespace to single spaces
|
|
201
|
+
return `${criticalMessage} ${highMessage} ${mediumMessage} ${lowMessage} ${noteMessage}`
|
|
202
|
+
.replace(/\s+/g, ' ')
|
|
203
|
+
.trim();
|
|
204
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class ReportOutputModel {
|
|
2
|
+
header: ReportOutputHeaderModel
|
|
3
|
+
body: ReportOutputBodyModel
|
|
4
|
+
|
|
5
|
+
constructor(header: ReportOutputHeaderModel, body: ReportOutputBodyModel) {
|
|
6
|
+
this.header = header
|
|
7
|
+
this.body = body
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ReportOutputHeaderModel {
|
|
12
|
+
vulnMessage: string
|
|
13
|
+
introducesMessage: string
|
|
14
|
+
|
|
15
|
+
constructor(vulnMessage: string, introducesMessage: string) {
|
|
16
|
+
this.vulnMessage = vulnMessage
|
|
17
|
+
this.introducesMessage = introducesMessage
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class ReportOutputBodyModel {
|
|
22
|
+
issueMessage: string
|
|
23
|
+
adviceMessage: string
|
|
24
|
+
|
|
25
|
+
constructor(bodyIssueMessage: string, bodyAdviceMessage: string) {
|
|
26
|
+
this.issueMessage = bodyIssueMessage
|
|
27
|
+
this.adviceMessage = bodyAdviceMessage
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export class ReportSeverityModel {
|
|
2
|
-
severity
|
|
3
|
-
priority
|
|
2
|
+
severity: string
|
|
3
|
+
priority: number
|
|
4
|
+
outputColour: string
|
|
5
|
+
cveName: string
|
|
4
6
|
|
|
5
|
-
constructor(severity: string, priority: number) {
|
|
7
|
+
constructor(severity: string, priority: number, outputColour: string, cveName: string) {
|
|
6
8
|
this.severity = severity
|
|
7
9
|
this.priority = priority
|
|
10
|
+
this.outputColour = outputColour
|
|
11
|
+
this.cveName = cveName
|
|
8
12
|
}
|
|
9
13
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class SeverityCountModel {
|
|
2
|
+
critical!: number
|
|
3
|
+
high!: number
|
|
4
|
+
medium!: number
|
|
5
|
+
low!: number
|
|
6
|
+
note!: number
|
|
7
|
+
|
|
8
|
+
//needed as default to stop NaN when new object constructed
|
|
9
|
+
constructor() {
|
|
10
|
+
this.critical = 0
|
|
11
|
+
this.high = 0
|
|
12
|
+
this.medium = 0
|
|
13
|
+
this.low = 0
|
|
14
|
+
this.note = 0
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from './commonReportingFunctions'
|
|
6
6
|
import {
|
|
7
7
|
convertGenericToTypedLibraries,
|
|
8
|
-
|
|
8
|
+
severityCountAllLibraries
|
|
9
9
|
} from './utils/reportUtils'
|
|
10
10
|
|
|
11
11
|
export async function vulnerabilityReport(
|
|
@@ -51,6 +51,6 @@ export function formatVulnerabilityOutput(
|
|
|
51
51
|
return [
|
|
52
52
|
hasSomeVulnerabilitiesReported,
|
|
53
53
|
numberOfCves,
|
|
54
|
-
|
|
54
|
+
severityCountAllLibraries(vulnerableLibraries)
|
|
55
55
|
]
|
|
56
56
|
}
|