@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,282 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
|
|
3
|
+
const readAndParseGemfile = projectPath => {
|
|
4
|
+
const fileName = filePathForWindows(projectPath + '/Gemfile')
|
|
5
|
+
const gemFile = fs.readFileSync(fileName, 'utf8')
|
|
6
|
+
const rubyArray = gemFile.split('\n')
|
|
7
|
+
|
|
8
|
+
let filteredRubyDep = rubyArray.filter(element => {
|
|
9
|
+
return (
|
|
10
|
+
!element.includes('#') &&
|
|
11
|
+
element.includes('gem') &&
|
|
12
|
+
!element.includes('source')
|
|
13
|
+
)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < filteredRubyDep.length; i++) {
|
|
17
|
+
filteredRubyDep[i] = filteredRubyDep[i].trim()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return filteredRubyDep
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const readAndParseGemLockFile = projectPath => {
|
|
24
|
+
const fileName = filePathForWindows(projectPath + '/Gemfile.lock')
|
|
25
|
+
const lockFile = fs.readFileSync(fileName, 'utf8')
|
|
26
|
+
const dependencyRegEx = /^\s*([A-Za-z0-9.!@#$%\-^&*_+]*)\s*(\((.*?)\))/
|
|
27
|
+
|
|
28
|
+
const lines = lockFile.split('\n')
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
dependencies: getDirectDependencies(lines, dependencyRegEx),
|
|
32
|
+
runtimeDetails: getLockFileRuntimeInfo(lines),
|
|
33
|
+
sources: getSourceArray(lines, dependencyRegEx)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const nonDependencyKeys = (line, sourceObject) => {
|
|
38
|
+
const GEMFILE_KEY_VALUE = /^\s*([^:(]*)\s*\s*(.*)/
|
|
39
|
+
let parts = GEMFILE_KEY_VALUE.exec(line)
|
|
40
|
+
let key = parts[1].trim()
|
|
41
|
+
let value = parts[2] || ''
|
|
42
|
+
|
|
43
|
+
sourceObject[key] = value
|
|
44
|
+
return sourceObject
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const populateResolveAndPlatform = (version, sourceObject) => {
|
|
48
|
+
const depArr = version.split('-')
|
|
49
|
+
sourceObject.resolved = depArr[0]
|
|
50
|
+
sourceObject.platform = depArr.length > 1 ? depArr[1] : 'UNSPECIFIED'
|
|
51
|
+
return sourceObject
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isUpperCase = str => {
|
|
55
|
+
return str === str.toUpperCase()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const getDirectDependencies = (lines, dependencyRegEx) => {
|
|
59
|
+
const dependencies = {}
|
|
60
|
+
|
|
61
|
+
let depIndex = 0
|
|
62
|
+
for (let i = 0; i < lines.length; i++) {
|
|
63
|
+
if (lines[i] === 'DEPENDENCIES') {
|
|
64
|
+
depIndex = i
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const getDepArray = lines.slice(depIndex)
|
|
68
|
+
|
|
69
|
+
for (let j = 1; j < getDepArray.length; j++) {
|
|
70
|
+
const element = getDepArray[j]
|
|
71
|
+
if (!isUpperCase(element)) {
|
|
72
|
+
const isDependencyWithVersion = dependencyRegEx.test(element)
|
|
73
|
+
if (isDependencyWithVersion) {
|
|
74
|
+
const dependency = dependencyRegEx.exec(element)
|
|
75
|
+
let name = dependency[1]
|
|
76
|
+
name = name.replace('!', '')
|
|
77
|
+
dependencies[name.trim()] = dependency[3]
|
|
78
|
+
} else {
|
|
79
|
+
let name = element
|
|
80
|
+
name = name.replace('!', ' ')
|
|
81
|
+
dependencies[name.trim()] = 'UNSPECIFIED'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return dependencies
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const getLockFileRuntimeInfo = lines => {
|
|
90
|
+
let rubVersionIndex = 0
|
|
91
|
+
for (let i = 0; i < lines.length; i++) {
|
|
92
|
+
if (lines[i] === 'RUBY VERSION') {
|
|
93
|
+
rubVersionIndex = i
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const runtimeDetails = {}
|
|
99
|
+
if (rubVersionIndex !== 0) {
|
|
100
|
+
const getRubyVersionArray = lines.slice(rubVersionIndex)
|
|
101
|
+
|
|
102
|
+
for (let element of getRubyVersionArray) {
|
|
103
|
+
if (!isUpperCase(element)) {
|
|
104
|
+
runtimeDetails['version'] = getVersion(element)
|
|
105
|
+
runtimeDetails['patchLevel'] = getPatchLevel(element)
|
|
106
|
+
|
|
107
|
+
if (element.includes('engine')) {
|
|
108
|
+
let splitElement = element.split(' ')
|
|
109
|
+
runtimeDetails[splitElement[0]] = splitElement[1]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return runtimeDetails
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const getVersion = element => {
|
|
118
|
+
const versionRegex = /^([ruby\s0-9.*]+)/
|
|
119
|
+
if (versionRegex.test(element)) {
|
|
120
|
+
let version = versionRegex.exec(element)[0]
|
|
121
|
+
|
|
122
|
+
if (version.includes('ruby')) {
|
|
123
|
+
return trimWhiteSpace(version.replace('ruby', ''))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const getPatchLevel = element => {
|
|
129
|
+
const patchLevelRegex = /(p\d+)/
|
|
130
|
+
if (patchLevelRegex.test(element)) {
|
|
131
|
+
return patchLevelRegex.exec(element)[0]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const formatSourceArr = sourceArr => {
|
|
136
|
+
return sourceArr.map(element => {
|
|
137
|
+
if (element.sourceType === 'GIT') {
|
|
138
|
+
delete element.specs
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (element.sourceType === 'GEM') {
|
|
142
|
+
delete element.branch
|
|
143
|
+
delete element.revision
|
|
144
|
+
delete element.depthLevel
|
|
145
|
+
delete element.specs
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (element.sourceType === 'PATH') {
|
|
149
|
+
delete element.branch
|
|
150
|
+
delete element.revision
|
|
151
|
+
delete element.depthLevel
|
|
152
|
+
delete element.specs
|
|
153
|
+
delete element.platform
|
|
154
|
+
}
|
|
155
|
+
return element
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const getSourceArray = (lines, dependencyRegEx) => {
|
|
160
|
+
const sourceObject = {
|
|
161
|
+
dependencies: {}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const whitespaceRegx = /^(\s*)/
|
|
165
|
+
let index = 0
|
|
166
|
+
|
|
167
|
+
let line = 0
|
|
168
|
+
const sources = []
|
|
169
|
+
while ((line = lines[index++]) !== undefined) {
|
|
170
|
+
let currentWS = whitespaceRegx.exec(line)[1].length
|
|
171
|
+
if (!line.includes(' bundler (')) {
|
|
172
|
+
if (currentWS === 0 && !line.includes(':') && line !== '') {
|
|
173
|
+
sourceObject.sourceType = line
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (currentWS !== 0 && line.includes(':')) {
|
|
177
|
+
nonDependencyKeys(line, sourceObject)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (currentWS > 2) {
|
|
181
|
+
let nexlineWS = whitespaceRegx.exec(lines[index])[1].length
|
|
182
|
+
sourceObject.dependencies = buildSourceDependencyWithVersion(
|
|
183
|
+
whitespaceRegx,
|
|
184
|
+
dependencyRegEx,
|
|
185
|
+
line,
|
|
186
|
+
currentWS,
|
|
187
|
+
sourceObject.name,
|
|
188
|
+
sourceObject.dependencies
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if (currentWS === 4 && sourceObject.depthLevel === undefined) {
|
|
192
|
+
const dependency = dependencyRegEx.exec(line)
|
|
193
|
+
sourceObject.name = dependency[1]
|
|
194
|
+
sourceObject.depthLevel = currentWS
|
|
195
|
+
populateResolveAndPlatform(dependency[3], sourceObject)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (currentWS === 4 && sourceObject.depthLevel) {
|
|
199
|
+
// create new Parent
|
|
200
|
+
const dependency = dependencyRegEx.exec(line)
|
|
201
|
+
sourceObject.name = dependency[1]
|
|
202
|
+
sourceObject.depthLevel = currentWS
|
|
203
|
+
populateResolveAndPlatform(dependency[3], sourceObject)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
(currentWS === 4 && nexlineWS === 4) ||
|
|
208
|
+
(currentWS === 6 && nexlineWS === 4) ||
|
|
209
|
+
nexlineWS === ''
|
|
210
|
+
) {
|
|
211
|
+
let newObj = {}
|
|
212
|
+
newObj = JSON.parse(JSON.stringify(sourceObject))
|
|
213
|
+
sources.push(newObj)
|
|
214
|
+
sourceObject.dependencies = {}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return formatSourceArr(sources)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const buildSourceDependencyWithVersion = (
|
|
223
|
+
whitespaceRegx,
|
|
224
|
+
dependencyRegEx,
|
|
225
|
+
line,
|
|
226
|
+
currentWhiteSpace,
|
|
227
|
+
name,
|
|
228
|
+
dependencies
|
|
229
|
+
) => {
|
|
230
|
+
const isDependencyWithVersion = dependencyRegEx.test(line)
|
|
231
|
+
|
|
232
|
+
if (currentWhiteSpace === 6) {
|
|
233
|
+
const dependency = dependencyRegEx.exec(line)
|
|
234
|
+
if (isDependencyWithVersion) {
|
|
235
|
+
if (name !== dependency[1]) {
|
|
236
|
+
dependencies[dependency[1]] = dependency[3]
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
dependencies[line.trim()] = 'UNSPECIFIED'
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return dependencies
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const getRubyDeps = config => {
|
|
247
|
+
try {
|
|
248
|
+
const parsedGem = readAndParseGemfile(config.projectPath)
|
|
249
|
+
const parsedLock = readAndParseGemLockFile(config.projectPath)
|
|
250
|
+
|
|
251
|
+
return { gemfilesDependanceies: parsedGem, gemfileLock: parsedLock }
|
|
252
|
+
} catch (err) {
|
|
253
|
+
console.log(err.message)
|
|
254
|
+
process.exit(1)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const trimWhiteSpace = string => {
|
|
259
|
+
return string.replace(/\s+/g, '')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const filePathForWindows = path => {
|
|
263
|
+
if (process.platform === 'win32') {
|
|
264
|
+
path = path.replace(/\//g, '\\')
|
|
265
|
+
}
|
|
266
|
+
return path
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
module.exports = {
|
|
270
|
+
getRubyDeps,
|
|
271
|
+
readAndParseGemfile,
|
|
272
|
+
readAndParseGemLockFile,
|
|
273
|
+
nonDependencyKeys,
|
|
274
|
+
populateResolveAndPlatform,
|
|
275
|
+
isUpperCase,
|
|
276
|
+
getDirectDependencies,
|
|
277
|
+
getLockFileRuntimeInfo,
|
|
278
|
+
getVersion,
|
|
279
|
+
getPatchLevel,
|
|
280
|
+
formatSourceArr,
|
|
281
|
+
getSourceArray
|
|
282
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const { getRubyDeps } = require('./analysis')
|
|
2
|
+
const { createRubyTSMessage } = require('../common/formatMessage')
|
|
3
|
+
|
|
4
|
+
const rubyAnalysis = (config, languageFiles) => {
|
|
5
|
+
const rubyDeps = getRubyDeps(config, languageFiles.RUBY)
|
|
6
|
+
return createRubyTSMessage(rubyDeps)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
rubyAnalysis
|
|
11
|
+
}
|
|
@@ -14,6 +14,11 @@ const autoDetectFileAndLanguage = async configToUse => {
|
|
|
14
14
|
process.exit(1)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
if (fileFinder.fileIsEmpty(entries[0])) {
|
|
18
|
+
console.log(i18n.__('scanFileIsEmpty'))
|
|
19
|
+
process.exit(1)
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
configToUse.file = entries[0]
|
|
18
23
|
if (configToUse.name === undefined) {
|
|
19
24
|
configToUse.name = entries[0]
|
|
@@ -43,11 +48,10 @@ const autoDetectAuditFilesAndLanguages = async () => {
|
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
const manualDetectAuditFilesAndLanguages =
|
|
47
|
-
let projectRootFilenames =
|
|
48
|
-
let identifiedLanguages =
|
|
49
|
-
projectRootFilenames
|
|
50
|
-
)
|
|
51
|
+
const manualDetectAuditFilesAndLanguages = projectPath => {
|
|
52
|
+
let projectRootFilenames = rootFile.getProjectRootFilenames(projectPath)
|
|
53
|
+
let identifiedLanguages =
|
|
54
|
+
languageResolver.deduceLanguageScaAnalysis(projectRootFilenames)
|
|
51
55
|
|
|
52
56
|
if (Object.keys(identifiedLanguages).length === 0) {
|
|
53
57
|
console.log(i18n.__('languageAnalysisNoLanguage', projectPath))
|
package/src/scan/fileUtils.js
CHANGED
|
@@ -22,7 +22,7 @@ const findFilesJava = async languagesFound => {
|
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
if (result.length > 0) {
|
|
25
|
-
return languagesFound.push({
|
|
25
|
+
return languagesFound.push({ JAVA: result })
|
|
26
26
|
}
|
|
27
27
|
return languagesFound
|
|
28
28
|
}
|
|
@@ -38,7 +38,7 @@ const findFilesJavascript = async languagesFound => {
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
if (result.length > 0) {
|
|
41
|
-
return languagesFound.push({
|
|
41
|
+
return languagesFound.push({ JAVASCRIPT: result })
|
|
42
42
|
}
|
|
43
43
|
return languagesFound
|
|
44
44
|
}
|
|
@@ -51,7 +51,7 @@ const findFilesPython = async languagesFound => {
|
|
|
51
51
|
})
|
|
52
52
|
|
|
53
53
|
if (result.length > 0) {
|
|
54
|
-
return languagesFound.push({
|
|
54
|
+
return languagesFound.push({ PYTHON: result })
|
|
55
55
|
}
|
|
56
56
|
return languagesFound
|
|
57
57
|
}
|
|
@@ -64,7 +64,7 @@ const findFilesGo = async languagesFound => {
|
|
|
64
64
|
})
|
|
65
65
|
|
|
66
66
|
if (result.length > 0) {
|
|
67
|
-
return languagesFound.push({
|
|
67
|
+
return languagesFound.push({ GO: result })
|
|
68
68
|
}
|
|
69
69
|
return languagesFound
|
|
70
70
|
}
|
|
@@ -77,7 +77,7 @@ const findFilesRuby = async languagesFound => {
|
|
|
77
77
|
})
|
|
78
78
|
|
|
79
79
|
if (result.length > 0) {
|
|
80
|
-
return languagesFound.push({
|
|
80
|
+
return languagesFound.push({ RUBY: result })
|
|
81
81
|
}
|
|
82
82
|
return languagesFound
|
|
83
83
|
}
|
|
@@ -90,7 +90,7 @@ const findFilesPhp = async languagesFound => {
|
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
if (result.length > 0) {
|
|
93
|
-
return languagesFound.push({
|
|
93
|
+
return languagesFound.push({ PHP: result })
|
|
94
94
|
}
|
|
95
95
|
return languagesFound
|
|
96
96
|
}
|
|
@@ -110,6 +110,13 @@ const fileExists = path => {
|
|
|
110
110
|
return fs.existsSync(path)
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
const fileIsEmpty = path => {
|
|
114
|
+
if (fileExists(path) && checkFilePermissions(path)) {
|
|
115
|
+
return fs.readFileSync(path).length === 0
|
|
116
|
+
}
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
|
|
113
120
|
module.exports = {
|
|
114
121
|
findFile,
|
|
115
122
|
fileExists,
|
|
@@ -119,5 +126,6 @@ module.exports = {
|
|
|
119
126
|
findFilesPython,
|
|
120
127
|
findFilesGo,
|
|
121
128
|
findFilesPhp,
|
|
122
|
-
findFilesRuby
|
|
129
|
+
findFilesRuby,
|
|
130
|
+
fileIsEmpty
|
|
123
131
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ProjectOverview,
|
|
3
2
|
ScanResultsInstances,
|
|
4
3
|
ScanResultsModel
|
|
5
4
|
} from './models/scanResultsModel'
|
|
@@ -20,7 +19,7 @@ import {
|
|
|
20
19
|
export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
21
20
|
const { scanResultsInstances } = scanResults
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
const projectOverview = getProjectOverview(scanResultsInstances)
|
|
24
23
|
if (scanResultsInstances.content.length === 0) {
|
|
25
24
|
console.log(i18n.__('scanNoVulnerabilitiesFound'))
|
|
26
25
|
console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
|
|
@@ -62,7 +61,7 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
62
61
|
})
|
|
63
62
|
let learnRow: string[] = []
|
|
64
63
|
let adviceRow = []
|
|
65
|
-
|
|
64
|
+
const headerRow = [
|
|
66
65
|
chalk
|
|
67
66
|
.hex(entry.colour)
|
|
68
67
|
.bold(`CONTRAST-${count.toString().padStart(3, '0')}`),
|
|
@@ -70,12 +69,15 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
70
69
|
chalk.hex(entry.colour).bold(`[${entry.severity}] ${entry.ruleId}`) +
|
|
71
70
|
entry.message
|
|
72
71
|
]
|
|
73
|
-
|
|
72
|
+
|
|
73
|
+
const codePath = entry.codePath?.replace(/^@/, '')
|
|
74
|
+
|
|
75
|
+
const codeRow = [
|
|
74
76
|
chalk.hex('#F6F5F5').bold(`Code`),
|
|
75
77
|
chalk.hex('#F6F5F5').bold(`:`),
|
|
76
|
-
chalk.hex('#F6F5F5').bold(`${
|
|
78
|
+
chalk.hex('#F6F5F5').bold(`${codePath}`)
|
|
77
79
|
]
|
|
78
|
-
|
|
80
|
+
const issueRow = [chalk.bold(`Issue`), chalk.bold(`:`), `${entry.issue}`]
|
|
79
81
|
|
|
80
82
|
table.push(headerRow, codeRow, issueRow)
|
|
81
83
|
|
|
@@ -123,7 +125,7 @@ function printVulnInfo(projectOverview: any) {
|
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
export function getProjectOverview(scanResultsInstances: ScanResultsInstances) {
|
|
126
|
-
|
|
128
|
+
const acc: any = {
|
|
127
129
|
critical: 0,
|
|
128
130
|
high: 0,
|
|
129
131
|
medium: 0,
|
|
@@ -146,7 +148,7 @@ export function getProjectOverview(scanResultsInstances: ScanResultsInstances) {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
export function formatLinks(objName: string, entry: any[]) {
|
|
149
|
-
|
|
151
|
+
const line = chalk.bold(objName + ' : ')
|
|
150
152
|
if (entry.length === 1) {
|
|
151
153
|
console.log(line + chalk.hex('#97DCF7').bold.underline(entry[0]))
|
|
152
154
|
} else {
|
|
@@ -216,10 +218,7 @@ export function getCodeFlowInfo(resultEntry: ResultContent) {
|
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
export function stripTags(oldString: string) {
|
|
219
|
-
return oldString
|
|
220
|
-
.replace(/\n/g, ' ')
|
|
221
|
-
.replace(/\s+/g, ' ')
|
|
222
|
-
.trim()
|
|
221
|
+
return oldString.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim()
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
export function assignBySeverity(
|
|
@@ -9,12 +9,12 @@ export class GroupedResultsModel {
|
|
|
9
9
|
issue?: string
|
|
10
10
|
priority?: number
|
|
11
11
|
message?: string | undefined
|
|
12
|
-
colour: string
|
|
13
|
-
codePath?: string
|
|
12
|
+
colour: string
|
|
13
|
+
codePath?: string
|
|
14
14
|
|
|
15
15
|
constructor(ruleId: string) {
|
|
16
16
|
this.ruleId = ruleId
|
|
17
17
|
this.colour = '#999999'
|
|
18
|
-
this.codePathSet = new Set<string>
|
|
18
|
+
this.codePathSet = new Set<string>()
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -4,11 +4,14 @@ export class ScanResultsModel {
|
|
|
4
4
|
projectOverview: ProjectOverview
|
|
5
5
|
scanDetail: ScanDetail
|
|
6
6
|
scanResultsInstances: ScanResultsInstances
|
|
7
|
+
newProject: boolean
|
|
7
8
|
|
|
8
9
|
constructor(scan: any) {
|
|
9
10
|
this.projectOverview = scan.projectOverview as ProjectOverview
|
|
10
11
|
this.scanDetail = scan.scanDetail as ScanDetail
|
|
11
|
-
this.scanResultsInstances =
|
|
12
|
+
this.scanResultsInstances =
|
|
13
|
+
scan.scanResultsInstances as ScanResultsInstances
|
|
14
|
+
this.newProject = scan.newProject
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
|
|
@@ -49,4 +52,4 @@ export interface ScanDetail {
|
|
|
49
52
|
|
|
50
53
|
export interface ScanResultsInstances {
|
|
51
54
|
content: ResultContent[]
|
|
52
|
-
}
|
|
55
|
+
}
|
|
@@ -8,9 +8,11 @@ const populateProjectId = async config => {
|
|
|
8
8
|
proj = await getExistingProjectIdByName(config, client).then(res => {
|
|
9
9
|
return res
|
|
10
10
|
})
|
|
11
|
+
|
|
12
|
+
return { projectId: proj, isNewProject: false }
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
return proj
|
|
15
|
+
return { projectId: proj, isNewProject: true }
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
const createProjectId = async (config, client) => {
|
package/src/scan/scan.ts
CHANGED
package/src/scan/scanConfig.js
CHANGED
|
@@ -31,11 +31,15 @@ const getScanConfig = argv => {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// if no name, take the full file path and use it as the project name
|
|
34
|
+
let projectNameSource
|
|
34
35
|
if (!scanParams.name && scanParams.file) {
|
|
35
36
|
scanParams.name = getFileName(scanParams.file)
|
|
37
|
+
projectNameSource = 'AUTO'
|
|
38
|
+
} else {
|
|
39
|
+
projectNameSource = 'USER'
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
return { ...paramsAuth, ...scanParams }
|
|
42
|
+
return { ...paramsAuth, ...scanParams, projectNameSource }
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
const getFileName = file => {
|
|
@@ -29,6 +29,11 @@ const fileAndLanguageLogic = async configToUse => {
|
|
|
29
29
|
console.log(i18n.__('fileNotExist'))
|
|
30
30
|
process.exit(1)
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
if (fileFunctions.fileIsEmpty(configToUse.file)) {
|
|
34
|
+
console.log(i18n.__('scanFileIsEmpty'))
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
32
37
|
return configToUse
|
|
33
38
|
} else {
|
|
34
39
|
if (configToUse.file === undefined || configToUse.file === null) {
|
|
@@ -41,10 +46,15 @@ const startScan = async configToUse => {
|
|
|
41
46
|
const startTime = performance.now()
|
|
42
47
|
await fileAndLanguageLogic(configToUse)
|
|
43
48
|
|
|
49
|
+
let newProject
|
|
50
|
+
|
|
44
51
|
if (!configToUse.projectId) {
|
|
45
|
-
|
|
46
|
-
configToUse
|
|
47
|
-
|
|
52
|
+
const { projectId, isNewProject } =
|
|
53
|
+
await populateProjectIdAndProjectName.populateProjectId(configToUse)
|
|
54
|
+
configToUse.projectId = projectId
|
|
55
|
+
newProject = isNewProject
|
|
56
|
+
} else {
|
|
57
|
+
newProject = false
|
|
48
58
|
}
|
|
49
59
|
const codeArtifactId = await scan.sendScan(configToUse)
|
|
50
60
|
|
|
@@ -54,6 +64,7 @@ const startScan = async configToUse => {
|
|
|
54
64
|
const scanDetail = await scanResults.returnScanResults(
|
|
55
65
|
configToUse,
|
|
56
66
|
codeArtifactId,
|
|
67
|
+
newProject,
|
|
57
68
|
getTimeout(configToUse),
|
|
58
69
|
startScanSpinner
|
|
59
70
|
)
|
|
@@ -74,7 +85,10 @@ const startScan = async configToUse => {
|
|
|
74
85
|
console.log(
|
|
75
86
|
`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
76
87
|
)
|
|
77
|
-
return {
|
|
88
|
+
return {
|
|
89
|
+
scanDetail,
|
|
90
|
+
scanResultsInstances: scanResultsInstances.body
|
|
91
|
+
}
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
}
|
package/src/scan/scanResults.js
CHANGED
|
@@ -31,11 +31,21 @@ const pollScanResults = async (config, scanId, client) => {
|
|
|
31
31
|
const returnScanResults = async (
|
|
32
32
|
config,
|
|
33
33
|
codeArtifactId,
|
|
34
|
+
newProject,
|
|
34
35
|
timeout,
|
|
35
36
|
startScanSpinner
|
|
36
37
|
) => {
|
|
37
38
|
const client = commonApi.getHttpClient(config)
|
|
38
39
|
let scanId = await getScanId(config, codeArtifactId, client)
|
|
40
|
+
|
|
41
|
+
// send metrics event to sast-event-collector
|
|
42
|
+
if (
|
|
43
|
+
process.env.CODESEC_INVOCATION_ENVIRONMENT &&
|
|
44
|
+
process.env.CODESEC_INVOCATION_ENVIRONMENT.toUpperCase() === 'GITHUB'
|
|
45
|
+
) {
|
|
46
|
+
await client.createNewEvent(config, scanId, newProject)
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
let startTime = new Date()
|
|
40
50
|
let complete = false
|
|
41
51
|
if (!_.isNil(scanId)) {
|
package/src/utils/commonApi.js
CHANGED
|
@@ -4,7 +4,8 @@ const {
|
|
|
4
4
|
unauthenticatedError,
|
|
5
5
|
forbiddenError,
|
|
6
6
|
proxyError,
|
|
7
|
-
genericError
|
|
7
|
+
genericError,
|
|
8
|
+
maxAppError
|
|
8
9
|
} = require('../common/errorHandling')
|
|
9
10
|
|
|
10
11
|
const handleResponseErrors = (res, api) => {
|
|
@@ -16,6 +17,8 @@ const handleResponseErrors = (res, api) => {
|
|
|
16
17
|
forbiddenError()
|
|
17
18
|
} else if (res.statusCode === 407) {
|
|
18
19
|
proxyError()
|
|
20
|
+
} else if (res.statusCode === 412) {
|
|
21
|
+
maxAppError()
|
|
19
22
|
} else {
|
|
20
23
|
genericError()
|
|
21
24
|
}
|