@contrast/contrast 1.0.18 → 1.0.20

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.
Files changed (47) hide show
  1. package/dist/audit/report/commonReportingFunctions.js +3 -4
  2. package/dist/audit/report/models/reportListModel.js +2 -1
  3. package/dist/audit/report/reportingFeature.js +1 -1
  4. package/dist/audit/report/utils/reportUtils.js +30 -11
  5. package/dist/cliConstants.js +1 -0
  6. package/dist/commands/audit/auditConfig.js +1 -2
  7. package/dist/commands/auth/auth.js +43 -7
  8. package/dist/commands/scan/sca/scaAnalysis.js +4 -2
  9. package/dist/common/HTTPClient.js +4 -4
  10. package/dist/common/errorHandling.js +13 -1
  11. package/dist/constants/constants.js +1 -1
  12. package/dist/constants/locales.js +18 -2
  13. package/dist/scaAnalysis/common/auditReport.js +16 -60
  14. package/dist/scaAnalysis/common/commonReportingFunctionsSca.js +154 -0
  15. package/dist/scaAnalysis/common/models/ScaReportModel.js +45 -0
  16. package/dist/scaAnalysis/common/scaServicesUpload.js +4 -3
  17. package/dist/scaAnalysis/common/utils/reportUtilsSca.js +76 -0
  18. package/dist/scaAnalysis/java/analysis.js +1 -28
  19. package/dist/scaAnalysis/java/index.js +1 -13
  20. package/dist/scan/formatScanOutput.js +19 -13
  21. package/dist/utils/getConfig.js +1 -1
  22. package/dist/utils/paramsUtil/configStoreParams.js +1 -12
  23. package/dist/utils/paramsUtil/paramHandler.js +1 -7
  24. package/package.json +5 -1
  25. package/src/audit/report/commonReportingFunctions.js +7 -5
  26. package/src/audit/report/models/reportListModel.ts +12 -2
  27. package/src/audit/report/reportingFeature.ts +1 -1
  28. package/src/audit/report/utils/reportUtils.ts +4 -4
  29. package/src/cliConstants.js +1 -0
  30. package/src/commands/audit/auditConfig.js +1 -2
  31. package/src/commands/auth/auth.js +49 -7
  32. package/src/commands/scan/sca/scaAnalysis.js +7 -2
  33. package/src/common/HTTPClient.js +5 -4
  34. package/src/common/errorHandling.js +14 -1
  35. package/src/constants/constants.js +1 -1
  36. package/src/constants/locales.js +19 -2
  37. package/src/scaAnalysis/common/auditReport.js +25 -80
  38. package/src/scaAnalysis/common/commonReportingFunctionsSca.js +276 -0
  39. package/src/scaAnalysis/common/models/ScaReportModel.ts +81 -0
  40. package/src/scaAnalysis/common/scaServicesUpload.js +5 -3
  41. package/src/scaAnalysis/common/utils/reportUtilsSca.ts +123 -0
  42. package/src/scaAnalysis/java/analysis.js +1 -28
  43. package/src/scaAnalysis/java/index.js +1 -18
  44. package/src/scan/formatScanOutput.ts +28 -17
  45. package/src/utils/getConfig.ts +1 -2
  46. package/src/utils/paramsUtil/configStoreParams.js +1 -14
  47. package/src/utils/paramsUtil/paramHandler.js +1 -9
@@ -51,7 +51,7 @@ const printFormattedOutput = (config, libraries, numberOfVulnerableLibraries, nu
51
51
  const report = new ReportList();
52
52
  for (const library of libraries) {
53
53
  const { name, version } = findNameAndVersion(library, config);
54
- const newOutputModel = new ReportModelStructure(new ReportCompositeKey(name, version, findHighestSeverityCVE(library.cveArray), severityCountAllCVEs(library.cveArray, new SeverityCountModel()).getTotal), library.cveArray);
54
+ const newOutputModel = new ReportModelStructure(new ReportCompositeKey(name, version, findHighestSeverityCVE(library.cveArray), severityCountAllCVEs(library.cveArray, new SeverityCountModel()).getTotal), library.cveArray, null);
55
55
  report.reportOutputList.push(newOutputModel);
56
56
  }
57
57
  const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(report.reportOutputList, [
@@ -120,8 +120,8 @@ function buildHeader(highestSeverity, contrastHeaderNum, libraryName, version, n
120
120
  return new ReportOutputHeaderModel(vulnMessage, introducesMessage);
121
121
  }
122
122
  function buildBody(cveArray, advice) {
123
- let assignPriorityToVulns = cveArray.map(result => findCVESeverity(result));
124
- const issueMessage = getIssueRow(assignPriorityToVulns);
123
+ const orderedCvesWithSeverityAssigned = orderByHighestPriority(cveArray.map(cve => findCVESeverity(cve)));
124
+ const issueMessage = getIssueRow(orderedCvesWithSeverityAssigned);
125
125
  const minOrMax = advice.maximum ? advice.maximum : advice.minimum;
126
126
  const displayAdvice = minOrMax
127
127
  ? `Change to version ${chalk.bold(minOrMax)}`
@@ -130,7 +130,6 @@ function buildBody(cveArray, advice) {
130
130
  return new ReportOutputBodyModel(issueMessage, adviceMessage);
131
131
  }
132
132
  function getIssueRow(cveArray) {
133
- orderByHighestPriority(cveArray);
134
133
  const cveMessagesList = getIssueCveMsgList(cveArray);
135
134
  return [chalk.bold('Issue'), ':', `${cveMessagesList.join(', ')}`];
136
135
  }
@@ -8,9 +8,10 @@ class ReportList {
8
8
  }
9
9
  exports.ReportList = ReportList;
10
10
  class ReportModelStructure {
11
- constructor(compositeKey, cveArray) {
11
+ constructor(compositeKey, cveArray, remediationAdvice) {
12
12
  this.compositeKey = compositeKey;
13
13
  this.cveArray = cveArray;
14
+ this.remediationAdvice = remediationAdvice;
14
15
  }
15
16
  }
16
17
  exports.ReportModelStructure = ReportModelStructure;
@@ -80,7 +80,7 @@ async function vulnerabilityReportV2(config, reportId) {
80
80
  console.log();
81
81
  const reportResponse = await (0, commonReportingFunctions_1.getReport)(config, reportId);
82
82
  if (reportResponse !== undefined) {
83
- let output = formatVulnerabilityOutput(reportResponse.vulnerabilities, config.applicationId, config, reportResponse.remediationGuidance
83
+ const output = formatVulnerabilityOutput(reportResponse.vulnerabilities, config.applicationId, config, reportResponse.remediationGuidance
84
84
  ? reportResponse.remediationGuidance
85
85
  : {});
86
86
  if (config.fail) {
@@ -1,13 +1,32 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
4
24
  };
5
25
  Object.defineProperty(exports, "__esModule", { value: true });
6
26
  exports.countVulnerableLibrariesBySeverity = exports.findNameAndVersion = exports.severityCountSingleCVE = exports.severityCountAllCVEs = exports.severityCountAllLibraries = exports.convertGenericToTypedLibraryVulns = exports.findCVESeverity = exports.orderByHighestPriority = exports.findHighestSeverityCVE = void 0;
7
27
  const reportLibraryModel_1 = require("../models/reportLibraryModel");
8
28
  const reportSeverityModel_1 = require("../models/reportSeverityModel");
9
- const constants_1 = __importDefault(require("../../../constants/constants"));
10
- const constants_2 = require("../../../constants/constants");
29
+ const constants_1 = __importStar(require("../../../constants/constants"));
11
30
  const lodash_1 = require("lodash");
12
31
  const severityCountModel_1 = require("../models/severityCountModel");
13
32
  const { supportedLanguages: { GO } } = constants_1.default;
@@ -16,27 +35,27 @@ function findHighestSeverityCVE(cveArray) {
16
35
  return (0, lodash_1.orderBy)(mappedToReportSeverityModels, cve => cve?.priority)[0];
17
36
  }
18
37
  exports.findHighestSeverityCVE = findHighestSeverityCVE;
19
- function orderByHighestPriority(cves) {
20
- return (0, lodash_1.orderBy)(cves, ['priority'], ['asc']);
38
+ function orderByHighestPriority(severityModels) {
39
+ return (0, lodash_1.orderBy)(severityModels, ['priority'], ['asc']);
21
40
  }
22
41
  exports.orderByHighestPriority = orderByHighestPriority;
23
42
  function findCVESeverity(cve) {
24
43
  const cveName = cve.name;
25
44
  if (cve.cvss3SeverityCode === 'CRITICAL' || cve.severityCode === 'CRITICAL') {
26
- return new reportSeverityModel_1.ReportSeverityModel('CRITICAL', constants_2.CRITICAL_PRIORITY, constants_2.CRITICAL_COLOUR, cveName);
45
+ return new reportSeverityModel_1.ReportSeverityModel('CRITICAL', constants_1.CRITICAL_PRIORITY, constants_1.CRITICAL_COLOUR, cveName);
27
46
  }
28
47
  else if (cve.cvss3SeverityCode === 'HIGH' || cve.severityCode === 'HIGH') {
29
- return new reportSeverityModel_1.ReportSeverityModel('HIGH', constants_2.HIGH_PRIORITY, constants_2.HIGH_COLOUR, cveName);
48
+ return new reportSeverityModel_1.ReportSeverityModel('HIGH', constants_1.HIGH_PRIORITY, constants_1.HIGH_COLOUR, cveName);
30
49
  }
31
50
  else if (cve.cvss3SeverityCode === 'MEDIUM' ||
32
51
  cve.severityCode === 'MEDIUM') {
33
- return new reportSeverityModel_1.ReportSeverityModel('MEDIUM', constants_2.MEDIUM_PRIORITY, constants_2.MEDIUM_COLOUR, cveName);
52
+ return new reportSeverityModel_1.ReportSeverityModel('MEDIUM', constants_1.MEDIUM_PRIORITY, constants_1.MEDIUM_COLOUR, cveName);
34
53
  }
35
54
  else if (cve.cvss3SeverityCode === 'LOW' || cve.severityCode === 'LOW') {
36
- return new reportSeverityModel_1.ReportSeverityModel('LOW', constants_2.LOW_PRIORITY, constants_2.LOW_COLOUR, cveName);
55
+ return new reportSeverityModel_1.ReportSeverityModel('LOW', constants_1.LOW_PRIORITY, constants_1.LOW_COLOUR, cveName);
37
56
  }
38
57
  else if (cve.cvss3SeverityCode === 'NOTE' || cve.severityCode === 'NOTE') {
39
- return new reportSeverityModel_1.ReportSeverityModel('NOTE', constants_2.NOTE_PRIORITY, constants_2.NOTE_COLOUR, cveName);
58
+ return new reportSeverityModel_1.ReportSeverityModel('NOTE', constants_1.NOTE_PRIORITY, constants_1.NOTE_COLOUR, cveName);
40
59
  }
41
60
  }
42
61
  exports.findCVESeverity = findCVESeverity;
@@ -185,6 +185,7 @@ const scanOptionDefinitions = [
185
185
  }
186
186
  ];
187
187
  const authOptionDefinitions = [
188
+ ...sharedConnectionOptionDefinitions,
188
189
  {
189
190
  name: 'help',
190
191
  alias: 'h',
@@ -5,8 +5,7 @@ const paramHandler = require('../../utils/paramsUtil/paramHandler');
5
5
  const getAuditConfig = async (contrastConf, command, argv) => {
6
6
  const auditParameters = await getCommandLineArgsCustom(contrastConf, command, argv, constants.commandLineDefinitions.auditOptionDefinitions);
7
7
  const paramsAuth = paramHandler.getAuth(auditParameters);
8
- const javaAgreement = paramHandler.getAgreement();
9
- return { ...paramsAuth, ...auditParameters, ...javaAgreement };
8
+ return { ...paramsAuth, ...auditParameters };
10
9
  };
11
10
  module.exports = {
12
11
  getAuditConfig
@@ -1,21 +1,31 @@
1
1
  "use strict";
2
2
  const { v4: uuidv4 } = require('uuid');
3
- const { setConfigValues } = require('../../utils/getConfig');
4
- const open = require('open');
3
+ const configFunctions = require('../../utils/getConfig');
5
4
  const commonApi = require('../../utils/commonApi');
6
- const { sleep } = require('../../utils/requestUtils');
5
+ const requestUtils = require('../../utils/requestUtils');
7
6
  const i18n = require('i18n');
8
7
  const { returnOra, startSpinner, failSpinner, succeedSpinner } = require('../../utils/oraWrapper');
9
8
  const { TIMEOUT, AUTH_UI_URL } = require('../../constants/constants');
10
9
  const parsedCLIOptions = require('../../utils/parsedCLIOptions');
11
10
  const constants = require('../../cliConstants');
12
11
  const commandLineUsage = require('command-line-usage');
12
+ const { commonMessageFormatter } = require('../../common/errorHandling');
13
+ const open = require('open');
14
+ const messages = require('../../constants/locales').en_locales();
13
15
  const processAuth = async (argv, config) => {
14
16
  let authParams = await parsedCLIOptions.getCommandLineArgsCustom(config, 'auth', argv, constants.commandLineDefinitions.authOptionDefinitions);
15
17
  if (authParams.help) {
16
18
  console.log(authUsageGuide);
17
19
  process.exit(0);
18
20
  }
21
+ if (checkForCustomCredentials(authParams)) {
22
+ processCustomCredentials(authParams, config);
23
+ }
24
+ else {
25
+ await startAuthProcess(config);
26
+ }
27
+ };
28
+ const startAuthProcess = async (config) => {
19
29
  const token = uuidv4();
20
30
  const url = `${AUTH_UI_URL}/?token=${token}`;
21
31
  console.log(i18n.__('redirectAuth', url));
@@ -25,9 +35,8 @@ const processAuth = async (argv, config) => {
25
35
  }, 0);
26
36
  const result = await isAuthComplete(token, TIMEOUT, config);
27
37
  if (result) {
28
- setConfigValues(config, result);
38
+ configFunctions.setConfigValues(config, result);
29
39
  }
30
- return;
31
40
  }
32
41
  finally {
33
42
  }
@@ -54,7 +63,7 @@ const isAuthComplete = async (token, timeout, config) => {
54
63
  }
55
64
  };
56
65
  const pollAuthResult = async (token, client) => {
57
- await sleep(5000);
66
+ await requestUtils.sleep(5000);
58
67
  return client
59
68
  .pollForAuth(token)
60
69
  .then(res => {
@@ -74,6 +83,33 @@ const authUsageGuide = commandLineUsage([
74
83
  content: [i18n.__('constantsAuthUsageContents')]
75
84
  }
76
85
  ]);
86
+ const checkForCustomCredentials = authParams => {
87
+ const hasSomeKeys = authParams.apiKey ||
88
+ authParams.organizationId ||
89
+ authParams.host ||
90
+ authParams.authorization;
91
+ const hasAllKeys = authParams.apiKey &&
92
+ authParams.organizationId &&
93
+ authParams.host &&
94
+ authParams.authorization;
95
+ if (hasAllKeys) {
96
+ return true;
97
+ }
98
+ if (hasSomeKeys) {
99
+ commonMessageFormatter(messages.authCommand.credentialsMissing, true);
100
+ }
101
+ return false;
102
+ };
103
+ const processCustomCredentials = (authParams, config) => {
104
+ const valuesToSet = {
105
+ apiKey: authParams.apiKey,
106
+ orgId: authParams.organizationId,
107
+ authHeader: authParams.authorization,
108
+ host: authParams.host
109
+ };
110
+ configFunctions.setConfigValues(config, valuesToSet);
111
+ commonMessageFormatter(messages.authCommand.credentialsAccepted, false);
112
+ };
77
113
  module.exports = {
78
- processAuth: processAuth
114
+ processAuth
79
115
  };
@@ -24,6 +24,7 @@ const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload');
24
24
  const settingsHelper = require('../../../utils/settingsHelper');
25
25
  const chalk = require('chalk');
26
26
  const saveResults = require('../../../scan/saveResults');
27
+ const { convertGenericToTypedReportModelSca } = require('../../../scaAnalysis/common/utils/reportUtilsSca');
27
28
  const processSca = async (config) => {
28
29
  config = await settingsHelper.getSettings(config);
29
30
  const startTime = performance.now();
@@ -102,8 +103,9 @@ const processSca = async (config) => {
102
103
  console.log('');
103
104
  const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'));
104
105
  startSpinner(reportSpinner);
105
- const [reports, reportId] = await scaUpload.scaTreeUpload(messageToSend, config);
106
- auditReport.processAuditReport(config, reports[0]);
106
+ const { reportArray, reportId } = await scaUpload.scaTreeUpload(messageToSend, config);
107
+ const reportModelLibraryList = convertGenericToTypedReportModelSca(reportArray);
108
+ auditReport.processAuditReport(config, reportModelLibraryList);
107
109
  succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'));
108
110
  if (config.save !== undefined) {
109
111
  await auditSave.auditSave(config, reportId);
@@ -332,18 +332,18 @@ function createSnapshotURL(config) {
332
332
  return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/snapshots`;
333
333
  }
334
334
  function createScaServiceReportURL(config, reportId) {
335
- let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/reports/${reportId}`;
335
+ let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/reports/${reportId}`;
336
336
  baseUrl = config.ignoreDev ? baseUrl.concat('?nodesToInclude=PROD') : baseUrl;
337
337
  return baseUrl;
338
338
  }
339
339
  function createScaServiceReportStatusURL(config, reportId) {
340
- return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/${reportId}/status`;
340
+ return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/ingests/${reportId}/status`;
341
341
  }
342
342
  function createScaServiceIngestsURL(config) {
343
- return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests`;
343
+ return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/ingests`;
344
344
  }
345
345
  function createScaServiceIngestURL(config) {
346
- let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/tree`;
346
+ let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/applications/${config.applicationId}/libraries/ingests/tree`;
347
347
  baseUrl = config.track ? baseUrl.concat('?persist=true') : baseUrl;
348
348
  return baseUrl;
349
349
  }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  const i18n = require('i18n');
3
+ const chalk = require('chalk');
3
4
  const libraryAnalysisError = () => {
4
5
  console.log(i18n.__('libraryAnalysisError'));
5
6
  };
@@ -79,6 +80,16 @@ const findCommandOnError = unknownOptions => {
79
80
  return foundCommands[0];
80
81
  }
81
82
  };
83
+ const commonMessageFormatter = (message, fail) => {
84
+ console.log(chalk.bold(i18n.__(message.title)));
85
+ console.log(i18n.__(message.body));
86
+ if (message.extra) {
87
+ console.log(i18n.__(message.extra));
88
+ }
89
+ if (fail) {
90
+ process.exit(1);
91
+ }
92
+ };
82
93
  module.exports = {
83
94
  genericError,
84
95
  unauthenticatedError,
@@ -95,5 +106,6 @@ module.exports = {
95
106
  reportFailureError,
96
107
  maxAppError,
97
108
  parametersError,
98
- invalidHostNameError
109
+ invalidHostNameError,
110
+ commonMessageFormatter
99
111
  };
@@ -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.18';
15
+ const APP_VERSION = '1.0.20';
16
16
  const TIMEOUT = 120000;
17
17
  const HIGH_COLOUR = '#ff9900';
18
18
  const CRITICAL_COLOUR = '#e35858';
@@ -134,7 +134,10 @@ const en_locales = () => {
134
134
  chalk.bold('\ncontrast scan') +
135
135
  " to run Contrast's industry leading SAST scanner. \nSupports Java, JavaScript and .Net \n" +
136
136
  chalk.bold('\ncontrast audit') +
137
- ' to find vulnerabilities in your open source dependencies.\nSupports Java, .NET, Node, Ruby, Python, Go and PHP \n' +
137
+ ' to find vulnerabilities in your open source dependencies.' +
138
+ '\nSupports Java, .NET, Node, Ruby, Python, Go and PHP.' +
139
+ '\nOur CLI runs native build tools to generate a complete dependency tree.' +
140
+ '\nIf you are running on untrusted code, consider running in a sandbox.\n' +
138
141
  chalk.bold('\ncontrast lambda') +
139
142
  ' to secure your AWS serverless functions. \nSupports Java and Python \n' +
140
143
  chalk.bold('\ncontrast help') +
@@ -181,7 +184,8 @@ const en_locales = () => {
181
184
  constantsAuditPrerequisitesContentSupportedLanguages: 'Supported languages and their requirements are:',
182
185
  constantsAuditPrerequisitesJavaContentMessage: `
183
186
  ${chalk.bold('Java:')} pom.xml ${chalk.bold('and')} Maven build platform including the dependency plugin.
184
- ${chalk.bold('Or')} build.gradle ${chalk.bold('and')} gradle dependencies or ./gradlew dependencies must be supported`,
187
+ ${chalk.bold('Or')} build.gradle ${chalk.bold('and')} gradle dependencies or ./gradlew dependencies must be supported
188
+ If you are running on untrusted code, consider running in a sandbox.`,
185
189
  constantsAuditPrerequisitesContentDotNetMessage: `
186
190
  ${chalk.bold('.NET framework and .NET core:')} MSBuild 15.0 or greater and a packages.lock.json file.
187
191
  Note: If the packages.lock.json file is unavailable it can be generated by setting RestorePackagesWithLockFile to true within each *.csproj file and running dotnet build.\n`,
@@ -216,6 +220,18 @@ const en_locales = () => {
216
220
  commonHelpLearnMoreEnterpriseText: ' https://docs.contrastsecurity.com/en/run-contrast-cli.html ',
217
221
  commonHelpJoinDiscussionHeader: chalk.hex('#9DC184')('Join the discussion:'),
218
222
  commonHelpJoinDiscussionText: ' https://dev.to/codesec',
223
+ authCommand: {
224
+ credentialsAccepted: {
225
+ title: 'Credentials accepted',
226
+ body: 'Now run contrast audit, lambda or scan',
227
+ extra: 'Or contrast help for full list of commands'
228
+ },
229
+ credentialsMissing: {
230
+ title: 'Credentials missing',
231
+ body: 'You have not entered the right parameters or enough information',
232
+ extra: 'Please check and try again e.g. contrast auth --api-key yourApiKey--organization-id yourOrg --authorization yourAuth --host https://yourHost\nOr contrast help for full list of commands'
233
+ }
234
+ },
219
235
  ...lambda
220
236
  };
221
237
  };
@@ -1,77 +1,33 @@
1
1
  "use strict";
2
- const { getSeverityCounts, createSummaryMessageTop, printVulnInfo, getReportTable, getIssueRow, printNoVulnFoundMsg } = require('../../audit/report/commonReportingFunctions');
3
- const { orderBy } = require('lodash');
4
- const { assignBySeverity } = require('../../scan/formatScanOutput');
5
- const chalk = require('chalk');
6
- const { CE_URL } = require('../../constants/constants');
2
+ const { getSeverityCounts, printNoVulnFoundMsg } = require('../../audit/report/commonReportingFunctions');
7
3
  const common = require('../../common/fail');
8
- const i18n = require('i18n');
9
- const processAuditReport = (config, results) => {
4
+ const { printFormattedOutputSca } = require('./commonReportingFunctionsSca');
5
+ const processAuditReport = (config, reportModelList) => {
10
6
  let severityCounts = {};
11
- if (results !== undefined) {
12
- severityCounts = formatScaServicesReport(config, results);
7
+ if (reportModelList !== undefined) {
8
+ severityCounts = formatScaServicesReport(config, reportModelList);
13
9
  }
14
10
  if (config.fail) {
15
11
  common.processFail(config, severityCounts);
16
12
  }
17
13
  };
18
- const formatScaServicesReport = (config, results) => {
19
- const projectOverviewCount = getSeverityCounts(results);
14
+ const formatScaServicesReport = (config, reportModelList) => {
15
+ const projectOverviewCount = getSeverityCounts(reportModelList);
20
16
  if (projectOverviewCount.total === 0) {
21
17
  printNoVulnFoundMsg();
22
- return projectOverviewCount;
23
18
  }
24
19
  else {
25
- let total = 0;
26
- const numberOfCves = results.length;
27
- const table = getReportTable();
28
- let contrastHeaderNumCounter = 0;
29
- let assignPriorityToResults = results.map(result => assignBySeverity(result, result));
30
- const numberOfVulns = results
31
- .map(result => result.vulnerabilities)
32
- .reduce((a, b) => {
33
- return (total += b.length);
34
- }, 0);
35
- const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(assignPriorityToResults, [
36
- reportListItem => {
37
- return reportListItem.priority;
38
- },
39
- reportListItem => {
40
- return reportListItem.vulnerabilities.length;
20
+ const numberOfVulnerableLibraries = reportModelList.map(library => {
21
+ let count = 0;
22
+ if (library.vulnerabilities.length > 0) {
23
+ count++;
41
24
  }
42
- ], ['asc', 'desc']);
43
- for (const result of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
44
- contrastHeaderNumCounter++;
45
- const cvesNum = result.vulnerabilities.length;
46
- const grammaticallyCorrectVul = result.vulnerabilities.length > 1 ? 'vulnerabilities' : 'vulnerability';
47
- const headerColour = chalk.hex(result.colour);
48
- const headerRow = [
49
- headerColour(`CONTRAST-${contrastHeaderNumCounter.toString().padStart(3, '0')}`),
50
- headerColour(`-`),
51
- headerColour(`[${result.severity}] `) +
52
- headerColour.bold(`${result.artifactName}`) +
53
- ` introduces ${cvesNum} ${grammaticallyCorrectVul}`
54
- ];
55
- const adviceRow = [
56
- chalk.bold(`Advice`),
57
- chalk.bold(`:`),
58
- `Change to version ${result.remediationAdvice.latestStableVersion}`
59
- ];
60
- let assignPriorityToVulns = result.vulnerabilities.map(result => assignBySeverity(result, result));
61
- const issueRow = getIssueRow(assignPriorityToVulns);
62
- table.push(headerRow, issueRow, adviceRow);
63
- console.log();
64
- }
65
- console.log();
66
- createSummaryMessageTop(numberOfCves, numberOfVulns);
67
- console.log(table.toString() + '\n');
68
- printVulnInfo(projectOverviewCount);
69
- if (config.host !== CE_URL) {
70
- console.log('\n' + chalk.bold(i18n.__('auditServicesMessageForTS')));
71
- console.log(`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs`);
72
- }
73
- return projectOverviewCount;
25
+ return count;
26
+ }).length;
27
+ let numberOfCves = reportModelList.reduce((count, current) => count + current.vulnerabilities.length, 0);
28
+ printFormattedOutputSca(config, reportModelList, numberOfVulnerableLibraries, numberOfCves);
74
29
  }
30
+ return projectOverviewCount;
75
31
  };
76
32
  module.exports = {
77
33
  formatScaServicesReport,
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ const { ReportList, ReportModelStructure, ReportCompositeKey } = require('../../audit/report/models/reportListModel');
3
+ const { countVulnerableLibrariesBySeverity } = require('../../audit/report/utils/reportUtils');
4
+ const { SeverityCountModel } = require('../../audit/report/models/severityCountModel');
5
+ const { orderBy } = require('lodash');
6
+ const { ReportOutputModel, ReportOutputHeaderModel, ReportOutputBodyModel } = require('../../audit/report/models/reportOutputModel');
7
+ const { CE_URL, CRITICAL_COLOUR, HIGH_COLOUR, MEDIUM_COLOUR, LOW_COLOUR, NOTE_COLOUR } = require('../../constants/constants');
8
+ const chalk = require('chalk');
9
+ const Table = require('cli-table3');
10
+ const { findHighestSeverityCVESca, severityCountAllCVEsSca, findCVESeveritySca, orderByHighestPrioritySca } = require('./utils/reportUtilsSca');
11
+ const { buildFormattedHeaderNum } = require('../../audit/report/commonReportingFunctions');
12
+ const createSummaryMessageTop = (numberOfVulnerableLibraries, numberOfCves) => {
13
+ numberOfVulnerableLibraries === 1
14
+ ? console.log(`\n\nFound 1 vulnerable library containing ${numberOfCves} CVE`)
15
+ : console.log(`\n\nFound ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs`);
16
+ };
17
+ const createSummaryMessageBottom = numberOfVulnerableLibraries => {
18
+ numberOfVulnerableLibraries === 1
19
+ ? console.log(`Found 1 vulnerability`)
20
+ : console.log(`Found ${numberOfVulnerableLibraries} vulnerabilities`);
21
+ };
22
+ const printFormattedOutputSca = (config, reportModelList, numberOfVulnerableLibraries, numberOfCves) => {
23
+ createSummaryMessageTop(numberOfVulnerableLibraries, numberOfCves);
24
+ console.log();
25
+ const report = new ReportList();
26
+ for (const library of reportModelList) {
27
+ const { artifactName, version, vulnerabilities, remediationAdvice } = library;
28
+ const newOutputModel = new ReportModelStructure(new ReportCompositeKey(artifactName, version, findHighestSeverityCVESca(vulnerabilities), severityCountAllCVEsSca(vulnerabilities, new SeverityCountModel()).getTotal), vulnerabilities, remediationAdvice);
29
+ report.reportOutputList.push(newOutputModel);
30
+ }
31
+ const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(report.reportOutputList, [
32
+ reportListItem => {
33
+ return reportListItem.compositeKey.highestSeverity.priority;
34
+ },
35
+ reportListItem => {
36
+ return reportListItem.compositeKey.numberOfSeverities;
37
+ }
38
+ ], ['asc', 'desc']);
39
+ let contrastHeaderNumCounter = 0;
40
+ for (const reportModel of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
41
+ contrastHeaderNumCounter++;
42
+ const { libraryName, libraryVersion, highestSeverity } = reportModel.compositeKey;
43
+ const { cveArray, remediationAdvice } = reportModel;
44
+ const numOfCVEs = reportModel.cveArray.length;
45
+ const table = getReportTable();
46
+ const header = buildHeader(highestSeverity, contrastHeaderNumCounter, libraryName, libraryVersion, numOfCVEs);
47
+ const body = buildBody(cveArray, remediationAdvice);
48
+ const reportOutputModel = new ReportOutputModel(header, body);
49
+ table.push(reportOutputModel.body.issueMessage, reportOutputModel.body.adviceMessage);
50
+ console.log(reportOutputModel.header.vulnMessage, reportOutputModel.header.introducesMessage);
51
+ console.log(table.toString() + '\n');
52
+ }
53
+ createSummaryMessageBottom(numberOfVulnerableLibraries);
54
+ const { criticalMessage, highMessage, mediumMessage, lowMessage, noteMessage } = buildFooter(outputOrderedByLowestSeverityAndLowestNumOfCvesFirst);
55
+ console.log(`${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`);
56
+ if (config.host !== CE_URL) {
57
+ console.log('\n' + chalk.bold('View your full dependency tree in Contrast:'));
58
+ console.log(`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`);
59
+ }
60
+ };
61
+ function getReportTable() {
62
+ return new Table({
63
+ chars: {
64
+ top: '',
65
+ 'top-mid': '',
66
+ 'top-left': '',
67
+ 'top-right': '',
68
+ bottom: '',
69
+ 'bottom-mid': '',
70
+ 'bottom-left': '',
71
+ 'bottom-right': '',
72
+ left: '',
73
+ 'left-mid': '',
74
+ mid: '',
75
+ 'mid-mid': '',
76
+ right: '',
77
+ 'right-mid': '',
78
+ middle: ' '
79
+ },
80
+ style: { 'padding-left': 0, 'padding-right': 0 },
81
+ colAligns: ['right'],
82
+ wordWrap: true,
83
+ colWidths: [12, 1, 100]
84
+ });
85
+ }
86
+ function buildHeader(highestSeverity, contrastHeaderNum, libraryName, version, numOfCVEs) {
87
+ const vulnerabilityPluralised = numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability';
88
+ const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum);
89
+ const headerColour = chalk.hex(highestSeverity.colour);
90
+ const headerNumAndSeverity = headerColour(`${formattedHeaderNum} - [${highestSeverity.severity}]`);
91
+ const libraryNameAndVersion = headerColour.bold(`${libraryName}-${version}`);
92
+ const vulnMessage = `${headerNumAndSeverity} ${libraryNameAndVersion}`;
93
+ const introducesMessage = `introduces ${numOfCVEs} ${vulnerabilityPluralised}`;
94
+ return new ReportOutputHeaderModel(vulnMessage, introducesMessage);
95
+ }
96
+ function buildBody(cveArray, advice) {
97
+ const orderedCvesWithSeverityAssigned = orderByHighestPrioritySca(cveArray.map(cve => findCVESeveritySca(cve)));
98
+ const issueMessage = getIssueRow(orderedCvesWithSeverityAssigned);
99
+ const adviceMessage = getAdviceRow(advice);
100
+ return new ReportOutputBodyModel(issueMessage, adviceMessage);
101
+ }
102
+ function getIssueRow(cveArray) {
103
+ const cveMessagesList = getIssueCveMsgList(cveArray);
104
+ return [chalk.bold('Issue'), ':', `${cveMessagesList.join(', ')}`];
105
+ }
106
+ function getAdviceRow(advice) {
107
+ const latestOrClosest = advice.latestStableVersion
108
+ ? advice.latestStableVersion
109
+ : advice.closestStableVersion;
110
+ const displayAdvice = latestOrClosest
111
+ ? `Change to version ${chalk.bold(latestOrClosest)}`
112
+ : 'No recommendation is available according to our data. Upgrade to the latest stable is the best advice we can give.';
113
+ return [chalk.bold(`Advice`), chalk.bold(`:`), `${displayAdvice}`];
114
+ }
115
+ const buildFooter = reportModelStructure => {
116
+ const { critical, high, medium, low, note } = countVulnerableLibrariesBySeverity(reportModelStructure);
117
+ const criticalMessage = chalk
118
+ .hex(CRITICAL_COLOUR)
119
+ .bold(`${critical} Critical`);
120
+ const highMessage = chalk.hex(HIGH_COLOUR).bold(`${high} High`);
121
+ const mediumMessage = chalk.hex(MEDIUM_COLOUR).bold(`${medium} Medium`);
122
+ const lowMessage = chalk.hex(LOW_COLOUR).bold(`${low} Low`);
123
+ const noteMessage = chalk.hex(NOTE_COLOUR).bold(`${note} Note`);
124
+ return {
125
+ criticalMessage,
126
+ highMessage,
127
+ mediumMessage,
128
+ lowMessage,
129
+ noteMessage
130
+ };
131
+ };
132
+ const getIssueCveMsgList = reportSeverityModels => {
133
+ const cveMessages = [];
134
+ reportSeverityModels.forEach(reportSeverityModel => {
135
+ const { colour, severity, name } = reportSeverityModel;
136
+ const severityShorthand = chalk
137
+ .hex(colour)
138
+ .bold(`[${severity.charAt(0).toUpperCase()}]`);
139
+ const builtMessage = severityShorthand + name;
140
+ cveMessages.push(builtMessage);
141
+ });
142
+ return cveMessages;
143
+ };
144
+ module.exports = {
145
+ createSummaryMessageTop,
146
+ createSummaryMessageBottom,
147
+ printFormattedOutputSca,
148
+ getReportTable,
149
+ buildHeader,
150
+ buildBody,
151
+ getIssueRow,
152
+ buildFormattedHeaderNum,
153
+ getIssueCveMsgList
154
+ };