@contrast/contrast 1.0.3 → 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 +4 -0
- package/README.md +20 -14
- package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
- package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
- package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +6 -14
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +29 -0
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +101 -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/reportOutputModel.js +24 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +12 -0
- package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +13 -0
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
- package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +99 -0
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +2 -14
- package/dist/commands/audit/auditConfig.js +8 -2
- package/dist/commands/audit/auditController.js +14 -5
- package/dist/commands/scan/processScan.js +10 -6
- package/dist/commands/scan/sca/scaAnalysis.js +49 -0
- package/dist/common/HTTPClient.js +18 -26
- package/dist/common/errorHandling.js +7 -17
- package/dist/common/versionChecker.js +14 -12
- package/dist/constants/constants.js +24 -2
- package/dist/constants/lambda.js +3 -1
- package/dist/constants/locales.js +42 -42
- package/dist/constants.js +25 -1
- package/dist/index.js +2 -2
- package/dist/lambda/help.js +22 -14
- package/dist/lambda/lambda.js +6 -0
- package/dist/scaAnalysis/common/formatMessage.js +19 -0
- package/dist/scaAnalysis/common/treeUpload.js +29 -0
- 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 +108 -0
- package/dist/scaAnalysis/java/index.js +18 -0
- package/dist/scaAnalysis/java/javaBuildDepsParser.js +339 -0
- package/dist/scan/autoDetection.js +46 -1
- package/dist/scan/fileUtils.js +73 -1
- package/dist/scan/formatScanOutput.js +215 -0
- package/dist/scan/help.js +3 -1
- package/dist/scan/models/groupedResultsModel.js +11 -0
- package/dist/scan/models/resultContentModel.js +2 -0
- package/dist/scan/models/scanResultsModel.js +11 -0
- package/dist/scan/scan.js +27 -126
- package/dist/scan/scanConfig.js +1 -1
- package/dist/scan/scanController.js +11 -5
- package/dist/scan/scanResults.js +15 -19
- package/dist/utils/getConfig.js +3 -0
- package/dist/utils/oraWrapper.js +5 -1
- package/package.json +3 -2
- package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
- package/src/audit/catalogueApplication/catalogueApplication.js +4 -16
- package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +11 -21
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +72 -0
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +204 -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/reportOutputModel.ts +29 -0
- package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +13 -0
- package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +16 -0
- package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
- package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +116 -0
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +2 -22
- package/src/commands/audit/auditConfig.ts +12 -3
- package/src/commands/audit/auditController.ts +20 -5
- package/src/commands/audit/processAudit.ts +3 -0
- package/src/commands/scan/processScan.js +13 -9
- package/src/commands/scan/sca/scaAnalysis.js +75 -0
- package/src/common/HTTPClient.js +31 -38
- package/src/common/errorHandling.ts +7 -25
- package/src/common/versionChecker.ts +24 -22
- package/src/constants/constants.js +24 -2
- package/src/constants/lambda.js +3 -1
- package/src/constants/locales.js +47 -56
- package/src/constants.js +29 -1
- package/src/index.ts +2 -3
- package/src/lambda/help.ts +22 -14
- package/src/lambda/lambda.ts +8 -0
- package/src/scaAnalysis/common/formatMessage.js +20 -0
- package/src/scaAnalysis/common/treeUpload.js +30 -0
- 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 +143 -0
- package/src/scaAnalysis/java/index.js +21 -0
- package/src/scaAnalysis/java/javaBuildDepsParser.js +404 -0
- package/src/scan/autoDetection.js +54 -1
- package/src/scan/fileUtils.js +91 -1
- package/src/scan/formatScanOutput.ts +250 -0
- package/src/scan/help.js +3 -1
- package/src/scan/models/groupedResultsModel.ts +20 -0
- package/src/scan/models/resultContentModel.ts +86 -0
- package/src/scan/models/scanResultsModel.ts +52 -0
- package/src/scan/scan.ts +63 -0
- package/src/scan/scanConfig.js +1 -1
- package/src/scan/scanController.js +15 -7
- package/src/scan/scanResults.js +21 -18
- package/src/utils/getConfig.ts +10 -0
- package/src/utils/oraWrapper.js +6 -1
- package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
- package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
- 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/scan/scan.js +0 -195
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,30 +69,21 @@ 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 };
|
|
68
73
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.log(e.message.toString());
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const returnScanProjectById = async (config) => {
|
|
75
|
-
const client = commonApi.getHttpClient(config);
|
|
76
|
-
let result;
|
|
77
|
-
try {
|
|
78
|
-
result = await client.getScanProjectById(config);
|
|
79
|
-
if (JSON.stringify(result.statusCode) == 200) {
|
|
80
|
-
return result.body;
|
|
74
|
+
if (JSON.stringify(result.statusCode) == 503) {
|
|
75
|
+
return { statusCode: result.statusCode };
|
|
81
76
|
}
|
|
82
77
|
}
|
|
83
78
|
catch (e) {
|
|
84
|
-
|
|
79
|
+
if (config.debug) {
|
|
80
|
+
console.log(e.message.toString());
|
|
81
|
+
}
|
|
85
82
|
}
|
|
86
83
|
};
|
|
87
84
|
module.exports = {
|
|
88
85
|
getScanId: getScanId,
|
|
89
86
|
returnScanResults: returnScanResults,
|
|
90
87
|
pollScanResults: pollScanResults,
|
|
91
|
-
returnScanResultsInstances: returnScanResultsInstances
|
|
92
|
-
returnScanProjectById: returnScanProjectById
|
|
88
|
+
returnScanResultsInstances: returnScanResultsInstances
|
|
93
89
|
};
|
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
|
}
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/contrast",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Contrast Security's command line tool",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dev": "npx ts-node src/index.ts"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
|
-
"node": ">=16
|
|
38
|
+
"node": ">=16"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@aws-sdk/client-iam": "^3.78.0",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"bluebird": "^3.7.2",
|
|
46
46
|
"boxen": "5.1.2",
|
|
47
47
|
"chalk": "4.1.2",
|
|
48
|
+
"cli-table3": "^0.6.2",
|
|
48
49
|
"command-line-args": "^5.2.1",
|
|
49
50
|
"command-line-usage": "^6.1.3",
|
|
50
51
|
"conf": "^10.1.2",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import i18n from 'i18n'
|
|
3
|
+
import {
|
|
4
|
+
reduceIdentifiedLanguages,
|
|
5
|
+
deduceLanguage
|
|
6
|
+
} from '../languageAnalysisEngine/reduceIdentifiedLanguages'
|
|
7
|
+
|
|
8
|
+
import { getProjectRootFilenames } from '../languageAnalysisEngine/getProjectRootFilenames'
|
|
9
|
+
|
|
10
|
+
export function identifyLanguages(config: any) {
|
|
11
|
+
const { projectPath } = config
|
|
12
|
+
const projectRootFilenames = getProjectRootFilenames(projectPath)
|
|
13
|
+
|
|
14
|
+
const identifiedLanguages = projectRootFilenames.reduce(
|
|
15
|
+
(accumulator: any, filename: string) => {
|
|
16
|
+
const deducedLanguages = deduceLanguage(filename)
|
|
17
|
+
return [...accumulator, ...deducedLanguages]
|
|
18
|
+
},
|
|
19
|
+
[]
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if (Object.keys(identifiedLanguages).length === 0) {
|
|
23
|
+
throw new Error(i18n.__('languageAnalysisNoLanguage', projectPath))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return reduceIdentifiedLanguages(identifiedLanguages)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function determineProjectLanguage(
|
|
30
|
+
reducedLanguages: Record<string, string>
|
|
31
|
+
) {
|
|
32
|
+
const reducedLanguagesKeys = Object.keys(reducedLanguages)
|
|
33
|
+
if (reducedLanguagesKeys.length === 1) {
|
|
34
|
+
return reducedLanguagesKeys[0]
|
|
35
|
+
} else {
|
|
36
|
+
throw new Error(
|
|
37
|
+
'Detected multiple languages. Please specify a single language using --language'
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
const i18n = require('i18n')
|
|
2
2
|
const { getHttpClient, handleResponseErrors } = require('../../utils/commonApi')
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const displaySuccessMessage = (config, appId) => {
|
|
9
|
-
console.log(
|
|
10
|
-
'\n **************************' +
|
|
11
|
-
i18n.__('successHeader') +
|
|
12
|
-
'************************** \n'
|
|
13
|
-
)
|
|
14
|
-
console.log('\n' + i18n.__('catalogueSuccessCommand') + appId + '\n')
|
|
15
|
-
console.log(locationOfApp(config, appId))
|
|
16
|
-
console.log(
|
|
17
|
-
'\n *********************************************************** \n'
|
|
18
|
-
)
|
|
4
|
+
const displaySuccessMessage = () => {
|
|
5
|
+
console.log(i18n.__('catalogueSuccessCommand'))
|
|
19
6
|
}
|
|
20
7
|
|
|
21
8
|
const catalogueApplication = async config => {
|
|
@@ -25,9 +12,10 @@ const catalogueApplication = async config => {
|
|
|
25
12
|
.catalogueCommand(config)
|
|
26
13
|
.then(res => {
|
|
27
14
|
if (res.statusCode === 201) {
|
|
28
|
-
displaySuccessMessage(config, res.body.application.app_id)
|
|
15
|
+
//displaySuccessMessage(config, res.body.application.app_id)
|
|
29
16
|
appId = res.body.application.app_id
|
|
30
17
|
} else {
|
|
18
|
+
// console.log(res.statusCode)
|
|
31
19
|
handleResponseErrors(res, 'catalogue')
|
|
32
20
|
}
|
|
33
21
|
})
|
package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js}
RENAMED
|
@@ -10,13 +10,17 @@ const pythonAE = require('../pythonAnalysisEngine')
|
|
|
10
10
|
const phpAE = require('../phpAnalysisEngine')
|
|
11
11
|
const goAE = require('../goAnalysisEngine')
|
|
12
12
|
const { vulnerabilityReport } = require('./report/reportingFeature')
|
|
13
|
-
const { vulnReportWithoutDevDep } = require('./report/newReportingFeature')
|
|
14
|
-
const { checkDevDeps } = require('./report/checkIgnoreDevDep')
|
|
15
13
|
const { newSendSnapShot } = require('../languageAnalysisEngine/sendSnapshot')
|
|
16
14
|
const fs = require('fs')
|
|
17
15
|
const chalk = require('chalk')
|
|
18
16
|
const saveFile = require('../../commands/audit/saveFile').default
|
|
19
17
|
const generateSbom = require('../../sbom/generateSbom').default
|
|
18
|
+
const {
|
|
19
|
+
failSpinner,
|
|
20
|
+
returnOra,
|
|
21
|
+
startSpinner,
|
|
22
|
+
succeedSpinner
|
|
23
|
+
} = require('../../utils/oraWrapper')
|
|
20
24
|
|
|
21
25
|
module.exports = exports = (err, analysis) => {
|
|
22
26
|
const { identifiedLanguageInfo } = analysis.languageAnalysis
|
|
@@ -47,29 +51,15 @@ module.exports = exports = (err, analysis) => {
|
|
|
47
51
|
return process.exit(5)
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
|
|
55
|
+
startSpinner(reportSpinner)
|
|
51
56
|
const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId)
|
|
57
|
+
succeedSpinner(reportSpinner, 'Contrast SCA analysis complete')
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
const ignoreDevUrl = await checkDevDeps(config)
|
|
55
|
-
if (ignoreDevUrl) {
|
|
56
|
-
await vulnReportWithoutDevDep(
|
|
57
|
-
analysis,
|
|
58
|
-
catalogueAppId,
|
|
59
|
-
snapshotResponse.id,
|
|
60
|
-
config
|
|
61
|
-
)
|
|
62
|
-
} else {
|
|
63
|
-
await vulnerabilityReport(analysis, catalogueAppId, config)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
59
|
+
await vulnerabilityReport(analysis, catalogueAppId, snapshotResponse.id)
|
|
66
60
|
|
|
67
61
|
//should be moved to processAudit.ts once promises implemented
|
|
68
62
|
await auditSave(config)
|
|
69
|
-
|
|
70
|
-
console.log(
|
|
71
|
-
'\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n'
|
|
72
|
-
)
|
|
73
63
|
}
|
|
74
64
|
|
|
75
65
|
if (identifiedLanguageInfo.language === DOTNET) {
|
|
@@ -120,7 +110,7 @@ async function auditSave(config) {
|
|
|
120
110
|
} else {
|
|
121
111
|
console.log(i18n.__('auditBadFiletypeSpecifiedForSave'))
|
|
122
112
|
}
|
|
123
|
-
} else {
|
|
113
|
+
} else if (config.save === null) {
|
|
124
114
|
console.log(i18n.__('auditNoFiletypeSpecifiedForSave'))
|
|
125
115
|
}
|
|
126
116
|
}
|
|
@@ -28,6 +28,77 @@ const isRubyLockFilename = filename => filename === 'Gemfile.lock'
|
|
|
28
28
|
const isPipfileLockLockFilename = filename => filename === 'Pipfile.lock'
|
|
29
29
|
const isGoProjectFilename = filename => filename === 'go.mod'
|
|
30
30
|
|
|
31
|
+
const deduceLanguageScaAnalysis = filenames => {
|
|
32
|
+
const deducedLanguages = []
|
|
33
|
+
let language = ''
|
|
34
|
+
|
|
35
|
+
filenames.forEach(filename => {
|
|
36
|
+
// Check for project filenames...
|
|
37
|
+
if (isJavaMavenProjectFilename(filename)) {
|
|
38
|
+
deducedLanguages.push(filename)
|
|
39
|
+
language = JAVA
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (isJavaGradleProjectFilename(filename)) {
|
|
43
|
+
deducedLanguages.push(filename)
|
|
44
|
+
language = JAVA
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isNodeProjectFilename(filename)) {
|
|
48
|
+
deducedLanguages.push(filename)
|
|
49
|
+
language = NODE
|
|
50
|
+
}
|
|
51
|
+
//
|
|
52
|
+
// if (isDotNetProjectFilename(filename)) {
|
|
53
|
+
// deducedLanguages.push({language: DOTNET, projectFilename: filename})
|
|
54
|
+
// }
|
|
55
|
+
//
|
|
56
|
+
// if (isRubyProjectFilename(filename)) {
|
|
57
|
+
// deducedLanguages.push({language: RUBY, projectFilename: filename})
|
|
58
|
+
// }
|
|
59
|
+
//
|
|
60
|
+
// if (isPythonProjectFilename(filename)) {
|
|
61
|
+
// deducedLanguages.push({language: PYTHON, projectFilename: filename})
|
|
62
|
+
// }
|
|
63
|
+
//
|
|
64
|
+
// if (isPhpProjectFilename(filename)) {
|
|
65
|
+
// deducedLanguages.push({language: PHP, projectFilename: filename})
|
|
66
|
+
// }
|
|
67
|
+
//
|
|
68
|
+
// // Check for lock filenames...
|
|
69
|
+
// if (isDotNetLockFilename(filename)) {
|
|
70
|
+
// deducedLanguages.push({language: DOTNET, lockFilename: filename})
|
|
71
|
+
// }
|
|
72
|
+
//
|
|
73
|
+
if (isNodeLockFilename(filename)) {
|
|
74
|
+
deducedLanguages.push(filename)
|
|
75
|
+
language = NODE
|
|
76
|
+
}
|
|
77
|
+
//
|
|
78
|
+
// if (isRubyLockFilename(filename)) {
|
|
79
|
+
// deducedLanguages.push({language: RUBY, lockFilename: filename})
|
|
80
|
+
// }
|
|
81
|
+
//
|
|
82
|
+
// // this is pipfileLock rather than python lock as there can be different python locks
|
|
83
|
+
// if (isPipfileLockLockFilename(filename)) {
|
|
84
|
+
// deducedLanguages.push({language: PYTHON, lockFilename: filename})
|
|
85
|
+
// }
|
|
86
|
+
//
|
|
87
|
+
// if (isPhpLockFilename(filename)) {
|
|
88
|
+
// deducedLanguages.push({language: PHP, lockFilename: filename})
|
|
89
|
+
// }
|
|
90
|
+
//
|
|
91
|
+
// go does not have a lockfile, it should have a go.mod file containing the modules
|
|
92
|
+
if (isGoProjectFilename(filename)) {
|
|
93
|
+
deducedLanguages.push({ language: GO, projectFilename: filename })
|
|
94
|
+
language = GO
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
let identifiedLanguages = { [language]: deducedLanguages }
|
|
98
|
+
|
|
99
|
+
return identifiedLanguages
|
|
100
|
+
}
|
|
101
|
+
|
|
31
102
|
const deduceLanguage = filename => {
|
|
32
103
|
const deducedLanguages = []
|
|
33
104
|
|
|
@@ -175,3 +246,4 @@ exports.isPhpProjectFilename = isPhpProjectFilename
|
|
|
175
246
|
exports.isPhpLockFilename = isPhpLockFilename
|
|
176
247
|
exports.deduceLanguage = deduceLanguage
|
|
177
248
|
exports.reduceIdentifiedLanguages = reduceIdentifiedLanguages
|
|
249
|
+
exports.deduceLanguageScaAnalysis = deduceLanguageScaAnalysis
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { getHttpClient, handleResponseErrors } from '../../../utils/commonApi'
|
|
2
|
+
import {
|
|
3
|
+
ReportCompositeKey,
|
|
4
|
+
ReportList,
|
|
5
|
+
ReportModelStructure
|
|
6
|
+
} from './models/reportListModel'
|
|
7
|
+
import { ReportSeverityModel } from './models/reportSeverityModel'
|
|
8
|
+
import { orderBy } from 'lodash'
|
|
9
|
+
import chalk from 'chalk'
|
|
10
|
+
import { 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";
|
|
23
|
+
|
|
24
|
+
export const createLibraryHeader = (
|
|
25
|
+
id: string,
|
|
26
|
+
numberOfVulnerableLibraries: number,
|
|
27
|
+
numberOfCves: number
|
|
28
|
+
) => {
|
|
29
|
+
numberOfVulnerableLibraries === 1
|
|
30
|
+
? console.log(
|
|
31
|
+
` Found 1 vulnerable library containing ${numberOfCves} CVE's`
|
|
32
|
+
)
|
|
33
|
+
: console.log(
|
|
34
|
+
` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's `
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const getReport = async (config: any, reportId: string) => {
|
|
39
|
+
const client = getHttpClient(config)
|
|
40
|
+
return client
|
|
41
|
+
.getReportById(config, reportId)
|
|
42
|
+
.then((res: { statusCode: number; body: any }) => {
|
|
43
|
+
if (res.statusCode === 200) {
|
|
44
|
+
return res.body
|
|
45
|
+
} else {
|
|
46
|
+
console.log('config-------------------')
|
|
47
|
+
console.log(config)
|
|
48
|
+
console.log('reportId----------------')
|
|
49
|
+
console.log(reportId)
|
|
50
|
+
console.log(JSON.stringify(res))
|
|
51
|
+
handleResponseErrors(res, 'report')
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.catch((err: any) => {
|
|
55
|
+
console.log(err)
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const printVulnerabilityResponse = (
|
|
60
|
+
vulnerabilities: ReportLibraryModel[],
|
|
61
|
+
config: any
|
|
62
|
+
) => {
|
|
63
|
+
let hasSomeVulnerabilitiesReported = false
|
|
64
|
+
printFormattedOutput(vulnerabilities, config)
|
|
65
|
+
if (Object.keys(vulnerabilities).length > 0) {
|
|
66
|
+
hasSomeVulnerabilitiesReported = true
|
|
67
|
+
}
|
|
68
|
+
return hasSomeVulnerabilitiesReported
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const printFormattedOutput = (
|
|
72
|
+
libraries: ReportLibraryModel[],
|
|
73
|
+
config: any
|
|
74
|
+
) => {
|
|
75
|
+
const report = new ReportList()
|
|
76
|
+
|
|
77
|
+
for (const library of libraries) {
|
|
78
|
+
const { name, version } = findNameAndVersion(library, config)
|
|
79
|
+
|
|
80
|
+
const newOutputModel = new ReportModelStructure(
|
|
81
|
+
new ReportCompositeKey(
|
|
82
|
+
name,
|
|
83
|
+
version,
|
|
84
|
+
findHighestSeverityCVE(library.cveArray) as ReportSeverityModel
|
|
85
|
+
),
|
|
86
|
+
library.cveArray
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
report.reportOutputList.push(newOutputModel)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const orderedOutputListLowestFirst = orderBy(
|
|
93
|
+
report.reportOutputList,
|
|
94
|
+
reportListItem => reportListItem.compositeKey.highestSeverity.priority,
|
|
95
|
+
['desc']
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
let contrastHeaderNumCounter = 0
|
|
99
|
+
for (const reportModel of orderedOutputListLowestFirst) {
|
|
100
|
+
contrastHeaderNumCounter++
|
|
101
|
+
const {libraryName, libraryVersion, highestSeverity} = reportModel.compositeKey
|
|
102
|
+
const numOfCVEs = reportModel.cveArray.length
|
|
103
|
+
|
|
104
|
+
const header = buildHeader(
|
|
105
|
+
highestSeverity,
|
|
106
|
+
contrastHeaderNumCounter,
|
|
107
|
+
libraryName,
|
|
108
|
+
libraryVersion,
|
|
109
|
+
numOfCVEs
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
const body = buildBody(
|
|
113
|
+
reportModel.cveArray
|
|
114
|
+
)
|
|
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
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return `CONTRAST-${formattedHeaderNum}`
|
|
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,30 @@
|
|
|
1
|
+
export class ReportLibraryModel {
|
|
2
|
+
name: string
|
|
3
|
+
cveArray: ReportCVEModel[]
|
|
4
|
+
|
|
5
|
+
constructor (name: string, cveArray: ReportCVEModel[]){
|
|
6
|
+
this.name = name
|
|
7
|
+
this.cveArray = cveArray
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ReportCVEModel {
|
|
12
|
+
name?: string
|
|
13
|
+
description?: string
|
|
14
|
+
authentication?: string
|
|
15
|
+
references?: []
|
|
16
|
+
severityCode?: string
|
|
17
|
+
cvss3SeverityCode?: string
|
|
18
|
+
|
|
19
|
+
constructor (
|
|
20
|
+
name: string,
|
|
21
|
+
description: string,
|
|
22
|
+
severityCode: string,
|
|
23
|
+
cvss3SeverityCode: string
|
|
24
|
+
){
|
|
25
|
+
this.name = name
|
|
26
|
+
this.description = description
|
|
27
|
+
this.severityCode = severityCode
|
|
28
|
+
this.cvss3SeverityCode = cvss3SeverityCode
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {ReportSeverityModel} from "./reportSeverityModel";
|
|
2
|
+
import {ReportCVEModel} from "./reportLibraryModel";
|
|
3
|
+
|
|
4
|
+
export class ReportList {
|
|
5
|
+
reportOutputList: ReportModelStructure[]
|
|
6
|
+
|
|
7
|
+
constructor (){
|
|
8
|
+
this.reportOutputList = []
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class ReportModelStructure {
|
|
13
|
+
compositeKey: ReportCompositeKey;
|
|
14
|
+
cveArray: ReportCVEModel[];
|
|
15
|
+
|
|
16
|
+
constructor (compositeKey: ReportCompositeKey, cveArray: ReportCVEModel[]){
|
|
17
|
+
this.compositeKey = compositeKey
|
|
18
|
+
this.cveArray = cveArray
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class ReportCompositeKey {
|
|
23
|
+
libraryName!: string;
|
|
24
|
+
libraryVersion!: string;
|
|
25
|
+
highestSeverity!: ReportSeverityModel;
|
|
26
|
+
|
|
27
|
+
constructor (libraryName: string, libraryVersion: string, highestSeverity: ReportSeverityModel){
|
|
28
|
+
this.libraryName = libraryName
|
|
29
|
+
this.libraryVersion = libraryVersion
|
|
30
|
+
this.highestSeverity = highestSeverity
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class ReportSeverityModel {
|
|
2
|
+
severity: string
|
|
3
|
+
priority: number
|
|
4
|
+
outputColour: string
|
|
5
|
+
cveName: string
|
|
6
|
+
|
|
7
|
+
constructor(severity: string, priority: number, outputColour: string, cveName: string) {
|
|
8
|
+
this.severity = severity
|
|
9
|
+
this.priority = priority
|
|
10
|
+
this.outputColour = outputColour
|
|
11
|
+
this.cveName = cveName
|
|
12
|
+
}
|
|
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
|
+
}
|