@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,77 @@
|
|
|
1
|
+
const { keyBy, merge } = require('lodash')
|
|
2
|
+
|
|
3
|
+
const parsePHPLockFileForScaServices = phpLockFile => {
|
|
4
|
+
const packages = keyBy(phpLockFile.packages, 'name')
|
|
5
|
+
const packagesDev = keyBy(phpLockFile['packages-dev'], 'name')
|
|
6
|
+
|
|
7
|
+
return merge(buildDepTree(packages, true), buildDepTree(packagesDev, false))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const buildDepTree = (packages, isProduction) => {
|
|
11
|
+
//builds deps into flat structure
|
|
12
|
+
const dependencyTree = {}
|
|
13
|
+
|
|
14
|
+
for (const packagesKey in packages) {
|
|
15
|
+
const currentObj = packages[packagesKey]
|
|
16
|
+
const { group, name } = findGroupAndName(currentObj.name)
|
|
17
|
+
|
|
18
|
+
const key = `${group}/${name}@${currentObj.version}`
|
|
19
|
+
dependencyTree[key] = {
|
|
20
|
+
group: group,
|
|
21
|
+
name: name,
|
|
22
|
+
version: currentObj.version,
|
|
23
|
+
directDependency: true,
|
|
24
|
+
isProduction: isProduction,
|
|
25
|
+
dependencies: []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const mergedChildDeps = merge(
|
|
29
|
+
buildSubDepsIntoFlatStructure(currentObj.require),
|
|
30
|
+
buildSubDepsIntoFlatStructure(currentObj['require-dev'])
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
for (const childKey in mergedChildDeps) {
|
|
34
|
+
const { group, name } = findGroupAndName(childKey)
|
|
35
|
+
const builtKey = `${group}/${name}`
|
|
36
|
+
dependencyTree[builtKey] = mergedChildDeps[childKey]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return dependencyTree
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// currently sub deps will be built into a flat structure
|
|
43
|
+
// but not ingested via the new services as they do not have concrete versions
|
|
44
|
+
const buildSubDepsIntoFlatStructure = childDeps => {
|
|
45
|
+
const dependencyTree = {}
|
|
46
|
+
|
|
47
|
+
for (const dep in childDeps) {
|
|
48
|
+
const version = childDeps[dep]
|
|
49
|
+
const { group, name } = findGroupAndName(dep)
|
|
50
|
+
const key = `${group}/${name}`
|
|
51
|
+
dependencyTree[key] = {
|
|
52
|
+
group: group,
|
|
53
|
+
name: name,
|
|
54
|
+
version: version,
|
|
55
|
+
directDependency: false,
|
|
56
|
+
isProduction: false,
|
|
57
|
+
dependencies: []
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return dependencyTree
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const findGroupAndName = groupAndName => {
|
|
64
|
+
if (groupAndName.includes('/')) {
|
|
65
|
+
const groupName = groupAndName.split('/')
|
|
66
|
+
return { group: groupName[0], name: groupName[1] }
|
|
67
|
+
} else {
|
|
68
|
+
return { group: groupAndName, name: groupAndName }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
parsePHPLockFileForScaServices,
|
|
74
|
+
buildDepTree,
|
|
75
|
+
buildSubDepsIntoFlatStructure,
|
|
76
|
+
findGroupAndName
|
|
77
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const multiReplace = require('string-multiple-replace')
|
|
2
2
|
const fs = require('fs')
|
|
3
|
+
const i18n = require('i18n')
|
|
3
4
|
|
|
4
5
|
const readAndParseProjectFile = file => {
|
|
5
6
|
const filePath = filePathForWindows(file + '/Pipfile')
|
|
@@ -23,12 +24,52 @@ const readAndParseLockFile = file => {
|
|
|
23
24
|
return parsedPipLock
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
+
const readLockFile = file => {
|
|
28
|
+
const filePath = filePathForWindows(file + '/Pipfile.lock')
|
|
29
|
+
const lockFile = fs.readFileSync(filePath, 'utf8')
|
|
30
|
+
let parsedPipLock = JSON.parse(lockFile)
|
|
31
|
+
return parsedPipLock['default']
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const scaPythonParser = pythonDependencies => {
|
|
35
|
+
let pythonParsedDeps = {}
|
|
36
|
+
for (let key in pythonDependencies) {
|
|
37
|
+
pythonParsedDeps[key] = {}
|
|
38
|
+
pythonParsedDeps[key].version = pythonDependencies[key].version.replace(
|
|
39
|
+
'==',
|
|
40
|
+
''
|
|
41
|
+
)
|
|
42
|
+
pythonParsedDeps[key].group = null
|
|
43
|
+
pythonParsedDeps[key].name = key
|
|
44
|
+
pythonParsedDeps[key].isProduction = true
|
|
45
|
+
pythonParsedDeps[key].dependencies = []
|
|
46
|
+
pythonParsedDeps[key].directDependency = true
|
|
47
|
+
}
|
|
48
|
+
return pythonParsedDeps
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const checkForCorrectFiles = languageFiles => {
|
|
52
|
+
if (!languageFiles.includes('Pipfile.lock')) {
|
|
53
|
+
throw new Error(i18n.__('languageAnalysisHasNoLockFile', 'python'))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!languageFiles.includes('Pipfile')) {
|
|
57
|
+
throw new Error(i18n.__('languageAnalysisProjectFileError', 'python'))
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const getPythonDeps = (config, languageFiles) => {
|
|
27
62
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
63
|
+
if (config.experimental) {
|
|
64
|
+
let pythonLockFileContents = readLockFile(config.file)
|
|
65
|
+
return scaPythonParser(pythonLockFileContents)
|
|
66
|
+
} else {
|
|
67
|
+
checkForCorrectFiles(languageFiles)
|
|
68
|
+
const parseProject = readAndParseProjectFile(config.file)
|
|
69
|
+
const parsePip = readAndParseLockFile(config.file)
|
|
30
70
|
|
|
31
|
-
|
|
71
|
+
return { pipfileLock: parsePip, pipfilDependanceies: parseProject }
|
|
72
|
+
}
|
|
32
73
|
} catch (err) {
|
|
33
74
|
console.log(err.message.toString())
|
|
34
75
|
process.exit(1)
|
|
@@ -44,6 +85,9 @@ const filePathForWindows = path => {
|
|
|
44
85
|
|
|
45
86
|
module.exports = {
|
|
46
87
|
getPythonDeps,
|
|
88
|
+
scaPythonParser,
|
|
89
|
+
readAndParseLockFile,
|
|
47
90
|
readAndParseProjectFile,
|
|
48
|
-
|
|
91
|
+
checkForCorrectFiles,
|
|
92
|
+
readLockFile
|
|
49
93
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
const { createPythonTSMessage } = require('../common/formatMessage')
|
|
2
|
-
const { getPythonDeps } = require('./analysis')
|
|
2
|
+
const { getPythonDeps, secondaryParser } = require('./analysis')
|
|
3
3
|
|
|
4
4
|
const pythonAnalysis = (config, languageFiles) => {
|
|
5
5
|
const pythonDeps = getPythonDeps(config, languageFiles.PYTHON)
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
if (config.experimental) {
|
|
8
|
+
return pythonDeps
|
|
9
|
+
} else {
|
|
10
|
+
return createPythonTSMessage(pythonDeps)
|
|
11
|
+
}
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
module.exports = {
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
|
+
const i18n = require('i18n')
|
|
3
|
+
|
|
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(
|
|
11
|
+
parsedLock.sources
|
|
12
|
+
)
|
|
13
|
+
let rubyTree = createRubyTree(rubyArray)
|
|
14
|
+
findChildrenDependencies(rubyTree)
|
|
15
|
+
processRootDependencies(parsedLock.dependencies, rubyTree)
|
|
16
|
+
return rubyTree
|
|
17
|
+
} else {
|
|
18
|
+
return { gemfilesDependanceies: parsedGem, gemfileLock: parsedLock }
|
|
19
|
+
}
|
|
20
|
+
} catch (err) {
|
|
21
|
+
throw err
|
|
22
|
+
}
|
|
23
|
+
}
|
|
2
24
|
|
|
3
25
|
const readAndParseGemfile = file => {
|
|
4
26
|
const gemFile = fs.readFileSync(file + '/Gemfile', 'utf8')
|
|
@@ -241,15 +263,128 @@ const buildSourceDependencyWithVersion = (
|
|
|
241
263
|
return dependencies
|
|
242
264
|
}
|
|
243
265
|
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const parsedLock = readAndParseGemLockFile(config.file)
|
|
266
|
+
const processRootDependencies = (rootDependencies, rubyTree) => {
|
|
267
|
+
const getParentObjectByName = queryToken =>
|
|
268
|
+
Object.values(rubyTree).filter(({ name }) => name === queryToken)
|
|
248
269
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
270
|
+
for (let parent in rootDependencies) {
|
|
271
|
+
let parentObject = getParentObjectByName(parent)
|
|
272
|
+
|
|
273
|
+
// ignore root dependencies that don't have a resolved version
|
|
274
|
+
if (parentObject[0]) {
|
|
275
|
+
let gav =
|
|
276
|
+
parentObject[0].group +
|
|
277
|
+
'/' +
|
|
278
|
+
parentObject[0].name +
|
|
279
|
+
'@' +
|
|
280
|
+
parentObject[0].version
|
|
281
|
+
|
|
282
|
+
rubyTree[gav] = parentObject[0]
|
|
283
|
+
rubyTree[gav].directDependency = true
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return rubyTree
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const createRubyTree = rubyArray => {
|
|
290
|
+
let rubyTree = {}
|
|
291
|
+
for (let x in rubyArray) {
|
|
292
|
+
let version = rubyArray[x].resolved
|
|
293
|
+
|
|
294
|
+
let gav = rubyArray[x].group + '/' + rubyArray[x].name + '@' + version
|
|
295
|
+
rubyTree[gav] = rubyArray[x]
|
|
296
|
+
rubyTree[gav].directDependency = false
|
|
297
|
+
rubyTree[gav].version = version
|
|
298
|
+
|
|
299
|
+
// add dependency array if none exists
|
|
300
|
+
if (!rubyTree[gav].dependencies) {
|
|
301
|
+
rubyTree[gav].dependencies = []
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
delete rubyTree[gav].resolved
|
|
305
|
+
}
|
|
306
|
+
return rubyTree
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const findChildrenDependencies = rubyTree => {
|
|
310
|
+
for (let dep in rubyTree) {
|
|
311
|
+
let unResolvedChildDepsKey = Object.keys(rubyTree[dep].dependencies)
|
|
312
|
+
rubyTree[dep].dependencies = resolveVersionOfChildDependencies(
|
|
313
|
+
unResolvedChildDepsKey,
|
|
314
|
+
rubyTree
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const resolveVersionOfChildDependencies = (
|
|
320
|
+
unResolvedChildDepsKey,
|
|
321
|
+
rubyObject
|
|
322
|
+
) => {
|
|
323
|
+
const getParentObjectByName = queryToken =>
|
|
324
|
+
Object.values(rubyObject).filter(({ name }) => name === queryToken)
|
|
325
|
+
let resolvedChildrenDependencies = []
|
|
326
|
+
for (let childDep in unResolvedChildDepsKey) {
|
|
327
|
+
let childDependencyName = unResolvedChildDepsKey[childDep]
|
|
328
|
+
let parent = getParentObjectByName(childDependencyName)
|
|
329
|
+
resolvedChildrenDependencies.push(
|
|
330
|
+
'null/' + childDependencyName + '@' + parent[0].version
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
return resolvedChildrenDependencies
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const removeRedundantAndPopulateDefinedElements = deps => {
|
|
337
|
+
return deps.map(element => {
|
|
338
|
+
if (element.sourceType === 'GIT') {
|
|
339
|
+
delete element.sourceType
|
|
340
|
+
delete element.remote
|
|
341
|
+
delete element.platform
|
|
342
|
+
|
|
343
|
+
element.group = null
|
|
344
|
+
element.isProduction = true
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (element.sourceType === 'GEM') {
|
|
348
|
+
element.group = null
|
|
349
|
+
element.isProduction = true
|
|
350
|
+
|
|
351
|
+
delete element.sourceType
|
|
352
|
+
delete element.remote
|
|
353
|
+
delete element.platform
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (element.sourceType === 'PATH') {
|
|
357
|
+
element.group = null
|
|
358
|
+
element.isProduction = true
|
|
359
|
+
|
|
360
|
+
delete element.platform
|
|
361
|
+
delete element.sourceType
|
|
362
|
+
delete element.remote
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (element.sourceType === 'BUNDLED WITH') {
|
|
366
|
+
element.group = null
|
|
367
|
+
element.isProduction = true
|
|
368
|
+
|
|
369
|
+
delete element.sourceType
|
|
370
|
+
delete element.remote
|
|
371
|
+
delete element.branch
|
|
372
|
+
delete element.revision
|
|
373
|
+
delete element.depthLevel
|
|
374
|
+
delete element.specs
|
|
375
|
+
delete element.platform
|
|
376
|
+
}
|
|
377
|
+
return element
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const checkForCorrectFiles = languageFiles => {
|
|
382
|
+
if (!languageFiles.includes('Gemfile.lock')) {
|
|
383
|
+
throw new Error(i18n.__('languageAnalysisHasNoLockFile', 'ruby'))
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (!languageFiles.includes('Gemfile')) {
|
|
387
|
+
throw new Error(i18n.__('languageAnalysisProjectFileError', 'ruby'))
|
|
253
388
|
}
|
|
254
389
|
}
|
|
255
390
|
|
|
@@ -269,5 +404,10 @@ module.exports = {
|
|
|
269
404
|
getVersion,
|
|
270
405
|
getPatchLevel,
|
|
271
406
|
formatSourceArr,
|
|
272
|
-
getSourceArray
|
|
407
|
+
getSourceArray,
|
|
408
|
+
checkForCorrectFiles,
|
|
409
|
+
removeRedundantAndPopulateDefinedElements,
|
|
410
|
+
createRubyTree,
|
|
411
|
+
findChildrenDependencies,
|
|
412
|
+
processRootDependencies
|
|
273
413
|
}
|
|
@@ -3,7 +3,12 @@ const { createRubyTSMessage } = require('../common/formatMessage')
|
|
|
3
3
|
|
|
4
4
|
const rubyAnalysis = (config, languageFiles) => {
|
|
5
5
|
const rubyDeps = analysis.getRubyDeps(config, languageFiles.RUBY)
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
if (config.experimental) {
|
|
8
|
+
return rubyDeps
|
|
9
|
+
} else {
|
|
10
|
+
return createRubyTSMessage(rubyDeps)
|
|
11
|
+
}
|
|
7
12
|
}
|
|
8
13
|
|
|
9
14
|
module.exports = {
|
|
@@ -62,12 +62,12 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
62
62
|
})
|
|
63
63
|
let learnRow: string[] = []
|
|
64
64
|
let adviceRow = []
|
|
65
|
+
const headerColour = chalk.hex(entry.colour)
|
|
65
66
|
const headerRow = [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
chalk.hex(entry.colour).bold(`[${entry.severity}] ${entry.ruleId}`) +
|
|
67
|
+
headerColour(`CONTRAST-${count.toString().padStart(3, '0')}`),
|
|
68
|
+
headerColour(`-`),
|
|
69
|
+
headerColour(`[${entry.severity}] `) +
|
|
70
|
+
headerColour.bold(`${entry.ruleId}`) +
|
|
71
71
|
entry.message
|
|
72
72
|
]
|
|
73
73
|
|
|
@@ -104,6 +104,8 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
104
104
|
})
|
|
105
105
|
}
|
|
106
106
|
printVulnInfo(projectOverview)
|
|
107
|
+
|
|
108
|
+
return projectOverview
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
function printVulnInfo(projectOverview: any) {
|
package/src/scan/help.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const commandLineUsage = require('command-line-usage')
|
|
2
2
|
const i18n = require('i18n')
|
|
3
3
|
const constants = require('../constants')
|
|
4
|
+
const { commonHelpLinks } = require('../common/commonHelp')
|
|
4
5
|
|
|
5
6
|
const scanUsageGuide = commandLineUsage([
|
|
6
7
|
{
|
|
@@ -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
|
|
|
43
42
|
module.exports = {
|
|
@@ -28,7 +28,11 @@ const createProjectId = async (config, client) => {
|
|
|
28
28
|
process.exit(1)
|
|
29
29
|
return
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
if (res.statusCode === 429) {
|
|
32
|
+
console.log(i18n.__('exceededFreeTier'))
|
|
33
|
+
process.exit(1)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
32
36
|
if (res.statusCode === 201) {
|
|
33
37
|
console.log(i18n.__('projectCreatedScan'))
|
|
34
38
|
if (config.verbose) {
|
package/src/scan/scan.ts
CHANGED
|
@@ -47,6 +47,10 @@ export const sendScan = async (config: any) => {
|
|
|
47
47
|
)
|
|
48
48
|
console.log(i18n.__('genericServiceError', res.statusCode))
|
|
49
49
|
}
|
|
50
|
+
if (res.statusCode === 429) {
|
|
51
|
+
console.log(i18n.__('exceededFreeTier'))
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
50
54
|
if (res.statusCode === 403) {
|
|
51
55
|
console.log(i18n.__('permissionsError'))
|
|
52
56
|
process.exit(1)
|
package/src/scan/scanConfig.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
const paramHandler = require('../utils/paramsUtil/paramHandler')
|
|
2
|
-
const constants = require('
|
|
3
|
-
const parsedCLIOptions = require('../../src/utils/parsedCLIOptions')
|
|
2
|
+
const constants = require('../constants.js')
|
|
4
3
|
const path = require('path')
|
|
5
4
|
const { supportedLanguagesScan } = require('../constants/constants')
|
|
6
5
|
const i18n = require('i18n')
|
|
7
6
|
const { scanUsageGuide } = require('./help')
|
|
7
|
+
const parsedCLIOptions = require('../utils/parsedCLIOptions')
|
|
8
8
|
|
|
9
|
-
const getScanConfig = argv => {
|
|
10
|
-
let scanParams = parsedCLIOptions.getCommandLineArgsCustom(
|
|
9
|
+
const getScanConfig = async (contrastConf, command, argv) => {
|
|
10
|
+
let scanParams = await parsedCLIOptions.getCommandLineArgsCustom(
|
|
11
|
+
contrastConf,
|
|
12
|
+
command,
|
|
11
13
|
argv,
|
|
12
14
|
constants.commandLineDefinitions.scanOptionDefinitions
|
|
13
15
|
)
|
package/src/scan/scanResults.js
CHANGED
|
@@ -4,11 +4,15 @@ const oraFunctions = require('../utils/oraWrapper')
|
|
|
4
4
|
const _ = require('lodash')
|
|
5
5
|
const i18n = require('i18n')
|
|
6
6
|
const oraWrapper = require('../utils/oraWrapper')
|
|
7
|
+
const readLine = require('readline')
|
|
7
8
|
|
|
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 => {
|
|
@@ -88,13 +92,57 @@ const returnScanResults = async (
|
|
|
88
92
|
startScanSpinner,
|
|
89
93
|
'Contrast Scan timed out at the specified ' + timeout + ' seconds.'
|
|
90
94
|
)
|
|
91
|
-
|
|
92
|
-
process.
|
|
95
|
+
|
|
96
|
+
const isCI = process.env.CONTRAST_CODESEC_CI
|
|
97
|
+
? JSON.parse(process.env.CONTRAST_CODESEC_CI.toLowerCase())
|
|
98
|
+
: false
|
|
99
|
+
if (!isCI) {
|
|
100
|
+
const retry = await retryScanPrompt()
|
|
101
|
+
timeout = retry.timeout
|
|
102
|
+
} else {
|
|
103
|
+
console.log('Please try again, allowing more time')
|
|
104
|
+
process.exit(1)
|
|
105
|
+
}
|
|
93
106
|
}
|
|
94
107
|
}
|
|
95
108
|
}
|
|
96
109
|
}
|
|
97
110
|
|
|
111
|
+
const retryScanPrompt = async () => {
|
|
112
|
+
const rl = readLine.createInterface({
|
|
113
|
+
input: process.stdin,
|
|
114
|
+
output: process.stdout
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
requestUtils.timeOutError(30000, reject)
|
|
119
|
+
|
|
120
|
+
rl.question(
|
|
121
|
+
'🔁 Do you want to continue waiting on Scan? [Y/N]\n',
|
|
122
|
+
async input => {
|
|
123
|
+
if (input.toLowerCase() === 'yes' || input.toLowerCase() === 'y') {
|
|
124
|
+
console.log('Continuing wait for Scan')
|
|
125
|
+
rl.close()
|
|
126
|
+
resolve({ timeout: 300 })
|
|
127
|
+
} else if (
|
|
128
|
+
input.toLowerCase() === 'no' ||
|
|
129
|
+
input.toLowerCase() === 'n'
|
|
130
|
+
) {
|
|
131
|
+
rl.close()
|
|
132
|
+
console.log('Contrast Scan Retry Cancelled: Exiting')
|
|
133
|
+
resolve(process.exit(1))
|
|
134
|
+
} else {
|
|
135
|
+
rl.close()
|
|
136
|
+
console.log('Invalid Input: Exiting')
|
|
137
|
+
resolve(process.exit(1))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
}).catch(e => {
|
|
142
|
+
throw e
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
98
146
|
const returnScanResultsInstances = async (config, scanId) => {
|
|
99
147
|
const client = commonApi.getHttpClient(config)
|
|
100
148
|
let result
|
|
@@ -118,5 +166,6 @@ module.exports = {
|
|
|
118
166
|
getScanId: getScanId,
|
|
119
167
|
returnScanResults: returnScanResults,
|
|
120
168
|
pollScanResults: pollScanResults,
|
|
121
|
-
returnScanResultsInstances: returnScanResultsInstances
|
|
169
|
+
returnScanResultsInstances: returnScanResultsInstances,
|
|
170
|
+
retryScanPrompt
|
|
122
171
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { getHttpClient } from '../utils/commonApi'
|
|
2
|
+
import * as crypto from 'crypto'
|
|
3
|
+
import { ContrastConf } from '../utils/getConfig'
|
|
4
|
+
|
|
5
|
+
export const TELEMETRY_CLI_COMMANDS_EVENT = 'CLI_COMMANDS'
|
|
6
|
+
export const TELEMETRY_CLI_TIME_TO_AUTH_EVENT = 'CLI_TIME_TO_AUTH'
|
|
7
|
+
|
|
8
|
+
export const sendTelemetryConfigAsConfObj = async (
|
|
9
|
+
config: ContrastConf,
|
|
10
|
+
command: string,
|
|
11
|
+
argv: string[],
|
|
12
|
+
result: string,
|
|
13
|
+
language: string
|
|
14
|
+
) => {
|
|
15
|
+
const hostParam = '--host'
|
|
16
|
+
const hostParamAlias = '-h'
|
|
17
|
+
const orgIdParam = '--organization-id'
|
|
18
|
+
const orgIdParamAlias = '-o'
|
|
19
|
+
const authParam = '--authorization'
|
|
20
|
+
const apiKeyParam = '--api-key'
|
|
21
|
+
|
|
22
|
+
let configToUse
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
paramExists(argv, hostParam, hostParamAlias) &&
|
|
26
|
+
paramExists(argv, orgIdParam, orgIdParamAlias) &&
|
|
27
|
+
paramExists(argv, authParam, null) &&
|
|
28
|
+
paramExists(argv, apiKeyParam, null)
|
|
29
|
+
) {
|
|
30
|
+
//if the user has passed the values as params
|
|
31
|
+
configToUse = {
|
|
32
|
+
host: findParamValueFromArgs(argv, hostParam, hostParamAlias),
|
|
33
|
+
organizationId: findParamValueFromArgs(argv, orgIdParam, orgIdParamAlias),
|
|
34
|
+
authorization: findParamValueFromArgs(argv, authParam, null),
|
|
35
|
+
apiKey: findParamValueFromArgs(argv, apiKeyParam, null)
|
|
36
|
+
}
|
|
37
|
+
} else if (
|
|
38
|
+
config &&
|
|
39
|
+
config.get('host') &&
|
|
40
|
+
config.get('organizationId') &&
|
|
41
|
+
config.get('authorization') &&
|
|
42
|
+
config.get('apiKey')
|
|
43
|
+
) {
|
|
44
|
+
configToUse = {
|
|
45
|
+
host: config.get('host')?.slice(0, -1), //slice off extra / in url, will 404 on teamserver if we don't
|
|
46
|
+
organizationId: config.get('organizationId'),
|
|
47
|
+
authorization: config.get('authorization'),
|
|
48
|
+
apiKey: config.get('apiKey')
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
//return when unable to get config
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return await sendTelemetryConfigAsObject(
|
|
56
|
+
configToUse,
|
|
57
|
+
command,
|
|
58
|
+
argv,
|
|
59
|
+
result,
|
|
60
|
+
language
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const sendTelemetryConfigAsObject = async (
|
|
65
|
+
config: any,
|
|
66
|
+
command: string,
|
|
67
|
+
argv: string[],
|
|
68
|
+
result: string,
|
|
69
|
+
language: string
|
|
70
|
+
) => {
|
|
71
|
+
const obfuscatedParams = obfuscateParams(argv)
|
|
72
|
+
|
|
73
|
+
const requestBody = {
|
|
74
|
+
event: TELEMETRY_CLI_COMMANDS_EVENT,
|
|
75
|
+
details: {
|
|
76
|
+
ip_address: '',
|
|
77
|
+
account_name: '',
|
|
78
|
+
account_host: '',
|
|
79
|
+
company_domain: '',
|
|
80
|
+
command: `contrast ${command} ${obfuscatedParams}`,
|
|
81
|
+
app_id:
|
|
82
|
+
config && config.applicationId
|
|
83
|
+
? sha1Base64Value(config.applicationId)
|
|
84
|
+
: 'undefined',
|
|
85
|
+
project_id:
|
|
86
|
+
config && config.projectId
|
|
87
|
+
? sha1Base64Value(config.projectId)
|
|
88
|
+
: 'undefined',
|
|
89
|
+
language: language,
|
|
90
|
+
result: result,
|
|
91
|
+
additional_info: '',
|
|
92
|
+
timestamp: new Date().toUTCString()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return await sendTelemetryRequest(config, requestBody)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const sendTelemetryRequest = async (config: any, requestBody: any) => {
|
|
100
|
+
const client = getHttpClient(config)
|
|
101
|
+
return client
|
|
102
|
+
.postTelemetry(config, requestBody)
|
|
103
|
+
.then((res: any) => {
|
|
104
|
+
if (res.statusCode !== 200 && config.debug === true) {
|
|
105
|
+
console.log('Telemetry failed to send with status', res.statusCode)
|
|
106
|
+
}
|
|
107
|
+
return { statusCode: res.statusCode, statusMessage: res.statusMessage }
|
|
108
|
+
})
|
|
109
|
+
.catch((err: any) => {
|
|
110
|
+
return
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const obfuscateParams = (argv: string[]) => {
|
|
115
|
+
return argv
|
|
116
|
+
.join(' ')
|
|
117
|
+
.replace(/--(authorization [A-Z0-9]+)/gi, '--authorization *****')
|
|
118
|
+
.replace(/-(o [A-Z0-9-]+)/gi, '-o *****')
|
|
119
|
+
.replace(/--(organization-id [A-Z0-9-]+)/gi, '--organization-id *****')
|
|
120
|
+
.replace(/--(api-key [A-Z0-9]+)/gi, '--api-key *****')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const paramExists = (
|
|
124
|
+
argv: string[],
|
|
125
|
+
param: string,
|
|
126
|
+
paramAlias: string | null
|
|
127
|
+
) => {
|
|
128
|
+
return argv.find((arg: string) => arg === param || arg === paramAlias)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const findParamValueFromArgs = (
|
|
132
|
+
argv: string[],
|
|
133
|
+
param: string,
|
|
134
|
+
paramAlias: string | null
|
|
135
|
+
) => {
|
|
136
|
+
let paramAsValue
|
|
137
|
+
|
|
138
|
+
argv.forEach((arg: string, index: number) => {
|
|
139
|
+
if (
|
|
140
|
+
arg === param ||
|
|
141
|
+
(arg === paramAlias &&
|
|
142
|
+
argv[index + 1] !== undefined &&
|
|
143
|
+
argv[index + 1] !== null)
|
|
144
|
+
) {
|
|
145
|
+
paramAsValue = argv[index + 1]
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
return paramAsValue
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export const sha1Base64Value = (value: any) => {
|
|
153
|
+
return crypto.createHash('sha1').update(value).digest('base64')
|
|
154
|
+
}
|
|
File without changes
|