@contrast/contrast 1.0.10 → 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.
- package/README.md +1 -1
- package/dist/audit/{languageAnalysisEngine/report → report}/commonReportingFunctions.js +56 -35
- package/dist/audit/report/models/reportGuidanceModel.js +6 -0
- package/dist/audit/{languageAnalysisEngine/report → report}/models/reportLibraryModel.js +0 -0
- package/dist/audit/{languageAnalysisEngine/report → report}/models/reportListModel.js +0 -0
- package/dist/audit/{languageAnalysisEngine/report → report}/models/reportOutputModel.js +1 -2
- package/dist/audit/{languageAnalysisEngine/report → report}/models/reportSeverityModel.js +0 -0
- package/dist/audit/{languageAnalysisEngine/report → report}/models/severityCountModel.js +1 -0
- package/dist/audit/{languageAnalysisEngine/report → report}/reportingFeature.js +12 -8
- package/dist/audit/{languageAnalysisEngine/report → report}/utils/reportUtils.js +3 -4
- package/dist/commands/audit/auditConfig.js +3 -3
- package/dist/commands/audit/help.js +3 -1
- package/dist/commands/audit/processAudit.js +14 -2
- package/dist/commands/auth/auth.js +1 -1
- package/dist/commands/config/config.js +2 -2
- package/dist/commands/scan/processScan.js +20 -4
- package/dist/commands/scan/sca/scaAnalysis.js +15 -5
- package/dist/common/HTTPClient.js +39 -2
- package/dist/common/commonHelp.js +19 -0
- package/dist/common/fail.js +70 -0
- package/dist/common/versionChecker.js +14 -6
- package/dist/constants/constants.js +2 -2
- package/dist/constants/locales.js +15 -5
- package/dist/constants.js +42 -5
- package/dist/index.js +6 -3
- package/dist/lambda/help.js +2 -3
- package/dist/lambda/lambda.js +7 -0
- package/dist/scaAnalysis/common/scaParserForGoAndJava.js +32 -0
- package/dist/scaAnalysis/common/scaServicesUpload.js +52 -0
- package/dist/scaAnalysis/common/treeUpload.js +20 -5
- package/dist/scaAnalysis/dotnet/analysis.js +15 -3
- package/dist/scaAnalysis/go/goAnalysis.js +8 -2
- package/dist/scaAnalysis/java/analysis.js +10 -6
- package/dist/scaAnalysis/java/index.js +7 -1
- package/dist/scaAnalysis/java/javaBuildDepsParser.js +19 -3
- package/dist/scaAnalysis/javascript/index.js +4 -0
- package/dist/scaAnalysis/javascript/scaServiceParser.js +109 -0
- package/dist/scaAnalysis/php/analysis.js +1 -1
- package/dist/scaAnalysis/php/index.js +12 -6
- package/dist/scaAnalysis/php/phpNewServicesMapper.js +62 -0
- package/dist/scaAnalysis/python/analysis.js +43 -5
- package/dist/scaAnalysis/python/index.js +7 -2
- package/dist/scaAnalysis/ruby/analysis.js +116 -9
- package/dist/scaAnalysis/ruby/index.js +6 -1
- package/dist/scan/formatScanOutput.js +6 -5
- package/dist/scan/help.js +2 -3
- package/dist/scan/populateProjectIdAndProjectName.js +5 -0
- package/dist/scan/scan.js +4 -0
- package/dist/scan/scanConfig.js +4 -4
- package/dist/scan/scanResults.js +46 -3
- package/dist/telemetry/telemetry.js +137 -0
- package/dist/{audit/languageAnalysisEngine/util → utils}/capabilities.js +0 -0
- package/dist/{audit/languageAnalysisEngine/util → utils}/generalAPI.js +14 -5
- package/dist/utils/getConfig.js +2 -4
- package/dist/utils/parsedCLIOptions.js +3 -1
- package/dist/utils/requestUtils.js +7 -1
- package/package.json +4 -2
- package/src/audit/{languageAnalysisEngine/report → report}/commonReportingFunctions.ts +80 -44
- package/src/audit/report/models/reportGuidanceModel.ts +5 -0
- package/src/audit/{languageAnalysisEngine/report → report}/models/reportLibraryModel.ts +0 -0
- package/src/audit/{languageAnalysisEngine/report → report}/models/reportListModel.ts +0 -0
- package/src/audit/{languageAnalysisEngine/report → report}/models/reportOutputModel.ts +1 -7
- package/src/audit/{languageAnalysisEngine/report → report}/models/reportSeverityModel.ts +0 -0
- package/src/audit/{languageAnalysisEngine/report → report}/models/severityCountModel.ts +2 -0
- package/src/audit/{languageAnalysisEngine/report → report}/reportingFeature.ts +16 -9
- package/src/audit/{languageAnalysisEngine/report → report}/utils/reportUtils.ts +4 -4
- package/src/commands/audit/auditConfig.ts +10 -3
- package/src/commands/audit/help.ts +3 -1
- package/src/commands/audit/processAudit.ts +24 -2
- package/src/commands/auth/auth.js +3 -1
- package/src/commands/config/config.js +4 -2
- package/src/commands/scan/processScan.js +32 -4
- package/src/commands/scan/sca/scaAnalysis.js +23 -5
- package/src/common/HTTPClient.js +59 -2
- package/src/common/commonHelp.ts +13 -0
- package/src/common/fail.js +79 -0
- package/src/common/versionChecker.ts +18 -8
- package/src/constants/constants.js +2 -2
- package/src/constants/locales.js +19 -7
- package/src/constants.js +46 -6
- package/src/index.ts +18 -4
- package/src/lambda/help.ts +2 -3
- package/src/lambda/lambda.ts +12 -0
- package/src/scaAnalysis/common/scaParserForGoAndJava.js +41 -0
- package/src/scaAnalysis/common/scaServicesUpload.js +54 -0
- package/src/scaAnalysis/common/treeUpload.js +21 -5
- package/src/scaAnalysis/dotnet/analysis.js +21 -3
- package/src/scaAnalysis/go/goAnalysis.js +9 -2
- package/src/scaAnalysis/java/analysis.js +11 -6
- package/src/scaAnalysis/java/index.js +9 -1
- package/src/scaAnalysis/java/javaBuildDepsParser.js +25 -6
- package/src/scaAnalysis/javascript/index.js +4 -0
- package/src/scaAnalysis/javascript/scaServiceParser.js +145 -0
- package/src/scaAnalysis/php/analysis.js +1 -1
- package/src/scaAnalysis/php/index.js +12 -6
- package/src/scaAnalysis/php/phpNewServicesMapper.js +77 -0
- package/src/scaAnalysis/python/analysis.js +49 -5
- package/src/scaAnalysis/python/index.js +7 -2
- package/src/scaAnalysis/ruby/analysis.js +149 -9
- package/src/scaAnalysis/ruby/index.js +6 -1
- package/src/scan/formatScanOutput.ts +7 -5
- package/src/scan/help.js +2 -3
- package/src/scan/populateProjectIdAndProjectName.js +5 -1
- package/src/scan/scan.ts +4 -0
- package/src/scan/scanConfig.js +6 -4
- package/src/scan/scanResults.js +52 -3
- package/src/telemetry/telemetry.ts +154 -0
- package/src/{audit/languageAnalysisEngine/util → utils}/capabilities.js +0 -0
- package/src/{audit/languageAnalysisEngine/util → utils}/generalAPI.js +16 -6
- package/src/utils/getConfig.ts +2 -11
- package/src/utils/parsedCLIOptions.js +14 -1
- package/src/utils/requestUtils.js +8 -1
|
@@ -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
|
+
};
|
|
@@ -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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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 =
|
|
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
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const multiReplace = require('string-multiple-replace');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const i18n = require('i18n');
|
|
4
5
|
const readAndParseProjectFile = file => {
|
|
5
6
|
const filePath = filePathForWindows(file + '/Pipfile');
|
|
6
7
|
const pipFile = fs.readFileSync(filePath, 'utf8');
|
|
@@ -18,11 +19,45 @@ const readAndParseLockFile = file => {
|
|
|
18
19
|
delete parsedPipLock['default'];
|
|
19
20
|
return parsedPipLock;
|
|
20
21
|
};
|
|
21
|
-
const
|
|
22
|
+
const readLockFile = file => {
|
|
23
|
+
const filePath = filePathForWindows(file + '/Pipfile.lock');
|
|
24
|
+
const lockFile = fs.readFileSync(filePath, 'utf8');
|
|
25
|
+
let parsedPipLock = JSON.parse(lockFile);
|
|
26
|
+
return parsedPipLock['default'];
|
|
27
|
+
};
|
|
28
|
+
const scaPythonParser = pythonDependencies => {
|
|
29
|
+
let pythonParsedDeps = {};
|
|
30
|
+
for (let key in pythonDependencies) {
|
|
31
|
+
pythonParsedDeps[key] = {};
|
|
32
|
+
pythonParsedDeps[key].version = pythonDependencies[key].version.replace('==', '');
|
|
33
|
+
pythonParsedDeps[key].group = null;
|
|
34
|
+
pythonParsedDeps[key].name = key;
|
|
35
|
+
pythonParsedDeps[key].isProduction = true;
|
|
36
|
+
pythonParsedDeps[key].dependencies = [];
|
|
37
|
+
pythonParsedDeps[key].directDependency = true;
|
|
38
|
+
}
|
|
39
|
+
return pythonParsedDeps;
|
|
40
|
+
};
|
|
41
|
+
const checkForCorrectFiles = languageFiles => {
|
|
42
|
+
if (!languageFiles.includes('Pipfile.lock')) {
|
|
43
|
+
throw new Error(i18n.__('languageAnalysisHasNoLockFile', 'python'));
|
|
44
|
+
}
|
|
45
|
+
if (!languageFiles.includes('Pipfile')) {
|
|
46
|
+
throw new Error(i18n.__('languageAnalysisProjectFileError', 'python'));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const getPythonDeps = (config, languageFiles) => {
|
|
22
50
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
51
|
+
if (config.experimental) {
|
|
52
|
+
let pythonLockFileContents = readLockFile(config.file);
|
|
53
|
+
return scaPythonParser(pythonLockFileContents);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
checkForCorrectFiles(languageFiles);
|
|
57
|
+
const parseProject = readAndParseProjectFile(config.file);
|
|
58
|
+
const parsePip = readAndParseLockFile(config.file);
|
|
59
|
+
return { pipfileLock: parsePip, pipfilDependanceies: parseProject };
|
|
60
|
+
}
|
|
26
61
|
}
|
|
27
62
|
catch (err) {
|
|
28
63
|
console.log(err.message.toString());
|
|
@@ -37,6 +72,9 @@ const filePathForWindows = path => {
|
|
|
37
72
|
};
|
|
38
73
|
module.exports = {
|
|
39
74
|
getPythonDeps,
|
|
75
|
+
scaPythonParser,
|
|
76
|
+
readAndParseLockFile,
|
|
40
77
|
readAndParseProjectFile,
|
|
41
|
-
|
|
78
|
+
checkForCorrectFiles,
|
|
79
|
+
readLockFile
|
|
42
80
|
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const { createPythonTSMessage } = require('../common/formatMessage');
|
|
3
|
-
const { getPythonDeps } = require('./analysis');
|
|
3
|
+
const { getPythonDeps, secondaryParser } = require('./analysis');
|
|
4
4
|
const pythonAnalysis = (config, languageFiles) => {
|
|
5
5
|
const pythonDeps = getPythonDeps(config, languageFiles.PYTHON);
|
|
6
|
-
|
|
6
|
+
if (config.experimental) {
|
|
7
|
+
return pythonDeps;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
return createPythonTSMessage(pythonDeps);
|
|
11
|
+
}
|
|
7
12
|
};
|
|
8
13
|
module.exports = {
|
|
9
14
|
pythonAnalysis
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const fs = require('fs');
|
|
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
|
+
};
|
|
3
24
|
const readAndParseGemfile = file => {
|
|
4
25
|
const gemFile = fs.readFileSync(file + '/Gemfile', 'utf8');
|
|
5
26
|
const rubyArray = gemFile.split('\n');
|
|
@@ -188,15 +209,96 @@ const buildSourceDependencyWithVersion = (whitespaceRegx, dependencyRegEx, line,
|
|
|
188
209
|
}
|
|
189
210
|
return dependencies;
|
|
190
211
|
};
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
+
}
|
|
196
225
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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;
|
|
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
|
+
});
|
|
295
|
+
};
|
|
296
|
+
const checkForCorrectFiles = languageFiles => {
|
|
297
|
+
if (!languageFiles.includes('Gemfile.lock')) {
|
|
298
|
+
throw new Error(i18n.__('languageAnalysisHasNoLockFile', 'ruby'));
|
|
299
|
+
}
|
|
300
|
+
if (!languageFiles.includes('Gemfile')) {
|
|
301
|
+
throw new Error(i18n.__('languageAnalysisProjectFileError', 'ruby'));
|
|
200
302
|
}
|
|
201
303
|
};
|
|
202
304
|
const trimWhiteSpace = string => {
|
|
@@ -214,5 +316,10 @@ module.exports = {
|
|
|
214
316
|
getVersion,
|
|
215
317
|
getPatchLevel,
|
|
216
318
|
formatSourceArr,
|
|
217
|
-
getSourceArray
|
|
319
|
+
getSourceArray,
|
|
320
|
+
checkForCorrectFiles,
|
|
321
|
+
removeRedundantAndPopulateDefinedElements,
|
|
322
|
+
createRubyTree,
|
|
323
|
+
findChildrenDependencies,
|
|
324
|
+
processRootDependencies
|
|
218
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
|
-
|
|
6
|
+
if (config.experimental) {
|
|
7
|
+
return rubyDeps;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
return createRubyTSMessage(rubyDeps);
|
|
11
|
+
}
|
|
7
12
|
};
|
|
8
13
|
module.exports = {
|
|
9
14
|
rubyAnalysis
|
|
@@ -53,12 +53,12 @@ function formatScanOutput(scanResults) {
|
|
|
53
53
|
});
|
|
54
54
|
let learnRow = [];
|
|
55
55
|
let adviceRow = [];
|
|
56
|
+
const headerColour = chalk_1.default.hex(entry.colour);
|
|
56
57
|
const headerRow = [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
chalk_1.default.hex(entry.colour).bold(`[${entry.severity}] ${entry.ruleId}`) +
|
|
58
|
+
headerColour(`CONTRAST-${count.toString().padStart(3, '0')}`),
|
|
59
|
+
headerColour(`-`),
|
|
60
|
+
headerColour(`[${entry.severity}] `) +
|
|
61
|
+
headerColour.bold(`${entry.ruleId}`) +
|
|
62
62
|
entry.message
|
|
63
63
|
];
|
|
64
64
|
const codePath = entry.codePath?.replace(/^@/, '');
|
|
@@ -90,6 +90,7 @@ function formatScanOutput(scanResults) {
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
printVulnInfo(projectOverview);
|
|
93
|
+
return projectOverview;
|
|
93
94
|
}
|
|
94
95
|
exports.formatScanOutput = formatScanOutput;
|
|
95
96
|
function printVulnInfo(projectOverview) {
|
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
|
|
@@ -25,6 +25,11 @@ const createProjectId = async (config, client) => {
|
|
|
25
25
|
process.exit(1);
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
+
if (res.statusCode === 429) {
|
|
29
|
+
console.log(i18n.__('exceededFreeTier'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
28
33
|
if (res.statusCode === 201) {
|
|
29
34
|
console.log(i18n.__('projectCreatedScan'));
|
|
30
35
|
if (config.verbose) {
|
package/dist/scan/scan.js
CHANGED
|
@@ -45,6 +45,10 @@ const sendScan = async (config) => {
|
|
|
45
45
|
oraWrapper_1.default.failSpinner(startUploadSpinner, i18n_1.default.__('uploadingScanFail'));
|
|
46
46
|
console.log(i18n_1.default.__('genericServiceError', res.statusCode));
|
|
47
47
|
}
|
|
48
|
+
if (res.statusCode === 429) {
|
|
49
|
+
console.log(i18n_1.default.__('exceededFreeTier'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
48
52
|
if (res.statusCode === 403) {
|
|
49
53
|
console.log(i18n_1.default.__('permissionsError'));
|
|
50
54
|
process.exit(1);
|
package/dist/scan/scanConfig.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const paramHandler = require('../utils/paramsUtil/paramHandler');
|
|
3
|
-
const constants = require('
|
|
4
|
-
const parsedCLIOptions = require('../../src/utils/parsedCLIOptions');
|
|
3
|
+
const constants = require('../constants.js');
|
|
5
4
|
const path = require('path');
|
|
6
5
|
const { supportedLanguagesScan } = require('../constants/constants');
|
|
7
6
|
const i18n = require('i18n');
|
|
8
7
|
const { scanUsageGuide } = require('./help');
|
|
9
|
-
const
|
|
10
|
-
|
|
8
|
+
const parsedCLIOptions = require('../utils/parsedCLIOptions');
|
|
9
|
+
const getScanConfig = async (contrastConf, command, argv) => {
|
|
10
|
+
let scanParams = await parsedCLIOptions.getCommandLineArgsCustom(contrastConf, command, argv, constants.commandLineDefinitions.scanOptionDefinitions);
|
|
11
11
|
if (scanParams.help) {
|
|
12
12
|
printHelpMessage();
|
|
13
13
|
process.exit(0);
|
package/dist/scan/scanResults.js
CHANGED
|
@@ -5,10 +5,14 @@ const oraFunctions = require('../utils/oraWrapper');
|
|
|
5
5
|
const _ = require('lodash');
|
|
6
6
|
const i18n = require('i18n');
|
|
7
7
|
const oraWrapper = require('../utils/oraWrapper');
|
|
8
|
+
const readLine = require('readline');
|
|
8
9
|
const getScanId = async (config, codeArtifactId, client) => {
|
|
9
10
|
return client
|
|
10
11
|
.getScanId(config, codeArtifactId)
|
|
11
12
|
.then(res => {
|
|
13
|
+
if (res.statusCode == 429) {
|
|
14
|
+
throw new Error(i18n.__('exceededFreeTier'));
|
|
15
|
+
}
|
|
12
16
|
return res.body.id;
|
|
13
17
|
})
|
|
14
18
|
.catch(err => {
|
|
@@ -61,12 +65,50 @@ const returnScanResults = async (config, codeArtifactId, newProject, timeout, st
|
|
|
61
65
|
let endTime = new Date() - startTime;
|
|
62
66
|
if (requestUtils.millisToSeconds(endTime) > timeout) {
|
|
63
67
|
oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan timed out at the specified ' + timeout + ' seconds.');
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
const isCI = process.env.CONTRAST_CODESEC_CI
|
|
69
|
+
? JSON.parse(process.env.CONTRAST_CODESEC_CI.toLowerCase())
|
|
70
|
+
: false;
|
|
71
|
+
if (!isCI) {
|
|
72
|
+
const retry = await retryScanPrompt();
|
|
73
|
+
timeout = retry.timeout;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log('Please try again, allowing more time');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
66
79
|
}
|
|
67
80
|
}
|
|
68
81
|
}
|
|
69
82
|
};
|
|
83
|
+
const retryScanPrompt = async () => {
|
|
84
|
+
const rl = readLine.createInterface({
|
|
85
|
+
input: process.stdin,
|
|
86
|
+
output: process.stdout
|
|
87
|
+
});
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
requestUtils.timeOutError(30000, reject);
|
|
90
|
+
rl.question('🔁 Do you want to continue waiting on Scan? [Y/N]\n', async (input) => {
|
|
91
|
+
if (input.toLowerCase() === 'yes' || input.toLowerCase() === 'y') {
|
|
92
|
+
console.log('Continuing wait for Scan');
|
|
93
|
+
rl.close();
|
|
94
|
+
resolve({ timeout: 300 });
|
|
95
|
+
}
|
|
96
|
+
else if (input.toLowerCase() === 'no' ||
|
|
97
|
+
input.toLowerCase() === 'n') {
|
|
98
|
+
rl.close();
|
|
99
|
+
console.log('Contrast Scan Retry Cancelled: Exiting');
|
|
100
|
+
resolve(process.exit(1));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
rl.close();
|
|
104
|
+
console.log('Invalid Input: Exiting');
|
|
105
|
+
resolve(process.exit(1));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}).catch(e => {
|
|
109
|
+
throw e;
|
|
110
|
+
});
|
|
111
|
+
};
|
|
70
112
|
const returnScanResultsInstances = async (config, scanId) => {
|
|
71
113
|
const client = commonApi.getHttpClient(config);
|
|
72
114
|
let result;
|
|
@@ -89,5 +131,6 @@ module.exports = {
|
|
|
89
131
|
getScanId: getScanId,
|
|
90
132
|
returnScanResults: returnScanResults,
|
|
91
133
|
pollScanResults: pollScanResults,
|
|
92
|
-
returnScanResultsInstances: returnScanResultsInstances
|
|
134
|
+
returnScanResultsInstances: returnScanResultsInstances,
|
|
135
|
+
retryScanPrompt
|
|
93
136
|
};
|