@contrast/contrast 1.0.10 → 1.0.11

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 (78) hide show
  1. package/README.md +1 -1
  2. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +39 -28
  3. package/dist/audit/languageAnalysisEngine/report/models/reportGuidanceModel.js +6 -0
  4. package/dist/audit/languageAnalysisEngine/report/models/reportOutputModel.js +1 -2
  5. package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +1 -0
  6. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +11 -7
  7. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +1 -2
  8. package/dist/commands/audit/auditConfig.js +3 -3
  9. package/dist/commands/audit/processAudit.js +4 -2
  10. package/dist/commands/auth/auth.js +1 -1
  11. package/dist/commands/config/config.js +2 -2
  12. package/dist/commands/scan/processScan.js +11 -4
  13. package/dist/commands/scan/sca/scaAnalysis.js +10 -3
  14. package/dist/common/HTTPClient.js +9 -0
  15. package/dist/common/fail.js +66 -0
  16. package/dist/common/versionChecker.js +1 -1
  17. package/dist/constants/constants.js +1 -1
  18. package/dist/constants/locales.js +6 -3
  19. package/dist/constants.js +39 -1
  20. package/dist/index.js +5 -2
  21. package/dist/scaAnalysis/common/scaParserForGoAndJava.js +32 -0
  22. package/dist/scaAnalysis/common/treeUpload.js +20 -5
  23. package/dist/scaAnalysis/dotnet/analysis.js +15 -3
  24. package/dist/scaAnalysis/go/goAnalysis.js +8 -2
  25. package/dist/scaAnalysis/java/analysis.js +10 -6
  26. package/dist/scaAnalysis/java/index.js +7 -1
  27. package/dist/scaAnalysis/java/javaBuildDepsParser.js +19 -3
  28. package/dist/scaAnalysis/python/analysis.js +43 -5
  29. package/dist/scaAnalysis/python/index.js +7 -2
  30. package/dist/scaAnalysis/ruby/analysis.js +14 -4
  31. package/dist/scan/formatScanOutput.js +6 -5
  32. package/dist/scan/populateProjectIdAndProjectName.js +5 -0
  33. package/dist/scan/scan.js +4 -0
  34. package/dist/scan/scanConfig.js +3 -3
  35. package/dist/scan/scanResults.js +39 -3
  36. package/dist/telemetry/telemetry.js +137 -0
  37. package/dist/utils/getConfig.js +2 -2
  38. package/dist/utils/parsedCLIOptions.js +3 -1
  39. package/dist/utils/requestUtils.js +7 -1
  40. package/package.json +1 -1
  41. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +57 -39
  42. package/src/audit/languageAnalysisEngine/report/models/reportGuidanceModel.ts +5 -0
  43. package/src/audit/languageAnalysisEngine/report/models/reportOutputModel.ts +1 -7
  44. package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +2 -0
  45. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +15 -8
  46. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +2 -2
  47. package/src/commands/audit/auditConfig.ts +10 -3
  48. package/src/commands/audit/processAudit.ts +16 -2
  49. package/src/commands/auth/auth.js +3 -1
  50. package/src/commands/config/config.js +4 -2
  51. package/src/commands/scan/processScan.js +18 -4
  52. package/src/commands/scan/sca/scaAnalysis.js +11 -3
  53. package/src/common/HTTPClient.js +14 -0
  54. package/src/common/fail.js +75 -0
  55. package/src/common/versionChecker.ts +1 -1
  56. package/src/constants/constants.js +1 -1
  57. package/src/constants/locales.js +8 -4
  58. package/src/constants.js +43 -1
  59. package/src/index.ts +17 -2
  60. package/src/scaAnalysis/common/scaParserForGoAndJava.js +41 -0
  61. package/src/scaAnalysis/common/treeUpload.js +21 -6
  62. package/src/scaAnalysis/dotnet/analysis.js +21 -3
  63. package/src/scaAnalysis/go/goAnalysis.js +9 -2
  64. package/src/scaAnalysis/java/analysis.js +11 -6
  65. package/src/scaAnalysis/java/index.js +9 -1
  66. package/src/scaAnalysis/java/javaBuildDepsParser.js +25 -6
  67. package/src/scaAnalysis/python/analysis.js +49 -5
  68. package/src/scaAnalysis/python/index.js +7 -2
  69. package/src/scaAnalysis/ruby/analysis.js +16 -4
  70. package/src/scan/formatScanOutput.ts +7 -5
  71. package/src/scan/populateProjectIdAndProjectName.js +5 -1
  72. package/src/scan/scan.ts +4 -0
  73. package/src/scan/scanConfig.js +5 -3
  74. package/src/scan/scanResults.js +46 -3
  75. package/src/telemetry/telemetry.ts +154 -0
  76. package/src/utils/getConfig.ts +4 -6
  77. package/src/utils/parsedCLIOptions.js +14 -1
  78. package/src/utils/requestUtils.js +8 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@ CodeSec delivers:
10
10
  ## Install
11
11
 
12
12
  ```shell
13
- npm install -g @contrast/contrast
13
+ npm install --location=global @contrast/contrast
14
14
  ```
15
15
 
16
16
  ## Authenticate
@@ -13,6 +13,7 @@ const severityCountModel_1 = require("./models/severityCountModel");
13
13
  const reportOutputModel_1 = require("./models/reportOutputModel");
14
14
  const constants_1 = require("../../../constants/constants");
15
15
  const cli_table3_1 = __importDefault(require("cli-table3"));
16
+ const reportGuidanceModel_1 = require("./models/reportGuidanceModel");
16
17
  const createSummaryMessage = (numberOfVulnerableLibraries, numberOfCves) => {
17
18
  numberOfVulnerableLibraries === 1
18
19
  ? console.log(`Found 1 vulnerable library containing ${numberOfCves} CVE`)
@@ -92,10 +93,10 @@ const printFormattedOutput = (config, libraries, numberOfVulnerableLibraries, nu
92
93
  colWidths: [12, 1, 100]
93
94
  });
94
95
  const header = buildHeader(highestSeverity, contrastHeaderNumCounter, libraryName, libraryVersion, numOfCVEs);
95
- const advice = gatherRemediationAdvice(guidance, reportModel);
96
+ const advice = gatherRemediationAdvice(guidance, libraryName, libraryVersion);
96
97
  const body = buildBody(reportModel.cveArray, advice);
97
98
  const reportOutputModel = new reportOutputModel_1.ReportOutputModel(header, body);
98
- table.push(reportOutputModel.body.issueMessage, reportOutputModel.body.issueMessageCves, reportOutputModel.body.adviceMessage);
99
+ table.push(reportOutputModel.body.issueMessage, reportOutputModel.body.adviceMessage);
99
100
  console.log(reportOutputModel.header.vulnMessage, reportOutputModel.header.introducesMessage);
100
101
  console.log(table.toString() + '\n');
101
102
  }
@@ -107,9 +108,10 @@ exports.printFormattedOutput = printFormattedOutput;
107
108
  function buildHeader(highestSeverity, contrastHeaderNum, libraryName, version, numOfCVEs) {
108
109
  const vulnerabilityPluralised = numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability';
109
110
  const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum);
110
- const vulnMessage = chalk_1.default
111
- .hex(highestSeverity.outputColour)
112
- .bold(`${formattedHeaderNum} - [${highestSeverity.severity}] ${libraryName}-${version}`);
111
+ const headerColour = chalk_1.default.hex(highestSeverity.outputColour);
112
+ const headerNumAndSeverity = headerColour(`${formattedHeaderNum} - [${highestSeverity.severity}]`);
113
+ const libraryNameAndVersion = headerColour.bold(`${libraryName}-${version}`);
114
+ const vulnMessage = `${headerNumAndSeverity} ${libraryNameAndVersion}`;
113
115
  const introducesMessage = `introduces ${numOfCVEs} ${vulnerabilityPluralised}`;
114
116
  return new reportOutputModel_1.ReportOutputHeaderModel(vulnMessage, introducesMessage);
115
117
  }
@@ -125,29 +127,27 @@ function buildBody(cveArray, advice) {
125
127
  cveMessages.push(builtMessage);
126
128
  });
127
129
  const numAndSeverityType = getNumOfAndSeverityType(cveArray);
128
- const issueMessage = [chalk_1.default.bold('Issue'), ':', `${numAndSeverityType}`];
129
- const issueMessageCves = ['', '', cveMessages.join(', ')];
130
- const displayAdvice = advice?.minimum
131
- ? `Update to version ${chalk_1.default.bold(advice.minimum)}`
132
- : `Update to latest version`;
130
+ const issueMessage = [
131
+ chalk_1.default.bold('Issue'),
132
+ ':',
133
+ `${numAndSeverityType} ${cveMessages.join(', ')}`
134
+ ];
135
+ const minOrMax = advice.maximum ? advice.maximum : advice.minimum;
136
+ const displayAdvice = minOrMax
137
+ ? `Change to version ${chalk_1.default.bold(minOrMax)}`
138
+ : 'No recommendation is available according to our data. Upgrade to the latest stable is the best advice we can give.';
133
139
  const adviceMessage = [chalk_1.default.bold('Advice'), ':', displayAdvice];
134
- return new reportOutputModel_1.ReportOutputBodyModel(issueMessage, issueMessageCves, adviceMessage);
140
+ return new reportOutputModel_1.ReportOutputBodyModel(issueMessage, adviceMessage);
135
141
  }
136
142
  exports.buildBody = buildBody;
137
- function gatherRemediationAdvice(guidance, reportModel) {
138
- const guidanceData = {
139
- minimum: undefined,
140
- maximum: undefined,
141
- latest: undefined
142
- };
143
- const data = guidance[reportModel.compositeKey.libraryName +
144
- '@' +
145
- reportModel.compositeKey.libraryVersion];
143
+ function gatherRemediationAdvice(guidance, libraryName, libraryVersion) {
144
+ const guidanceModel = new reportGuidanceModel_1.ReportGuidanceModel();
145
+ const data = guidance[libraryName + '@' + libraryVersion];
146
146
  if (data) {
147
- guidanceData.minimum = data.minUpgradeVersion;
148
- guidanceData.maximum = data.maxUpgradeVersion;
147
+ guidanceModel.minimum = data.minUpgradeVersion;
148
+ guidanceModel.maximum = data.maxUpgradeVersion;
149
149
  }
150
- return guidanceData;
150
+ return guidanceModel;
151
151
  }
152
152
  exports.gatherRemediationAdvice = gatherRemediationAdvice;
153
153
  function buildFormattedHeaderNum(contrastHeaderNum) {
@@ -156,11 +156,22 @@ function buildFormattedHeaderNum(contrastHeaderNum) {
156
156
  exports.buildFormattedHeaderNum = buildFormattedHeaderNum;
157
157
  function getNumOfAndSeverityType(cveArray) {
158
158
  const { critical, high, medium, low, note } = (0, reportUtils_1.severityCountAllCVEs)(cveArray, new severityCountModel_1.SeverityCountModel());
159
- const criticalMessage = critical > 0 ? `${critical} Critical` : '';
160
- const highMessage = high > 0 ? `${high} High` : '';
161
- const mediumMessage = medium > 0 ? `${medium} Medium` : '';
162
- const lowMessage = low > 0 ? `${low} Low` : '';
163
- const noteMessage = note > 0 ? `${note} Note` : '';
159
+ const criticalNumCheck = critical > 0;
160
+ const highNumCheck = high > 0;
161
+ const highDivider = highNumCheck ? '|' : '';
162
+ const mediumNumCheck = medium > 0;
163
+ const mediumDivider = mediumNumCheck ? '|' : '';
164
+ const lowNumCheck = low > 0;
165
+ const lowDivider = lowNumCheck ? '|' : '';
166
+ const noteNumCheck = low > 0;
167
+ const noteDivider = noteNumCheck ? '|' : '';
168
+ const criticalMessage = criticalNumCheck
169
+ ? `${critical} Critical ${highDivider}`
170
+ : '';
171
+ const highMessage = highNumCheck ? `${high} High ${mediumDivider}` : '';
172
+ const mediumMessage = mediumNumCheck ? `${medium} Medium ${lowDivider}` : '';
173
+ const lowMessage = lowNumCheck ? `${low} Low ${noteDivider}` : '';
174
+ const noteMessage = noteNumCheck ? `${note} Note` : '';
164
175
  return `${criticalMessage} ${highMessage} ${mediumMessage} ${lowMessage} ${noteMessage}`
165
176
  .replace(/\s+/g, ' ')
166
177
  .trim();
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReportGuidanceModel = void 0;
4
+ class ReportGuidanceModel {
5
+ }
6
+ exports.ReportGuidanceModel = ReportGuidanceModel;
@@ -16,9 +16,8 @@ class ReportOutputHeaderModel {
16
16
  }
17
17
  exports.ReportOutputHeaderModel = ReportOutputHeaderModel;
18
18
  class ReportOutputBodyModel {
19
- constructor(issueMessage, issueMessageCves, adviceMessage) {
19
+ constructor(issueMessage, adviceMessage) {
20
20
  this.issueMessage = issueMessage;
21
- this.issueMessageCves = issueMessageCves;
22
21
  this.adviceMessage = adviceMessage;
23
22
  }
24
23
  }
@@ -8,6 +8,7 @@ class SeverityCountModel {
8
8
  this.medium = 0;
9
9
  this.low = 0;
10
10
  this.note = 0;
11
+ this.total = 0;
11
12
  }
12
13
  get getTotal() {
13
14
  return this.critical + this.high + this.medium + this.low + this.note;
@@ -32,6 +32,8 @@ const reportUtils_1 = require("./utils/reportUtils");
32
32
  const i18n_1 = __importDefault(require("i18n"));
33
33
  const chalk_1 = __importDefault(require("chalk"));
34
34
  const constants = __importStar(require("../../../constants/constants"));
35
+ const severityCountModel_1 = require("./models/severityCountModel");
36
+ const common = __importStar(require("../../../common/fail"));
35
37
  function convertKeysToStandardFormat(config, guidance) {
36
38
  let convertedGuidance = guidance;
37
39
  switch (config.language) {
@@ -70,16 +72,16 @@ function formatVulnerabilityOutput(libraryVulnerabilityResponse, id, config, rem
70
72
  console.log(i18n_1.default.__('scanNoVulnerabilitiesFoundGoodWork'));
71
73
  console.log(chalk_1.default.bold(`Found ${numberOfVulnerableLibraries} vulnerabilities`));
72
74
  console.log(i18n_1.default.__('foundDetailedVulnerabilities', String(0), String(0), String(0), String(0), String(0)));
75
+ return [false, 0, [new severityCountModel_1.SeverityCountModel()]];
73
76
  }
74
77
  else {
75
78
  let numberOfCves = 0;
76
79
  vulnerableLibraries.forEach(lib => (numberOfCves += lib.cveArray.length));
77
80
  const hasSomeVulnerabilitiesReported = (0, commonReportingFunctions_1.printVulnerabilityResponse)(config, vulnerableLibraries, numberOfVulnerableLibraries, numberOfCves, guidance);
78
- return [
79
- hasSomeVulnerabilitiesReported,
80
- numberOfCves,
81
- (0, reportUtils_1.severityCountAllLibraries)(vulnerableLibraries)
82
- ];
81
+ let severityCount = new severityCountModel_1.SeverityCountModel();
82
+ severityCount = (0, reportUtils_1.severityCountAllLibraries)(vulnerableLibraries, severityCount);
83
+ severityCount.total = severityCount.getTotal;
84
+ return [hasSomeVulnerabilitiesReported, numberOfCves, severityCount];
83
85
  }
84
86
  }
85
87
  exports.formatVulnerabilityOutput = formatVulnerabilityOutput;
@@ -87,10 +89,12 @@ async function vulnerabilityReportV2(config, reportId) {
87
89
  console.log();
88
90
  const reportResponse = await (0, commonReportingFunctions_1.getReport)(config, reportId);
89
91
  if (reportResponse !== undefined) {
90
- const name = config.applicationName;
91
- formatVulnerabilityOutput(reportResponse.vulnerabilities, config.applicationId, config, reportResponse.remediationGuidance
92
+ let output = formatVulnerabilityOutput(reportResponse.vulnerabilities, config.applicationId, config, reportResponse.remediationGuidance
92
93
  ? reportResponse.remediationGuidance
93
94
  : {});
95
+ if (config.fail) {
96
+ common.processFail(config, output[2]);
97
+ }
94
98
  }
95
99
  }
96
100
  exports.vulnerabilityReportV2 = vulnerabilityReportV2;
@@ -46,8 +46,7 @@ function convertGenericToTypedLibraryVulns(libraries) {
46
46
  });
47
47
  }
48
48
  exports.convertGenericToTypedLibraryVulns = convertGenericToTypedLibraryVulns;
49
- function severityCountAllLibraries(vulnerableLibraries) {
50
- const severityCount = new severityCountModel_1.SeverityCountModel();
49
+ function severityCountAllLibraries(vulnerableLibraries, severityCount) {
51
50
  vulnerableLibraries.forEach(lib => severityCountAllCVEs(lib.cveArray, severityCount));
52
51
  return severityCount;
53
52
  }
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getAuditConfig = void 0;
7
7
  const paramHandler_1 = __importDefault(require("../../utils/paramsUtil/paramHandler"));
8
8
  const constants_1 = __importDefault(require("../../constants"));
9
- const parsedCLIOptions_1 = __importDefault(require("../../utils/parsedCLIOptions"));
10
- const getAuditConfig = (argv) => {
11
- const auditParameters = parsedCLIOptions_1.default.getCommandLineArgsCustom(argv, constants_1.default.commandLineDefinitions.auditOptionDefinitions);
9
+ const parsedCLIOptions_1 = require("../../utils/parsedCLIOptions");
10
+ const getAuditConfig = async (contrastConf, command, argv) => {
11
+ const auditParameters = await (0, parsedCLIOptions_1.getCommandLineArgsCustom)(contrastConf, command, argv, constants_1.default.commandLineDefinitions.auditOptionDefinitions);
12
12
  const paramsAuth = paramHandler_1.default.getAuth(auditParameters);
13
13
  return { ...paramsAuth, ...auditParameters };
14
14
  };
@@ -4,13 +4,15 @@ exports.processAudit = void 0;
4
4
  const auditConfig_1 = require("./auditConfig");
5
5
  const help_1 = require("./help");
6
6
  const scaAnalysis_1 = require("../scan/sca/scaAnalysis");
7
- const processAudit = async (argv) => {
7
+ const telemetry_1 = require("../../telemetry/telemetry");
8
+ const processAudit = async (contrastConf, argv) => {
8
9
  if (argv.indexOf('--help') != -1) {
9
10
  printHelpMessage();
10
11
  process.exit(0);
11
12
  }
12
- const config = (0, auditConfig_1.getAuditConfig)(argv);
13
+ const config = await (0, auditConfig_1.getAuditConfig)(contrastConf, 'audit', argv);
13
14
  await (0, scaAnalysis_1.processSca)(config);
15
+ await (0, telemetry_1.sendTelemetryConfigAsObject)(config, 'audit', argv, 'SUCCESS', config.language);
14
16
  };
15
17
  exports.processAudit = processAudit;
16
18
  const printHelpMessage = () => {
@@ -11,7 +11,7 @@ const parsedCLIOptions = require('../../utils/parsedCLIOptions');
11
11
  const constants = require('../../constants');
12
12
  const commandLineUsage = require('command-line-usage');
13
13
  const processAuth = async (argv, config) => {
14
- let authParams = parsedCLIOptions.getCommandLineArgsCustom(argv, constants.commandLineDefinitions.authOptionDefinitions);
14
+ let authParams = await parsedCLIOptions.getCommandLineArgsCustom(config, 'auth', argv, constants.commandLineDefinitions.authOptionDefinitions);
15
15
  if (authParams.help) {
16
16
  console.log(authUsageGuide);
17
17
  process.exit(0);
@@ -3,9 +3,9 @@ const parsedCLIOptions = require('../../utils/parsedCLIOptions');
3
3
  const constants = require('../../constants');
4
4
  const commandLineUsage = require('command-line-usage');
5
5
  const i18n = require('i18n');
6
- const processConfig = (argv, config) => {
6
+ const processConfig = async (argv, config) => {
7
7
  try {
8
- let configParams = parsedCLIOptions.getCommandLineArgsCustom(argv, constants.commandLineDefinitions.configOptionDefinitions);
8
+ let configParams = await parsedCLIOptions.getCommandLineArgsCustom(config, 'config', argv, constants.commandLineDefinitions.configOptionDefinitions);
9
9
  if (configParams.help) {
10
10
  console.log(configUsageGuide);
11
11
  process.exit(0);
@@ -5,18 +5,25 @@ const { saveScanFile } = require('../../utils/saveFile');
5
5
  const { ScanResultsModel } = require('../../scan/models/scanResultsModel');
6
6
  const { formatScanOutput } = require('../../scan/formatScanOutput');
7
7
  const { processSca } = require('./sca/scaAnalysis');
8
- const processScan = async (argvMain) => {
9
- let config = scanConfig.getScanConfig(argvMain);
8
+ const common = require('../../common/fail');
9
+ const { sendTelemetryConfigAsObject } = require('../../telemetry/telemetry');
10
+ const processScan = async (contrastConf, argv) => {
11
+ let config = await scanConfig.getScanConfig(contrastConf, 'scan', argv);
12
+ let output = undefined;
10
13
  if (config.experimental) {
11
- await processSca(config);
14
+ await processSca(config, argv);
12
15
  }
13
16
  let scanResults = new ScanResultsModel(await startScan(config));
17
+ await sendTelemetryConfigAsObject(config, 'scan', argv, 'SUCCESS', scanResults.scanDetail.language);
14
18
  if (scanResults.scanResultsInstances !== undefined) {
15
- formatScanOutput(scanResults);
19
+ output = formatScanOutput(scanResults);
16
20
  }
17
21
  if (config.save !== undefined) {
18
22
  await saveScanFile(config, scanResults);
19
23
  }
24
+ if (config.fail) {
25
+ common.processFail(config, output);
26
+ }
20
27
  };
21
28
  module.exports = {
22
29
  processScan
@@ -15,20 +15,25 @@ const i18n = require('i18n');
15
15
  const { vulnerabilityReportV2 } = require('../../../audit/languageAnalysisEngine/report/reportingFeature');
16
16
  const auditSave = require('../../../audit/save');
17
17
  const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet');
18
+ const { auditUsageGuide } = require('../../audit/help');
18
19
  const rootFile = require('../../../audit/languageAnalysisEngine/getProjectRootFilenames');
19
20
  const path = require('path');
20
21
  const processSca = async (config) => {
21
22
  const startTime = performance.now();
22
23
  let filesFound;
24
+ if (config.help) {
25
+ console.log(auditUsageGuide);
26
+ process.exit(0);
27
+ }
23
28
  const projectStats = await rootFile.getProjectStats(config.file);
24
29
  let pathWithFile = projectStats.isFile();
25
- let fileName = config.file;
30
+ config.fileName = config.file;
26
31
  config.file = pathWithFile
27
32
  ? rootFile.getDirectoryFromPathGiven(config.file).concat('/')
28
33
  : config.file;
29
34
  filesFound = await autoDetection.autoDetectAuditFilesAndLanguages(config.file);
30
35
  if (filesFound.length > 1 && pathWithFile) {
31
- filesFound = filesFound.filter(i => Object.values(i)[0].includes(path.basename(fileName)));
36
+ filesFound = filesFound.filter(i => Object.values(i)[0].includes(path.basename(config.fileName)));
32
37
  }
33
38
  let messageToSend = undefined;
34
39
  if (filesFound.length === 1) {
@@ -89,7 +94,9 @@ const processSca = async (config) => {
89
94
  throw new Error();
90
95
  }
91
96
  else {
92
- throw new Error('multiple language files detected, please use --file to specify a directory or the file where dependencies are declared');
97
+ throw new Error(`multiple language files detected \n` +
98
+ JSON.stringify(filesFound) +
99
+ `\nplease use --file to audit one language only. Example: contrast audit --file package-lock.json`);
93
100
  }
94
101
  }
95
102
  };
@@ -253,6 +253,12 @@ HTTPClient.prototype.getLatestVersion = function getLatestVersion() {
253
253
  'https://pkg.contrastsecurity.com/artifactory/cli/latest-version.txt';
254
254
  return requestUtils.sendRequest({ method: 'get', options });
255
255
  };
256
+ HTTPClient.prototype.postTelemetry = function postTelemetry(config, requestBody) {
257
+ const options = _.cloneDeep(this.requestOptions);
258
+ options.url = createTelemetryEventUrl(config);
259
+ options.body = requestBody;
260
+ return requestUtils.sendRequest({ method: 'post', options });
261
+ };
256
262
  HTTPClient.prototype.postAnalyticsFunction = function (config, provider, body) {
257
263
  const url = createAnalyticsFunctionPostUrl(config, provider);
258
264
  const options = { ...this.requestOptions, body, url };
@@ -319,6 +325,9 @@ function createDataUrl() {
319
325
  function createSbomUrl(config, type) {
320
326
  return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/${type}`;
321
327
  }
328
+ function createTelemetryEventUrl(config) {
329
+ return `${config.host}/Contrast/api/sast/organizations/${config.organizationId}/cli`;
330
+ }
322
331
  module.exports = HTTPClient;
323
332
  module.exports.pollForAuthUrl = pollForAuthUrl;
324
333
  module.exports.getServerlessHost = getServerlessHost;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ const i18n = require('i18n');
3
+ const processFail = (config, reportResults) => {
4
+ if (config.severity !== undefined) {
5
+ if (reportResults[config.severity] !== undefined &&
6
+ isSeverityViolation(config.severity, reportResults)) {
7
+ failPipeline('failSeverityOptionErrorMessage');
8
+ }
9
+ }
10
+ if (config.severity === undefined && reportResults.total > 0) {
11
+ failPipeline('failThresholdOptionErrorMessage');
12
+ }
13
+ };
14
+ const isSeverityViolation = (severity, reportResults) => {
15
+ let count = 0;
16
+ switch (severity) {
17
+ case 'critical':
18
+ count += reportResults.critical;
19
+ break;
20
+ case 'high':
21
+ count += reportResults.high + reportResults.critical;
22
+ break;
23
+ case 'medium':
24
+ count += reportResults.medium + reportResults.low + reportResults.critical;
25
+ break;
26
+ case 'low':
27
+ count +=
28
+ reportResults.high + reportResults.critical + reportResults.medium;
29
+ break;
30
+ case 'note':
31
+ if (reportResults.note == reportResults.total) {
32
+ count = 0;
33
+ }
34
+ else {
35
+ count = reportResults.total;
36
+ }
37
+ break;
38
+ default:
39
+ count = 0;
40
+ }
41
+ return count > 0;
42
+ };
43
+ const failPipeline = (message = '') => {
44
+ console.log('\n ******************************** ' +
45
+ i18n.__('snapshotFailureHeader') +
46
+ ' *********************************\n' +
47
+ i18n.__(message));
48
+ process.exit(1);
49
+ };
50
+ const parseSeverity = severity => {
51
+ const severities = ['NOTE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
52
+ if (severities.includes(severity.toUpperCase())) {
53
+ return severity.toLowerCase();
54
+ }
55
+ else {
56
+ console.log(severity +
57
+ ' Not recognised as a severity type please use LOW, MEDIUM, HIGH, CRITICAL, NOTE');
58
+ return undefined;
59
+ }
60
+ };
61
+ module.exports = {
62
+ failPipeline,
63
+ processFail,
64
+ isSeverityViolation,
65
+ parseSeverity
66
+ };
@@ -23,7 +23,7 @@ const getLatestVersion = async (config) => {
23
23
  }
24
24
  };
25
25
  async function findLatestCLIVersion(config) {
26
- const messageHidden = config.get('updateMessageHidden');
26
+ const messageHidden = config.get('isCI');
27
27
  if (!messageHidden) {
28
28
  let latestCLIVersion = await getLatestVersion(config);
29
29
  latestCLIVersion = latestCLIVersion.substring(8);
@@ -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.10';
15
+ const APP_VERSION = '1.0.11';
16
16
  const TIMEOUT = 120000;
17
17
  const HIGH_COLOUR = '#ff9900';
18
18
  const CRITICAL_COLOUR = '#e35858';
@@ -79,8 +79,9 @@ const en_locales = () => {
79
79
  constantsHostId: 'Provide the name of the host and optionally the port expressed as "<host>:<port>".',
80
80
  constantsApplicationName: 'The name of the application cataloged by Contrast UI',
81
81
  constantsCatalogueApplication: 'Provide this if you want to catalogue an application',
82
+ failOptionErrorMessage: ' FAIL - CVEs have been detected that match at least the cve_severity option specified.',
82
83
  constantsLanguage: 'Valid values are JAVA, DOTNET, NODE, PYTHON and RUBY. If there are multiple project configuration files in the project_path, language is also required. Also, provide this when cataloguing an application',
83
- constantsFilePath: `Path of the file you want to perform an SCA audit on. If no folder is specified, Contrast searches for dependency files in the working directory.`,
84
+ constantsFilePath: `Specify a directory or the file where dependencies are declared. (By default, CodeSec will search for project files in the current directory.)`,
84
85
  constantsSilent: 'Silences JSON output.',
85
86
  constantsAppGroups: 'Assign your application to one or more pre-existing groups when using the catalogue command. Group lists should be comma separated.',
86
87
  constantsVersion: 'Displays CLI Version you are currently on.',
@@ -93,7 +94,8 @@ const en_locales = () => {
93
94
  constantsProjectId: 'The ID associated with a scan project. Replace <ProjectID> with the ID for the scan project. To find the ID, select a scan project in Contrast and locate the last number in the URL.',
94
95
  constantsReport: 'Display vulnerability information for this application',
95
96
  constantsFail: 'Set the process to fail if this option is set in combination with --cve_severity.',
96
- failOptionErrorMessage: ' FAIL - CVEs have been detected that match at least the cve_severity or cve_threshold option specified.',
97
+ failThresholdOptionErrorMessage: 'More than 0 vulnerabilities found',
98
+ failSeverityOptionErrorMessage: ' FAIL - Results detected vulnerabilities over accepted severity level',
97
99
  constantsSeverity: 'Allows the user to report libraries with vulnerabilities above a chosen severity level. For example, cve_severity medium only reports libraries with vulnerabilities at medium or higher severity. Values for level are high, medium or low.',
98
100
  constantsCount: 'The number of CVEs that must be exceeded to fail a build',
99
101
  constantsHeader: 'CodeSec by Contrast Security',
@@ -276,7 +278,8 @@ const en_locales = () => {
276
278
  constantsAuditPrerequisitesContentPHPMessage: `${chalk.bold('PHP:')} composer.json and composer.lock\n`,
277
279
  constantsAuditOptions: 'Audit Options',
278
280
  auditOptionsSaveDescription: 'Generate and save an SBOM (Software Bill of Materials)\n',
279
- auditOptionsSaveOptionsDescription: 'Valid options are: spdx, cyclonedx (cycloneDX is the default format)',
281
+ auditOptionsSaveOptionsDescription: 'Valid options are: --save spdx and --save cyclonedx (CycloneDX is the default format.)',
282
+ exceededFreeTier: `It looks like you are really loving CodeSec! \nYou have reached the monthly scan limit on the FREE tier. \nPlease contact sales@contrastsecurity.com to upgrade.`,
280
283
  scanNotCompleted: 'Scan not completed. Check for framework and language support here: %s',
281
284
  auditNotCompleted: 'audit not completed. Please try again',
282
285
  scanNoVulnerabilitiesFound: '🎉 No vulnerabilities found.',
package/dist/constants.js CHANGED
@@ -2,6 +2,7 @@
2
2
  const commandLineUsage = require('command-line-usage');
3
3
  const i18n = require('i18n');
4
4
  const { en_locales } = require('./constants/locales.js');
5
+ const { parseSeverity } = require('./common/fail');
5
6
  i18n.configure({
6
7
  staticCatalog: {
7
8
  en: en_locales()
@@ -93,6 +94,22 @@ const scanOptionDefinitions = [
93
94
  '}: ' +
94
95
  i18n.__('constantsProxyServer')
95
96
  },
97
+ {
98
+ name: 'fail',
99
+ type: Boolean,
100
+ description: '{bold ' +
101
+ i18n.__('constantsOptional') +
102
+ '}: ' +
103
+ i18n.__('failOptionErrorMessage')
104
+ },
105
+ {
106
+ name: 'severity',
107
+ type: severity => parseSeverity(severity),
108
+ description: '{bold ' +
109
+ i18n.__('constantsOptional') +
110
+ '}: ' +
111
+ i18n.__('constantsSeverity')
112
+ },
96
113
  {
97
114
  name: 'ff',
98
115
  type: Boolean,
@@ -195,6 +212,22 @@ const auditOptionDefinitions = [
195
212
  '}: ' +
196
213
  i18n.__('constantsFilePath')
197
214
  },
215
+ {
216
+ name: 'fail',
217
+ type: Boolean,
218
+ description: '{bold ' +
219
+ i18n.__('constantsOptional') +
220
+ '}: ' +
221
+ i18n.__('failOptionErrorMessage')
222
+ },
223
+ {
224
+ name: 'severity',
225
+ type: severity => parseSeverity(severity),
226
+ description: '{bold ' +
227
+ i18n.__('constantsOptional') +
228
+ '}: ' +
229
+ i18n.__('constantsSeverity')
230
+ },
198
231
  {
199
232
  name: 'app-groups',
200
233
  description: '{bold ' +
@@ -227,6 +260,7 @@ const auditOptionDefinitions = [
227
260
  {
228
261
  name: 'ignore-dev',
229
262
  type: Boolean,
263
+ alias: 'i',
230
264
  description: '{bold ' +
231
265
  i18n.__('constantsOptional') +
232
266
  '}: ' +
@@ -259,7 +293,6 @@ const auditOptionDefinitions = [
259
293
  },
260
294
  {
261
295
  name: 'host',
262
- alias: 'h',
263
296
  description: '{bold ' +
264
297
  i18n.__('constantsRequired') +
265
298
  '}: ' +
@@ -302,6 +335,11 @@ const auditOptionDefinitions = [
302
335
  i18n.__('constantsOptional') +
303
336
  '}: ' +
304
337
  i18n.__('scanOptionsTimeoutSummary')
338
+ },
339
+ {
340
+ name: 'help',
341
+ alias: 'h',
342
+ type: Boolean
305
343
  }
306
344
  ];
307
345
  const mainUsageGuide = commandLineUsage([
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ const lambda_1 = require("./lambda/lambda");
15
15
  const getConfig_1 = require("./utils/getConfig");
16
16
  const versionChecker_1 = require("./common/versionChecker");
17
17
  const errorHandling_1 = require("./common/errorHandling");
18
+ const telemetry_1 = require("./telemetry/telemetry");
18
19
  const { commandLineDefinitions: { mainUsageGuide, mainDefinition } } = constants_1.default;
19
20
  const config = (0, getConfig_1.localConfig)(constants_2.APP_NAME, constants_2.APP_VERSION);
20
21
  const getMainOption = () => {
@@ -58,10 +59,10 @@ const start = async () => {
58
59
  return await (0, lambda_1.processLambda)(argvMain);
59
60
  }
60
61
  if (command === 'scan') {
61
- return await (0, processScan_1.processScan)(argvMain);
62
+ return await (0, processScan_1.processScan)(config, argvMain);
62
63
  }
63
64
  if (command === 'audit') {
64
- return await (0, processAudit_1.processAudit)(argvMain);
65
+ return await (0, processAudit_1.processAudit)(config, argvMain);
65
66
  }
66
67
  if (command === 'help' ||
67
68
  argvMain.includes('--help') ||
@@ -73,9 +74,11 @@ const start = async () => {
73
74
  foundCommand
74
75
  ? console.log(`Unknown Command: Did you mean "${foundCommand}"? \nUse "${foundCommand} --help" for the full list of options`)
75
76
  : console.log(`Unknown Command: ${command} \nUse --help for the full list`);
77
+ await (0, telemetry_1.sendTelemetryConfigAsConfObj)(config, command, argvMain, 'FAILURE', 'undefined');
76
78
  }
77
79
  else {
78
80
  console.log(`Unknown Command: ${command} \nUse --help for the full list`);
81
+ await (0, telemetry_1.sendTelemetryConfigAsConfObj)(config, command, argvMain, 'FAILURE', 'undefined');
79
82
  }
80
83
  process.exit(9);
81
84
  }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ const parseDependenciesForSCAServices = dependencyTreeObject => {
3
+ let parsedDependencyTree = {};
4
+ let subDeps;
5
+ for (let tree in dependencyTreeObject) {
6
+ let unParsedDependencyTree = dependencyTreeObject[tree];
7
+ for (let dependency in unParsedDependencyTree) {
8
+ subDeps = parseSubDependencies(unParsedDependencyTree[dependency].edges);
9
+ let parsedDependency = {
10
+ name: unParsedDependencyTree[dependency].artifactID,
11
+ group: unParsedDependencyTree[dependency].group,
12
+ version: unParsedDependencyTree[dependency].version,
13
+ directDependency: unParsedDependencyTree[dependency].type === 'direct',
14
+ isProduction: true,
15
+ dependencies: subDeps
16
+ };
17
+ parsedDependencyTree[dependency] = parsedDependency;
18
+ }
19
+ }
20
+ return parsedDependencyTree;
21
+ };
22
+ const parseSubDependencies = dependencies => {
23
+ let subDeps = [];
24
+ for (let x in dependencies) {
25
+ subDeps.push(dependencies[x]);
26
+ }
27
+ return subDeps;
28
+ };
29
+ module.exports = {
30
+ parseDependenciesForSCAServices,
31
+ parseSubDependencies
32
+ };