@contrast/contrast 1.0.2 → 1.0.3
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/README.md +6 -4
- package/dist/audit/languageAnalysisEngine/langugageAnalysisFactory.js +25 -0
- 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 +4 -22
- package/dist/common/HTTPClient.js +11 -0
- package/dist/common/errorHandling.js +9 -23
- package/dist/common/{findLatestCLIVersion.js → versionChecker.js} +10 -3
- package/dist/constants/constants.js +4 -2
- package/dist/constants/locales.js +17 -7
- package/dist/constants.js +34 -2
- package/dist/index.js +48 -30
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scan/help.js +4 -2
- package/dist/scan/populateProjectIdAndProjectName.js +1 -0
- package/dist/scan/saveResults.js +9 -10
- package/dist/scan/scan.js +27 -2
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +7 -2
- package/dist/scan/scanResults.js +5 -0
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +1 -1
- package/src/audit/languageAnalysisEngine/langugageAnalysisFactory.js +32 -0
- 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 +4 -23
- package/src/common/HTTPClient.js +13 -0
- package/src/common/errorHandling.ts +11 -24
- package/src/common/versionChecker.ts +39 -0
- package/src/constants/constants.js +5 -4
- package/src/constants/locales.js +23 -8
- package/src/constants.js +37 -2
- package/src/index.ts +63 -36
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scan/help.js +4 -2
- package/src/scan/populateProjectIdAndProjectName.js +1 -0
- package/src/scan/saveResults.js +8 -9
- package/src/scan/scan.js +30 -2
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +9 -2
- package/src/scan/scanResults.js +10 -0
- package/src/utils/getConfig.ts +2 -0
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/src/common/findLatestCLIVersion.ts +0 -27
package/dist/scan/saveResults.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const writeResultsToFile = (responseBody, name = 'results.sarif') => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
3
|
+
const writeResultsToFile = async (responseBody, name = 'results.sarif') => {
|
|
4
|
+
try {
|
|
5
|
+
fs.writeFileSync(name, JSON.stringify(responseBody, null, 2));
|
|
6
|
+
console.log(`Scan Results saved to ${name}`);
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
console.log('Error writing Scan Results to file');
|
|
10
|
+
}
|
|
12
11
|
};
|
|
13
12
|
module.exports = {
|
|
14
|
-
writeResultsToFile
|
|
13
|
+
writeResultsToFile: writeResultsToFile
|
|
15
14
|
};
|
package/dist/scan/scan.js
CHANGED
|
@@ -50,6 +50,7 @@ const sendScan = async (config) => {
|
|
|
50
50
|
oraWrapper.failSpinner(startUploadSpinner, i18n.__('uploadingScanFail'));
|
|
51
51
|
if (res.statusCode === 403) {
|
|
52
52
|
console.log(i18n.__('permissionsError'));
|
|
53
|
+
process.exit(1);
|
|
53
54
|
}
|
|
54
55
|
console.log(i18n.__('genericServiceError', res.statusCode));
|
|
55
56
|
process.exit(1);
|
|
@@ -79,6 +80,15 @@ const formatScanOutput = (overview, results) => {
|
|
|
79
80
|
console.log(`\t ${count}. ${lineInfo}`);
|
|
80
81
|
count++;
|
|
81
82
|
});
|
|
83
|
+
if (entry?.cwe && entry?.cwe.length > 0) {
|
|
84
|
+
formatLinks('cwe', entry.cwe);
|
|
85
|
+
}
|
|
86
|
+
if (entry?.reference && entry?.reference.length > 0) {
|
|
87
|
+
formatLinks('reference', entry.reference);
|
|
88
|
+
}
|
|
89
|
+
if (entry?.owasp && entry?.owasp.length > 0) {
|
|
90
|
+
formatLinks('owasp', entry.owasp);
|
|
91
|
+
}
|
|
82
92
|
console.log(chalk.bold('How to fix:'));
|
|
83
93
|
console.log(entry.recommendation);
|
|
84
94
|
console.log();
|
|
@@ -88,10 +98,18 @@ const formatScanOutput = (overview, results) => {
|
|
|
88
98
|
overview.medium +
|
|
89
99
|
overview.low +
|
|
90
100
|
overview.note;
|
|
91
|
-
|
|
101
|
+
let vulMessage = totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`;
|
|
102
|
+
console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`));
|
|
92
103
|
console.log(i18n.__('foundDetailedVulnerabilities', overview.critical, overview.high, overview.medium, overview.low, overview.note));
|
|
93
104
|
}
|
|
94
105
|
};
|
|
106
|
+
const formatLinks = (objName, entry) => {
|
|
107
|
+
console.log(chalk.bold(objName + ':'));
|
|
108
|
+
entry.forEach(link => {
|
|
109
|
+
console.log(link);
|
|
110
|
+
});
|
|
111
|
+
console.log();
|
|
112
|
+
};
|
|
95
113
|
const getGroups = content => {
|
|
96
114
|
const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId));
|
|
97
115
|
let groupTypeResults = [];
|
|
@@ -99,12 +117,18 @@ const getGroups = content => {
|
|
|
99
117
|
let groupResultsObj = {
|
|
100
118
|
ruleId: groupName,
|
|
101
119
|
lineInfoSet: new Set(),
|
|
120
|
+
cwe: '',
|
|
121
|
+
owasp: '',
|
|
122
|
+
reference: '',
|
|
102
123
|
recommendation: '',
|
|
103
124
|
severity: ''
|
|
104
125
|
};
|
|
105
126
|
content.forEach(resultEntry => {
|
|
106
127
|
if (resultEntry.ruleId === groupName) {
|
|
107
128
|
groupResultsObj.severity = resultEntry.severity;
|
|
129
|
+
groupResultsObj.cwe = resultEntry.cwe;
|
|
130
|
+
groupResultsObj.owasp = resultEntry.owasp;
|
|
131
|
+
groupResultsObj.reference = resultEntry.reference;
|
|
108
132
|
groupResultsObj.recommendation = resultEntry.recommendation
|
|
109
133
|
? stripMustacheTags(resultEntry.recommendation)
|
|
110
134
|
: 'No Recommendations Data Found';
|
|
@@ -132,5 +156,6 @@ module.exports = {
|
|
|
132
156
|
allowedFileTypes: allowedFileTypes,
|
|
133
157
|
isFileAllowed: isFileAllowed,
|
|
134
158
|
stripMustacheTags: stripMustacheTags,
|
|
135
|
-
formatScanOutput: formatScanOutput
|
|
159
|
+
formatScanOutput: formatScanOutput,
|
|
160
|
+
formatLinks: formatLinks
|
|
136
161
|
};
|
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(0);
|
|
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)
|
|
@@ -41,6 +42,10 @@ const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinn
|
|
|
41
42
|
complete = true;
|
|
42
43
|
oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.');
|
|
43
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
|
+
}
|
|
44
49
|
process.exit(1);
|
|
45
50
|
}
|
|
46
51
|
}
|
|
@@ -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
|
@@ -13,6 +13,10 @@ const { vulnerabilityReport } = require('./report/reportingFeature')
|
|
|
13
13
|
const { vulnReportWithoutDevDep } = require('./report/newReportingFeature')
|
|
14
14
|
const { checkDevDeps } = require('./report/checkIgnoreDevDep')
|
|
15
15
|
const { newSendSnapShot } = require('../languageAnalysisEngine/sendSnapshot')
|
|
16
|
+
const fs = require('fs')
|
|
17
|
+
const chalk = require('chalk')
|
|
18
|
+
const saveFile = require('../../commands/audit/saveFile').default
|
|
19
|
+
const generateSbom = require('../../sbom/generateSbom').default
|
|
16
20
|
|
|
17
21
|
module.exports = exports = (err, analysis) => {
|
|
18
22
|
const { identifiedLanguageInfo } = analysis.languageAnalysis
|
|
@@ -59,6 +63,10 @@ module.exports = exports = (err, analysis) => {
|
|
|
59
63
|
await vulnerabilityReport(analysis, catalogueAppId, config)
|
|
60
64
|
}
|
|
61
65
|
}
|
|
66
|
+
|
|
67
|
+
//should be moved to processAudit.ts once promises implemented
|
|
68
|
+
await auditSave(config)
|
|
69
|
+
|
|
62
70
|
console.log(
|
|
63
71
|
'\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n'
|
|
64
72
|
)
|
|
@@ -92,3 +100,27 @@ module.exports = exports = (err, analysis) => {
|
|
|
92
100
|
goAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
93
101
|
}
|
|
94
102
|
}
|
|
103
|
+
|
|
104
|
+
async function auditSave(config) {
|
|
105
|
+
//should be moved to processAudit.ts once promises implemented
|
|
106
|
+
if (config.save) {
|
|
107
|
+
if (config.save.toLowerCase() === 'sbom') {
|
|
108
|
+
saveFile(config, await generateSbom(config))
|
|
109
|
+
|
|
110
|
+
const filename = `${config.applicationId}-sbom-cyclonedx.json`
|
|
111
|
+
if (fs.existsSync(filename)) {
|
|
112
|
+
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`)
|
|
113
|
+
} else {
|
|
114
|
+
console.log(
|
|
115
|
+
chalk.yellow.bold(
|
|
116
|
+
`\n Unable to save ${filename} Software Bill of Materials (SBOM)`
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
console.log(i18n.__('auditBadFiletypeSpecifiedForSave'))
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
console.log(i18n.__('auditNoFiletypeSpecifiedForSave'))
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -11,8 +11,21 @@ const {
|
|
|
11
11
|
succeedSpinner
|
|
12
12
|
} = require('../../utils/oraWrapper')
|
|
13
13
|
const { TIMEOUT, AUTH_UI_URL } = require('../../constants/constants')
|
|
14
|
+
const parsedCLIOptions = require('../../utils/parsedCLIOptions')
|
|
15
|
+
const constants = require('../../constants')
|
|
16
|
+
const commandLineUsage = require('command-line-usage')
|
|
17
|
+
|
|
18
|
+
const processAuth = async (argv, config) => {
|
|
19
|
+
let authParams = parsedCLIOptions.getCommandLineArgsCustom(
|
|
20
|
+
argv,
|
|
21
|
+
constants.commandLineDefinitions.authOptionDefinitions
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if (authParams.help) {
|
|
25
|
+
console.log(authUsageGuide)
|
|
26
|
+
process.exit(0)
|
|
27
|
+
}
|
|
14
28
|
|
|
15
|
-
const processAuth = async config => {
|
|
16
29
|
const token = uuidv4()
|
|
17
30
|
const url = `${AUTH_UI_URL}/?token=${token}`
|
|
18
31
|
|
|
@@ -68,6 +81,17 @@ const pollAuthResult = async (token, client) => {
|
|
|
68
81
|
})
|
|
69
82
|
}
|
|
70
83
|
|
|
84
|
+
const authUsageGuide = commandLineUsage([
|
|
85
|
+
{
|
|
86
|
+
header: i18n.__('authHeader'),
|
|
87
|
+
content: [i18n.__('constantsAuthHeaderContents')]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
header: i18n.__('constantsAuthUsageHeader'),
|
|
91
|
+
content: [i18n.__('constantsAuthUsageContents')]
|
|
92
|
+
}
|
|
93
|
+
])
|
|
94
|
+
|
|
71
95
|
module.exports = {
|
|
72
96
|
processAuth: processAuth
|
|
73
97
|
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
const
|
|
1
|
+
const parsedCLIOptions = require('../../utils/parsedCLIOptions')
|
|
2
|
+
const constants = require('../../constants')
|
|
3
|
+
const commandLineUsage = require('command-line-usage')
|
|
4
|
+
const i18n = require('i18n')
|
|
2
5
|
|
|
3
6
|
const processConfig = (argv, config) => {
|
|
4
|
-
const options = [{ name: 'clear', alias: 'c', type: Boolean }]
|
|
5
|
-
|
|
6
7
|
try {
|
|
7
|
-
|
|
8
|
+
let configParams = parsedCLIOptions.getCommandLineArgsCustom(
|
|
8
9
|
argv,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
constants.commandLineDefinitions.configOptionDefinitions
|
|
11
|
+
)
|
|
12
|
+
if (configParams.help) {
|
|
13
|
+
console.log(configUsageGuide)
|
|
14
|
+
process.exit(0)
|
|
15
|
+
}
|
|
16
|
+
if (configParams.clear) {
|
|
13
17
|
config.clear()
|
|
14
18
|
} else {
|
|
15
19
|
console.log(JSON.parse(JSON.stringify(config.store)))
|
|
@@ -20,6 +24,16 @@ const processConfig = (argv, config) => {
|
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
const configUsageGuide = commandLineUsage([
|
|
28
|
+
{
|
|
29
|
+
header: i18n.__('configHeader')
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
content: [i18n.__('constantsConfigUsageContents')],
|
|
33
|
+
optionList: constants.commandLineDefinitions.configOptionDefinitions
|
|
34
|
+
}
|
|
35
|
+
])
|
|
36
|
+
|
|
23
37
|
module.exports = {
|
|
24
38
|
processConfig: processConfig
|
|
25
39
|
}
|
|
@@ -2,16 +2,9 @@ const { startScan } = require('../../scan/scanController')
|
|
|
2
2
|
const { formatScanOutput } = require('../../scan/scan')
|
|
3
3
|
const { scanUsageGuide } = require('../../scan/help')
|
|
4
4
|
const scanConfig = require('../../scan/scanConfig')
|
|
5
|
-
const
|
|
6
|
-
const commonApi = require('../../utils/commonApi')
|
|
7
|
-
const i18n = require('i18n')
|
|
5
|
+
const { saveScanFile } = require('../../utils/saveFile')
|
|
8
6
|
|
|
9
7
|
const processScan = async argvMain => {
|
|
10
|
-
if (argvMain.indexOf('--help') !== -1) {
|
|
11
|
-
printHelpMessage()
|
|
12
|
-
process.exit(1)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
8
|
let config = scanConfig.getScanConfig(argvMain)
|
|
16
9
|
|
|
17
10
|
let scanResults = await startScan(config)
|
|
@@ -22,23 +15,11 @@ const processScan = async argvMain => {
|
|
|
22
15
|
)
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
if (config.save) {
|
|
26
|
-
|
|
27
|
-
const scanId = scanResults.scanDetail.id
|
|
28
|
-
const client = commonApi.getHttpClient(config)
|
|
29
|
-
const rawResults = await client.getSpecificScanResultSarif(config, scanId)
|
|
30
|
-
saveResults.writeResultsToFile(rawResults?.body)
|
|
31
|
-
} else {
|
|
32
|
-
console.log(i18n.__('scanNoFiletypeSpecifiedForSave'))
|
|
33
|
-
}
|
|
18
|
+
if (config.save !== undefined) {
|
|
19
|
+
await saveScanFile(config, scanResults)
|
|
34
20
|
}
|
|
35
21
|
}
|
|
36
22
|
|
|
37
|
-
const printHelpMessage = () => {
|
|
38
|
-
console.log(scanUsageGuide)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
23
|
module.exports = {
|
|
42
|
-
processScan
|
|
43
|
-
printHelpMessage
|
|
24
|
+
processScan
|
|
44
25
|
}
|
package/src/common/HTTPClient.js
CHANGED
|
@@ -130,6 +130,9 @@ HTTPClient.prototype.createProjectId = function createProjectId(config) {
|
|
|
130
130
|
name: config.name,
|
|
131
131
|
archived: 'false'
|
|
132
132
|
}
|
|
133
|
+
if (config.language) {
|
|
134
|
+
options.body.language = config.language
|
|
135
|
+
}
|
|
133
136
|
options.url = createHarmonyProjectsUrl(config)
|
|
134
137
|
return requestUtils.sendRequest({ method: 'post', options })
|
|
135
138
|
}
|
|
@@ -317,6 +320,12 @@ HTTPClient.prototype.checkLibrary = function checkLibrary(data) {
|
|
|
317
320
|
return requestUtils.sendRequest({ method: 'post', options })
|
|
318
321
|
}
|
|
319
322
|
|
|
323
|
+
HTTPClient.prototype.getSbom = function getSbom(config) {
|
|
324
|
+
const options = _.cloneDeep(this.requestOptions)
|
|
325
|
+
options.url = createSbomCycloneDXUrl(config)
|
|
326
|
+
return requestUtils.sendRequest({ method: 'get', options })
|
|
327
|
+
}
|
|
328
|
+
|
|
320
329
|
// scan
|
|
321
330
|
const createGetScanIdURL = config => {
|
|
322
331
|
return `${config.host}/Contrast/api/sast/v1/organizations/${config.organizationId}/projects/${config.projectId}/scans/`
|
|
@@ -386,6 +395,10 @@ const createGetDependencyTree = (protocol, orgUuid, appId, reportId) => {
|
|
|
386
395
|
return `${protocol}/Contrast/api/ng/sca/organizations/${orgUuid}/applications/${appId}/reports/${reportId}`
|
|
387
396
|
}
|
|
388
397
|
|
|
398
|
+
function createSbomCycloneDXUrl(config) {
|
|
399
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/cyclonedx`
|
|
400
|
+
}
|
|
401
|
+
|
|
389
402
|
module.exports = HTTPClient
|
|
390
403
|
module.exports.pollForAuthUrl = pollForAuthUrl
|
|
391
404
|
module.exports.getServerlessHost = getServerlessHost
|
|
@@ -73,6 +73,7 @@ const badRequestError = (catalogue: boolean) => {
|
|
|
73
73
|
|
|
74
74
|
const forbiddenError = () => {
|
|
75
75
|
generalError('forbiddenRequestErrorHeader', 'forbiddenRequestErrorMessage')
|
|
76
|
+
process.exit(1)
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
const proxyError = () => {
|
|
@@ -120,7 +121,7 @@ const generalError = (header: string, message?: string) => {
|
|
|
120
121
|
console.log(finalMessage)
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
const
|
|
124
|
+
const findCommandOnError = (unknownOptions: string[]) => {
|
|
124
125
|
const commandKeywords = {
|
|
125
126
|
auth: 'auth',
|
|
126
127
|
audit: 'audit',
|
|
@@ -128,34 +129,20 @@ const approximateCommandOnError = (unknownOptions: string[]) => {
|
|
|
128
129
|
lambda: 'lambda',
|
|
129
130
|
config: 'config'
|
|
130
131
|
}
|
|
131
|
-
const sortedUnknownOptions = sortBy(unknownOptions, param =>
|
|
132
|
-
param === 'auth' ||
|
|
133
|
-
param === 'audit' ||
|
|
134
|
-
param === 'scan' ||
|
|
135
|
-
param === 'lambda' ||
|
|
136
|
-
param === 'config'
|
|
137
|
-
? 0
|
|
138
|
-
: 1
|
|
139
|
-
)
|
|
140
132
|
|
|
141
|
-
const
|
|
133
|
+
const containsCommandKeyword = unknownOptions.some(
|
|
142
134
|
// @ts-ignore
|
|
143
135
|
command => commandKeywords[command]
|
|
144
136
|
)
|
|
145
137
|
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
.replace(new RegExp(foundCommands.join('|'), 'g'), '')
|
|
152
|
-
.trim()
|
|
153
|
-
|
|
154
|
-
const approximateCommand = `${foundCommands[0]} ${approximateParams}`
|
|
138
|
+
if (containsCommandKeyword) {
|
|
139
|
+
const foundCommands = unknownOptions.filter(
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
command => commandKeywords[command]
|
|
142
|
+
)
|
|
155
143
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
approximateCommandKeyword: foundCommands[0]
|
|
144
|
+
//return the first command found
|
|
145
|
+
return foundCommands[0]
|
|
159
146
|
}
|
|
160
147
|
}
|
|
161
148
|
|
|
@@ -171,5 +158,5 @@ export {
|
|
|
171
158
|
getErrorMessage,
|
|
172
159
|
handleResponseErrors,
|
|
173
160
|
libraryAnalysisError,
|
|
174
|
-
|
|
161
|
+
findCommandOnError
|
|
175
162
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import latestVersion from 'latest-version'
|
|
2
|
+
import { APP_VERSION } from '../constants/constants'
|
|
3
|
+
import boxen from 'boxen'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import semver from 'semver'
|
|
6
|
+
|
|
7
|
+
export async function findLatestCLIVersion() {
|
|
8
|
+
const latestCLIVersion = await latestVersion('@contrast/contrast')
|
|
9
|
+
|
|
10
|
+
if (semver.lt(APP_VERSION, latestCLIVersion)) {
|
|
11
|
+
const updateAvailableMessage = `Update available ${chalk.yellow(
|
|
12
|
+
APP_VERSION
|
|
13
|
+
)} → ${chalk.green(latestCLIVersion)}`
|
|
14
|
+
|
|
15
|
+
const npmUpdateAvailableCommand = `Run ${chalk.cyan(
|
|
16
|
+
'npm i @contrast/contrast -g'
|
|
17
|
+
)} to update via npm`
|
|
18
|
+
|
|
19
|
+
const homebrewUpdateAvailableCommand = `Run ${chalk.cyan(
|
|
20
|
+
'brew install contrastsecurity/tap/contrast'
|
|
21
|
+
)} to update via brew`
|
|
22
|
+
|
|
23
|
+
console.log(
|
|
24
|
+
boxen(
|
|
25
|
+
`${updateAvailableMessage}\n${npmUpdateAvailableCommand}\n\n${homebrewUpdateAvailableCommand}`,
|
|
26
|
+
{
|
|
27
|
+
titleAlignment: 'center',
|
|
28
|
+
margin: 1,
|
|
29
|
+
padding: 1,
|
|
30
|
+
align: 'center'
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function isCorrectNodeVersion(currentVersion: string) {
|
|
38
|
+
return semver.satisfies(currentVersion, '>=16.13.2 <17')
|
|
39
|
+
}
|
|
@@ -8,17 +8,17 @@ const GO = 'GO'
|
|
|
8
8
|
// we set the langauge as Node instead of PHP since we're using the Node engine in TS
|
|
9
9
|
const PHP = 'PHP'
|
|
10
10
|
const JAVASCRIPT = 'JAVASCRIPT'
|
|
11
|
-
|
|
12
11
|
const LOW = 'LOW'
|
|
13
12
|
const MEDIUM = 'MEDIUM'
|
|
14
13
|
const HIGH = 'HIGH'
|
|
15
14
|
const CRITICAL = 'CRITICAL'
|
|
16
|
-
|
|
17
15
|
const APP_NAME = 'contrast'
|
|
18
|
-
const APP_VERSION = '1.0.
|
|
16
|
+
const APP_VERSION = '1.0.3'
|
|
19
17
|
const TIMEOUT = 120000
|
|
18
|
+
|
|
20
19
|
const AUTH_UI_URL = 'https://cli-auth.contrastsecurity.com'
|
|
21
20
|
const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com'
|
|
21
|
+
const SARIF_FILE = 'SARIF'
|
|
22
22
|
|
|
23
23
|
module.exports = {
|
|
24
24
|
supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
|
|
@@ -30,5 +30,6 @@ module.exports = {
|
|
|
30
30
|
APP_NAME,
|
|
31
31
|
TIMEOUT,
|
|
32
32
|
AUTH_UI_URL,
|
|
33
|
-
AUTH_CALLBACK_URL
|
|
33
|
+
AUTH_CALLBACK_URL,
|
|
34
|
+
SARIF_FILE
|
|
34
35
|
}
|