@contrast/contrast 1.0.11 → 1.0.12

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 (51) hide show
  1. package/dist/audit/{languageAnalysisEngine/report → report}/commonReportingFunctions.js +17 -7
  2. package/dist/audit/{languageAnalysisEngine/report → report}/models/reportGuidanceModel.js +0 -0
  3. package/dist/audit/{languageAnalysisEngine/report → report}/models/reportLibraryModel.js +0 -0
  4. package/dist/audit/{languageAnalysisEngine/report → report}/models/reportListModel.js +0 -0
  5. package/dist/audit/{languageAnalysisEngine/report → report}/models/reportOutputModel.js +0 -0
  6. package/dist/audit/{languageAnalysisEngine/report → report}/models/reportSeverityModel.js +0 -0
  7. package/dist/audit/{languageAnalysisEngine/report → report}/models/severityCountModel.js +0 -0
  8. package/dist/audit/{languageAnalysisEngine/report → report}/reportingFeature.js +2 -2
  9. package/dist/audit/{languageAnalysisEngine/report → report}/utils/reportUtils.js +2 -2
  10. package/dist/commands/audit/help.js +3 -1
  11. package/dist/commands/scan/sca/scaAnalysis.js +2 -2
  12. package/dist/common/commonHelp.js +19 -0
  13. package/dist/common/versionChecker.js +4 -2
  14. package/dist/constants/constants.js +2 -2
  15. package/dist/constants/locales.js +7 -0
  16. package/dist/constants.js +3 -4
  17. package/dist/lambda/help.js +2 -3
  18. package/dist/scaAnalysis/php/analysis.js +1 -1
  19. package/dist/scaAnalysis/php/index.js +12 -6
  20. package/dist/scaAnalysis/php/phpNewServicesMapper.js +62 -0
  21. package/dist/scan/help.js +2 -3
  22. package/dist/scan/scanConfig.js +1 -1
  23. package/dist/scan/scanResults.js +8 -1
  24. package/dist/utils/getConfig.js +2 -4
  25. package/package.json +4 -2
  26. package/src/audit/{languageAnalysisEngine/report → report}/commonReportingFunctions.ts +23 -5
  27. package/src/audit/{languageAnalysisEngine/report → report}/models/reportGuidanceModel.ts +0 -0
  28. package/src/audit/{languageAnalysisEngine/report → report}/models/reportLibraryModel.ts +0 -0
  29. package/src/audit/{languageAnalysisEngine/report → report}/models/reportListModel.ts +0 -0
  30. package/src/audit/{languageAnalysisEngine/report → report}/models/reportOutputModel.ts +0 -0
  31. package/src/audit/{languageAnalysisEngine/report → report}/models/reportSeverityModel.ts +0 -0
  32. package/src/audit/{languageAnalysisEngine/report → report}/models/severityCountModel.ts +0 -0
  33. package/src/audit/{languageAnalysisEngine/report → report}/reportingFeature.ts +2 -2
  34. package/src/audit/{languageAnalysisEngine/report → report}/utils/reportUtils.ts +2 -2
  35. package/src/commands/audit/help.ts +3 -1
  36. package/src/commands/scan/sca/scaAnalysis.js +2 -2
  37. package/src/common/HTTPClient.js +1 -0
  38. package/src/common/commonHelp.ts +13 -0
  39. package/src/common/versionChecker.ts +4 -4
  40. package/src/constants/constants.js +2 -2
  41. package/src/constants/locales.js +9 -0
  42. package/src/constants.js +3 -5
  43. package/src/lambda/help.ts +2 -3
  44. package/src/scaAnalysis/common/treeUpload.js +1 -0
  45. package/src/scaAnalysis/php/analysis.js +1 -1
  46. package/src/scaAnalysis/php/index.js +12 -6
  47. package/src/scaAnalysis/php/phpNewServicesMapper.js +77 -0
  48. package/src/scan/help.js +2 -3
  49. package/src/scan/scanConfig.js +1 -1
  50. package/src/scan/scanResults.js +7 -1
  51. package/src/utils/getConfig.ts +2 -9
@@ -3,23 +3,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getNumOfAndSeverityType = exports.buildFormattedHeaderNum = exports.gatherRemediationAdvice = exports.buildBody = exports.buildHeader = exports.printFormattedOutput = exports.printVulnerabilityResponse = exports.getReport = exports.createSummaryMessage = void 0;
7
- const commonApi_1 = require("../../../utils/commonApi");
6
+ exports.getNumOfAndSeverityType = exports.buildFormattedHeaderNum = exports.gatherRemediationAdvice = exports.buildBody = exports.buildHeader = exports.printFormattedOutput = exports.printVulnerabilityResponse = exports.getReport = exports.createSummaryMessageBottom = exports.createSummaryMessageTop = void 0;
7
+ const commonApi_1 = require("../../utils/commonApi");
8
8
  const reportListModel_1 = require("./models/reportListModel");
9
9
  const lodash_1 = require("lodash");
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const reportUtils_1 = require("./utils/reportUtils");
12
12
  const severityCountModel_1 = require("./models/severityCountModel");
13
13
  const reportOutputModel_1 = require("./models/reportOutputModel");
14
- const constants_1 = require("../../../constants/constants");
14
+ const constants_1 = require("../../constants/constants");
15
15
  const cli_table3_1 = __importDefault(require("cli-table3"));
16
16
  const reportGuidanceModel_1 = require("./models/reportGuidanceModel");
17
- const createSummaryMessage = (numberOfVulnerableLibraries, numberOfCves) => {
17
+ const createSummaryMessageTop = (numberOfVulnerableLibraries, numberOfCves) => {
18
18
  numberOfVulnerableLibraries === 1
19
19
  ? console.log(`Found 1 vulnerable library containing ${numberOfCves} CVE`)
20
20
  : console.log(`Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs`);
21
21
  };
22
- exports.createSummaryMessage = createSummaryMessage;
22
+ exports.createSummaryMessageTop = createSummaryMessageTop;
23
+ const createSummaryMessageBottom = (numberOfVulnerableLibraries) => {
24
+ numberOfVulnerableLibraries === 1
25
+ ? console.log(`Found 1 vulnerable library`)
26
+ : console.log(`Found ${numberOfVulnerableLibraries} vulnerable libraries`);
27
+ };
28
+ exports.createSummaryMessageBottom = createSummaryMessageBottom;
23
29
  const getReport = async (config, reportId) => {
24
30
  const client = (0, commonApi_1.getHttpClient)(config);
25
31
  return client
@@ -48,7 +54,7 @@ const printVulnerabilityResponse = (config, vulnerableLibraries, numberOfVulnera
48
54
  };
49
55
  exports.printVulnerabilityResponse = printVulnerabilityResponse;
50
56
  const printFormattedOutput = (config, libraries, numberOfVulnerableLibraries, numberOfCves, guidance) => {
51
- (0, exports.createSummaryMessage)(numberOfVulnerableLibraries, numberOfCves);
57
+ (0, exports.createSummaryMessageTop)(numberOfVulnerableLibraries, numberOfCves);
52
58
  console.log();
53
59
  const report = new reportListModel_1.ReportList();
54
60
  for (const library of libraries) {
@@ -100,9 +106,13 @@ const printFormattedOutput = (config, libraries, numberOfVulnerableLibraries, nu
100
106
  console.log(reportOutputModel.header.vulnMessage, reportOutputModel.header.introducesMessage);
101
107
  console.log(table.toString() + '\n');
102
108
  }
103
- (0, exports.createSummaryMessage)(numberOfVulnerableLibraries, numberOfCves);
109
+ (0, exports.createSummaryMessageBottom)(numberOfVulnerableLibraries);
104
110
  const { criticalMessage, highMessage, mediumMessage, lowMessage, noteMessage } = buildFooter(outputOrderedByLowestSeverityAndLowestNumOfCvesFirst);
105
111
  console.log(`${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`);
112
+ if (config.host !== constants_1.CE_URL) {
113
+ console.log('\n' + chalk_1.default.bold('View your full dependency tree in Contrast:'));
114
+ console.log(`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`);
115
+ }
106
116
  };
107
117
  exports.printFormattedOutput = printFormattedOutput;
108
118
  function buildHeader(highestSeverity, contrastHeaderNum, libraryName, version, numOfCVEs) {
@@ -31,9 +31,9 @@ const commonReportingFunctions_1 = require("./commonReportingFunctions");
31
31
  const reportUtils_1 = require("./utils/reportUtils");
32
32
  const i18n_1 = __importDefault(require("i18n"));
33
33
  const chalk_1 = __importDefault(require("chalk"));
34
- const constants = __importStar(require("../../../constants/constants"));
34
+ const constants = __importStar(require("../../constants/constants"));
35
35
  const severityCountModel_1 = require("./models/severityCountModel");
36
- const common = __importStar(require("../../../common/fail"));
36
+ const common = __importStar(require("../../common/fail"));
37
37
  function convertKeysToStandardFormat(config, guidance) {
38
38
  let convertedGuidance = guidance;
39
39
  switch (config.language) {
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.countVulnerableLibrariesBySeverity = exports.findNameAndVersion = exports.severityCountSingleCVE = exports.severityCountAllCVEs = exports.severityCountAllLibraries = exports.convertGenericToTypedLibraryVulns = exports.findCVESeverity = exports.findCVESeveritiesAndOrderByHighestPriority = exports.findHighestSeverityCVE = void 0;
7
7
  const reportLibraryModel_1 = require("../models/reportLibraryModel");
8
8
  const reportSeverityModel_1 = require("../models/reportSeverityModel");
9
- const constants_1 = __importDefault(require("./../../../../constants/constants"));
10
- const constants_2 = require("../../../../constants/constants");
9
+ const constants_1 = __importDefault(require("../../../constants/constants"));
10
+ const constants_2 = require("../../../constants/constants");
11
11
  const lodash_1 = require("lodash");
12
12
  const severityCountModel_1 = require("../models/severityCountModel");
13
13
  const { supportedLanguages: { GO } } = constants_1.default;
@@ -7,6 +7,7 @@ exports.auditUsageGuide = void 0;
7
7
  const command_line_usage_1 = __importDefault(require("command-line-usage"));
8
8
  const i18n_1 = __importDefault(require("i18n"));
9
9
  const constants_1 = __importDefault(require("../../constants"));
10
+ const commonHelp_1 = require("../../common/commonHelp");
10
11
  const auditUsageGuide = (0, command_line_usage_1.default)([
11
12
  {
12
13
  header: i18n_1.default.__('auditHeader'),
@@ -53,6 +54,7 @@ const auditUsageGuide = (0, command_line_usage_1.default)([
53
54
  'app-groups',
54
55
  'metadata'
55
56
  ]
56
- }
57
+ },
58
+ (0, commonHelp_1.commonHelpLinks)()
57
59
  ]);
58
60
  exports.auditUsageGuide = auditUsageGuide;
@@ -12,7 +12,7 @@ const javascriptAnalysis = require('../../../scaAnalysis/javascript');
12
12
  const { pollForSnapshotCompletition } = require('../../../audit/languageAnalysisEngine/sendSnapshot');
13
13
  const { returnOra, startSpinner, succeedSpinner } = require('../../../utils/oraWrapper');
14
14
  const i18n = require('i18n');
15
- const { vulnerabilityReportV2 } = require('../../../audit/languageAnalysisEngine/report/reportingFeature');
15
+ const { vulnerabilityReportV2 } = require('../../../audit/report/reportingFeature');
16
16
  const auditSave = require('../../../audit/save');
17
17
  const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet');
18
18
  const { auditUsageGuide } = require('../../audit/help');
@@ -54,7 +54,7 @@ const processSca = async (config) => {
54
54
  messageToSend = rubyAnalysis(config, filesFound[0]);
55
55
  config.language = RUBY;
56
56
  break;
57
- case 'PHP':
57
+ case PHP:
58
58
  messageToSend = phpAnalysis.phpAnalysis(config, filesFound[0]);
59
59
  config.language = PHP;
60
60
  break;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.commonHelpLinks = void 0;
7
+ const i18n_1 = __importDefault(require("i18n"));
8
+ function commonHelpLinks() {
9
+ return {
10
+ header: i18n_1.default.__('commonHelpHeader'),
11
+ content: [
12
+ i18n_1.default.__('commonHelpCheckOutHeader') + i18n_1.default.__('commonHelpCheckOutText'),
13
+ i18n_1.default.__('commonHelpLearnMoreHeader') + i18n_1.default.__('commonHelpLearnMoreText'),
14
+ i18n_1.default.__('commonHelpJoinDiscussionHeader') +
15
+ i18n_1.default.__('commonHelpJoinDiscussionText')
16
+ ]
17
+ };
18
+ }
19
+ exports.commonHelpLinks = commonHelpLinks;
@@ -23,8 +23,10 @@ const getLatestVersion = async (config) => {
23
23
  }
24
24
  };
25
25
  async function findLatestCLIVersion(config) {
26
- const messageHidden = config.get('isCI');
27
- if (!messageHidden) {
26
+ const isCI = process.env.CONTRAST_CODESEC_CI
27
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
28
+ : false;
29
+ if (!isCI) {
28
30
  let latestCLIVersion = await getLatestVersion(config);
29
31
  latestCLIVersion = latestCLIVersion.substring(8);
30
32
  if (semver_1.default.lt(constants_1.APP_VERSION, latestCLIVersion)) {
@@ -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.11';
15
+ const APP_VERSION = '1.0.12';
16
16
  const TIMEOUT = 120000;
17
17
  const HIGH_COLOUR = '#ff9900';
18
18
  const CRITICAL_COLOUR = '#e35858';
@@ -29,7 +29,7 @@ const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com';
29
29
  const SARIF_FILE = 'SARIF';
30
30
  const SBOM_CYCLONE_DX_FILE = 'cyclonedx';
31
31
  const SBOM_SPDX_FILE = 'spdx';
32
- const CE_URL = 'https://ce.contrastsecurity.com/';
32
+ const CE_URL = 'https://ce.contrastsecurity.com';
33
33
  module.exports = {
34
34
  supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
35
35
  supportedLanguagesScan: { JAVASCRIPT, DOTNET, JAVA },
@@ -295,6 +295,13 @@ const en_locales = () => {
295
295
  auditReportFailureMessage: 'Unable to generate library report',
296
296
  auditSCAAnalysisBegins: 'Contrast SCA audit started',
297
297
  auditSCAAnalysisComplete: 'Contrast audit complete',
298
+ commonHelpHeader: 'Need More Help?',
299
+ commonHelpCheckOutHeader: chalk.hex('#9DC184')('Check out:'),
300
+ commonHelpCheckOutText: ' https://support.contrastsecurity.com',
301
+ commonHelpLearnMoreHeader: chalk.hex('#9DC184')('Learn more at:'),
302
+ commonHelpLearnMoreText: ' https://developer.contrastsecurity.com',
303
+ commonHelpJoinDiscussionHeader: chalk.hex('#9DC184')('Join the discussion:'),
304
+ commonHelpJoinDiscussionText: ' https://dev.to/codesec',
298
305
  ...lambda
299
306
  };
300
307
  };
package/dist/constants.js CHANGED
@@ -3,6 +3,7 @@ const commandLineUsage = require('command-line-usage');
3
3
  const i18n = require('i18n');
4
4
  const { en_locales } = require('./constants/locales.js');
5
5
  const { parseSeverity } = require('./common/fail');
6
+ const { commonHelpLinks } = require('./common/commonHelp');
6
7
  i18n.configure({
7
8
  staticCatalog: {
8
9
  en: en_locales()
@@ -366,15 +367,13 @@ const mainUsageGuide = commandLineUsage([
366
367
  { name: i18n.__('helpName'), summary: i18n.__('helpSummary') }
367
368
  ]
368
369
  },
369
- {
370
- content: '{underline https://developer.contrastsecurity.com/} \n For technical support head to {underline https://support.contrastsecurity.com}'
371
- },
372
370
  {
373
371
  header: i18n.__('configHeader2'),
374
372
  content: [
375
373
  { name: i18n.__('clearHeader'), summary: i18n.__('clearContent') }
376
374
  ]
377
- }
375
+ },
376
+ commonHelpLinks()
378
377
  ]);
379
378
  const mainDefinition = [{ name: 'command', defaultOption: true }];
380
379
  module.exports = {
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.lambdaUsageGuide = void 0;
7
7
  const command_line_usage_1 = __importDefault(require("command-line-usage"));
8
8
  const i18n_1 = __importDefault(require("i18n"));
9
+ const commonHelp_1 = require("../common/commonHelp");
9
10
  const lambdaUsageGuide = (0, command_line_usage_1.default)([
10
11
  {
11
12
  header: i18n_1.default.__('lambdaHeader'),
@@ -80,8 +81,6 @@ const lambdaUsageGuide = (0, command_line_usage_1.default)([
80
81
  { name: i18n_1.default.__('lambdaHelpOption'), summary: i18n_1.default.__('helpSummary') }
81
82
  ]
82
83
  },
83
- {
84
- content: '{underline https://www.contrastsecurity.com/developer/codesec}'
85
- }
84
+ (0, commonHelp_1.commonHelpLinks)()
86
85
  ]);
87
86
  exports.lambdaUsageGuide = lambdaUsageGuide;
@@ -5,7 +5,7 @@ const _ = require('lodash');
5
5
  const readFile = (config, nameOfFile) => {
6
6
  if (config.file) {
7
7
  try {
8
- return fs.readFileSync(config.file + '/' + nameOfFile);
8
+ return fs.readFileSync(config.file + '/' + nameOfFile, 'utf8');
9
9
  }
10
10
  catch (error) {
11
11
  console.log('Unable to find file');
@@ -1,17 +1,23 @@
1
1
  "use strict";
2
2
  const { readFile, parseProjectFiles } = require('./analysis');
3
3
  const { createPhpTSMessage } = require('../common/formatMessage');
4
- const phpAnalysis = (config, files) => {
5
- let analysis = readFiles(config, files.PHP);
6
- const phpDep = parseProjectFiles(analysis);
7
- return createPhpTSMessage(phpDep);
4
+ const { parsePHPLockFileForScaServices } = require('./phpNewServicesMapper');
5
+ const phpAnalysis = config => {
6
+ let analysis = readFiles(config);
7
+ if (config.experimental) {
8
+ return parsePHPLockFileForScaServices(analysis.rawLockFileContents);
9
+ }
10
+ else {
11
+ const phpDep = parseProjectFiles(analysis);
12
+ return createPhpTSMessage(phpDep);
13
+ }
8
14
  };
9
- const readFiles = (config, files) => {
15
+ const readFiles = config => {
10
16
  let php = {};
11
17
  php.composerJSON = JSON.parse(readFile(config, 'composer.json'));
12
18
  php.rawLockFileContents = JSON.parse(readFile(config, 'composer.lock'));
13
19
  return php;
14
20
  };
15
21
  module.exports = {
16
- phpAnalysis
22
+ phpAnalysis: phpAnalysis
17
23
  };
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ const { keyBy, merge } = require('lodash');
3
+ const parsePHPLockFileForScaServices = phpLockFile => {
4
+ const packages = keyBy(phpLockFile.packages, 'name');
5
+ const packagesDev = keyBy(phpLockFile['packages-dev'], 'name');
6
+ return merge(buildDepTree(packages, true), buildDepTree(packagesDev, false));
7
+ };
8
+ const buildDepTree = (packages, isProduction) => {
9
+ const dependencyTree = {};
10
+ for (const packagesKey in packages) {
11
+ const currentObj = packages[packagesKey];
12
+ const { group, name } = findGroupAndName(currentObj.name);
13
+ const key = `${group}/${name}@${currentObj.version}`;
14
+ dependencyTree[key] = {
15
+ group: group,
16
+ name: name,
17
+ version: currentObj.version,
18
+ directDependency: true,
19
+ isProduction: isProduction,
20
+ dependencies: []
21
+ };
22
+ const mergedChildDeps = merge(buildSubDepsIntoFlatStructure(currentObj.require), buildSubDepsIntoFlatStructure(currentObj['require-dev']));
23
+ for (const childKey in mergedChildDeps) {
24
+ const { group, name } = findGroupAndName(childKey);
25
+ const builtKey = `${group}/${name}`;
26
+ dependencyTree[builtKey] = mergedChildDeps[childKey];
27
+ }
28
+ }
29
+ return dependencyTree;
30
+ };
31
+ const buildSubDepsIntoFlatStructure = childDeps => {
32
+ const dependencyTree = {};
33
+ for (const dep in childDeps) {
34
+ const version = childDeps[dep];
35
+ const { group, name } = findGroupAndName(dep);
36
+ const key = `${group}/${name}`;
37
+ dependencyTree[key] = {
38
+ group: group,
39
+ name: name,
40
+ version: version,
41
+ directDependency: false,
42
+ isProduction: false,
43
+ dependencies: []
44
+ };
45
+ }
46
+ return dependencyTree;
47
+ };
48
+ const findGroupAndName = groupAndName => {
49
+ if (groupAndName.includes('/')) {
50
+ const groupName = groupAndName.split('/');
51
+ return { group: groupName[0], name: groupName[1] };
52
+ }
53
+ else {
54
+ return { group: groupAndName, name: groupAndName };
55
+ }
56
+ };
57
+ module.exports = {
58
+ parsePHPLockFileForScaServices,
59
+ buildDepTree,
60
+ buildSubDepsIntoFlatStructure,
61
+ findGroupAndName
62
+ };
package/dist/scan/help.js CHANGED
@@ -2,6 +2,7 @@
2
2
  const commandLineUsage = require('command-line-usage');
3
3
  const i18n = require('i18n');
4
4
  const constants = require('../constants');
5
+ const { commonHelpLinks } = require('../common/commonHelp');
5
6
  const scanUsageGuide = commandLineUsage([
6
7
  {
7
8
  header: i18n.__('scanHeader')
@@ -35,9 +36,7 @@ const scanUsageGuide = commandLineUsage([
35
36
  'application-name'
36
37
  ]
37
38
  },
38
- {
39
- content: '{underline https://www.contrastsecurity.com}'
40
- }
39
+ commonHelpLinks()
41
40
  ]);
42
41
  module.exports = {
43
42
  scanUsageGuide
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  const paramHandler = require('../utils/paramsUtil/paramHandler');
3
- const constants = require('../../src/constants.js');
3
+ const constants = require('../constants.js');
4
4
  const path = require('path');
5
5
  const { supportedLanguagesScan } = require('../constants/constants');
6
6
  const i18n = require('i18n');
@@ -65,10 +65,17 @@ const returnScanResults = async (config, codeArtifactId, newProject, timeout, st
65
65
  let endTime = new Date() - startTime;
66
66
  if (requestUtils.millisToSeconds(endTime) > timeout) {
67
67
  oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan timed out at the specified ' + timeout + ' seconds.');
68
- if (!config.isCI) {
68
+ const isCI = process.env.CONTRAST_CODESEC_CI
69
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
70
+ : false;
71
+ if (!isCI) {
69
72
  const retry = await retryScanPrompt();
70
73
  timeout = retry.timeout;
71
74
  }
75
+ else {
76
+ console.log('Please try again, allowing more time');
77
+ process.exit(1);
78
+ }
72
79
  }
73
80
  }
74
81
  }
@@ -5,16 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.setConfigValues = exports.localConfig = void 0;
7
7
  const conf_1 = __importDefault(require("conf"));
8
+ const constants_1 = require("../constants/constants");
8
9
  const localConfig = (name, version) => {
9
10
  const config = new conf_1.default({
10
11
  configName: name
11
12
  });
12
13
  config.set('version', version);
13
- if (process.env.CONTRAST_CODSEC_CI) {
14
- config.set('isCI', JSON.parse(process.env.CONTRAST_CODSEC_CI.toLowerCase()));
15
- }
16
14
  if (!config.has('host')) {
17
- config.set('host', 'https://ce.contrastsecurity.com/');
15
+ config.set('host', constants_1.CE_URL);
18
16
  }
19
17
  return config;
20
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -23,7 +23,9 @@
23
23
  "test": "jest --testPathIgnorePatterns=./test-integration/",
24
24
  "test-int": "jest ./test-integration/",
25
25
  "test-int-scan": "jest ./test-integration/scan",
26
- "test-int-audit": "jest test-integration/audit/audit-int.spec.js",
26
+ "test-int-audit": "jest test-integration/audit",
27
+ "test-int-audit-reports": "jest test-integration/audit/audit-language-reports.spec.js",
28
+ "test-int-audit-features": "jest test-integration/audit/auditFeatures/",
27
29
  "format": "prettier --write \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
28
30
  "check-format": "prettier --check \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
29
31
  "coverage-local": "nyc --reporter=text mocha './test/**/*.spec.js'",
@@ -1,4 +1,4 @@
1
- import { getHttpClient, handleResponseErrors } from '../../../utils/commonApi'
1
+ import { getHttpClient, handleResponseErrors } from '../../utils/commonApi'
2
2
  import {
3
3
  ReportCompositeKey,
4
4
  ReportList,
@@ -22,16 +22,17 @@ import {
22
22
  ReportOutputModel
23
23
  } from './models/reportOutputModel'
24
24
  import {
25
+ CE_URL,
25
26
  CRITICAL_COLOUR,
26
27
  HIGH_COLOUR,
27
28
  LOW_COLOUR,
28
29
  MEDIUM_COLOUR,
29
30
  NOTE_COLOUR
30
- } from '../../../constants/constants'
31
+ } from '../../constants/constants'
31
32
  import Table from 'cli-table3'
32
33
  import { ReportGuidanceModel } from './models/reportGuidanceModel'
33
34
 
34
- export const createSummaryMessage = (
35
+ export const createSummaryMessageTop = (
35
36
  numberOfVulnerableLibraries: number,
36
37
  numberOfCves: number
37
38
  ) => {
@@ -42,6 +43,14 @@ export const createSummaryMessage = (
42
43
  )
43
44
  }
44
45
 
46
+ export const createSummaryMessageBottom = (
47
+ numberOfVulnerableLibraries: number
48
+ ) => {
49
+ numberOfVulnerableLibraries === 1
50
+ ? console.log(`Found 1 vulnerable library`)
51
+ : console.log(`Found ${numberOfVulnerableLibraries} vulnerable libraries`)
52
+ }
53
+
45
54
  export const getReport = async (config: any, reportId: string) => {
46
55
  const client = getHttpClient(config)
47
56
  return client
@@ -87,7 +96,7 @@ export const printFormattedOutput = (
87
96
  numberOfCves: number,
88
97
  guidance: any
89
98
  ) => {
90
- createSummaryMessage(numberOfVulnerableLibraries, numberOfCves)
99
+ createSummaryMessageTop(numberOfVulnerableLibraries, numberOfCves)
91
100
  console.log()
92
101
  const report = new ReportList()
93
102
 
@@ -183,7 +192,7 @@ export const printFormattedOutput = (
183
192
  console.log(table.toString() + '\n')
184
193
  }
185
194
 
186
- createSummaryMessage(numberOfVulnerableLibraries, numberOfCves)
195
+ createSummaryMessageBottom(numberOfVulnerableLibraries)
187
196
  const {
188
197
  criticalMessage,
189
198
  highMessage,
@@ -194,6 +203,15 @@ export const printFormattedOutput = (
194
203
  console.log(
195
204
  `${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`
196
205
  )
206
+
207
+ if (config.host !== CE_URL) {
208
+ console.log(
209
+ '\n' + chalk.bold('View your full dependency tree in Contrast:')
210
+ )
211
+ console.log(
212
+ `${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`
213
+ )
214
+ }
197
215
  }
198
216
 
199
217
  export function buildHeader(
@@ -8,9 +8,9 @@ import {
8
8
  } from './utils/reportUtils'
9
9
  import i18n from 'i18n'
10
10
  import chalk from 'chalk'
11
- import * as constants from '../../../constants/constants'
11
+ import * as constants from '../../constants/constants'
12
12
  import { SeverityCountModel } from './models/severityCountModel'
13
- import * as common from '../../../common/fail'
13
+ import * as common from '../../common/fail'
14
14
 
15
15
  export function convertKeysToStandardFormat(config: any, guidance: any) {
16
16
  let convertedGuidance = guidance
@@ -3,7 +3,7 @@ import {
3
3
  ReportLibraryModel
4
4
  } from '../models/reportLibraryModel'
5
5
  import { ReportSeverityModel } from '../models/reportSeverityModel'
6
- import languageAnalysisEngine from './../../../../constants/constants'
6
+ import languageAnalysisEngine from '../../../constants/constants'
7
7
  import {
8
8
  CRITICAL_COLOUR,
9
9
  CRITICAL_PRIORITY,
@@ -15,7 +15,7 @@ import {
15
15
  MEDIUM_PRIORITY,
16
16
  NOTE_COLOUR,
17
17
  NOTE_PRIORITY
18
- } from '../../../../constants/constants'
18
+ } from '../../../constants/constants'
19
19
  import { orderBy } from 'lodash'
20
20
  import { SeverityCountModel } from '../models/severityCountModel'
21
21
  import { ReportModelStructure } from '../models/reportListModel'
@@ -1,6 +1,7 @@
1
1
  import commandLineUsage from 'command-line-usage'
2
2
  import i18n from 'i18n'
3
3
  import constants from '../../constants'
4
+ import { commonHelpLinks } from '../../common/commonHelp'
4
5
 
5
6
  const auditUsageGuide = commandLineUsage([
6
7
  {
@@ -48,7 +49,8 @@ const auditUsageGuide = commandLineUsage([
48
49
  'app-groups',
49
50
  'metadata'
50
51
  ]
51
- }
52
+ },
53
+ commonHelpLinks()
52
54
  ])
53
55
 
54
56
  export { auditUsageGuide }
@@ -21,7 +21,7 @@ const {
21
21
  const i18n = require('i18n')
22
22
  const {
23
23
  vulnerabilityReportV2
24
- } = require('../../../audit/languageAnalysisEngine/report/reportingFeature')
24
+ } = require('../../../audit/report/reportingFeature')
25
25
  const auditSave = require('../../../audit/save')
26
26
  const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet')
27
27
  const { auditUsageGuide } = require('../../audit/help')
@@ -77,7 +77,7 @@ const processSca = async config => {
77
77
  messageToSend = rubyAnalysis(config, filesFound[0])
78
78
  config.language = RUBY
79
79
  break
80
- case 'PHP':
80
+ case PHP:
81
81
  messageToSend = phpAnalysis.phpAnalysis(config, filesFound[0])
82
82
  config.language = PHP
83
83
  break
@@ -212,6 +212,7 @@ HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
212
212
  let url = createSnapshotURL(config)
213
213
  options.url = url
214
214
  options.body = requestBody
215
+
215
216
  return requestUtils.sendRequest({ method: 'post', options })
216
217
  }
217
218
 
@@ -0,0 +1,13 @@
1
+ import i18n from 'i18n'
2
+
3
+ export function commonHelpLinks() {
4
+ return {
5
+ header: i18n.__('commonHelpHeader'),
6
+ content: [
7
+ i18n.__('commonHelpCheckOutHeader') + i18n.__('commonHelpCheckOutText'),
8
+ i18n.__('commonHelpLearnMoreHeader') + i18n.__('commonHelpLearnMoreText'),
9
+ i18n.__('commonHelpJoinDiscussionHeader') +
10
+ i18n.__('commonHelpJoinDiscussionText')
11
+ ]
12
+ }
13
+ }
@@ -18,11 +18,11 @@ const getLatestVersion = async (config: any) => {
18
18
  }
19
19
  }
20
20
 
21
- // @ts-ignore
22
21
  export async function findLatestCLIVersion(config: ContrastConf) {
23
- const messageHidden = config.get('isCI') as boolean
24
-
25
- if (!messageHidden) {
22
+ const isCI = process.env.CONTRAST_CODESEC_CI
23
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
24
+ : false
25
+ if (!isCI) {
26
26
  let latestCLIVersion: string = await getLatestVersion(config)
27
27
  //strip key
28
28
  latestCLIVersion = latestCLIVersion.substring(8)
@@ -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.11'
17
+ const APP_VERSION = '1.0.12'
18
18
  const TIMEOUT = 120000
19
19
  const HIGH_COLOUR = '#ff9900'
20
20
  const CRITICAL_COLOUR = '#e35858'
@@ -32,7 +32,7 @@ const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com'
32
32
  const SARIF_FILE = 'SARIF'
33
33
  const SBOM_CYCLONE_DX_FILE = 'cyclonedx'
34
34
  const SBOM_SPDX_FILE = 'spdx'
35
- const CE_URL = 'https://ce.contrastsecurity.com/'
35
+ const CE_URL = 'https://ce.contrastsecurity.com'
36
36
 
37
37
  module.exports = {
38
38
  supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
@@ -441,6 +441,15 @@ const en_locales = () => {
441
441
  auditReportFailureMessage: 'Unable to generate library report',
442
442
  auditSCAAnalysisBegins: 'Contrast SCA audit started',
443
443
  auditSCAAnalysisComplete: 'Contrast audit complete',
444
+ commonHelpHeader: 'Need More Help?',
445
+ commonHelpCheckOutHeader: chalk.hex('#9DC184')('Check out:'),
446
+ commonHelpCheckOutText: ' https://support.contrastsecurity.com',
447
+ commonHelpLearnMoreHeader: chalk.hex('#9DC184')('Learn more at:'),
448
+ commonHelpLearnMoreText: ' https://developer.contrastsecurity.com',
449
+ commonHelpJoinDiscussionHeader: chalk.hex('#9DC184')(
450
+ 'Join the discussion:'
451
+ ),
452
+ commonHelpJoinDiscussionText: ' https://dev.to/codesec',
444
453
  ...lambda
445
454
  }
446
455
  }
package/src/constants.js CHANGED
@@ -2,6 +2,7 @@ const commandLineUsage = require('command-line-usage')
2
2
  const i18n = require('i18n')
3
3
  const { en_locales } = require('./constants/locales.js')
4
4
  const { parseSeverity } = require('./common/fail')
5
+ const { commonHelpLinks } = require('./common/commonHelp')
5
6
 
6
7
  i18n.configure({
7
8
  staticCatalog: {
@@ -410,16 +411,13 @@ const mainUsageGuide = commandLineUsage([
410
411
  { name: i18n.__('helpName'), summary: i18n.__('helpSummary') }
411
412
  ]
412
413
  },
413
- {
414
- content:
415
- '{underline https://developer.contrastsecurity.com/} \n For technical support head to {underline https://support.contrastsecurity.com}'
416
- },
417
414
  {
418
415
  header: i18n.__('configHeader2'),
419
416
  content: [
420
417
  { name: i18n.__('clearHeader'), summary: i18n.__('clearContent') }
421
418
  ]
422
- }
419
+ },
420
+ commonHelpLinks()
423
421
  ])
424
422
 
425
423
  const mainDefinition = [{ name: 'command', defaultOption: true }]
@@ -1,5 +1,6 @@
1
1
  import commandLineUsage from 'command-line-usage'
2
2
  import i18n from 'i18n'
3
+ import { commonHelpLinks } from '../common/commonHelp'
3
4
 
4
5
  const lambdaUsageGuide = commandLineUsage([
5
6
  {
@@ -80,9 +81,7 @@ const lambdaUsageGuide = commandLineUsage([
80
81
  { name: i18n.__('lambdaHelpOption'), summary: i18n.__('helpSummary') }
81
82
  ]
82
83
  },
83
- {
84
- content: '{underline https://www.contrastsecurity.com/developer/codesec}'
85
- }
84
+ commonHelpLinks()
86
85
  ])
87
86
 
88
87
  export { lambdaUsageGuide }
@@ -10,6 +10,7 @@ const commonSendSnapShot = async (analysis, config) => {
10
10
  cliVersion: APP_VERSION,
11
11
  snapshot: analysis
12
12
  })
13
+
13
14
  const client = commonApi.getHttpClient(config)
14
15
  return client
15
16
  .sendSnapshot(requestBody, config)
@@ -5,7 +5,7 @@ const _ = require('lodash')
5
5
  const readFile = (config, nameOfFile) => {
6
6
  if (config.file) {
7
7
  try {
8
- return fs.readFileSync(config.file + '/' + nameOfFile)
8
+ return fs.readFileSync(config.file + '/' + nameOfFile, 'utf8')
9
9
  } catch (error) {
10
10
  console.log('Unable to find file')
11
11
  console.log(error)
@@ -1,13 +1,19 @@
1
1
  const { readFile, parseProjectFiles } = require('./analysis')
2
2
  const { createPhpTSMessage } = require('../common/formatMessage')
3
+ const { parsePHPLockFileForScaServices } = require('./phpNewServicesMapper')
3
4
 
4
- const phpAnalysis = (config, files) => {
5
- let analysis = readFiles(config, files.PHP)
6
- const phpDep = parseProjectFiles(analysis)
7
- return createPhpTSMessage(phpDep)
5
+ const phpAnalysis = config => {
6
+ let analysis = readFiles(config)
7
+
8
+ if (config.experimental) {
9
+ return parsePHPLockFileForScaServices(analysis.rawLockFileContents)
10
+ } else {
11
+ const phpDep = parseProjectFiles(analysis)
12
+ return createPhpTSMessage(phpDep)
13
+ }
8
14
  }
9
15
 
10
- const readFiles = (config, files) => {
16
+ const readFiles = config => {
11
17
  let php = {}
12
18
 
13
19
  php.composerJSON = JSON.parse(readFile(config, 'composer.json'))
@@ -18,5 +24,5 @@ const readFiles = (config, files) => {
18
24
  }
19
25
 
20
26
  module.exports = {
21
- phpAnalysis
27
+ phpAnalysis: phpAnalysis
22
28
  }
@@ -0,0 +1,77 @@
1
+ const { keyBy, merge } = require('lodash')
2
+
3
+ const parsePHPLockFileForScaServices = phpLockFile => {
4
+ const packages = keyBy(phpLockFile.packages, 'name')
5
+ const packagesDev = keyBy(phpLockFile['packages-dev'], 'name')
6
+
7
+ return merge(buildDepTree(packages, true), buildDepTree(packagesDev, false))
8
+ }
9
+
10
+ const buildDepTree = (packages, isProduction) => {
11
+ //builds deps into flat structure
12
+ const dependencyTree = {}
13
+
14
+ for (const packagesKey in packages) {
15
+ const currentObj = packages[packagesKey]
16
+ const { group, name } = findGroupAndName(currentObj.name)
17
+
18
+ const key = `${group}/${name}@${currentObj.version}`
19
+ dependencyTree[key] = {
20
+ group: group,
21
+ name: name,
22
+ version: currentObj.version,
23
+ directDependency: true,
24
+ isProduction: isProduction,
25
+ dependencies: []
26
+ }
27
+
28
+ const mergedChildDeps = merge(
29
+ buildSubDepsIntoFlatStructure(currentObj.require),
30
+ buildSubDepsIntoFlatStructure(currentObj['require-dev'])
31
+ )
32
+
33
+ for (const childKey in mergedChildDeps) {
34
+ const { group, name } = findGroupAndName(childKey)
35
+ const builtKey = `${group}/${name}`
36
+ dependencyTree[builtKey] = mergedChildDeps[childKey]
37
+ }
38
+ }
39
+ return dependencyTree
40
+ }
41
+
42
+ // currently sub deps will be built into a flat structure
43
+ // but not ingested via the new services as they do not have concrete versions
44
+ const buildSubDepsIntoFlatStructure = childDeps => {
45
+ const dependencyTree = {}
46
+
47
+ for (const dep in childDeps) {
48
+ const version = childDeps[dep]
49
+ const { group, name } = findGroupAndName(dep)
50
+ const key = `${group}/${name}`
51
+ dependencyTree[key] = {
52
+ group: group,
53
+ name: name,
54
+ version: version,
55
+ directDependency: false,
56
+ isProduction: false,
57
+ dependencies: []
58
+ }
59
+ }
60
+ return dependencyTree
61
+ }
62
+
63
+ const findGroupAndName = groupAndName => {
64
+ if (groupAndName.includes('/')) {
65
+ const groupName = groupAndName.split('/')
66
+ return { group: groupName[0], name: groupName[1] }
67
+ } else {
68
+ return { group: groupAndName, name: groupAndName }
69
+ }
70
+ }
71
+
72
+ module.exports = {
73
+ parsePHPLockFileForScaServices,
74
+ buildDepTree,
75
+ buildSubDepsIntoFlatStructure,
76
+ findGroupAndName
77
+ }
package/src/scan/help.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const commandLineUsage = require('command-line-usage')
2
2
  const i18n = require('i18n')
3
3
  const constants = require('../constants')
4
+ const { commonHelpLinks } = require('../common/commonHelp')
4
5
 
5
6
  const scanUsageGuide = commandLineUsage([
6
7
  {
@@ -35,9 +36,7 @@ const scanUsageGuide = commandLineUsage([
35
36
  'application-name'
36
37
  ]
37
38
  },
38
- {
39
- content: '{underline https://www.contrastsecurity.com}'
40
- }
39
+ commonHelpLinks()
41
40
  ])
42
41
 
43
42
  module.exports = {
@@ -1,5 +1,5 @@
1
1
  const paramHandler = require('../utils/paramsUtil/paramHandler')
2
- const constants = require('../../src/constants.js')
2
+ const constants = require('../constants.js')
3
3
  const path = require('path')
4
4
  const { supportedLanguagesScan } = require('../constants/constants')
5
5
  const i18n = require('i18n')
@@ -93,9 +93,15 @@ const returnScanResults = async (
93
93
  'Contrast Scan timed out at the specified ' + timeout + ' seconds.'
94
94
  )
95
95
 
96
- if (!config.isCI) {
96
+ const isCI = process.env.CONTRAST_CODESEC_CI
97
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
98
+ : false
99
+ if (!isCI) {
97
100
  const retry = await retryScanPrompt()
98
101
  timeout = retry.timeout
102
+ } else {
103
+ console.log('Please try again, allowing more time')
104
+ process.exit(1)
99
105
  }
100
106
  }
101
107
  }
@@ -1,4 +1,5 @@
1
1
  import Conf from 'conf'
2
+ import { CE_URL } from '../constants/constants'
2
3
 
3
4
  type ContrastConfOptions = Partial<{
4
5
  version: string
@@ -7,7 +8,6 @@ type ContrastConfOptions = Partial<{
7
8
  orgId: string
8
9
  authHeader: string
9
10
  numOfRuns: number
10
- isCI: boolean
11
11
  }>
12
12
 
13
13
  type ContrastConf = Conf<ContrastConfOptions>
@@ -18,15 +18,8 @@ const localConfig = (name: string, version: string) => {
18
18
  })
19
19
  config.set('version', version)
20
20
 
21
- if (process.env.CONTRAST_CODSEC_CI) {
22
- config.set(
23
- 'isCI',
24
- JSON.parse(process.env.CONTRAST_CODSEC_CI.toLowerCase()) as boolean
25
- )
26
- }
27
-
28
21
  if (!config.has('host')) {
29
- config.set('host', 'https://ce.contrastsecurity.com/')
22
+ config.set('host', CE_URL)
30
23
  }
31
24
  return config
32
25
  }