@contrast/contrast 1.0.14 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/save.js +7 -2
- package/dist/commands/audit/help.js +3 -1
- package/dist/commands/scan/processScan.js +0 -6
- package/dist/commands/scan/sca/scaAnalysis.js +32 -14
- package/dist/common/HTTPClient.js +22 -3
- package/dist/constants/constants.js +10 -2
- package/dist/constants/locales.js +2 -0
- package/dist/constants.js +29 -0
- package/dist/sbom/generateSbom.js +18 -1
- package/dist/scaAnalysis/common/scaServicesUpload.js +14 -7
- package/dist/utils/commonApi.js +1 -0
- package/dist/utils/settingsHelper.js +24 -0
- package/package.json +1 -1
- package/src/audit/save.js +14 -6
- package/src/commands/audit/help.ts +3 -1
- package/src/commands/scan/processScan.js +0 -8
- package/src/commands/scan/sca/scaAnalysis.js +52 -31
- package/src/common/HTTPClient.js +26 -3
- package/src/constants/constants.js +12 -2
- package/src/constants/locales.js +3 -0
- package/src/constants.js +32 -0
- package/src/sbom/generateSbom.ts +20 -0
- package/src/scaAnalysis/common/scaServicesUpload.js +15 -7
- package/src/utils/commonApi.js +1 -0
- package/src/utils/settingsHelper.js +26 -0
package/dist/audit/save.js
CHANGED
|
@@ -5,7 +5,7 @@ const chalk = require('chalk');
|
|
|
5
5
|
const save = require('../commands/audit/saveFile');
|
|
6
6
|
const sbom = require('../sbom/generateSbom');
|
|
7
7
|
const { SBOM_CYCLONE_DX_FILE, SBOM_SPDX_FILE } = require('../constants/constants');
|
|
8
|
-
async function auditSave(config) {
|
|
8
|
+
async function auditSave(config, reportId) {
|
|
9
9
|
let fileFormat;
|
|
10
10
|
switch (config.save) {
|
|
11
11
|
case null:
|
|
@@ -19,7 +19,12 @@ async function auditSave(config) {
|
|
|
19
19
|
break;
|
|
20
20
|
}
|
|
21
21
|
if (fileFormat) {
|
|
22
|
-
|
|
22
|
+
if (config.experimental) {
|
|
23
|
+
save.saveFile(config, fileFormat, await sbom.generateSCASbom(config, fileFormat, reportId));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
save.saveFile(config, fileFormat, await sbom.generateSbom(config, fileFormat));
|
|
27
|
+
}
|
|
23
28
|
const filename = `${config.applicationId}-sbom-${fileFormat}.json`;
|
|
24
29
|
if (fs.existsSync(filename)) {
|
|
25
30
|
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`);
|
|
@@ -4,18 +4,12 @@ const { startScan } = require('../../scan/scanController');
|
|
|
4
4
|
const { saveScanFile } = require('../../utils/saveFile');
|
|
5
5
|
const { ScanResultsModel } = require('../../scan/models/scanResultsModel');
|
|
6
6
|
const { formatScanOutput } = require('../../scan/formatScanOutput');
|
|
7
|
-
const { processSca } = require('./sca/scaAnalysis');
|
|
8
7
|
const common = require('../../common/fail');
|
|
9
8
|
const { sendTelemetryConfigAsObject } = require('../../telemetry/telemetry');
|
|
10
9
|
const chalk = require('chalk');
|
|
11
|
-
const generalAPI = require('../../utils/generalAPI');
|
|
12
10
|
const processScan = async (contrastConf, argv) => {
|
|
13
11
|
let config = await scanConfig.getScanConfig(contrastConf, 'scan', argv);
|
|
14
12
|
let output = undefined;
|
|
15
|
-
config.mode = await generalAPI.getMode(config);
|
|
16
|
-
if (config.experimental) {
|
|
17
|
-
await processSca(config, argv);
|
|
18
|
-
}
|
|
19
13
|
let scanResults = new ScanResultsModel(await startScan(config));
|
|
20
14
|
await sendTelemetryConfigAsObject(config, 'scan', argv, 'SUCCESS', scanResults.scanDetail.language);
|
|
21
15
|
if (scanResults.scanResultsInstances !== undefined) {
|
|
@@ -18,9 +18,11 @@ const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet');
|
|
|
18
18
|
const { auditUsageGuide } = require('../../audit/help');
|
|
19
19
|
const rootFile = require('../../../audit/languageAnalysisEngine/getProjectRootFilenames');
|
|
20
20
|
const path = require('path');
|
|
21
|
-
const
|
|
21
|
+
const auditReport = require('../../../scaAnalysis/common/auditReport');
|
|
22
|
+
const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload');
|
|
23
|
+
const settingsHelper = require('../../../utils/settingsHelper');
|
|
22
24
|
const processSca = async (config) => {
|
|
23
|
-
config
|
|
25
|
+
config = await settingsHelper.getSettings(config);
|
|
24
26
|
const startTime = performance.now();
|
|
25
27
|
let filesFound;
|
|
26
28
|
if (config.help) {
|
|
@@ -75,19 +77,35 @@ const processSca = async (config) => {
|
|
|
75
77
|
if (!config.applicationId) {
|
|
76
78
|
config.applicationId = await auditController.dealWithNoAppId(config);
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
if (config.experimental) {
|
|
81
|
+
console.log('');
|
|
82
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'));
|
|
83
|
+
startSpinner(reportSpinner);
|
|
84
|
+
const [reports, reportId] = await scaUpload.scaTreeUpload(messageToSend, config);
|
|
85
|
+
auditReport.processAuditReport(config, reports[0]);
|
|
86
|
+
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'));
|
|
87
|
+
if (config.save !== undefined) {
|
|
88
|
+
await auditSave.auditSave(config, reportId);
|
|
89
|
+
}
|
|
90
|
+
const endTime = performance.now() - startTime;
|
|
91
|
+
const scanDurationMs = endTime - startTime;
|
|
92
|
+
console.log(`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log('');
|
|
96
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'));
|
|
97
|
+
startSpinner(reportSpinner);
|
|
98
|
+
const snapshotResponse = await treeUpload.commonSendSnapShot(messageToSend, config);
|
|
99
|
+
await pollForSnapshotCompletition(config, snapshotResponse.id, reportSpinner);
|
|
100
|
+
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'));
|
|
101
|
+
await vulnerabilityReportV2(config, snapshotResponse.id);
|
|
102
|
+
if (config.save !== undefined) {
|
|
103
|
+
await auditSave.auditSave(config);
|
|
104
|
+
}
|
|
105
|
+
const endTime = performance.now() - startTime;
|
|
106
|
+
const scanDurationMs = endTime - startTime;
|
|
107
|
+
console.log(`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
87
108
|
}
|
|
88
|
-
const endTime = performance.now() - startTime;
|
|
89
|
-
const scanDurationMs = endTime - startTime;
|
|
90
|
-
console.log(`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
91
109
|
}
|
|
92
110
|
else {
|
|
93
111
|
if (filesFound.length === 0) {
|
|
@@ -185,6 +185,12 @@ HTTPClient.prototype.scaServiceReportStatus = function scaServiceReport(config,
|
|
|
185
185
|
options.url = url;
|
|
186
186
|
return requestUtils.sendRequest({ method: 'get', options });
|
|
187
187
|
};
|
|
188
|
+
HTTPClient.prototype.scaServiceIngests = function scaServiceIngests(config) {
|
|
189
|
+
const options = _.cloneDeep(this.requestOptions);
|
|
190
|
+
let url = createScaServiceIngestsURL(config);
|
|
191
|
+
options.url = url;
|
|
192
|
+
return requestUtils.sendRequest({ method: 'get', options });
|
|
193
|
+
};
|
|
188
194
|
HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
|
|
189
195
|
const options = _.cloneDeep(this.requestOptions);
|
|
190
196
|
if (config.ignoreDev) {
|
|
@@ -266,6 +272,11 @@ HTTPClient.prototype.getSbom = function getSbom(config, type) {
|
|
|
266
272
|
options.url = createSbomUrl(config, type);
|
|
267
273
|
return requestUtils.sendRequest({ method: 'get', options });
|
|
268
274
|
};
|
|
275
|
+
HTTPClient.prototype.getSCASbom = function getSbom(config, type, reportId) {
|
|
276
|
+
const options = _.cloneDeep(this.requestOptions);
|
|
277
|
+
options.url = createSCASbomUrl(config, type, reportId);
|
|
278
|
+
return requestUtils.sendRequest({ method: 'get', options });
|
|
279
|
+
};
|
|
269
280
|
HTTPClient.prototype.getLatestVersion = function getLatestVersion() {
|
|
270
281
|
const options = _.cloneDeep(this.requestOptions);
|
|
271
282
|
options.url =
|
|
@@ -321,13 +332,18 @@ function createSnapshotURL(config) {
|
|
|
321
332
|
return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/snapshots`;
|
|
322
333
|
}
|
|
323
334
|
function createScaServiceReportURL(config, reportId) {
|
|
324
|
-
return
|
|
335
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/reports/${reportId}`;
|
|
325
336
|
}
|
|
326
337
|
function createScaServiceReportStatusURL(config, reportId) {
|
|
327
|
-
return
|
|
338
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/${reportId}/status`;
|
|
339
|
+
}
|
|
340
|
+
function createScaServiceIngestsURL(config) {
|
|
341
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests`;
|
|
328
342
|
}
|
|
329
343
|
function createScaServiceIngestURL(config) {
|
|
330
|
-
|
|
344
|
+
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/tree`;
|
|
345
|
+
baseUrl = config.track ? baseUrl.concat('?persist=true') : baseUrl;
|
|
346
|
+
return baseUrl;
|
|
331
347
|
}
|
|
332
348
|
const createAppCreateURL = config => {
|
|
333
349
|
return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/create`;
|
|
@@ -353,6 +369,9 @@ function createDataUrl() {
|
|
|
353
369
|
function createSbomUrl(config, type) {
|
|
354
370
|
return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/${type}`;
|
|
355
371
|
}
|
|
372
|
+
function createSCASbomUrl(config, type, reportId) {
|
|
373
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/sbom/${reportId}?toolType=${type}`;
|
|
374
|
+
}
|
|
356
375
|
function createTelemetryEventUrl(config) {
|
|
357
376
|
return `${config.host}/Contrast/api/sast/organizations/${config.organizationId}/cli`;
|
|
358
377
|
}
|
|
@@ -12,7 +12,7 @@ const MEDIUM = 'MEDIUM';
|
|
|
12
12
|
const HIGH = 'HIGH';
|
|
13
13
|
const CRITICAL = 'CRITICAL';
|
|
14
14
|
const APP_NAME = 'contrast';
|
|
15
|
-
const APP_VERSION = '1.0.
|
|
15
|
+
const APP_VERSION = '1.0.15';
|
|
16
16
|
const TIMEOUT = 120000;
|
|
17
17
|
const HIGH_COLOUR = '#ff9900';
|
|
18
18
|
const CRITICAL_COLOUR = '#e35858';
|
|
@@ -30,6 +30,10 @@ const SARIF_FILE = 'SARIF';
|
|
|
30
30
|
const SBOM_CYCLONE_DX_FILE = 'cyclonedx';
|
|
31
31
|
const SBOM_SPDX_FILE = 'spdx';
|
|
32
32
|
const CE_URL = 'https://ce.contrastsecurity.com';
|
|
33
|
+
const SAAS = 'SAAS';
|
|
34
|
+
const EOP = 'EOP';
|
|
35
|
+
const MODE_BUILD = 'BUILD';
|
|
36
|
+
const MODE_REPO = 'REPO';
|
|
33
37
|
module.exports = {
|
|
34
38
|
supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
|
|
35
39
|
supportedLanguagesScan: { JAVASCRIPT, DOTNET, JAVA },
|
|
@@ -55,5 +59,9 @@ module.exports = {
|
|
|
55
59
|
LOW_PRIORITY,
|
|
56
60
|
NOTE_PRIORITY,
|
|
57
61
|
SBOM_CYCLONE_DX_FILE,
|
|
58
|
-
SBOM_SPDX_FILE
|
|
62
|
+
SBOM_SPDX_FILE,
|
|
63
|
+
SAAS,
|
|
64
|
+
EOP,
|
|
65
|
+
MODE_BUILD,
|
|
66
|
+
MODE_REPO
|
|
59
67
|
};
|
|
@@ -213,6 +213,8 @@ const en_locales = () => {
|
|
|
213
213
|
scanOptionsTimeoutSummary: 'Time in seconds to wait for scan to complete. Default value is 300 seconds.',
|
|
214
214
|
scanOptionsFileNameSummary: 'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .exe or .zip file in the working directory.',
|
|
215
215
|
scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
|
|
216
|
+
auditOptionsTrackSummary: ' Save the results to the UI.',
|
|
217
|
+
auditOptionsBranchSummary: ' Set the branch name to associate the library results to.',
|
|
216
218
|
authSuccessMessage: 'Authentication successful',
|
|
217
219
|
runAuthSuccessMessage: chalk.bold('CodeSec by Contrast') +
|
|
218
220
|
'\nScan, secure and ship your code in minutes for FREE. \n' +
|
package/dist/constants.js
CHANGED
|
@@ -341,6 +341,35 @@ const auditOptionDefinitions = [
|
|
|
341
341
|
name: 'help',
|
|
342
342
|
alias: 'h',
|
|
343
343
|
type: Boolean
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: 'debug',
|
|
347
|
+
alias: 'd',
|
|
348
|
+
type: Boolean
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'verbose',
|
|
352
|
+
alias: 'v',
|
|
353
|
+
type: Boolean,
|
|
354
|
+
description: '{bold ' +
|
|
355
|
+
i18n.__('constantsOptional') +
|
|
356
|
+
'}:' +
|
|
357
|
+
i18n.__('scanOptionsVerboseSummary')
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'track',
|
|
361
|
+
type: Boolean,
|
|
362
|
+
description: '{bold ' +
|
|
363
|
+
i18n.__('constantsOptional') +
|
|
364
|
+
'}:' +
|
|
365
|
+
i18n.__('auditOptionsTrackSummary')
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: 'branch',
|
|
369
|
+
description: '{bold ' +
|
|
370
|
+
i18n.__('constantsOptional') +
|
|
371
|
+
'}:' +
|
|
372
|
+
i18n.__('auditOptionsBranchSummary')
|
|
344
373
|
}
|
|
345
374
|
];
|
|
346
375
|
const mainUsageGuide = commandLineUsage([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateSbom = void 0;
|
|
3
|
+
exports.generateSCASbom = exports.generateSbom = void 0;
|
|
4
4
|
const commonApi_1 = require("../utils/commonApi");
|
|
5
5
|
const generateSbom = (config, type) => {
|
|
6
6
|
const client = (0, commonApi_1.getHttpClient)(config);
|
|
@@ -19,3 +19,20 @@ const generateSbom = (config, type) => {
|
|
|
19
19
|
});
|
|
20
20
|
};
|
|
21
21
|
exports.generateSbom = generateSbom;
|
|
22
|
+
const generateSCASbom = (config, type, reportId) => {
|
|
23
|
+
const client = (0, commonApi_1.getHttpClient)(config);
|
|
24
|
+
return client
|
|
25
|
+
.getSCASbom(config, type, reportId)
|
|
26
|
+
.then((res) => {
|
|
27
|
+
if (res.statusCode === 200) {
|
|
28
|
+
return res.body;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log('Unable to retrieve Software Bill of Materials (SBOM)');
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.catch((err) => {
|
|
35
|
+
console.log(err);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
exports.generateSCASbom = generateSCASbom;
|
|
@@ -13,6 +13,9 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
13
13
|
version: APP_VERSION
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
if (config.branch) {
|
|
17
|
+
requestBody.branchName = config.branch;
|
|
18
|
+
}
|
|
16
19
|
const client = commonApi.getHttpClient(config);
|
|
17
20
|
const reportID = await client
|
|
18
21
|
.scaServiceIngest(requestBody, config)
|
|
@@ -27,26 +30,30 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
27
30
|
.catch(err => {
|
|
28
31
|
throw err;
|
|
29
32
|
});
|
|
30
|
-
|
|
33
|
+
if (config.debug) {
|
|
34
|
+
console.log(' polling report', reportID);
|
|
35
|
+
}
|
|
31
36
|
let keepChecking = true;
|
|
32
37
|
let res;
|
|
33
38
|
while (keepChecking) {
|
|
34
39
|
res = await client.scaServiceReportStatus(config, reportID).then(res => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
if (config.debug) {
|
|
41
|
+
console.log(res.statusCode);
|
|
42
|
+
console.log(res.body);
|
|
43
|
+
}
|
|
44
|
+
if (res.body.status === 'COMPLETED') {
|
|
38
45
|
keepChecking = false;
|
|
39
46
|
return client.scaServiceReport(config, reportID).then(res => {
|
|
40
|
-
return res.body;
|
|
47
|
+
return [res.body, reportID];
|
|
41
48
|
});
|
|
42
49
|
}
|
|
43
50
|
});
|
|
44
51
|
if (!keepChecking) {
|
|
45
|
-
return res;
|
|
52
|
+
return [res, reportID];
|
|
46
53
|
}
|
|
47
54
|
await requestUtils.sleep(5000);
|
|
48
55
|
}
|
|
49
|
-
return res;
|
|
56
|
+
return [res, reportID];
|
|
50
57
|
};
|
|
51
58
|
module.exports = {
|
|
52
59
|
scaTreeUpload
|
package/dist/utils/commonApi.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const commonApi = require('./commonApi');
|
|
3
|
+
const { getMode } = require('./generalAPI');
|
|
4
|
+
const { SAAS, MODE_BUILD } = require('../constants/constants');
|
|
5
|
+
const getSettings = async (config) => {
|
|
6
|
+
config.isEOP = (await getMode(config)).toUpperCase() === SAAS ? false : true;
|
|
7
|
+
config.mode = MODE_BUILD;
|
|
8
|
+
config.scaServices = await isSCAServicesAvailable(config);
|
|
9
|
+
return config;
|
|
10
|
+
};
|
|
11
|
+
const isSCAServicesAvailable = async (config) => {
|
|
12
|
+
const client = commonApi.getHttpClient(config);
|
|
13
|
+
return client
|
|
14
|
+
.scaServiceIngests(config)
|
|
15
|
+
.then(res => {
|
|
16
|
+
return res.statusCode !== 403;
|
|
17
|
+
})
|
|
18
|
+
.catch(err => {
|
|
19
|
+
console.log(err);
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
module.exports = {
|
|
23
|
+
getSettings
|
|
24
|
+
};
|
package/package.json
CHANGED
package/src/audit/save.js
CHANGED
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
SBOM_SPDX_FILE
|
|
9
9
|
} = require('../constants/constants')
|
|
10
10
|
|
|
11
|
-
async function auditSave(config) {
|
|
11
|
+
async function auditSave(config, reportId) {
|
|
12
12
|
let fileFormat
|
|
13
13
|
switch (config.save) {
|
|
14
14
|
case null:
|
|
@@ -23,11 +23,19 @@ async function auditSave(config) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (fileFormat) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if (config.experimental) {
|
|
27
|
+
save.saveFile(
|
|
28
|
+
config,
|
|
29
|
+
fileFormat,
|
|
30
|
+
await sbom.generateSCASbom(config, fileFormat, reportId)
|
|
31
|
+
)
|
|
32
|
+
} else {
|
|
33
|
+
save.saveFile(
|
|
34
|
+
config,
|
|
35
|
+
fileFormat,
|
|
36
|
+
await sbom.generateSbom(config, fileFormat)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
31
39
|
const filename = `${config.applicationId}-sbom-${fileFormat}.json`
|
|
32
40
|
if (fs.existsSync(filename)) {
|
|
33
41
|
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`)
|
|
@@ -3,21 +3,13 @@ const { startScan } = require('../../scan/scanController')
|
|
|
3
3
|
const { saveScanFile } = require('../../utils/saveFile')
|
|
4
4
|
const { ScanResultsModel } = require('../../scan/models/scanResultsModel')
|
|
5
5
|
const { formatScanOutput } = require('../../scan/formatScanOutput')
|
|
6
|
-
const { processSca } = require('./sca/scaAnalysis')
|
|
7
6
|
const common = require('../../common/fail')
|
|
8
7
|
const { sendTelemetryConfigAsObject } = require('../../telemetry/telemetry')
|
|
9
8
|
const chalk = require('chalk')
|
|
10
|
-
const generalAPI = require('../../utils/generalAPI')
|
|
11
9
|
|
|
12
10
|
const processScan = async (contrastConf, argv) => {
|
|
13
11
|
let config = await scanConfig.getScanConfig(contrastConf, 'scan', argv)
|
|
14
12
|
let output = undefined
|
|
15
|
-
config.mode = await generalAPI.getMode(config)
|
|
16
|
-
|
|
17
|
-
//try SCA analysis first
|
|
18
|
-
if (config.experimental) {
|
|
19
|
-
await processSca(config, argv)
|
|
20
|
-
}
|
|
21
13
|
|
|
22
14
|
let scanResults = new ScanResultsModel(await startScan(config))
|
|
23
15
|
await sendTelemetryConfigAsObject(
|
|
@@ -27,10 +27,13 @@ const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet')
|
|
|
27
27
|
const { auditUsageGuide } = require('../../audit/help')
|
|
28
28
|
const rootFile = require('../../../audit/languageAnalysisEngine/getProjectRootFilenames')
|
|
29
29
|
const path = require('path')
|
|
30
|
-
const
|
|
30
|
+
const auditReport = require('../../../scaAnalysis/common/auditReport')
|
|
31
|
+
const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload')
|
|
32
|
+
const settingsHelper = require('../../../utils/settingsHelper')
|
|
31
33
|
|
|
32
34
|
const processSca = async config => {
|
|
33
|
-
|
|
35
|
+
//checks to see whether to use old TS / new SCA path
|
|
36
|
+
config = await settingsHelper.getSettings(config)
|
|
34
37
|
|
|
35
38
|
const startTime = performance.now()
|
|
36
39
|
let filesFound
|
|
@@ -103,38 +106,56 @@ const processSca = async config => {
|
|
|
103
106
|
config.applicationId = await auditController.dealWithNoAppId(config)
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const snapshotResponse = await treeUpload.commonSendSnapShot(
|
|
115
|
-
messageToSend,
|
|
116
|
-
config
|
|
117
|
-
)
|
|
109
|
+
if (config.experimental) {
|
|
110
|
+
console.log('') //empty log for space before spinner
|
|
111
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
|
|
112
|
+
startSpinner(reportSpinner)
|
|
113
|
+
const [reports, reportId] = await scaUpload.scaTreeUpload(
|
|
114
|
+
messageToSend,
|
|
115
|
+
config
|
|
116
|
+
)
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
config,
|
|
122
|
-
snapshotResponse.id,
|
|
123
|
-
reportSpinner
|
|
124
|
-
)
|
|
125
|
-
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
|
|
118
|
+
auditReport.processAuditReport(config, reports[0])
|
|
119
|
+
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
const endTime = performance.now() - startTime
|
|
132
|
-
const scanDurationMs = endTime - startTime
|
|
121
|
+
if (config.save !== undefined) {
|
|
122
|
+
await auditSave.auditSave(config, reportId)
|
|
123
|
+
}
|
|
133
124
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
const endTime = performance.now() - startTime
|
|
126
|
+
const scanDurationMs = endTime - startTime
|
|
127
|
+
console.log(
|
|
128
|
+
`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
129
|
+
)
|
|
130
|
+
} else {
|
|
131
|
+
console.log('') //empty log for space before spinner
|
|
132
|
+
//send message to TS
|
|
133
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
|
|
134
|
+
startSpinner(reportSpinner)
|
|
135
|
+
const snapshotResponse = await treeUpload.commonSendSnapShot(
|
|
136
|
+
messageToSend,
|
|
137
|
+
config
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
//poll for completion
|
|
141
|
+
await pollForSnapshotCompletition(
|
|
142
|
+
config,
|
|
143
|
+
snapshotResponse.id,
|
|
144
|
+
reportSpinner
|
|
145
|
+
)
|
|
146
|
+
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
|
|
147
|
+
|
|
148
|
+
await vulnerabilityReportV2(config, snapshotResponse.id)
|
|
149
|
+
if (config.save !== undefined) {
|
|
150
|
+
await auditSave.auditSave(config)
|
|
151
|
+
}
|
|
152
|
+
const endTime = performance.now() - startTime
|
|
153
|
+
const scanDurationMs = endTime - startTime
|
|
154
|
+
|
|
155
|
+
console.log(
|
|
156
|
+
`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
157
|
+
)
|
|
158
|
+
}
|
|
138
159
|
} else {
|
|
139
160
|
if (filesFound.length === 0) {
|
|
140
161
|
console.log(i18n.__('languageAnalysisNoLanguage'))
|
package/src/common/HTTPClient.js
CHANGED
|
@@ -246,6 +246,13 @@ HTTPClient.prototype.scaServiceReportStatus = function scaServiceReport(
|
|
|
246
246
|
return requestUtils.sendRequest({ method: 'get', options })
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
HTTPClient.prototype.scaServiceIngests = function scaServiceIngests(config) {
|
|
250
|
+
const options = _.cloneDeep(this.requestOptions)
|
|
251
|
+
let url = createScaServiceIngestsURL(config)
|
|
252
|
+
options.url = url
|
|
253
|
+
return requestUtils.sendRequest({ method: 'get', options })
|
|
254
|
+
}
|
|
255
|
+
|
|
249
256
|
HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
|
|
250
257
|
const options = _.cloneDeep(this.requestOptions)
|
|
251
258
|
if (config.ignoreDev) {
|
|
@@ -370,6 +377,12 @@ HTTPClient.prototype.getSbom = function getSbom(config, type) {
|
|
|
370
377
|
return requestUtils.sendRequest({ method: 'get', options })
|
|
371
378
|
}
|
|
372
379
|
|
|
380
|
+
HTTPClient.prototype.getSCASbom = function getSbom(config, type, reportId) {
|
|
381
|
+
const options = _.cloneDeep(this.requestOptions)
|
|
382
|
+
options.url = createSCASbomUrl(config, type, reportId)
|
|
383
|
+
return requestUtils.sendRequest({ method: 'get', options })
|
|
384
|
+
}
|
|
385
|
+
|
|
373
386
|
HTTPClient.prototype.getLatestVersion = function getLatestVersion() {
|
|
374
387
|
const options = _.cloneDeep(this.requestOptions)
|
|
375
388
|
options.url =
|
|
@@ -447,15 +460,21 @@ function createSnapshotURL(config) {
|
|
|
447
460
|
}
|
|
448
461
|
|
|
449
462
|
function createScaServiceReportURL(config, reportId) {
|
|
450
|
-
return
|
|
463
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/reports/${reportId}`
|
|
451
464
|
}
|
|
452
465
|
|
|
453
466
|
function createScaServiceReportStatusURL(config, reportId) {
|
|
454
|
-
return
|
|
467
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/${reportId}/status`
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function createScaServiceIngestsURL(config) {
|
|
471
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests`
|
|
455
472
|
}
|
|
456
473
|
|
|
457
474
|
function createScaServiceIngestURL(config) {
|
|
458
|
-
|
|
475
|
+
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/tree`
|
|
476
|
+
baseUrl = config.track ? baseUrl.concat('?persist=true') : baseUrl
|
|
477
|
+
return baseUrl
|
|
459
478
|
}
|
|
460
479
|
|
|
461
480
|
const createAppCreateURL = config => {
|
|
@@ -492,6 +511,10 @@ function createSbomUrl(config, type) {
|
|
|
492
511
|
return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/${type}`
|
|
493
512
|
}
|
|
494
513
|
|
|
514
|
+
function createSCASbomUrl(config, type, reportId) {
|
|
515
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/sbom/${reportId}?toolType=${type}`
|
|
516
|
+
}
|
|
517
|
+
|
|
495
518
|
function createTelemetryEventUrl(config) {
|
|
496
519
|
return `${config.host}/Contrast/api/sast/organizations/${config.organizationId}/cli`
|
|
497
520
|
}
|
|
@@ -14,7 +14,7 @@ const HIGH = 'HIGH'
|
|
|
14
14
|
const CRITICAL = 'CRITICAL'
|
|
15
15
|
// App
|
|
16
16
|
const APP_NAME = 'contrast'
|
|
17
|
-
const APP_VERSION = '1.0.
|
|
17
|
+
const APP_VERSION = '1.0.15'
|
|
18
18
|
const TIMEOUT = 120000
|
|
19
19
|
const HIGH_COLOUR = '#ff9900'
|
|
20
20
|
const CRITICAL_COLOUR = '#e35858'
|
|
@@ -34,6 +34,12 @@ const SBOM_CYCLONE_DX_FILE = 'cyclonedx'
|
|
|
34
34
|
const SBOM_SPDX_FILE = 'spdx'
|
|
35
35
|
const CE_URL = 'https://ce.contrastsecurity.com'
|
|
36
36
|
|
|
37
|
+
//configuration
|
|
38
|
+
const SAAS = 'SAAS'
|
|
39
|
+
const EOP = 'EOP'
|
|
40
|
+
const MODE_BUILD = 'BUILD'
|
|
41
|
+
const MODE_REPO = 'REPO'
|
|
42
|
+
|
|
37
43
|
module.exports = {
|
|
38
44
|
supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
|
|
39
45
|
supportedLanguagesScan: { JAVASCRIPT, DOTNET, JAVA },
|
|
@@ -59,5 +65,9 @@ module.exports = {
|
|
|
59
65
|
LOW_PRIORITY,
|
|
60
66
|
NOTE_PRIORITY,
|
|
61
67
|
SBOM_CYCLONE_DX_FILE,
|
|
62
|
-
SBOM_SPDX_FILE
|
|
68
|
+
SBOM_SPDX_FILE,
|
|
69
|
+
SAAS,
|
|
70
|
+
EOP,
|
|
71
|
+
MODE_BUILD,
|
|
72
|
+
MODE_REPO
|
|
63
73
|
}
|
package/src/constants/locales.js
CHANGED
|
@@ -317,6 +317,9 @@ const en_locales = () => {
|
|
|
317
317
|
scanOptionsFileNameSummary:
|
|
318
318
|
'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .exe or .zip file in the working directory.',
|
|
319
319
|
scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
|
|
320
|
+
auditOptionsTrackSummary: ' Save the results to the UI.',
|
|
321
|
+
auditOptionsBranchSummary:
|
|
322
|
+
' Set the branch name to associate the library results to.',
|
|
320
323
|
authSuccessMessage: 'Authentication successful',
|
|
321
324
|
runAuthSuccessMessage:
|
|
322
325
|
chalk.bold('CodeSec by Contrast') +
|
package/src/constants.js
CHANGED
|
@@ -384,6 +384,38 @@ const auditOptionDefinitions = [
|
|
|
384
384
|
name: 'help',
|
|
385
385
|
alias: 'h',
|
|
386
386
|
type: Boolean
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'debug',
|
|
390
|
+
alias: 'd',
|
|
391
|
+
type: Boolean
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'verbose',
|
|
395
|
+
alias: 'v',
|
|
396
|
+
type: Boolean,
|
|
397
|
+
description:
|
|
398
|
+
'{bold ' +
|
|
399
|
+
i18n.__('constantsOptional') +
|
|
400
|
+
'}:' +
|
|
401
|
+
i18n.__('scanOptionsVerboseSummary')
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: 'track',
|
|
405
|
+
type: Boolean,
|
|
406
|
+
description:
|
|
407
|
+
'{bold ' +
|
|
408
|
+
i18n.__('constantsOptional') +
|
|
409
|
+
'}:' +
|
|
410
|
+
i18n.__('auditOptionsTrackSummary')
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: 'branch',
|
|
414
|
+
description:
|
|
415
|
+
'{bold ' +
|
|
416
|
+
i18n.__('constantsOptional') +
|
|
417
|
+
'}:' +
|
|
418
|
+
i18n.__('auditOptionsBranchSummary')
|
|
387
419
|
}
|
|
388
420
|
]
|
|
389
421
|
|
package/src/sbom/generateSbom.ts
CHANGED
|
@@ -15,3 +15,23 @@ export const generateSbom = (config: any, type: string) => {
|
|
|
15
15
|
console.log(err)
|
|
16
16
|
})
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export const generateSCASbom = (
|
|
20
|
+
config: any,
|
|
21
|
+
type: string,
|
|
22
|
+
reportId: string
|
|
23
|
+
) => {
|
|
24
|
+
const client = getHttpClient(config)
|
|
25
|
+
return client
|
|
26
|
+
.getSCASbom(config, type, reportId)
|
|
27
|
+
.then((res: { statusCode: number; body: any }) => {
|
|
28
|
+
if (res.statusCode === 200) {
|
|
29
|
+
return res.body
|
|
30
|
+
} else {
|
|
31
|
+
console.log('Unable to retrieve Software Bill of Materials (SBOM)')
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.catch((err: any) => {
|
|
35
|
+
console.log(err)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
@@ -14,6 +14,10 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
if (config.branch) {
|
|
18
|
+
requestBody.branchName = config.branch
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
const client = commonApi.getHttpClient(config)
|
|
18
22
|
const reportID = await client
|
|
19
23
|
.scaServiceIngest(requestBody, config)
|
|
@@ -27,28 +31,32 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
27
31
|
.catch(err => {
|
|
28
32
|
throw err
|
|
29
33
|
})
|
|
30
|
-
|
|
34
|
+
if (config.debug) {
|
|
35
|
+
console.log(' polling report', reportID)
|
|
36
|
+
}
|
|
31
37
|
|
|
32
38
|
let keepChecking = true
|
|
33
39
|
let res
|
|
34
40
|
while (keepChecking) {
|
|
35
41
|
res = await client.scaServiceReportStatus(config, reportID).then(res => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
if (config.debug) {
|
|
43
|
+
console.log(res.statusCode)
|
|
44
|
+
console.log(res.body)
|
|
45
|
+
}
|
|
46
|
+
if (res.body.status === 'COMPLETED') {
|
|
39
47
|
keepChecking = false
|
|
40
48
|
return client.scaServiceReport(config, reportID).then(res => {
|
|
41
|
-
return res.body
|
|
49
|
+
return [res.body, reportID]
|
|
42
50
|
})
|
|
43
51
|
}
|
|
44
52
|
})
|
|
45
53
|
|
|
46
54
|
if (!keepChecking) {
|
|
47
|
-
return res
|
|
55
|
+
return [res, reportID]
|
|
48
56
|
}
|
|
49
57
|
await requestUtils.sleep(5000)
|
|
50
58
|
}
|
|
51
|
-
return res
|
|
59
|
+
return [res, reportID]
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
module.exports = {
|
package/src/utils/commonApi.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const commonApi = require('./commonApi')
|
|
2
|
+
const { getMode } = require('./generalAPI')
|
|
3
|
+
const { SAAS, MODE_BUILD } = require('../constants/constants')
|
|
4
|
+
|
|
5
|
+
const getSettings = async config => {
|
|
6
|
+
config.isEOP = (await getMode(config)).toUpperCase() === SAAS ? false : true
|
|
7
|
+
config.mode = MODE_BUILD
|
|
8
|
+
config.scaServices = await isSCAServicesAvailable(config)
|
|
9
|
+
return config
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const isSCAServicesAvailable = async config => {
|
|
13
|
+
const client = commonApi.getHttpClient(config)
|
|
14
|
+
return client
|
|
15
|
+
.scaServiceIngests(config)
|
|
16
|
+
.then(res => {
|
|
17
|
+
return res.statusCode !== 403
|
|
18
|
+
})
|
|
19
|
+
.catch(err => {
|
|
20
|
+
console.log(err)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
getSettings
|
|
26
|
+
}
|