@contrast/contrast 1.0.12 → 1.0.13

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 (37) hide show
  1. package/dist/commands/audit/processAudit.js +10 -0
  2. package/dist/commands/scan/processScan.js +9 -0
  3. package/dist/commands/scan/sca/scaAnalysis.js +3 -0
  4. package/dist/common/HTTPClient.js +30 -2
  5. package/dist/common/fail.js +7 -3
  6. package/dist/common/versionChecker.js +11 -5
  7. package/dist/constants/constants.js +1 -1
  8. package/dist/constants/locales.js +2 -2
  9. package/dist/index.js +1 -1
  10. package/dist/lambda/lambda.js +7 -0
  11. package/dist/scaAnalysis/common/scaServicesUpload.js +52 -0
  12. package/dist/scaAnalysis/javascript/index.js +4 -0
  13. package/dist/scaAnalysis/javascript/scaServiceParser.js +109 -0
  14. package/dist/scaAnalysis/ruby/analysis.js +106 -9
  15. package/dist/scaAnalysis/ruby/index.js +6 -1
  16. package/dist/scan/scanResults.js +1 -1
  17. package/dist/{audit/languageAnalysisEngine/util → utils}/capabilities.js +0 -0
  18. package/dist/{audit/languageAnalysisEngine/util → utils}/generalAPI.js +14 -5
  19. package/package.json +1 -1
  20. package/src/commands/audit/processAudit.ts +8 -0
  21. package/src/commands/scan/processScan.js +14 -0
  22. package/src/commands/scan/sca/scaAnalysis.js +10 -0
  23. package/src/common/HTTPClient.js +44 -2
  24. package/src/common/fail.js +7 -3
  25. package/src/common/versionChecker.ts +16 -6
  26. package/src/constants/constants.js +1 -1
  27. package/src/constants/locales.js +2 -3
  28. package/src/index.ts +1 -2
  29. package/src/lambda/lambda.ts +12 -0
  30. package/src/scaAnalysis/common/scaServicesUpload.js +54 -0
  31. package/src/scaAnalysis/javascript/index.js +4 -0
  32. package/src/scaAnalysis/javascript/scaServiceParser.js +145 -0
  33. package/src/scaAnalysis/ruby/analysis.js +137 -9
  34. package/src/scaAnalysis/ruby/index.js +6 -1
  35. package/src/scan/scanResults.js +1 -1
  36. package/src/{audit/languageAnalysisEngine/util → utils}/capabilities.js +0 -0
  37. package/src/{audit/languageAnalysisEngine/util → utils}/generalAPI.js +16 -6
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.processAudit = void 0;
4
7
  const auditConfig_1 = require("./auditConfig");
5
8
  const help_1 = require("./help");
6
9
  const scaAnalysis_1 = require("../scan/sca/scaAnalysis");
7
10
  const telemetry_1 = require("../../telemetry/telemetry");
11
+ const chalk_1 = __importDefault(require("chalk"));
8
12
  const processAudit = async (contrastConf, argv) => {
9
13
  if (argv.indexOf('--help') != -1) {
10
14
  printHelpMessage();
@@ -12,9 +16,15 @@ const processAudit = async (contrastConf, argv) => {
12
16
  }
13
17
  const config = await (0, auditConfig_1.getAuditConfig)(contrastConf, 'audit', argv);
14
18
  await (0, scaAnalysis_1.processSca)(config);
19
+ postRunMessage();
15
20
  await (0, telemetry_1.sendTelemetryConfigAsObject)(config, 'audit', argv, 'SUCCESS', config.language);
16
21
  };
17
22
  exports.processAudit = processAudit;
18
23
  const printHelpMessage = () => {
19
24
  console.log(help_1.auditUsageGuide);
20
25
  };
26
+ const postRunMessage = () => {
27
+ console.log('\n' + chalk_1.default.underline.bold('Other Codesec Features:'));
28
+ console.log("'contrast scan' to run CodeSec’s industry leading SAST scanner");
29
+ console.log("'contrast lambda' to secure your AWS serverless functions\n");
30
+ };
@@ -7,9 +7,12 @@ const { formatScanOutput } = require('../../scan/formatScanOutput');
7
7
  const { processSca } = require('./sca/scaAnalysis');
8
8
  const common = require('../../common/fail');
9
9
  const { sendTelemetryConfigAsObject } = require('../../telemetry/telemetry');
10
+ const chalk = require('chalk');
11
+ const generalAPI = require('../../utils/generalAPI');
10
12
  const processScan = async (contrastConf, argv) => {
11
13
  let config = await scanConfig.getScanConfig(contrastConf, 'scan', argv);
12
14
  let output = undefined;
15
+ config.mode = await generalAPI.getMode(config);
13
16
  if (config.experimental) {
14
17
  await processSca(config, argv);
15
18
  }
@@ -24,6 +27,12 @@ const processScan = async (contrastConf, argv) => {
24
27
  if (config.fail) {
25
28
  common.processFail(config, output);
26
29
  }
30
+ postRunMessage();
31
+ };
32
+ const postRunMessage = () => {
33
+ console.log('\n' + chalk.underline.bold('Other Codesec Features:'));
34
+ console.log("'contrast audit' to find vulnerabilities in your open source dependencies");
35
+ console.log("'contrast lambda' to secure your AWS serverless functions\n");
27
36
  };
28
37
  module.exports = {
29
38
  processScan
@@ -2,6 +2,7 @@
2
2
  const autoDetection = require('../../../scan/autoDetection');
3
3
  const javaAnalysis = require('../../../scaAnalysis/java');
4
4
  const treeUpload = require('../../../scaAnalysis/common/treeUpload');
5
+ const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload');
5
6
  const auditController = require('../../audit/auditController');
6
7
  const { supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP, DOTNET } } = require('../../../constants/constants');
7
8
  const goAnalysis = require('../../../scaAnalysis/go/goAnalysis');
@@ -18,7 +19,9 @@ const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet');
18
19
  const { auditUsageGuide } = require('../../audit/help');
19
20
  const rootFile = require('../../../audit/languageAnalysisEngine/getProjectRootFilenames');
20
21
  const path = require('path');
22
+ const generalAPI = require('../../../utils/generalAPI');
21
23
  const processSca = async (config) => {
24
+ config.mode = await generalAPI.getMode(config);
22
25
  const startTime = performance.now();
23
26
  let filesFound;
24
27
  if (config.help) {
@@ -130,9 +130,9 @@ HTTPClient.prototype.getScanProjectById = function getScanProjectById(config) {
130
130
  options.url = createScanProjectUrl(config);
131
131
  return requestUtils.sendRequest({ method: 'get', options });
132
132
  };
133
- HTTPClient.prototype.getGlobalProperties = function getGlobalProperties() {
133
+ HTTPClient.prototype.getGlobalProperties = function getGlobalProperties(host) {
134
134
  const options = _.cloneDeep(this.requestOptions);
135
- let url = createGlobalPropertiesUrl(options.uri);
135
+ let url = createGlobalPropertiesUrl(host);
136
136
  options.url = url;
137
137
  return requestUtils.sendRequest({ method: 'get', options });
138
138
  };
@@ -166,6 +166,25 @@ HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
166
166
  options.body = requestBody;
167
167
  return requestUtils.sendRequest({ method: 'post', options });
168
168
  };
169
+ HTTPClient.prototype.scaServiceIngest = function scaServiceIngest(requestBody, config) {
170
+ const options = _.cloneDeep(this.requestOptions);
171
+ let url = createScaServiceIngestURL(config);
172
+ options.url = url;
173
+ options.body = requestBody;
174
+ return requestUtils.sendRequest({ method: 'post', options });
175
+ };
176
+ HTTPClient.prototype.scaServiceReport = function scaServiceReport(config, reportId) {
177
+ const options = _.cloneDeep(this.requestOptions);
178
+ let url = createScaServiceReportURL(config, reportId);
179
+ options.url = url;
180
+ return requestUtils.sendRequest({ method: 'get', options });
181
+ };
182
+ HTTPClient.prototype.scaServiceReportStatus = function scaServiceReport(config, reportId) {
183
+ const options = _.cloneDeep(this.requestOptions);
184
+ let url = createScaServiceReportStatusURL(config, reportId);
185
+ options.url = url;
186
+ return requestUtils.sendRequest({ method: 'get', options });
187
+ };
169
188
  HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
170
189
  const options = _.cloneDeep(this.requestOptions);
171
190
  if (config.ignoreDev) {
@@ -301,6 +320,15 @@ const pollForAuthUrl = () => {
301
320
  function createSnapshotURL(config) {
302
321
  return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/snapshots`;
303
322
  }
323
+ function createScaServiceReportURL(config, reportId) {
324
+ return ``;
325
+ }
326
+ function createScaServiceReportStatusURL(config, reportId) {
327
+ return ``;
328
+ }
329
+ function createScaServiceIngestURL(config) {
330
+ return ``;
331
+ }
304
332
  const createAppCreateURL = config => {
305
333
  return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/create`;
306
334
  };
@@ -21,11 +21,15 @@ const isSeverityViolation = (severity, reportResults) => {
21
21
  count += reportResults.high + reportResults.critical;
22
22
  break;
23
23
  case 'medium':
24
- count += reportResults.medium + reportResults.low + reportResults.critical;
24
+ count +=
25
+ reportResults.medium + reportResults.high + reportResults.critical;
25
26
  break;
26
27
  case 'low':
27
28
  count +=
28
- reportResults.high + reportResults.critical + reportResults.medium;
29
+ reportResults.high +
30
+ reportResults.critical +
31
+ reportResults.medium +
32
+ reportResults.low;
29
33
  break;
30
34
  case 'note':
31
35
  if (reportResults.note == reportResults.total) {
@@ -45,7 +49,7 @@ const failPipeline = (message = '') => {
45
49
  i18n.__('snapshotFailureHeader') +
46
50
  ' *********************************\n' +
47
51
  i18n.__(message));
48
- process.exit(1);
52
+ process.exit(2);
49
53
  };
50
54
  const parseSeverity = severity => {
51
55
  const severities = ['NOTE', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
@@ -3,7 +3,7 @@ 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.isCorrectNodeVersion = exports.findLatestCLIVersion = void 0;
6
+ exports.isCorrectNodeVersion = exports.findLatestCLIVersion = exports.getLatestVersion = void 0;
7
7
  const constants_1 = require("../constants/constants");
8
8
  const boxen_1 = __importDefault(require("boxen"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
@@ -19,16 +19,22 @@ const getLatestVersion = async (config) => {
19
19
  }
20
20
  }
21
21
  catch (e) {
22
- return;
22
+ return undefined;
23
23
  }
24
24
  };
25
+ exports.getLatestVersion = getLatestVersion;
25
26
  async function findLatestCLIVersion(config) {
26
27
  const isCI = process.env.CONTRAST_CODESEC_CI
27
- ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
28
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI.toLowerCase())
28
29
  : false;
29
30
  if (!isCI) {
30
- let latestCLIVersion = await getLatestVersion(config);
31
- latestCLIVersion = latestCLIVersion.substring(8);
31
+ let latestCLIVersion = await (0, exports.getLatestVersion)(config);
32
+ if (latestCLIVersion === undefined) {
33
+ config.set('numOfRuns', 0);
34
+ console.log('Failed to retrieve latest version info. Continuing execution.');
35
+ return;
36
+ }
37
+ latestCLIVersion = latestCLIVersion.substring(8).replace('\n', '');
32
38
  if (semver_1.default.lt(constants_1.APP_VERSION, latestCLIVersion)) {
33
39
  const updateAvailableMessage = `Update available ${chalk_1.default.yellow(constants_1.APP_VERSION)} → ${chalk_1.default.green(latestCLIVersion)}`;
34
40
  const npmUpdateAvailableCommand = `Run ${chalk_1.default.cyan('npm i @contrast/contrast -g')} to update via npm`;
@@ -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.12';
15
+ const APP_VERSION = '1.0.13';
16
16
  const TIMEOUT = 120000;
17
17
  const HIGH_COLOUR = '#ff9900';
18
18
  const CRITICAL_COLOUR = '#e35858';
@@ -20,7 +20,7 @@ const en_locales = () => {
20
20
  unauthenticatedErrorHeader: '401 error - Unauthenticated',
21
21
  unauthenticatedErrorMessage: 'Please check the following keys are correct:\n--organization-id, --api-key or --authorization',
22
22
  badRequestErrorHeader: '400 error - Bad Request',
23
- badRequestErrorMessage: 'Please check the following key is correct: \n--application-id',
23
+ badRequestErrorMessage: 'Please check your parameters and try again',
24
24
  badRequestCatalogueErrorMessage: 'The application name already exists, please use a unique name',
25
25
  forbiddenRequestErrorHeader: '403 error - Forbidden',
26
26
  forbiddenRequestErrorMessage: 'You do not have permission to access this server.',
@@ -213,7 +213,7 @@ const en_locales = () => {
213
213
  scanOptionsFileNameSummary: 'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .exe or .zip file in the working directory.',
214
214
  scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
215
215
  authSuccessMessage: 'Authentication successful',
216
- runAuthSuccessMessage: "Now you can use CodeSec by Contrast \nRun: \n'contrast scan' on your file \n'contrast audit' on a file or directory,\n'contrast lambda' on an AWS function.\nor 'contrast help' to learn more about the capabilities.",
216
+ runAuthSuccessMessage: "Now you can use CodeSec by Contrast \nRun: \n'contrast scan' to run CodeSec’s industry leading SAST scanner \n'contrast audit' to find vulnerabilities in your open source dependencies \n'contrast lambda' to secure your AWS serverless functions\nor 'contrast help' to learn more about the capabilities.",
217
217
  authWaitingMessage: 'Waiting for auth...',
218
218
  authTimedOutMessage: 'Auth Timed out, try again',
219
219
  zipErrorScan: 'We only support zip files for JAVASCRIPT language, please set the flag --language JAVASCRIPT',
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ const start = async () => {
45
45
  return;
46
46
  }
47
47
  config.set('numOfRuns', config.get('numOfRuns') + 1);
48
- if (config.get('numOfRuns') >= 1) {
48
+ if (config.get('numOfRuns') >= 10) {
49
49
  await (0, versionChecker_1.findLatestCLIVersion)(config);
50
50
  config.set('numOfRuns', 0);
51
51
  }
@@ -23,6 +23,7 @@ const oraWrapper_1 = __importDefault(require("../utils/oraWrapper"));
23
23
  const analytics_1 = require("./analytics");
24
24
  const types_1 = require("./types");
25
25
  const constants_2 = require("../constants/constants");
26
+ const chalk_1 = __importDefault(require("chalk"));
26
27
  const failedStates = [
27
28
  'UNSUPPORTED',
28
29
  'EXCLUDED',
@@ -109,6 +110,7 @@ const processLambda = async (argv) => {
109
110
  }
110
111
  await (0, analytics_1.postAnalytics)(endCommandAnalytics).catch((error) => {
111
112
  });
113
+ postRunMessage();
112
114
  if (errorMsg) {
113
115
  process.exit(1);
114
116
  }
@@ -191,3 +193,8 @@ const handleLambdaHelp = () => {
191
193
  printHelpMessage();
192
194
  process.exit(0);
193
195
  };
196
+ const postRunMessage = () => {
197
+ console.log('\n' + chalk_1.default.underline.bold('Other Codesec Features:'));
198
+ console.log("'contrast scan' to run CodeSec’s industry leading SAST scanner");
199
+ console.log("'contrast audit' to find vulnerabilities in your open source dependencies\n");
200
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ const commonApi = require('../../utils/commonApi');
3
+ const { APP_VERSION } = require('../../constants/constants');
4
+ const requestUtils = require('../../utils/requestUtils');
5
+ const scaTreeUpload = async (analysis, config) => {
6
+ const requestBody = {
7
+ applicationId: config.applicationId,
8
+ dependencyTree: analysis,
9
+ organizationId: config.organizationId,
10
+ language: 'NODE',
11
+ tool: {
12
+ name: 'Contrast Codesec',
13
+ version: APP_VERSION
14
+ }
15
+ };
16
+ const client = commonApi.getHttpClient(config);
17
+ const reportID = await client
18
+ .scaServiceIngest(requestBody, config)
19
+ .then(res => {
20
+ if (res.statusCode === 201) {
21
+ return res.body.libraryIngestJobId;
22
+ }
23
+ else {
24
+ throw new Error(res.statusCode + ` error ingesting dependencies`);
25
+ }
26
+ })
27
+ .catch(err => {
28
+ throw err;
29
+ });
30
+ let keepChecking = true;
31
+ while (keepChecking) {
32
+ keepChecking = await client
33
+ .scaServiceReportStatus(config, reportID)
34
+ .then(res => {
35
+ console.log(res.body);
36
+ if (res.body.status == 'COMPLETED') {
37
+ client.scaServiceReport(config, reportID).then(res => {
38
+ console.log(res.statusCode);
39
+ console.log(res.body);
40
+ });
41
+ return (keepChecking = false);
42
+ }
43
+ else {
44
+ return (keepChecking = true);
45
+ }
46
+ });
47
+ await requestUtils.sleep(5000);
48
+ }
49
+ };
50
+ module.exports = {
51
+ scaTreeUpload
52
+ };
@@ -2,6 +2,7 @@
2
2
  const analysis = require('./analysis');
3
3
  const i18n = require('i18n');
4
4
  const formatMessage = require('../common/formatMessage');
5
+ const scaServiceParser = require('./scaServiceParser');
5
6
  const jsAnalysis = async (config, languageFiles) => {
6
7
  checkForCorrectFiles(languageFiles);
7
8
  if (!config.file.endsWith('/')) {
@@ -12,6 +13,9 @@ const jsAnalysis = async (config, languageFiles) => {
12
13
  const buildNodeTree = async (config, files) => {
13
14
  let analysis = await readFiles(config, files);
14
15
  const rawNode = await parseFiles(config, files, analysis);
16
+ if (config.experimental) {
17
+ return scaServiceParser.parseJS(rawNode);
18
+ }
15
19
  return formatMessage.createJavaScriptTSMessage(rawNode);
16
20
  };
17
21
  const readFiles = async (config, files) => {
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ const parseJS = rawNode => {
3
+ let dependencyTree = {};
4
+ let combinedPackageJSONDep = {
5
+ ...rawNode.packageJSON?.dependencies,
6
+ ...rawNode.packageJSON?.devDependencies
7
+ };
8
+ let analyseLock = chooseLockFile(rawNode);
9
+ if (analyseLock.type === 'yarn') {
10
+ dependencyTree = yarnCreateDepTree(dependencyTree, combinedPackageJSONDep, analyseLock.lockFile, rawNode);
11
+ }
12
+ if (analyseLock.type === 'npm') {
13
+ dependencyTree = npmCreateDepTree(dependencyTree, combinedPackageJSONDep, analyseLock.lockFile, rawNode);
14
+ }
15
+ return dependencyTree;
16
+ };
17
+ const npmCreateDepTree = (dependencyTree, combinedPackageJSONDep, packageLock, rawNode) => {
18
+ for (const [key, value] of Object.entries(packageLock)) {
19
+ dependencyTree[key] = {
20
+ name: key,
21
+ version: getResolvedVersion(key, packageLock),
22
+ group: null,
23
+ isProduction: checkIfInPackageJSON(rawNode.packageJSON.dependencies, key),
24
+ directDependency: checkIfInPackageJSON(combinedPackageJSONDep, key),
25
+ dependencies: createNPMChildDependencies(packageLock, key)
26
+ };
27
+ }
28
+ return dependencyTree;
29
+ };
30
+ const yarnCreateDepTree = (dependencyTree, combinedPackageJSONDep, packageLock, rawNode) => {
31
+ for (const [key, value] of Object.entries(packageLock)) {
32
+ let gav = getNameFromGAV(key);
33
+ let nag = getDepNameWithoutVersion(key);
34
+ dependencyTree[key] = {
35
+ name: gav,
36
+ version: getResolvedVersion(key, packageLock),
37
+ group: null,
38
+ isProduction: checkIfInPackageJSON(rawNode.packageJSON.dependencies, nag),
39
+ directDependency: checkIfInPackageJSON(combinedPackageJSONDep, nag),
40
+ dependencies: createChildDependencies(packageLock, key)
41
+ };
42
+ }
43
+ return dependencyTree;
44
+ };
45
+ const chooseLockFile = rawNode => {
46
+ if (rawNode?.yarn?.yarnLockFile !== undefined) {
47
+ return { lockFile: rawNode?.yarn?.yarnLockFile?.object, type: 'yarn' };
48
+ }
49
+ else if (rawNode.npmLockFile !== undefined) {
50
+ return { lockFile: rawNode?.npmLockFile?.dependencies, type: 'npm' };
51
+ }
52
+ else {
53
+ return undefined;
54
+ }
55
+ };
56
+ const createKeyName = (dep, version) => {
57
+ return dep + '@' + version;
58
+ };
59
+ const checkIfInPackageJSON = (list, dep) => {
60
+ return Object.keys(list).includes(dep);
61
+ };
62
+ const createChildDependencies = (lockFileDep, currentDep) => {
63
+ let depArray = [];
64
+ if (lockFileDep[currentDep]?.dependencies) {
65
+ for (const [key, value] of Object.entries(lockFileDep[currentDep]?.dependencies)) {
66
+ depArray.push(createKeyName(key, value));
67
+ }
68
+ }
69
+ return depArray;
70
+ };
71
+ const createNPMChildDependencies = (lockFileDep, currentDep) => {
72
+ let depArray = [];
73
+ if (lockFileDep[currentDep]?.requires) {
74
+ for (const [key, value] of Object.entries(lockFileDep[currentDep]?.requires)) {
75
+ depArray.push(key);
76
+ }
77
+ }
78
+ return depArray;
79
+ };
80
+ const getDepNameWithoutVersion = depKey => {
81
+ let dependency = depKey.split('@');
82
+ if (dependency.length - 1 > 1) {
83
+ return '@' + dependency[1];
84
+ }
85
+ return dependency[0];
86
+ };
87
+ const getNameFromGAV = depKey => {
88
+ let dependency = depKey.split('/');
89
+ if (dependency.length == 2) {
90
+ dependency = getDepNameWithoutVersion(dependency[1]);
91
+ return dependency;
92
+ }
93
+ if (dependency.length == 1) {
94
+ dependency = getDepNameWithoutVersion(depKey);
95
+ return dependency;
96
+ }
97
+ return depKey;
98
+ };
99
+ const getResolvedVersion = (depKey, packageLock) => {
100
+ return packageLock[depKey]?.version;
101
+ };
102
+ module.exports = {
103
+ parseJS,
104
+ checkIfInPackageJSON,
105
+ getNameFromGAV,
106
+ getResolvedVersion,
107
+ chooseLockFile,
108
+ createNPMChildDependencies
109
+ };
@@ -1,6 +1,26 @@
1
1
  "use strict";
2
2
  const fs = require('fs');
3
3
  const i18n = require('i18n');
4
+ const getRubyDeps = (config, languageFiles) => {
5
+ try {
6
+ checkForCorrectFiles(languageFiles);
7
+ const parsedGem = readAndParseGemfile(config.file);
8
+ const parsedLock = readAndParseGemLockFile(config.file);
9
+ if (config.experimental) {
10
+ const rubyArray = removeRedundantAndPopulateDefinedElements(parsedLock.sources);
11
+ let rubyTree = createRubyTree(rubyArray);
12
+ findChildrenDependencies(rubyTree);
13
+ processRootDependencies(parsedLock.dependencies, rubyTree);
14
+ return rubyTree;
15
+ }
16
+ else {
17
+ return { gemfilesDependanceies: parsedGem, gemfileLock: parsedLock };
18
+ }
19
+ }
20
+ catch (err) {
21
+ throw err;
22
+ }
23
+ };
4
24
  const readAndParseGemfile = file => {
5
25
  const gemFile = fs.readFileSync(file + '/Gemfile', 'utf8');
6
26
  const rubyArray = gemFile.split('\n');
@@ -189,16 +209,89 @@ const buildSourceDependencyWithVersion = (whitespaceRegx, dependencyRegEx, line,
189
209
  }
190
210
  return dependencies;
191
211
  };
192
- const getRubyDeps = (config, languageFiles) => {
193
- try {
194
- checkForCorrectFiles(languageFiles);
195
- const parsedGem = readAndParseGemfile(config.file);
196
- const parsedLock = readAndParseGemLockFile(config.file);
197
- return { gemfilesDependanceies: parsedGem, gemfileLock: parsedLock };
212
+ const processRootDependencies = (rootDependencies, rubyTree) => {
213
+ const getParentObjectByName = queryToken => Object.values(rubyTree).filter(({ name }) => name === queryToken);
214
+ for (let parent in rootDependencies) {
215
+ let parentObject = getParentObjectByName(parent);
216
+ if (parentObject[0]) {
217
+ let gav = parentObject[0].group +
218
+ '/' +
219
+ parentObject[0].name +
220
+ '@' +
221
+ parentObject[0].version;
222
+ rubyTree[gav] = parentObject[0];
223
+ rubyTree[gav].directDependency = true;
224
+ }
198
225
  }
199
- catch (err) {
200
- throw err;
226
+ return rubyTree;
227
+ };
228
+ const createRubyTree = rubyArray => {
229
+ let rubyTree = {};
230
+ for (let x in rubyArray) {
231
+ let version = rubyArray[x].resolved;
232
+ let gav = rubyArray[x].group + '/' + rubyArray[x].name + '@' + version;
233
+ rubyTree[gav] = rubyArray[x];
234
+ rubyTree[gav].directDependency = false;
235
+ rubyTree[gav].version = version;
236
+ if (!rubyTree[gav].dependencies) {
237
+ rubyTree[gav].dependencies = [];
238
+ }
239
+ delete rubyTree[gav].resolved;
201
240
  }
241
+ return rubyTree;
242
+ };
243
+ const findChildrenDependencies = rubyTree => {
244
+ for (let dep in rubyTree) {
245
+ let unResolvedChildDepsKey = Object.keys(rubyTree[dep].dependencies);
246
+ rubyTree[dep].dependencies = resolveVersionOfChildDependencies(unResolvedChildDepsKey, rubyTree);
247
+ }
248
+ };
249
+ const resolveVersionOfChildDependencies = (unResolvedChildDepsKey, rubyObject) => {
250
+ const getParentObjectByName = queryToken => Object.values(rubyObject).filter(({ name }) => name === queryToken);
251
+ let resolvedChildrenDependencies = [];
252
+ for (let childDep in unResolvedChildDepsKey) {
253
+ let childDependencyName = unResolvedChildDepsKey[childDep];
254
+ let parent = getParentObjectByName(childDependencyName);
255
+ resolvedChildrenDependencies.push('null/' + childDependencyName + '@' + parent[0].version);
256
+ }
257
+ return resolvedChildrenDependencies;
258
+ };
259
+ const removeRedundantAndPopulateDefinedElements = deps => {
260
+ return deps.map(element => {
261
+ if (element.sourceType === 'GIT') {
262
+ delete element.sourceType;
263
+ delete element.remote;
264
+ delete element.platform;
265
+ element.group = null;
266
+ element.isProduction = true;
267
+ }
268
+ if (element.sourceType === 'GEM') {
269
+ element.group = null;
270
+ element.isProduction = true;
271
+ delete element.sourceType;
272
+ delete element.remote;
273
+ delete element.platform;
274
+ }
275
+ if (element.sourceType === 'PATH') {
276
+ element.group = null;
277
+ element.isProduction = true;
278
+ delete element.platform;
279
+ delete element.sourceType;
280
+ delete element.remote;
281
+ }
282
+ if (element.sourceType === 'BUNDLED WITH') {
283
+ element.group = null;
284
+ element.isProduction = true;
285
+ delete element.sourceType;
286
+ delete element.remote;
287
+ delete element.branch;
288
+ delete element.revision;
289
+ delete element.depthLevel;
290
+ delete element.specs;
291
+ delete element.platform;
292
+ }
293
+ return element;
294
+ });
202
295
  };
203
296
  const checkForCorrectFiles = languageFiles => {
204
297
  if (!languageFiles.includes('Gemfile.lock')) {
@@ -224,5 +317,9 @@ module.exports = {
224
317
  getPatchLevel,
225
318
  formatSourceArr,
226
319
  getSourceArray,
227
- checkForCorrectFiles
320
+ checkForCorrectFiles,
321
+ removeRedundantAndPopulateDefinedElements,
322
+ createRubyTree,
323
+ findChildrenDependencies,
324
+ processRootDependencies
228
325
  };
@@ -3,7 +3,12 @@ const analysis = require('./analysis');
3
3
  const { createRubyTSMessage } = require('../common/formatMessage');
4
4
  const rubyAnalysis = (config, languageFiles) => {
5
5
  const rubyDeps = analysis.getRubyDeps(config, languageFiles.RUBY);
6
- return createRubyTSMessage(rubyDeps);
6
+ if (config.experimental) {
7
+ return rubyDeps;
8
+ }
9
+ else {
10
+ return createRubyTSMessage(rubyDeps);
11
+ }
7
12
  };
8
13
  module.exports = {
9
14
  rubyAnalysis
@@ -66,7 +66,7 @@ const returnScanResults = async (config, codeArtifactId, newProject, timeout, st
66
66
  if (requestUtils.millisToSeconds(endTime) > timeout) {
67
67
  oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan timed out at the specified ' + timeout + ' seconds.');
68
68
  const isCI = process.env.CONTRAST_CODESEC_CI
69
- ? JSON.parse(process.env.CONTRAST_CODESEC_CI)
69
+ ? JSON.parse(process.env.CONTRAST_CODESEC_CI.toLowerCase())
70
70
  : false;
71
71
  if (!isCI) {
72
72
  const retry = await retryScanPrompt();
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  const { featuresTeamServer } = require('./capabilities');
3
3
  const semver = require('semver');
4
- const { handleResponseErrors } = require('../../../common/errorHandling');
5
- const { getHttpClient } = require('../../../utils/commonApi');
4
+ const { handleResponseErrors } = require('../common/errorHandling');
5
+ const commonApi = require('./commonApi');
6
+ const { isNil } = require('lodash');
6
7
  const getGlobalProperties = async (config) => {
7
- const client = getHttpClient(config);
8
+ const client = commonApi.getHttpClient(config);
8
9
  return client
9
- .getGlobalProperties(config)
10
+ .getGlobalProperties(config.host)
10
11
  .then(res => {
11
12
  if (res.statusCode === 200) {
12
13
  return res.body;
@@ -19,6 +20,13 @@ const getGlobalProperties = async (config) => {
19
20
  console.log(err);
20
21
  });
21
22
  };
23
+ const getMode = async (config) => {
24
+ const features = await getGlobalProperties(config);
25
+ if (!isNil(features?.mode)) {
26
+ return features.mode;
27
+ }
28
+ return '';
29
+ };
22
30
  const getFeatures = version => {
23
31
  const featuresEnabled = [];
24
32
  featuresTeamServer.forEach(feature => {
@@ -35,5 +43,6 @@ const isFeatureEnabled = (features, featureName) => {
35
43
  module.exports = {
36
44
  getGlobalProperties,
37
45
  getFeatures,
38
- isFeatureEnabled
46
+ isFeatureEnabled,
47
+ getMode
39
48
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {