@contrast/contrast 2.0.2-beta.0 → 2.0.2-beta.2

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.
@@ -395,6 +395,7 @@ const fingerprintOptionDefinitions = [
395
395
  {
396
396
  name: 'depth',
397
397
  type: Number,
398
+ default: 3,
398
399
  description: '{bold ' + i18n.__('constantsOptional') + '}: ' + i18n.__('depthOption')
399
400
  },
400
401
  {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  const fingerprintConfig = require('./fingerprintConfig');
3
3
  const repoServices = require('./repoServices');
4
- const autoDetection = require('../../scan/autoDetection');
4
+ const autoDetection = require('../../common/autoDetection');
5
5
  const saveResults = require('../../scan/saveResults');
6
6
  const projectConfig = require('./projectGroup');
7
7
  const processFingerprint = async (contrastConf, argvMain) => {
@@ -79,10 +79,10 @@ const createProjectsArray = params => {
79
79
  });
80
80
  return projectsArray;
81
81
  };
82
- const createProject = param => {
82
+ const createProject = (param, shortenedProjectName) => {
83
83
  return {
84
84
  path: param.fileName,
85
- name: param.repo ? param.fileName : getProjectName(param),
85
+ name: param.repo ? shortenedProjectName : getProjectName(param),
86
86
  source: 'SCA',
87
87
  language: param.language,
88
88
  packageManager: param.packageManager,
@@ -121,11 +121,11 @@ const addAdditionalData = (body, data) => {
121
121
  body.projectLanguage = data.projectLanguage ? data.projectLanguage : null;
122
122
  body.projectType = data.projectType ? data.projectType : null;
123
123
  };
124
- const registerProjectIdOnCliServices = async (config, projectId, additionalData = undefined) => {
124
+ const registerProjectIdOnCliServices = async (config, projectId, shortenedProjectName, additionalData = undefined) => {
125
125
  const client = commonApi.getHttpClient(config);
126
126
  let cliServicesBody = {
127
127
  projectId: projectId,
128
- name: config.repo ? config.fileName : getProjectName(config)
128
+ name: config.repo ? shortenedProjectName : getProjectName(config)
129
129
  };
130
130
  if (additionalData) {
131
131
  addAdditionalData(cliServicesBody, additionalData);
@@ -149,10 +149,10 @@ const registerProjectIdOnCliServices = async (config, projectId, additionalData
149
149
  });
150
150
  return result;
151
151
  };
152
- const registerProjectWithGroupProjectId = async (config) => {
152
+ const registerProjectWithGroupProjectId = async (config, shortenedProjectName) => {
153
153
  const client = commonApi.getHttpClient(config);
154
154
  config.language = config.language === 'NODE' ? 'JAVASCRIPT' : config.language;
155
- let body = createProject(config);
155
+ let body = createProject(config, shortenedProjectName);
156
156
  let result = await client.registerProject(config, body).then(res => {
157
157
  if (config.debug || config.verbose) {
158
158
  console.log('\nregister Project With Group ProjectId');
@@ -68,7 +68,7 @@ const registerNewRepo = async (config) => {
68
68
  return result;
69
69
  };
70
70
  const retrieveProjectInfoViaRepoId = async (config) => {
71
- const client = await commonApi.getHttpClient(config);
71
+ const client = commonApi.getHttpClient(config);
72
72
  let result = await client
73
73
  .retrieveProjectByRepoId(config)
74
74
  .then(res => {
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  const i18n = require('i18n');
3
- const fileFinder = require('./fileUtils');
4
- const { supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP, DOTNET } } = require('../constants/constants');
3
+ const fileFinder = require('../scan/fileUtils');
4
+ const { supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, DOTNET } } = require('../constants/constants');
5
+ const { shortenFilePath } = require('../scan/fileUtils');
5
6
  const autoDetectFingerprintInfo = async (filePath, depth, config) => {
6
7
  let complexObj = await fileFinder.findAllFiles(filePath, depth);
7
8
  let result = [];
@@ -10,6 +11,7 @@ const autoDetectFingerprintInfo = async (filePath, depth, config) => {
10
11
  count++;
11
12
  if (!i.includes('package.json')) {
12
13
  result.push({
14
+ name: shortenFilePath(i),
13
15
  filePath: i,
14
16
  id: count.toString(),
15
17
  repositoryId: config.repositoryId,
@@ -100,18 +102,40 @@ const hasWhiteSpace = s => {
100
102
  const filename = s.split('/').pop();
101
103
  return filename.indexOf(' ') >= 0;
102
104
  };
103
- const dealWithMultiJava = filesFound => {
104
- let hasMultiJava = filesFound.filter(data => {
105
- return (Object.keys(data)[0] === JAVA &&
106
- Object.values(data)[0].includes('build.gradle') &&
107
- Object.values(data)[0].includes('pom.xml'));
108
- }).length > 0;
105
+ const dealWithMultiJava = (filesFound, config, isFile) => {
106
+ if (isFile) {
107
+ return multiJavaFilePathFullySpecified(filesFound, config);
108
+ }
109
+ else {
110
+ return multiJavaNoFilePathFullySpecified(filesFound);
111
+ }
112
+ };
113
+ const multiJavaNoFilePathFullySpecified = filesFound => {
114
+ const hasMultiJava = filesFound.filter(data => Object.keys(data)[0] === JAVA &&
115
+ Object.values(data)[0].includes('build.gradle') &&
116
+ Object.values(data)[0].includes('pom.xml')).length > 0;
109
117
  if (hasMultiJava) {
110
118
  console.log('Multiple Java language dependency files detected');
111
119
  console.log('Please use --file to audit one only. \nExample: contrast audit --file pom.xml');
112
120
  process.exit(1);
113
121
  }
114
- return false;
122
+ return filesFound;
123
+ };
124
+ const multiJavaFilePathFullySpecified = (filesFound, config) => {
125
+ const filteredFiles = filesFound[0].JAVA.filter(fileTypes => config.fileName.endsWith(fileTypes));
126
+ filesFound[0].JAVA = filteredFiles;
127
+ filesFound[0].filePath = filteredFiles[0];
128
+ if (filteredFiles[0] === 'pom.xml') {
129
+ filesFound[0].packageManager = 'MAVEN';
130
+ }
131
+ else {
132
+ filesFound[0].packageManager = 'GRADLE';
133
+ }
134
+ if (config.debug || config.verbose) {
135
+ console.log('\nAuto detection - detected multiple Java files');
136
+ console.log(`\nAuto detection - using ${filesFound[0].filePath} as based on full file path`);
137
+ }
138
+ return filesFound;
115
139
  };
116
140
  const errorOnFileDetection = entries => {
117
141
  if (entries.length > 1) {
@@ -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 = '2.0.2-beta.0';
15
+ const APP_VERSION = '2.0.2-beta.2';
16
16
  const TIMEOUT = 120000;
17
17
  const HIGH_COLOUR = '#ff9900';
18
18
  const CRITICAL_COLOUR = '#e35858';
@@ -22,6 +22,9 @@ const scaTreeUpload = async (analysis, config, reportSpinner) => {
22
22
  version: APP_VERSION
23
23
  }
24
24
  };
25
+ if (config.debug || config.verbose) {
26
+ console.log('requestBody', requestBody);
27
+ }
25
28
  if (config.branch) {
26
29
  requestBody.branchName = config.branch;
27
30
  }
@@ -2,6 +2,7 @@
2
2
  const projectConfig = require('../commands/github/projectGroup');
3
3
  const repoService = require('../commands/github/repoServices');
4
4
  const scaServicesUpload = require('../scaAnalysis/common/scaServicesUpload');
5
+ const { shortenFilePath } = require('../scan/fileUtils');
5
6
  const dealWithNoProjectId = async (analysis, config, reportSpinner) => {
6
7
  await projectConfig.registerNewProjectGroup(config);
7
8
  let projectId = await projectConfig.getProjectIdByOrg(config);
@@ -18,18 +19,19 @@ const repoProcess = async (analysis, config, reportSpinner) => {
18
19
  console.log('Failed to retrieve Repository Id');
19
20
  process.exit(1);
20
21
  }
22
+ let shortenedProjectName = shortenFilePath(config.fileName);
21
23
  let repoInfo = await repoService.retrieveProjectInfoViaRepoId(config);
22
24
  repoInfo = repoInfo.find(element => config.fileName === element.path &&
23
- config.fileName === element.name &&
25
+ shortenedProjectName === element.name &&
24
26
  config.projectGroupId === element.projectGroupId);
25
27
  if (config.projectGroupId &&
26
28
  !repoInfo?.projectId &&
27
29
  (repoInfo === undefined || repoInfo.length === 0)) {
28
30
  console.log('*** has projectGroupId, no projectId and repo has no project found that matches');
29
- repoInfo = await projectConfig.registerProjectWithGroupProjectId(config);
31
+ repoInfo = await projectConfig.registerProjectWithGroupProjectId(config, shortenedProjectName);
30
32
  console.log('new registered group', repoInfo);
31
33
  const language = repoInfo.language === 'JAVASCRIPT' ? 'NODE' : repoInfo.language;
32
- await projectConfig.registerProjectIdOnCliServices(config, repoInfo.projectId);
34
+ await projectConfig.registerProjectIdOnCliServices(config, repoInfo.projectId, shortenedProjectName);
33
35
  }
34
36
  config.projectId = repoInfo.projectId;
35
37
  return await scaServicesUpload.scaTreeUpload(analysis, config, reportSpinner);
@@ -1,94 +1,117 @@
1
1
  "use strict";
2
2
  const fs = require('fs');
3
- const xml2js = require('xml2js');
3
+ const { XMLParser } = require('fast-xml-parser');
4
4
  const readPomFile = project => {
5
5
  const mavenFilePath = project.cwd + '/pom.xml';
6
6
  const projectFile = fs.readFileSync(mavenFilePath);
7
- let jsonPomFile;
8
- xml2js.parseString(projectFile, (err, result) => {
9
- if (err) {
10
- throw err;
11
- }
12
- const json = JSON.stringify(result, null);
13
- jsonPomFile = JSON.parse(json);
14
- });
15
- return jsonPomFile;
16
- };
17
- const getFromVersionsTag = (dependencyName, versionIdentifier, jsonPomFile) => {
18
- let formattedVersion = versionIdentifier.replace(/[{}]/g, '').replace('$', '');
19
- if (jsonPomFile.project.properties[0].hasOwnProperty([formattedVersion])) {
20
- return jsonPomFile.project.properties[0][formattedVersion][0];
21
- }
22
- else {
23
- return null;
24
- }
7
+ const parser = new XMLParser();
8
+ return parser.parse(projectFile);
25
9
  };
26
10
  const parsePomFile = jsonPomFile => {
27
11
  let dependencyTree = {};
28
- let parsedVersion;
29
- let dependencies;
30
- jsonPomFile.project.hasOwnProperty('dependencies')
31
- ? (dependencies = jsonPomFile.project.dependencies[0].dependency)
32
- : (dependencies =
33
- jsonPomFile.project.dependencyManagement[0].dependencies[0].dependency);
34
- for (let x in dependencies) {
35
- let dependencyObject = dependencies[x];
36
- if (!dependencyObject.hasOwnProperty('version')) {
37
- parsedVersion = getVersion(jsonPomFile, dependencyObject);
38
- }
39
- else {
40
- dependencyObject.version[0].includes('${versions.')
41
- ? (parsedVersion = getFromVersionsTag(dependencyObject.artifactId[0], dependencyObject.version[0], jsonPomFile))
42
- : (parsedVersion = dependencyObject.version[0]);
43
- }
44
- let depName = dependencyObject.groupId +
45
- '/' +
46
- dependencyObject.artifactId +
47
- '@' +
48
- parsedVersion;
49
- let parsedDependency = {
50
- name: dependencyObject.artifactId[0],
51
- group: dependencyObject.groupId[0],
52
- version: parsedVersion,
53
- directDependency: true,
54
- productionDependency: true,
55
- dependencies: []
56
- };
57
- dependencyTree[depName] = parsedDependency;
12
+ let dependencies = [];
13
+ let dependencyManagement = [];
14
+ if (jsonPomFile.project && jsonPomFile.project.dependencies) {
15
+ dependencies = jsonPomFile.project.dependencies.dependency;
16
+ }
17
+ if (jsonPomFile.project && jsonPomFile.project.dependencyManagement) {
18
+ dependencyManagement =
19
+ jsonPomFile.project.dependencyManagement.dependencies.dependency;
58
20
  }
59
- const retrieveParent = getParentDependency(jsonPomFile);
21
+ const mergedAndFilteredDeps = dependencies.map(obj1 => {
22
+ const obj2 = dependencyManagement.find(obj2 => obj2.groupId === obj1.groupId && obj2.artifactId === obj1.artifactId);
23
+ return obj2 ? { ...obj1, ...obj2 } : obj1;
24
+ });
25
+ buildDependencies(mergedAndFilteredDeps, dependencyTree, jsonPomFile);
60
26
  return {
61
- parentPom: retrieveParent,
27
+ parentPom: getParentDependency(jsonPomFile),
62
28
  dependencyTree
63
29
  };
64
30
  };
65
31
  const getParentDependency = jsonPomFile => {
66
- let parent = {};
67
- jsonPomFile.project.hasOwnProperty('parent')
68
- ? (parent = buildParent(jsonPomFile.project.parent))
69
- : (parent = undefined);
70
- return parent;
32
+ if (jsonPomFile.project && jsonPomFile.project.parent) {
33
+ return buildParent(jsonPomFile.project.parent);
34
+ }
35
+ else {
36
+ return undefined;
37
+ }
71
38
  };
72
39
  const buildParent = parent => {
73
40
  return {
74
- group: parent[0].groupId[0],
75
- name: parent[0].artifactId[0],
76
- version: parent[0].version[0]
41
+ group: parent.groupId,
42
+ name: parent.artifactId,
43
+ version: parent.version
77
44
  };
78
45
  };
79
- const getVersion = (pomFile, dependencyWithoutVersion) => {
80
- let parentVersion = pomFile.project.parent[0].version[0];
81
- let parentGroupName = pomFile.project.parent[0].groupId[0];
82
- if (parentGroupName === dependencyWithoutVersion.groupId[0]) {
83
- return parentVersion;
46
+ const getVersionFromParent = (parentObj, dependencyWithoutVersion) => {
47
+ const { groupId, version } = parentObj;
48
+ if (groupId === dependencyWithoutVersion.groupId) {
49
+ return version;
84
50
  }
85
51
  else {
86
52
  return null;
87
53
  }
88
54
  };
55
+ const getVersionFromProperties = (properties, dep) => {
56
+ if (properties && dep.version.includes('${')) {
57
+ const currentDepVersionPlaceholder = dep.version
58
+ .replace('${', '')
59
+ .replace('}', '');
60
+ for (const prop in properties) {
61
+ if (prop === currentDepVersionPlaceholder) {
62
+ return properties[prop];
63
+ }
64
+ }
65
+ }
66
+ };
67
+ const buildDependencies = (dependencies, dependencyTree, jsonPomFile) => {
68
+ const parent = getParentDependency(jsonPomFile);
69
+ for (const dep of dependencies) {
70
+ const versionAsString = dep.version ? dep.version.toString() : dep.version;
71
+ if (versionAsString && !versionAsString.includes('${')) {
72
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + versionAsString;
73
+ dependencyTree[depName] = buildDep(dep, dep.version);
74
+ }
75
+ else if (jsonPomFile.project.properties &&
76
+ dep.version &&
77
+ dep.version.includes('${')) {
78
+ searchAndBuildFromProperties(jsonPomFile, dep, dependencyTree);
79
+ }
80
+ else if (!dep.version) {
81
+ if (parent && parent.version) {
82
+ const { parent } = jsonPomFile.project;
83
+ const parsedVersion = getVersionFromParent(parent, dep);
84
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + parsedVersion;
85
+ dependencyTree[depName] = buildDep(dep, parsedVersion);
86
+ }
87
+ }
88
+ }
89
+ };
90
+ const searchAndBuildFromProperties = (jsonPomFile, dep, dependencyTree) => {
91
+ const { properties } = jsonPomFile.project;
92
+ let versionFromProperties = getVersionFromProperties(properties, dep);
93
+ if (versionFromProperties) {
94
+ versionFromProperties = versionFromProperties.toString();
95
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + versionFromProperties;
96
+ dependencyTree[depName] = buildDep(dep, versionFromProperties);
97
+ }
98
+ else {
99
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + null;
100
+ dependencyTree[depName] = buildDep(dep, null);
101
+ }
102
+ };
103
+ const buildDep = (dep, version) => {
104
+ return {
105
+ name: dep.artifactId,
106
+ group: dep.groupId,
107
+ version: version,
108
+ directDependency: true,
109
+ productionDependency: true,
110
+ dependencies: []
111
+ };
112
+ };
89
113
  module.exports = {
90
114
  readPomFile,
91
- getVersion,
92
- parsePomFile,
93
- getFromVersionsTag
115
+ getVersionFromParent,
116
+ parsePomFile
94
117
  };
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  const { supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP, DOTNET } } = require('../constants/constants');
3
3
  const { returnOra, startSpinner, succeedSpinner } = require('../utils/oraWrapper');
4
- const autoDetection = require('../scan/autoDetection');
4
+ const autoDetection = require('../common/autoDetection');
5
5
  const rootFile = require('../audit/languageAnalysisEngine/getProjectRootFilenames');
6
6
  const path = require('path');
7
7
  const i18n = require('i18n');
@@ -35,7 +35,7 @@ const processSca = async (config) => {
35
35
  : config.file;
36
36
  filesFound = await autoDetection.autoDetectAuditFilesAndLanguages(config.file);
37
37
  filesFound = await autoDetection.detectPackageManager(filesFound);
38
- autoDetection.dealWithMultiJava(filesFound);
38
+ autoDetection.dealWithMultiJava(filesFound, config, pathWithFile);
39
39
  if (filesFound.length > 1 && pathWithFile) {
40
40
  filesFound = filesFound.filter(i => Object.values(i)[0].includes(path.basename(config.fileName)));
41
41
  }
@@ -10,19 +10,14 @@ const findFile = async () => {
10
10
  onlyFiles: true
11
11
  });
12
12
  };
13
- const findAllFiles = async (filePath, depth = 2) => {
13
+ const findAllFiles = async (filePath, depth = 3) => {
14
14
  const result = await fg([
15
15
  '**/pom.xml',
16
- '**/build.gradle',
17
- '**/build.gradle.kts',
18
16
  '**/package.json',
19
17
  '**/package-lock.json',
20
- '**/yarn.lock',
21
- '**/Pipfile',
22
- '**/*.csproj',
23
- '**/Gemfile',
24
- '**/go.mod'
18
+ '**/yarn.lock'
25
19
  ], {
20
+ ignore: ['**/node_modules/**', '**/target/**', '**/bin/**'],
26
21
  dot: false,
27
22
  deep: depth,
28
23
  onlyFiles: true,
@@ -162,6 +157,15 @@ const fileIsEmpty = path => {
162
157
  }
163
158
  return false;
164
159
  };
160
+ const shortenFilePath = filePath => {
161
+ let splitPath = filePath.split('home/runner/work/');
162
+ if (splitPath.length > 1) {
163
+ let splitPath2 = splitPath[1].split('/');
164
+ splitPath2.shift();
165
+ return splitPath2.join('/').toString();
166
+ }
167
+ return filePath;
168
+ };
165
169
  module.exports = {
166
170
  findFile,
167
171
  fileExists,
@@ -174,5 +178,6 @@ module.exports = {
174
178
  findFilesRuby,
175
179
  findFilesDotNet,
176
180
  fileIsEmpty,
177
- findAllFiles
181
+ findAllFiles,
182
+ shortenFilePath
178
183
  };
@@ -4,7 +4,7 @@ const { returnOra, startSpinner, succeedSpinner, stopSpinner } = require('../uti
4
4
  const populateProjectIdAndProjectName = require('./populateProjectIdAndProjectName');
5
5
  const scan = require('./scan');
6
6
  const scanResults = require('./scanResults');
7
- const autoDetection = require('./autoDetection');
7
+ const autoDetection = require('../common/autoDetection');
8
8
  const fileFunctions = require('./fileUtils');
9
9
  const { performance } = require('perf_hooks');
10
10
  const getTimeout = config => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "2.0.2-beta.0",
3
+ "version": "2.0.2-beta.2",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -61,6 +61,7 @@
61
61
  "cross-spawn": "^7.0.3",
62
62
  "dotenv": "^16.0.0",
63
63
  "fast-glob": "^3.2.11",
64
+ "fast-xml-parser": "^4.2.2",
64
65
  "got": "11.8.6",
65
66
  "gradle-to-js": "^2.0.1",
66
67
  "hpagent": "^1.2.0",
@@ -445,6 +445,7 @@ const fingerprintOptionDefinitions = [
445
445
  {
446
446
  name: 'depth',
447
447
  type: Number,
448
+ default: 3,
448
449
  description:
449
450
  '{bold ' + i18n.__('constantsOptional') + '}: ' + i18n.__('depthOption')
450
451
  },
@@ -1,6 +1,6 @@
1
1
  const fingerprintConfig = require('./fingerprintConfig')
2
2
  const repoServices = require('./repoServices')
3
- const autoDetection = require('../../scan/autoDetection')
3
+ const autoDetection = require('../../common/autoDetection')
4
4
  const saveResults = require('../../scan/saveResults')
5
5
  const projectConfig = require('./projectGroup')
6
6
  const processFingerprint = async (contrastConf, argvMain) => {
@@ -91,10 +91,10 @@ const createProjectsArray = params => {
91
91
  return projectsArray
92
92
  }
93
93
 
94
- const createProject = param => {
94
+ const createProject = (param, shortenedProjectName) => {
95
95
  return {
96
96
  path: param.fileName,
97
- name: param.repo ? param.fileName : getProjectName(param),
97
+ name: param.repo ? shortenedProjectName : getProjectName(param),
98
98
  source: 'SCA',
99
99
  language: param.language,
100
100
  packageManager: param.packageManager,
@@ -143,13 +143,14 @@ const addAdditionalData = (body, data) => {
143
143
  const registerProjectIdOnCliServices = async (
144
144
  config,
145
145
  projectId,
146
+ shortenedProjectName,
146
147
  additionalData = undefined
147
148
  ) => {
148
149
  const client = commonApi.getHttpClient(config)
149
150
 
150
151
  let cliServicesBody = {
151
152
  projectId: projectId,
152
- name: config.repo ? config.fileName : getProjectName(config)
153
+ name: config.repo ? shortenedProjectName : getProjectName(config)
153
154
  }
154
155
 
155
156
  if (additionalData) {
@@ -176,11 +177,14 @@ const registerProjectIdOnCliServices = async (
176
177
  return result
177
178
  }
178
179
 
179
- const registerProjectWithGroupProjectId = async config => {
180
+ const registerProjectWithGroupProjectId = async (
181
+ config,
182
+ shortenedProjectName
183
+ ) => {
180
184
  const client = commonApi.getHttpClient(config)
181
185
  config.language = config.language === 'NODE' ? 'JAVASCRIPT' : config.language
182
186
 
183
- let body = createProject(config)
187
+ let body = createProject(config, shortenedProjectName)
184
188
  let result = await client.registerProject(config, body).then(res => {
185
189
  if (config.debug || config.verbose) {
186
190
  console.log('\nregister Project With Group ProjectId')
@@ -75,7 +75,7 @@ const registerNewRepo = async config => {
75
75
  }
76
76
 
77
77
  const retrieveProjectInfoViaRepoId = async config => {
78
- const client = await commonApi.getHttpClient(config)
78
+ const client = commonApi.getHttpClient(config)
79
79
 
80
80
  let result = await client
81
81
  .retrieveProjectByRepoId(config)
@@ -1,8 +1,10 @@
1
1
  const i18n = require('i18n')
2
- const fileFinder = require('./fileUtils')
2
+ const fileFinder = require('../scan/fileUtils')
3
3
  const {
4
- supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP, DOTNET }
4
+ supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, DOTNET }
5
5
  } = require('../constants/constants')
6
+ const { shortenFilePath } = require('../scan/fileUtils')
7
+
6
8
  const autoDetectFingerprintInfo = async (filePath, depth, config) => {
7
9
  let complexObj = await fileFinder.findAllFiles(filePath, depth)
8
10
  let result = []
@@ -11,6 +13,7 @@ const autoDetectFingerprintInfo = async (filePath, depth, config) => {
11
13
  count++
12
14
  if (!i.includes('package.json')) {
13
15
  result.push({
16
+ name: shortenFilePath(i),
14
17
  filePath: i,
15
18
  id: count.toString(),
16
19
  repositoryId: config.repositoryId,
@@ -18,7 +21,6 @@ const autoDetectFingerprintInfo = async (filePath, depth, config) => {
18
21
  })
19
22
  }
20
23
  })
21
-
22
24
  return result
23
25
  }
24
26
 
@@ -114,15 +116,23 @@ const hasWhiteSpace = s => {
114
116
  return filename.indexOf(' ') >= 0
115
117
  }
116
118
 
117
- const dealWithMultiJava = filesFound => {
118
- let hasMultiJava =
119
- filesFound.filter(data => {
120
- return (
119
+ const dealWithMultiJava = (filesFound, config, isFile) => {
120
+ if (isFile) {
121
+ return multiJavaFilePathFullySpecified(filesFound, config)
122
+ } else {
123
+ return multiJavaNoFilePathFullySpecified(filesFound)
124
+ }
125
+ }
126
+
127
+ const multiJavaNoFilePathFullySpecified = filesFound => {
128
+ const hasMultiJava =
129
+ filesFound.filter(
130
+ data =>
121
131
  Object.keys(data)[0] === JAVA &&
122
132
  Object.values(data)[0].includes('build.gradle') &&
123
133
  Object.values(data)[0].includes('pom.xml')
124
- )
125
- }).length > 0
134
+ ).length > 0
135
+
126
136
  if (hasMultiJava) {
127
137
  console.log('Multiple Java language dependency files detected')
128
138
  console.log(
@@ -130,7 +140,31 @@ const dealWithMultiJava = filesFound => {
130
140
  )
131
141
  process.exit(1)
132
142
  }
133
- return false
143
+
144
+ return filesFound
145
+ }
146
+
147
+ const multiJavaFilePathFullySpecified = (filesFound, config) => {
148
+ const filteredFiles = filesFound[0].JAVA.filter(fileTypes =>
149
+ config.fileName.endsWith(fileTypes)
150
+ )
151
+ filesFound[0].JAVA = filteredFiles
152
+ filesFound[0].filePath = filteredFiles[0]
153
+
154
+ if (filteredFiles[0] === 'pom.xml') {
155
+ filesFound[0].packageManager = 'MAVEN'
156
+ } else {
157
+ filesFound[0].packageManager = 'GRADLE'
158
+ }
159
+
160
+ if (config.debug || config.verbose) {
161
+ console.log('\nAuto detection - detected multiple Java files')
162
+ console.log(
163
+ `\nAuto detection - using ${filesFound[0].filePath} as based on full file path`
164
+ )
165
+ }
166
+
167
+ return filesFound
134
168
  }
135
169
 
136
170
  const errorOnFileDetection = entries => {
@@ -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 = '2.0.2-beta.0'
17
+ const APP_VERSION = '2.0.2-beta.2'
18
18
  const TIMEOUT = 120000
19
19
  const HIGH_COLOUR = '#ff9900'
20
20
  const CRITICAL_COLOUR = '#e35858'
@@ -28,6 +28,10 @@ const scaTreeUpload = async (analysis, config, reportSpinner) => {
28
28
  }
29
29
  }
30
30
 
31
+ if (config.debug || config.verbose) {
32
+ console.log('requestBody', requestBody)
33
+ }
34
+
31
35
  if (config.branch) {
32
36
  requestBody.branchName = config.branch
33
37
  }
@@ -1,6 +1,7 @@
1
1
  const projectConfig = require('../commands/github/projectGroup')
2
2
  const repoService = require('../commands/github/repoServices')
3
3
  const scaServicesUpload = require('../scaAnalysis/common/scaServicesUpload')
4
+ const { shortenFilePath } = require('../scan/fileUtils')
4
5
 
5
6
  const dealWithNoProjectId = async (analysis, config, reportSpinner) => {
6
7
  await projectConfig.registerNewProjectGroup(config)
@@ -20,12 +21,14 @@ const repoProcess = async (analysis, config, reportSpinner) => {
20
21
  process.exit(1)
21
22
  }
22
23
 
24
+ let shortenedProjectName = shortenFilePath(config.fileName)
25
+
23
26
  let repoInfo = await repoService.retrieveProjectInfoViaRepoId(config)
24
27
 
25
28
  repoInfo = repoInfo.find(
26
29
  element =>
27
30
  config.fileName === element.path &&
28
- config.fileName === element.name &&
31
+ shortenedProjectName === element.name &&
29
32
  config.projectGroupId === element.projectGroupId
30
33
  )
31
34
 
@@ -57,7 +60,10 @@ const repoProcess = async (analysis, config, reportSpinner) => {
57
60
  console.log(
58
61
  '*** has projectGroupId, no projectId and repo has no project found that matches'
59
62
  )
60
- repoInfo = await projectConfig.registerProjectWithGroupProjectId(config)
63
+ repoInfo = await projectConfig.registerProjectWithGroupProjectId(
64
+ config,
65
+ shortenedProjectName
66
+ )
61
67
  console.log('new registered group', repoInfo)
62
68
  const language =
63
69
  repoInfo.language === 'JAVASCRIPT' ? 'NODE' : repoInfo.language
@@ -71,7 +77,8 @@ const repoProcess = async (analysis, config, reportSpinner) => {
71
77
 
72
78
  await projectConfig.registerProjectIdOnCliServices(
73
79
  config,
74
- repoInfo.projectId
80
+ repoInfo.projectId,
81
+ shortenedProjectName
75
82
  )
76
83
  }
77
84
  config.projectId = repoInfo.projectId
@@ -1,112 +1,138 @@
1
1
  const fs = require('fs')
2
- const xml2js = require('xml2js')
2
+ const { XMLParser } = require('fast-xml-parser')
3
3
 
4
4
  const readPomFile = project => {
5
5
  const mavenFilePath = project.cwd + '/pom.xml'
6
6
  const projectFile = fs.readFileSync(mavenFilePath)
7
- let jsonPomFile
8
- xml2js.parseString(projectFile, (err, result) => {
9
- if (err) {
10
- throw err
11
- }
12
- const json = JSON.stringify(result, null)
13
- jsonPomFile = JSON.parse(json)
14
- })
15
- return jsonPomFile
16
- }
17
-
18
- const getFromVersionsTag = (dependencyName, versionIdentifier, jsonPomFile) => {
19
- // reading:
20
- // <!-- DEPENDENCY VERSIONS -->
21
- // <versions.animal-sniffer>1.16</versions.animal-sniffer>
22
- let formattedVersion = versionIdentifier.replace(/[{}]/g, '').replace('$', '')
23
-
24
- if (jsonPomFile.project.properties[0].hasOwnProperty([formattedVersion])) {
25
- return jsonPomFile.project.properties[0][formattedVersion][0]
26
- } else {
27
- return null
28
- }
7
+ const parser = new XMLParser()
8
+ return parser.parse(projectFile)
29
9
  }
30
10
 
31
11
  const parsePomFile = jsonPomFile => {
32
12
  let dependencyTree = {}
33
- let parsedVersion
34
- let dependencies
35
-
36
- jsonPomFile.project.hasOwnProperty('dependencies')
37
- ? (dependencies = jsonPomFile.project.dependencies[0].dependency)
38
- : (dependencies =
39
- jsonPomFile.project.dependencyManagement[0].dependencies[0].dependency)
40
-
41
- for (let x in dependencies) {
42
- let dependencyObject = dependencies[x]
43
- if (!dependencyObject.hasOwnProperty('version')) {
44
- parsedVersion = getVersion(jsonPomFile, dependencyObject)
45
- } else {
46
- dependencyObject.version[0].includes('${versions.')
47
- ? (parsedVersion = getFromVersionsTag(
48
- dependencyObject.artifactId[0],
49
- dependencyObject.version[0],
50
- jsonPomFile
51
- ))
52
- : (parsedVersion = dependencyObject.version[0])
53
- }
13
+ let dependencies = []
14
+ let dependencyManagement = []
54
15
 
55
- let depName =
56
- dependencyObject.groupId +
57
- '/' +
58
- dependencyObject.artifactId +
59
- '@' +
60
- parsedVersion
61
-
62
- let parsedDependency = {
63
- name: dependencyObject.artifactId[0],
64
- group: dependencyObject.groupId[0],
65
- version: parsedVersion,
66
- directDependency: true,
67
- productionDependency: true,
68
- dependencies: []
69
- }
70
- dependencyTree[depName] = parsedDependency
16
+ if (jsonPomFile.project && jsonPomFile.project.dependencies) {
17
+ dependencies = jsonPomFile.project.dependencies.dependency
18
+ }
19
+
20
+ if (jsonPomFile.project && jsonPomFile.project.dependencyManagement) {
21
+ dependencyManagement =
22
+ jsonPomFile.project.dependencyManagement.dependencies.dependency
71
23
  }
72
24
 
73
- const retrieveParent = getParentDependency(jsonPomFile)
25
+ //merge dependencies with dependencyManagement deps
26
+ //filter out any that don't appear in both by groupId and artifactId
27
+ const mergedAndFilteredDeps = dependencies.map(obj1 => {
28
+ const obj2 = dependencyManagement.find(
29
+ obj2 =>
30
+ obj2.groupId === obj1.groupId && obj2.artifactId === obj1.artifactId
31
+ )
32
+ return obj2 ? { ...obj1, ...obj2 } : obj1
33
+ })
74
34
 
35
+ buildDependencies(mergedAndFilteredDeps, dependencyTree, jsonPomFile)
75
36
  return {
76
- parentPom: retrieveParent,
37
+ parentPom: getParentDependency(jsonPomFile),
77
38
  dependencyTree
78
39
  }
79
40
  }
80
41
 
81
42
  const getParentDependency = jsonPomFile => {
82
- let parent = {}
83
- jsonPomFile.project.hasOwnProperty('parent')
84
- ? (parent = buildParent(jsonPomFile.project.parent))
85
- : (parent = undefined)
86
- return parent
43
+ if (jsonPomFile.project && jsonPomFile.project.parent) {
44
+ return buildParent(jsonPomFile.project.parent)
45
+ } else {
46
+ return undefined
47
+ }
87
48
  }
88
49
 
89
50
  const buildParent = parent => {
90
51
  return {
91
- group: parent[0].groupId[0],
92
- name: parent[0].artifactId[0],
93
- version: parent[0].version[0]
52
+ group: parent.groupId,
53
+ name: parent.artifactId,
54
+ version: parent.version
94
55
  }
95
56
  }
96
57
 
97
- const getVersion = (pomFile, dependencyWithoutVersion) => {
98
- let parentVersion = pomFile.project.parent[0].version[0]
99
- let parentGroupName = pomFile.project.parent[0].groupId[0]
100
- if (parentGroupName === dependencyWithoutVersion.groupId[0]) {
101
- return parentVersion
58
+ const getVersionFromParent = (parentObj, dependencyWithoutVersion) => {
59
+ const { groupId, version } = parentObj
60
+ if (groupId === dependencyWithoutVersion.groupId) {
61
+ return version
102
62
  } else {
103
63
  return null
104
64
  }
105
65
  }
106
66
 
67
+ const getVersionFromProperties = (properties, dep) => {
68
+ if (properties && dep.version.includes('${')) {
69
+ const currentDepVersionPlaceholder = dep.version
70
+ .replace('${', '')
71
+ .replace('}', '')
72
+
73
+ for (const prop in properties) {
74
+ if (prop === currentDepVersionPlaceholder) {
75
+ return properties[prop]
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ const buildDependencies = (dependencies, dependencyTree, jsonPomFile) => {
82
+ const parent = getParentDependency(jsonPomFile)
83
+ for (const dep of dependencies) {
84
+ //sometimes versions are parsed as numbers, convert to string
85
+ const versionAsString = dep.version ? dep.version.toString() : dep.version
86
+ if (versionAsString && !versionAsString.includes('${')) {
87
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + versionAsString
88
+ dependencyTree[depName] = buildDep(dep, dep.version)
89
+ } else if (
90
+ jsonPomFile.project.properties &&
91
+ dep.version &&
92
+ dep.version.includes('${')
93
+ ) {
94
+ searchAndBuildFromProperties(jsonPomFile, dep, dependencyTree)
95
+ } else if (!dep.version) {
96
+ if (parent && parent.version) {
97
+ //get version where group matches from parent tag
98
+ const { parent } = jsonPomFile.project
99
+ const parsedVersion = getVersionFromParent(parent, dep)
100
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + parsedVersion
101
+ dependencyTree[depName] = buildDep(dep, parsedVersion)
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ const searchAndBuildFromProperties = (jsonPomFile, dep, dependencyTree) => {
108
+ //get version from properties tag
109
+ const { properties } = jsonPomFile.project
110
+ let versionFromProperties = getVersionFromProperties(properties, dep)
111
+
112
+ if (versionFromProperties) {
113
+ versionFromProperties = versionFromProperties.toString()
114
+ const depName =
115
+ dep.groupId + '/' + dep.artifactId + '@' + versionFromProperties
116
+ dependencyTree[depName] = buildDep(dep, versionFromProperties)
117
+ } else {
118
+ const depName = dep.groupId + '/' + dep.artifactId + '@' + null
119
+ dependencyTree[depName] = buildDep(dep, null)
120
+ }
121
+ }
122
+
123
+ const buildDep = (dep, version) => {
124
+ return {
125
+ name: dep.artifactId,
126
+ group: dep.groupId,
127
+ version: version,
128
+ directDependency: true,
129
+ productionDependency: true,
130
+ dependencies: []
131
+ }
132
+ }
133
+
107
134
  module.exports = {
108
135
  readPomFile,
109
- getVersion,
110
- parsePomFile,
111
- getFromVersionsTag
136
+ getVersionFromParent,
137
+ parsePomFile
112
138
  }
@@ -6,7 +6,7 @@ const {
6
6
  startSpinner,
7
7
  succeedSpinner
8
8
  } = require('../utils/oraWrapper')
9
- const autoDetection = require('../scan/autoDetection')
9
+ const autoDetection = require('../common/autoDetection')
10
10
  const rootFile = require('../audit/languageAnalysisEngine/getProjectRootFilenames')
11
11
  const path = require('path')
12
12
  const i18n = require('i18n')
@@ -48,7 +48,8 @@ const processSca = async config => {
48
48
 
49
49
  filesFound = await autoDetection.autoDetectAuditFilesAndLanguages(config.file)
50
50
  filesFound = await autoDetection.detectPackageManager(filesFound)
51
- autoDetection.dealWithMultiJava(filesFound)
51
+
52
+ autoDetection.dealWithMultiJava(filesFound, config, pathWithFile)
52
53
 
53
54
  if (filesFound.length > 1 && pathWithFile) {
54
55
  filesFound = filesFound.filter(i =>
@@ -11,21 +11,22 @@ const findFile = async () => {
11
11
  })
12
12
  }
13
13
 
14
- const findAllFiles = async (filePath, depth = 2) => {
14
+ const findAllFiles = async (filePath, depth = 3) => {
15
15
  const result = await fg(
16
16
  [
17
17
  '**/pom.xml',
18
- '**/build.gradle',
19
- '**/build.gradle.kts',
18
+ // '**/build.gradle',
19
+ // '**/build.gradle.kts',
20
20
  '**/package.json',
21
21
  '**/package-lock.json',
22
- '**/yarn.lock',
23
- '**/Pipfile',
24
- '**/*.csproj',
25
- '**/Gemfile',
26
- '**/go.mod'
22
+ '**/yarn.lock'
23
+ // '**/Pipfile',
24
+ // '**/*.csproj',
25
+ // '**/Gemfile',
26
+ // '**/go.mod'
27
27
  ],
28
28
  {
29
+ ignore: ['**/node_modules/**', '**/target/**', '**/bin/**'],
29
30
  dot: false,
30
31
  deep: depth,
31
32
  onlyFiles: true,
@@ -190,6 +191,16 @@ const fileIsEmpty = path => {
190
191
  return false
191
192
  }
192
193
 
194
+ const shortenFilePath = filePath => {
195
+ let splitPath = filePath.split('home/runner/work/')
196
+ if (splitPath.length > 1) {
197
+ let splitPath2 = splitPath[1].split('/')
198
+ splitPath2.shift()
199
+ return splitPath2.join('/').toString()
200
+ }
201
+ return filePath
202
+ }
203
+
193
204
  module.exports = {
194
205
  findFile,
195
206
  fileExists,
@@ -202,5 +213,6 @@ module.exports = {
202
213
  findFilesRuby,
203
214
  findFilesDotNet,
204
215
  fileIsEmpty,
205
- findAllFiles
216
+ findAllFiles,
217
+ shortenFilePath
206
218
  }
@@ -8,7 +8,7 @@ const {
8
8
  const populateProjectIdAndProjectName = require('./populateProjectIdAndProjectName')
9
9
  const scan = require('./scan')
10
10
  const scanResults = require('./scanResults')
11
- const autoDetection = require('./autoDetection')
11
+ const autoDetection = require('../common/autoDetection')
12
12
  const fileFunctions = require('./fileUtils')
13
13
  const { performance } = require('perf_hooks')
14
14