@contrast/contrast 1.0.6 → 1.0.7
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/.prettierignore +0 -6
- package/dist/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +4 -2
- package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
- package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
- package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
- package/dist/audit/languageAnalysisEngine/languageAnalysisFactory.js +2 -0
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +10 -1
- package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +6 -9
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +65 -3
- package/dist/commands/audit/processAudit.js +1 -1
- package/dist/commands/scan/sca/scaAnalysis.js +13 -2
- package/dist/common/HTTPClient.js +50 -15
- package/dist/common/errorHandling.js +6 -1
- package/dist/common/versionChecker.js +1 -1
- package/dist/constants/constants.js +1 -1
- package/dist/constants/locales.js +3 -1
- package/dist/lambda/analytics.js +11 -0
- package/dist/lambda/lambda.js +35 -4
- package/dist/lambda/types.js +13 -0
- package/dist/scaAnalysis/common/formatMessage.js +17 -1
- package/dist/scaAnalysis/java/analysis.js +3 -6
- package/dist/scaAnalysis/java/index.js +2 -2
- package/dist/scaAnalysis/python/analysis.js +41 -0
- package/dist/scaAnalysis/python/index.js +10 -0
- package/dist/scaAnalysis/ruby/analysis.js +226 -0
- package/dist/scaAnalysis/ruby/index.js +10 -0
- package/dist/scan/autoDetection.js +6 -2
- package/dist/scan/fileUtils.js +14 -7
- package/dist/scan/formatScanOutput.js +9 -11
- package/dist/scan/models/groupedResultsModel.js +1 -1
- package/dist/scan/models/scanResultsModel.js +3 -1
- package/dist/scan/populateProjectIdAndProjectName.js +2 -1
- package/dist/scan/scan.js +1 -0
- package/dist/scan/scanConfig.js +6 -1
- package/dist/scan/scanController.js +16 -3
- package/dist/scan/scanResults.js +5 -1
- package/dist/utils/commonApi.js +4 -1
- package/package.json +11 -7
- package/src/audit/catalogueApplication/catalogueApplication.js +0 -1
- package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +11 -8
- package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
- package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
- package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
- package/src/audit/languageAnalysisEngine/languageAnalysisFactory.js +8 -0
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +10 -9
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +34 -29
- package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +3 -3
- package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +15 -11
- package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +6 -1
- package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +43 -27
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +78 -3
- package/src/commands/audit/processAudit.ts +1 -1
- package/src/commands/scan/sca/scaAnalysis.js +13 -5
- package/src/common/HTTPClient.js +65 -25
- package/src/common/errorHandling.ts +10 -1
- package/src/common/versionChecker.ts +1 -1
- package/src/constants/constants.js +1 -1
- package/src/constants/locales.js +3 -1
- package/src/lambda/analytics.ts +9 -0
- package/src/lambda/arn.ts +2 -1
- package/src/lambda/lambda.ts +37 -17
- package/src/lambda/types.ts +35 -0
- package/src/lambda/utils.ts +2 -7
- package/src/scaAnalysis/common/formatMessage.js +19 -1
- package/src/scaAnalysis/go/goAnalysis.js +2 -3
- package/src/scaAnalysis/java/analysis.js +5 -6
- package/src/scaAnalysis/java/index.js +2 -2
- package/src/scaAnalysis/python/analysis.js +48 -0
- package/src/scaAnalysis/python/index.js +11 -0
- package/src/scaAnalysis/ruby/analysis.js +282 -0
- package/src/scaAnalysis/ruby/index.js +11 -0
- package/src/scan/autoDetection.js +9 -5
- package/src/scan/fileUtils.js +15 -7
- package/src/scan/formatScanOutput.ts +11 -12
- package/src/scan/models/groupedResultsModel.ts +3 -3
- package/src/scan/models/resultContentModel.ts +1 -1
- package/src/scan/models/scanResultsModel.ts +5 -2
- package/src/scan/populateProjectIdAndProjectName.js +3 -1
- package/src/scan/scan.ts +1 -0
- package/src/scan/scanConfig.js +5 -1
- package/src/scan/scanController.js +18 -4
- package/src/scan/scanResults.js +10 -0
- package/src/utils/commonApi.js +4 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const multiReplace = require('string-multiple-replace');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const readAndParseProjectFile = projectPath => {
|
|
5
|
+
const filePath = filePathForWindows(projectPath + '/Pipfile');
|
|
6
|
+
const pipFile = fs.readFileSync(filePath, 'utf8');
|
|
7
|
+
const matcherObj = { '"': '' };
|
|
8
|
+
const sequencer = ['"'];
|
|
9
|
+
const parsedPipfile = multiReplace(pipFile, matcherObj, sequencer);
|
|
10
|
+
const pythonArray = parsedPipfile.split('\n');
|
|
11
|
+
return pythonArray.filter(element => element !== '' && !element.includes('#'));
|
|
12
|
+
};
|
|
13
|
+
const readAndParseLockFile = projectPath => {
|
|
14
|
+
const filePath = filePathForWindows(projectPath + '/Pipfile.lock');
|
|
15
|
+
const lockFile = fs.readFileSync(filePath, 'utf8');
|
|
16
|
+
let parsedPipLock = JSON.parse(lockFile);
|
|
17
|
+
parsedPipLock['defaults'] = parsedPipLock['default'];
|
|
18
|
+
return parsedPipLock;
|
|
19
|
+
};
|
|
20
|
+
const getPythonDeps = config => {
|
|
21
|
+
try {
|
|
22
|
+
const parseProject = readAndParseProjectFile(config.projectPath);
|
|
23
|
+
const parsePip = readAndParseLockFile(config.projectPath);
|
|
24
|
+
return { pipfileLock: parseProject, pipfilDependanceies: parsePip };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.log(err.message.toString());
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const filePathForWindows = path => {
|
|
32
|
+
if (process.platform === 'win32') {
|
|
33
|
+
path = path.replace(/\//g, '\\');
|
|
34
|
+
}
|
|
35
|
+
return path;
|
|
36
|
+
};
|
|
37
|
+
module.exports = {
|
|
38
|
+
getPythonDeps,
|
|
39
|
+
readAndParseProjectFile,
|
|
40
|
+
readAndParseLockFile
|
|
41
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { createPythonTSMessage } = require('../common/formatMessage');
|
|
3
|
+
const { getPythonDeps } = require('./analysis');
|
|
4
|
+
const pythonAnalysis = (config, languageFiles) => {
|
|
5
|
+
const pythonDeps = getPythonDeps(config, languageFiles.PYTHON);
|
|
6
|
+
return createPythonTSMessage(pythonDeps);
|
|
7
|
+
};
|
|
8
|
+
module.exports = {
|
|
9
|
+
pythonAnalysis
|
|
10
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const readAndParseGemfile = projectPath => {
|
|
4
|
+
const fileName = filePathForWindows(projectPath + '/Gemfile');
|
|
5
|
+
const gemFile = fs.readFileSync(fileName, 'utf8');
|
|
6
|
+
const rubyArray = gemFile.split('\n');
|
|
7
|
+
let filteredRubyDep = rubyArray.filter(element => {
|
|
8
|
+
return (!element.includes('#') &&
|
|
9
|
+
element.includes('gem') &&
|
|
10
|
+
!element.includes('source'));
|
|
11
|
+
});
|
|
12
|
+
for (let i = 0; i < filteredRubyDep.length; i++) {
|
|
13
|
+
filteredRubyDep[i] = filteredRubyDep[i].trim();
|
|
14
|
+
}
|
|
15
|
+
return filteredRubyDep;
|
|
16
|
+
};
|
|
17
|
+
const readAndParseGemLockFile = projectPath => {
|
|
18
|
+
const fileName = filePathForWindows(projectPath + '/Gemfile.lock');
|
|
19
|
+
const lockFile = fs.readFileSync(fileName, 'utf8');
|
|
20
|
+
const dependencyRegEx = /^\s*([A-Za-z0-9.!@#$%\-^&*_+]*)\s*(\((.*?)\))/;
|
|
21
|
+
const lines = lockFile.split('\n');
|
|
22
|
+
return {
|
|
23
|
+
dependencies: getDirectDependencies(lines, dependencyRegEx),
|
|
24
|
+
runtimeDetails: getLockFileRuntimeInfo(lines),
|
|
25
|
+
sources: getSourceArray(lines, dependencyRegEx)
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const nonDependencyKeys = (line, sourceObject) => {
|
|
29
|
+
const GEMFILE_KEY_VALUE = /^\s*([^:(]*)\s*\s*(.*)/;
|
|
30
|
+
let parts = GEMFILE_KEY_VALUE.exec(line);
|
|
31
|
+
let key = parts[1].trim();
|
|
32
|
+
let value = parts[2] || '';
|
|
33
|
+
sourceObject[key] = value;
|
|
34
|
+
return sourceObject;
|
|
35
|
+
};
|
|
36
|
+
const populateResolveAndPlatform = (version, sourceObject) => {
|
|
37
|
+
const depArr = version.split('-');
|
|
38
|
+
sourceObject.resolved = depArr[0];
|
|
39
|
+
sourceObject.platform = depArr.length > 1 ? depArr[1] : 'UNSPECIFIED';
|
|
40
|
+
return sourceObject;
|
|
41
|
+
};
|
|
42
|
+
const isUpperCase = str => {
|
|
43
|
+
return str === str.toUpperCase();
|
|
44
|
+
};
|
|
45
|
+
const getDirectDependencies = (lines, dependencyRegEx) => {
|
|
46
|
+
const dependencies = {};
|
|
47
|
+
let depIndex = 0;
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
if (lines[i] === 'DEPENDENCIES') {
|
|
50
|
+
depIndex = i;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const getDepArray = lines.slice(depIndex);
|
|
54
|
+
for (let j = 1; j < getDepArray.length; j++) {
|
|
55
|
+
const element = getDepArray[j];
|
|
56
|
+
if (!isUpperCase(element)) {
|
|
57
|
+
const isDependencyWithVersion = dependencyRegEx.test(element);
|
|
58
|
+
if (isDependencyWithVersion) {
|
|
59
|
+
const dependency = dependencyRegEx.exec(element);
|
|
60
|
+
let name = dependency[1];
|
|
61
|
+
name = name.replace('!', '');
|
|
62
|
+
dependencies[name.trim()] = dependency[3];
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
let name = element;
|
|
66
|
+
name = name.replace('!', ' ');
|
|
67
|
+
dependencies[name.trim()] = 'UNSPECIFIED';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return dependencies;
|
|
72
|
+
};
|
|
73
|
+
const getLockFileRuntimeInfo = lines => {
|
|
74
|
+
let rubVersionIndex = 0;
|
|
75
|
+
for (let i = 0; i < lines.length; i++) {
|
|
76
|
+
if (lines[i] === 'RUBY VERSION') {
|
|
77
|
+
rubVersionIndex = i;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const runtimeDetails = {};
|
|
82
|
+
if (rubVersionIndex !== 0) {
|
|
83
|
+
const getRubyVersionArray = lines.slice(rubVersionIndex);
|
|
84
|
+
for (let element of getRubyVersionArray) {
|
|
85
|
+
if (!isUpperCase(element)) {
|
|
86
|
+
runtimeDetails['version'] = getVersion(element);
|
|
87
|
+
runtimeDetails['patchLevel'] = getPatchLevel(element);
|
|
88
|
+
if (element.includes('engine')) {
|
|
89
|
+
let splitElement = element.split(' ');
|
|
90
|
+
runtimeDetails[splitElement[0]] = splitElement[1];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return runtimeDetails;
|
|
96
|
+
};
|
|
97
|
+
const getVersion = element => {
|
|
98
|
+
const versionRegex = /^([ruby\s0-9.*]+)/;
|
|
99
|
+
if (versionRegex.test(element)) {
|
|
100
|
+
let version = versionRegex.exec(element)[0];
|
|
101
|
+
if (version.includes('ruby')) {
|
|
102
|
+
return trimWhiteSpace(version.replace('ruby', ''));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const getPatchLevel = element => {
|
|
107
|
+
const patchLevelRegex = /(p\d+)/;
|
|
108
|
+
if (patchLevelRegex.test(element)) {
|
|
109
|
+
return patchLevelRegex.exec(element)[0];
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const formatSourceArr = sourceArr => {
|
|
113
|
+
return sourceArr.map(element => {
|
|
114
|
+
if (element.sourceType === 'GIT') {
|
|
115
|
+
delete element.specs;
|
|
116
|
+
}
|
|
117
|
+
if (element.sourceType === 'GEM') {
|
|
118
|
+
delete element.branch;
|
|
119
|
+
delete element.revision;
|
|
120
|
+
delete element.depthLevel;
|
|
121
|
+
delete element.specs;
|
|
122
|
+
}
|
|
123
|
+
if (element.sourceType === 'PATH') {
|
|
124
|
+
delete element.branch;
|
|
125
|
+
delete element.revision;
|
|
126
|
+
delete element.depthLevel;
|
|
127
|
+
delete element.specs;
|
|
128
|
+
delete element.platform;
|
|
129
|
+
}
|
|
130
|
+
return element;
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
const getSourceArray = (lines, dependencyRegEx) => {
|
|
134
|
+
const sourceObject = {
|
|
135
|
+
dependencies: {}
|
|
136
|
+
};
|
|
137
|
+
const whitespaceRegx = /^(\s*)/;
|
|
138
|
+
let index = 0;
|
|
139
|
+
let line = 0;
|
|
140
|
+
const sources = [];
|
|
141
|
+
while ((line = lines[index++]) !== undefined) {
|
|
142
|
+
let currentWS = whitespaceRegx.exec(line)[1].length;
|
|
143
|
+
if (!line.includes(' bundler (')) {
|
|
144
|
+
if (currentWS === 0 && !line.includes(':') && line !== '') {
|
|
145
|
+
sourceObject.sourceType = line;
|
|
146
|
+
}
|
|
147
|
+
if (currentWS !== 0 && line.includes(':')) {
|
|
148
|
+
nonDependencyKeys(line, sourceObject);
|
|
149
|
+
}
|
|
150
|
+
if (currentWS > 2) {
|
|
151
|
+
let nexlineWS = whitespaceRegx.exec(lines[index])[1].length;
|
|
152
|
+
sourceObject.dependencies = buildSourceDependencyWithVersion(whitespaceRegx, dependencyRegEx, line, currentWS, sourceObject.name, sourceObject.dependencies);
|
|
153
|
+
if (currentWS === 4 && sourceObject.depthLevel === undefined) {
|
|
154
|
+
const dependency = dependencyRegEx.exec(line);
|
|
155
|
+
sourceObject.name = dependency[1];
|
|
156
|
+
sourceObject.depthLevel = currentWS;
|
|
157
|
+
populateResolveAndPlatform(dependency[3], sourceObject);
|
|
158
|
+
}
|
|
159
|
+
if (currentWS === 4 && sourceObject.depthLevel) {
|
|
160
|
+
const dependency = dependencyRegEx.exec(line);
|
|
161
|
+
sourceObject.name = dependency[1];
|
|
162
|
+
sourceObject.depthLevel = currentWS;
|
|
163
|
+
populateResolveAndPlatform(dependency[3], sourceObject);
|
|
164
|
+
}
|
|
165
|
+
if ((currentWS === 4 && nexlineWS === 4) ||
|
|
166
|
+
(currentWS === 6 && nexlineWS === 4) ||
|
|
167
|
+
nexlineWS === '') {
|
|
168
|
+
let newObj = {};
|
|
169
|
+
newObj = JSON.parse(JSON.stringify(sourceObject));
|
|
170
|
+
sources.push(newObj);
|
|
171
|
+
sourceObject.dependencies = {};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return formatSourceArr(sources);
|
|
177
|
+
};
|
|
178
|
+
const buildSourceDependencyWithVersion = (whitespaceRegx, dependencyRegEx, line, currentWhiteSpace, name, dependencies) => {
|
|
179
|
+
const isDependencyWithVersion = dependencyRegEx.test(line);
|
|
180
|
+
if (currentWhiteSpace === 6) {
|
|
181
|
+
const dependency = dependencyRegEx.exec(line);
|
|
182
|
+
if (isDependencyWithVersion) {
|
|
183
|
+
if (name !== dependency[1]) {
|
|
184
|
+
dependencies[dependency[1]] = dependency[3];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
dependencies[line.trim()] = 'UNSPECIFIED';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return dependencies;
|
|
192
|
+
};
|
|
193
|
+
const getRubyDeps = config => {
|
|
194
|
+
try {
|
|
195
|
+
const parsedGem = readAndParseGemfile(config.projectPath);
|
|
196
|
+
const parsedLock = readAndParseGemLockFile(config.projectPath);
|
|
197
|
+
return { gemfilesDependanceies: parsedGem, gemfileLock: parsedLock };
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
console.log(err.message);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const trimWhiteSpace = string => {
|
|
205
|
+
return string.replace(/\s+/g, '');
|
|
206
|
+
};
|
|
207
|
+
const filePathForWindows = path => {
|
|
208
|
+
if (process.platform === 'win32') {
|
|
209
|
+
path = path.replace(/\//g, '\\');
|
|
210
|
+
}
|
|
211
|
+
return path;
|
|
212
|
+
};
|
|
213
|
+
module.exports = {
|
|
214
|
+
getRubyDeps,
|
|
215
|
+
readAndParseGemfile,
|
|
216
|
+
readAndParseGemLockFile,
|
|
217
|
+
nonDependencyKeys,
|
|
218
|
+
populateResolveAndPlatform,
|
|
219
|
+
isUpperCase,
|
|
220
|
+
getDirectDependencies,
|
|
221
|
+
getLockFileRuntimeInfo,
|
|
222
|
+
getVersion,
|
|
223
|
+
getPatchLevel,
|
|
224
|
+
formatSourceArr,
|
|
225
|
+
getSourceArray
|
|
226
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { getRubyDeps } = require('./analysis');
|
|
3
|
+
const { createRubyTSMessage } = require('../common/formatMessage');
|
|
4
|
+
const rubyAnalysis = (config, languageFiles) => {
|
|
5
|
+
const rubyDeps = getRubyDeps(config, languageFiles.RUBY);
|
|
6
|
+
return createRubyTSMessage(rubyDeps);
|
|
7
|
+
};
|
|
8
|
+
module.exports = {
|
|
9
|
+
rubyAnalysis
|
|
10
|
+
};
|
|
@@ -11,6 +11,10 @@ const autoDetectFileAndLanguage = async (configToUse) => {
|
|
|
11
11
|
console.log(i18n.__('fileHasWhiteSpacesError'));
|
|
12
12
|
process.exit(1);
|
|
13
13
|
}
|
|
14
|
+
if (fileFinder.fileIsEmpty(entries[0])) {
|
|
15
|
+
console.log(i18n.__('scanFileIsEmpty'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
14
18
|
configToUse.file = entries[0];
|
|
15
19
|
if (configToUse.name === undefined) {
|
|
16
20
|
configToUse.name = entries[0];
|
|
@@ -36,8 +40,8 @@ const autoDetectAuditFilesAndLanguages = async () => {
|
|
|
36
40
|
console.log('found multiple languages, please specify one using --file to run SCA analysis');
|
|
37
41
|
}
|
|
38
42
|
};
|
|
39
|
-
const manualDetectAuditFilesAndLanguages =
|
|
40
|
-
let projectRootFilenames =
|
|
43
|
+
const manualDetectAuditFilesAndLanguages = projectPath => {
|
|
44
|
+
let projectRootFilenames = rootFile.getProjectRootFilenames(projectPath);
|
|
41
45
|
let identifiedLanguages = languageResolver.deduceLanguageScaAnalysis(projectRootFilenames);
|
|
42
46
|
if (Object.keys(identifiedLanguages).length === 0) {
|
|
43
47
|
console.log(i18n.__('languageAnalysisNoLanguage', projectPath));
|
package/dist/scan/fileUtils.js
CHANGED
|
@@ -17,7 +17,7 @@ const findFilesJava = async (languagesFound) => {
|
|
|
17
17
|
onlyFiles: true
|
|
18
18
|
});
|
|
19
19
|
if (result.length > 0) {
|
|
20
|
-
return languagesFound.push({
|
|
20
|
+
return languagesFound.push({ JAVA: result });
|
|
21
21
|
}
|
|
22
22
|
return languagesFound;
|
|
23
23
|
};
|
|
@@ -28,7 +28,7 @@ const findFilesJavascript = async (languagesFound) => {
|
|
|
28
28
|
onlyFiles: true
|
|
29
29
|
});
|
|
30
30
|
if (result.length > 0) {
|
|
31
|
-
return languagesFound.push({
|
|
31
|
+
return languagesFound.push({ JAVASCRIPT: result });
|
|
32
32
|
}
|
|
33
33
|
return languagesFound;
|
|
34
34
|
};
|
|
@@ -39,7 +39,7 @@ const findFilesPython = async (languagesFound) => {
|
|
|
39
39
|
onlyFiles: true
|
|
40
40
|
});
|
|
41
41
|
if (result.length > 0) {
|
|
42
|
-
return languagesFound.push({
|
|
42
|
+
return languagesFound.push({ PYTHON: result });
|
|
43
43
|
}
|
|
44
44
|
return languagesFound;
|
|
45
45
|
};
|
|
@@ -50,7 +50,7 @@ const findFilesGo = async (languagesFound) => {
|
|
|
50
50
|
onlyFiles: true
|
|
51
51
|
});
|
|
52
52
|
if (result.length > 0) {
|
|
53
|
-
return languagesFound.push({
|
|
53
|
+
return languagesFound.push({ GO: result });
|
|
54
54
|
}
|
|
55
55
|
return languagesFound;
|
|
56
56
|
};
|
|
@@ -61,7 +61,7 @@ const findFilesRuby = async (languagesFound) => {
|
|
|
61
61
|
onlyFiles: true
|
|
62
62
|
});
|
|
63
63
|
if (result.length > 0) {
|
|
64
|
-
return languagesFound.push({
|
|
64
|
+
return languagesFound.push({ RUBY: result });
|
|
65
65
|
}
|
|
66
66
|
return languagesFound;
|
|
67
67
|
};
|
|
@@ -72,7 +72,7 @@ const findFilesPhp = async (languagesFound) => {
|
|
|
72
72
|
onlyFiles: true
|
|
73
73
|
});
|
|
74
74
|
if (result.length > 0) {
|
|
75
|
-
return languagesFound.push({
|
|
75
|
+
return languagesFound.push({ PHP: result });
|
|
76
76
|
}
|
|
77
77
|
return languagesFound;
|
|
78
78
|
};
|
|
@@ -90,6 +90,12 @@ const checkFilePermissions = file => {
|
|
|
90
90
|
const fileExists = path => {
|
|
91
91
|
return fs.existsSync(path);
|
|
92
92
|
};
|
|
93
|
+
const fileIsEmpty = path => {
|
|
94
|
+
if (fileExists(path) && checkFilePermissions(path)) {
|
|
95
|
+
return fs.readFileSync(path).length === 0;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
};
|
|
93
99
|
module.exports = {
|
|
94
100
|
findFile,
|
|
95
101
|
fileExists,
|
|
@@ -99,5 +105,6 @@ module.exports = {
|
|
|
99
105
|
findFilesPython,
|
|
100
106
|
findFilesGo,
|
|
101
107
|
findFilesPhp,
|
|
102
|
-
findFilesRuby
|
|
108
|
+
findFilesRuby,
|
|
109
|
+
fileIsEmpty
|
|
103
110
|
};
|
|
@@ -12,7 +12,7 @@ const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
|
12
12
|
const constants_1 = require("../constants/constants");
|
|
13
13
|
function formatScanOutput(scanResults) {
|
|
14
14
|
const { scanResultsInstances } = scanResults;
|
|
15
|
-
|
|
15
|
+
const projectOverview = getProjectOverview(scanResultsInstances);
|
|
16
16
|
if (scanResultsInstances.content.length === 0) {
|
|
17
17
|
console.log(i18n_1.default.__('scanNoVulnerabilitiesFound'));
|
|
18
18
|
console.log(i18n_1.default.__('scanNoVulnerabilitiesFoundSecureCode'));
|
|
@@ -52,7 +52,7 @@ function formatScanOutput(scanResults) {
|
|
|
52
52
|
});
|
|
53
53
|
let learnRow = [];
|
|
54
54
|
let adviceRow = [];
|
|
55
|
-
|
|
55
|
+
const headerRow = [
|
|
56
56
|
chalk_1.default
|
|
57
57
|
.hex(entry.colour)
|
|
58
58
|
.bold(`CONTRAST-${count.toString().padStart(3, '0')}`),
|
|
@@ -60,12 +60,13 @@ function formatScanOutput(scanResults) {
|
|
|
60
60
|
chalk_1.default.hex(entry.colour).bold(`[${entry.severity}] ${entry.ruleId}`) +
|
|
61
61
|
entry.message
|
|
62
62
|
];
|
|
63
|
-
|
|
63
|
+
const codePath = entry.codePath?.replace(/^@/, '');
|
|
64
|
+
const codeRow = [
|
|
64
65
|
chalk_1.default.hex('#F6F5F5').bold(`Code`),
|
|
65
66
|
chalk_1.default.hex('#F6F5F5').bold(`:`),
|
|
66
|
-
chalk_1.default.hex('#F6F5F5').bold(`${
|
|
67
|
+
chalk_1.default.hex('#F6F5F5').bold(`${codePath}`)
|
|
67
68
|
];
|
|
68
|
-
|
|
69
|
+
const issueRow = [chalk_1.default.bold(`Issue`), chalk_1.default.bold(`:`), `${entry.issue}`];
|
|
69
70
|
table.push(headerRow, codeRow, issueRow);
|
|
70
71
|
if (entry?.advice) {
|
|
71
72
|
adviceRow = [
|
|
@@ -98,7 +99,7 @@ function printVulnInfo(projectOverview) {
|
|
|
98
99
|
console.log(i18n_1.default.__('foundDetailedVulnerabilities', String(projectOverview.critical), String(projectOverview.high), String(projectOverview.medium), String(projectOverview.low), String(projectOverview.note)));
|
|
99
100
|
}
|
|
100
101
|
function getProjectOverview(scanResultsInstances) {
|
|
101
|
-
|
|
102
|
+
const acc = {
|
|
102
103
|
critical: 0,
|
|
103
104
|
high: 0,
|
|
104
105
|
medium: 0,
|
|
@@ -118,7 +119,7 @@ function getProjectOverview(scanResultsInstances) {
|
|
|
118
119
|
}
|
|
119
120
|
exports.getProjectOverview = getProjectOverview;
|
|
120
121
|
function formatLinks(objName, entry) {
|
|
121
|
-
|
|
122
|
+
const line = chalk_1.default.bold(objName + ' : ');
|
|
122
123
|
if (entry.length === 1) {
|
|
123
124
|
console.log(line + chalk_1.default.hex('#97DCF7').bold.underline(entry[0]));
|
|
124
125
|
}
|
|
@@ -179,10 +180,7 @@ function getCodeFlowInfo(resultEntry) {
|
|
|
179
180
|
}
|
|
180
181
|
exports.getCodeFlowInfo = getCodeFlowInfo;
|
|
181
182
|
function stripTags(oldString) {
|
|
182
|
-
return oldString
|
|
183
|
-
.replace(/\n/g, ' ')
|
|
184
|
-
.replace(/\s+/g, ' ')
|
|
185
|
-
.trim();
|
|
183
|
+
return oldString.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
186
184
|
}
|
|
187
185
|
exports.stripTags = stripTags;
|
|
188
186
|
function assignBySeverity(entry, assignedObj) {
|
|
@@ -5,7 +5,9 @@ class ScanResultsModel {
|
|
|
5
5
|
constructor(scan) {
|
|
6
6
|
this.projectOverview = scan.projectOverview;
|
|
7
7
|
this.scanDetail = scan.scanDetail;
|
|
8
|
-
this.scanResultsInstances =
|
|
8
|
+
this.scanResultsInstances =
|
|
9
|
+
scan.scanResultsInstances;
|
|
10
|
+
this.newProject = scan.newProject;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
exports.ScanResultsModel = ScanResultsModel;
|
|
@@ -8,8 +8,9 @@ const populateProjectId = async (config) => {
|
|
|
8
8
|
proj = await getExistingProjectIdByName(config, client).then(res => {
|
|
9
9
|
return res;
|
|
10
10
|
});
|
|
11
|
+
return { projectId: proj, isNewProject: false };
|
|
11
12
|
}
|
|
12
|
-
return proj;
|
|
13
|
+
return { projectId: proj, isNewProject: true };
|
|
13
14
|
};
|
|
14
15
|
const createProjectId = async (config, client) => {
|
|
15
16
|
return client
|
package/dist/scan/scan.js
CHANGED
package/dist/scan/scanConfig.js
CHANGED
|
@@ -21,10 +21,15 @@ const getScanConfig = argv => {
|
|
|
21
21
|
process.exit(1);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
+
let projectNameSource;
|
|
24
25
|
if (!scanParams.name && scanParams.file) {
|
|
25
26
|
scanParams.name = getFileName(scanParams.file);
|
|
27
|
+
projectNameSource = 'AUTO';
|
|
26
28
|
}
|
|
27
|
-
|
|
29
|
+
else {
|
|
30
|
+
projectNameSource = 'USER';
|
|
31
|
+
}
|
|
32
|
+
return { ...paramsAuth, ...scanParams, projectNameSource };
|
|
28
33
|
};
|
|
29
34
|
const getFileName = file => {
|
|
30
35
|
return file.split(path.sep).pop();
|
|
@@ -24,6 +24,10 @@ const fileAndLanguageLogic = async (configToUse) => {
|
|
|
24
24
|
console.log(i18n.__('fileNotExist'));
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
|
+
if (fileFunctions.fileIsEmpty(configToUse.file)) {
|
|
28
|
+
console.log(i18n.__('scanFileIsEmpty'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
27
31
|
return configToUse;
|
|
28
32
|
}
|
|
29
33
|
else {
|
|
@@ -35,14 +39,20 @@ const fileAndLanguageLogic = async (configToUse) => {
|
|
|
35
39
|
const startScan = async (configToUse) => {
|
|
36
40
|
const startTime = performance.now();
|
|
37
41
|
await fileAndLanguageLogic(configToUse);
|
|
42
|
+
let newProject;
|
|
38
43
|
if (!configToUse.projectId) {
|
|
39
|
-
|
|
44
|
+
const { projectId, isNewProject } = await populateProjectIdAndProjectName.populateProjectId(configToUse);
|
|
45
|
+
configToUse.projectId = projectId;
|
|
46
|
+
newProject = isNewProject;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
newProject = false;
|
|
40
50
|
}
|
|
41
51
|
const codeArtifactId = await scan.sendScan(configToUse);
|
|
42
52
|
if (!configToUse.ff) {
|
|
43
53
|
const startScanSpinner = returnOra('🚀 Contrast Scan started');
|
|
44
54
|
startSpinner(startScanSpinner);
|
|
45
|
-
const scanDetail = await scanResults.returnScanResults(configToUse, codeArtifactId, getTimeout(configToUse), startScanSpinner);
|
|
55
|
+
const scanDetail = await scanResults.returnScanResults(configToUse, codeArtifactId, newProject, getTimeout(configToUse), startScanSpinner);
|
|
46
56
|
const scanResultsInstances = await scanResults.returnScanResultsInstances(configToUse, scanDetail.id);
|
|
47
57
|
const endTime = performance.now();
|
|
48
58
|
const scanDurationMs = endTime - startTime;
|
|
@@ -54,7 +64,10 @@ const startScan = async (configToUse) => {
|
|
|
54
64
|
else {
|
|
55
65
|
succeedSpinner(startScanSpinner, 'Contrast Scan complete');
|
|
56
66
|
console.log(`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
57
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
scanDetail,
|
|
69
|
+
scanResultsInstances: scanResultsInstances.body
|
|
70
|
+
};
|
|
58
71
|
}
|
|
59
72
|
}
|
|
60
73
|
};
|
package/dist/scan/scanResults.js
CHANGED
|
@@ -26,9 +26,13 @@ const pollScanResults = async (config, scanId, client) => {
|
|
|
26
26
|
console.log(err);
|
|
27
27
|
});
|
|
28
28
|
};
|
|
29
|
-
const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinner) => {
|
|
29
|
+
const returnScanResults = async (config, codeArtifactId, newProject, timeout, startScanSpinner) => {
|
|
30
30
|
const client = commonApi.getHttpClient(config);
|
|
31
31
|
let scanId = await getScanId(config, codeArtifactId, client);
|
|
32
|
+
if (process.env.CODESEC_INVOCATION_ENVIRONMENT &&
|
|
33
|
+
process.env.CODESEC_INVOCATION_ENVIRONMENT.toUpperCase() === 'GITHUB') {
|
|
34
|
+
await client.createNewEvent(config, scanId, newProject);
|
|
35
|
+
}
|
|
32
36
|
let startTime = new Date();
|
|
33
37
|
let complete = false;
|
|
34
38
|
if (!_.isNil(scanId)) {
|
package/dist/utils/commonApi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const HttpClient = require('./../common/HTTPClient');
|
|
3
|
-
const { badRequestError, unauthenticatedError, forbiddenError, proxyError, genericError } = require('../common/errorHandling');
|
|
3
|
+
const { badRequestError, unauthenticatedError, forbiddenError, proxyError, genericError, maxAppError } = require('../common/errorHandling');
|
|
4
4
|
const handleResponseErrors = (res, api) => {
|
|
5
5
|
if (res.statusCode === 400) {
|
|
6
6
|
api === 'catalogue' ? badRequestError(true) : badRequestError(false);
|
|
@@ -14,6 +14,9 @@ const handleResponseErrors = (res, api) => {
|
|
|
14
14
|
else if (res.statusCode === 407) {
|
|
15
15
|
proxyError();
|
|
16
16
|
}
|
|
17
|
+
else if (res.statusCode === 412) {
|
|
18
|
+
maxAppError();
|
|
19
|
+
}
|
|
17
20
|
else {
|
|
18
21
|
genericError();
|
|
19
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/contrast",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Contrast Security's command line tool",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"test-int": "jest ./test-integration/",
|
|
25
25
|
"test-int-scan": "jest ./test-integration/scan",
|
|
26
26
|
"test-int-audit": "jest ./test-integration/audit",
|
|
27
|
-
"format": "prettier --write \"**/*.{ts,tsx,js,
|
|
28
|
-
"check-format": "prettier --check \"**/*.{ts,tsx,js,
|
|
27
|
+
"format": "prettier --write \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
|
|
28
|
+
"check-format": "prettier --check \"**/*.{ts,tsx,js,json,md,yml}\" .eslintrc.*",
|
|
29
29
|
"coverage-local": "nyc --reporter=text mocha './test/**/*.spec.js'",
|
|
30
30
|
"coverage": "yarn test --coverage",
|
|
31
31
|
"lint": "eslint --config .eslintrc.json . --ext .ts",
|
|
@@ -74,12 +74,13 @@
|
|
|
74
74
|
"@types/i18n": "^0.13.2",
|
|
75
75
|
"@types/jest": "^27.4.1",
|
|
76
76
|
"@types/lodash": "^4.14.182",
|
|
77
|
+
"@types/node": "*",
|
|
77
78
|
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
|
78
79
|
"@typescript-eslint/parser": "^5.21.0",
|
|
79
80
|
"csv-writer": "^1.6.0",
|
|
80
81
|
"eslint": "^8.14.0",
|
|
81
82
|
"eslint-config-prettier": "^8.5.0",
|
|
82
|
-
"eslint-plugin-prettier": "^4.
|
|
83
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
83
84
|
"husky": "^3.1.0",
|
|
84
85
|
"jest": "^27.5.1",
|
|
85
86
|
"jest-junit": "^13.2.0",
|
|
@@ -87,7 +88,7 @@
|
|
|
87
88
|
"npm-license-crawler": "^0.2.1",
|
|
88
89
|
"nyc": "^15.1.0",
|
|
89
90
|
"pkg": "^5.6.0",
|
|
90
|
-
"prettier": "^
|
|
91
|
+
"prettier": "^2.7.1",
|
|
91
92
|
"tmp": "^0.2.1",
|
|
92
93
|
"ts-jest": "^27.1.4",
|
|
93
94
|
"ts-node": "^10.7.0",
|
|
@@ -103,12 +104,15 @@
|
|
|
103
104
|
],
|
|
104
105
|
"prettier": {
|
|
105
106
|
"semi": false,
|
|
107
|
+
"trailingComma": "none",
|
|
108
|
+
"arrowParens": "avoid",
|
|
109
|
+
"bracketSpacing": true,
|
|
106
110
|
"singleQuote": true,
|
|
111
|
+
"bracketSameLine": true,
|
|
107
112
|
"overrides": [
|
|
108
113
|
{
|
|
109
114
|
"files": [
|
|
110
|
-
".eslintrc
|
|
111
|
-
".babelrc"
|
|
115
|
+
".eslintrc"
|
|
112
116
|
],
|
|
113
117
|
"options": {
|
|
114
118
|
"parser": "json"
|