@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.
@@ -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
- save.saveFile(config, fileFormat, await sbom.generateSbom(config, fileFormat));
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}`);
@@ -52,7 +52,9 @@ const auditUsageGuide = (0, command_line_usage_1.default)([
52
52
  'language',
53
53
  'experimental',
54
54
  'app-groups',
55
- 'metadata'
55
+ 'metadata',
56
+ 'track',
57
+ 'branch'
56
58
  ]
57
59
  },
58
60
  (0, commonHelp_1.commonHelpLinks)()
@@ -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 generalAPI = require('../../../utils/generalAPI');
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.mode = await generalAPI.getMode(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
- console.log('');
79
- const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'));
80
- startSpinner(reportSpinner);
81
- const snapshotResponse = await treeUpload.commonSendSnapShot(messageToSend, config);
82
- await pollForSnapshotCompletition(config, snapshotResponse.id, reportSpinner);
83
- succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'));
84
- await vulnerabilityReportV2(config, snapshotResponse.id);
85
- if (config.save !== undefined) {
86
- await auditSave.auditSave(config);
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
- return ``;
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.14';
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
- console.log(' polling report');
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
- console.log(res.statusCode);
36
- console.log(res.body);
37
- if (res.body.status == 'COMPLETED') {
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
@@ -18,6 +18,7 @@ const handleResponseErrors = (res, api) => {
18
18
  maxAppError();
19
19
  }
20
20
  else {
21
+ console.log(res.statusCode);
21
22
  genericError(res);
22
23
  }
23
24
  };
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
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
- save.saveFile(
27
- config,
28
- fileFormat,
29
- await sbom.generateSbom(config, fileFormat)
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}`)
@@ -47,7 +47,9 @@ const auditUsageGuide = commandLineUsage([
47
47
  'language',
48
48
  'experimental',
49
49
  'app-groups',
50
- 'metadata'
50
+ 'metadata',
51
+ 'track',
52
+ 'branch'
51
53
  ]
52
54
  },
53
55
  commonHelpLinks()
@@ -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 generalAPI = require('../../../utils/generalAPI')
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
- config.mode = await generalAPI.getMode(config)
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
- // if (config.experimental) {
107
- // // const reports = await scaUpload.scaTreeUpload(messageToSend, config)
108
- // auditReport.processAuditReport(config, 'reports')
109
- // } else {
110
- console.log('') //empty log for space before spinner
111
- //send message to TS
112
- const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
113
- startSpinner(reportSpinner)
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
- //poll for completion
120
- await pollForSnapshotCompletition(
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
- await vulnerabilityReportV2(config, snapshotResponse.id)
128
- if (config.save !== undefined) {
129
- await auditSave.auditSave(config)
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
- console.log(
135
- `----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
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'))
@@ -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
- return ``
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.14'
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
  }
@@ -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
 
@@ -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
- console.log(' polling report')
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
- console.log(res.statusCode)
37
- console.log(res.body)
38
- if (res.body.status == 'COMPLETED') {
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 = {
@@ -20,6 +20,7 @@ const handleResponseErrors = (res, api) => {
20
20
  } else if (res.statusCode === 412) {
21
21
  maxAppError()
22
22
  } else {
23
+ console.log(res.statusCode)
23
24
  genericError(res)
24
25
  }
25
26
  }
@@ -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
+ }