@contrast/contrast 1.0.4 → 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.
Files changed (115) hide show
  1. package/.prettierignore +0 -3
  2. package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
  3. package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
  4. package/dist/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +4 -2
  5. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
  6. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
  7. package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
  8. package/dist/audit/languageAnalysisEngine/languageAnalysisFactory.js +6 -2
  9. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +39 -1
  10. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +69 -30
  11. package/dist/audit/languageAnalysisEngine/report/models/reportOutputModel.js +24 -0
  12. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +3 -1
  13. package/dist/audit/languageAnalysisEngine/report/models/severityCountModel.js +13 -0
  14. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +2 -2
  15. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +56 -45
  16. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +65 -17
  17. package/dist/commands/audit/auditConfig.js +8 -2
  18. package/dist/commands/audit/auditController.js +9 -3
  19. package/dist/commands/audit/processAudit.js +1 -1
  20. package/dist/commands/scan/processScan.js +7 -4
  21. package/dist/commands/scan/sca/scaAnalysis.js +60 -0
  22. package/dist/common/HTTPClient.js +50 -16
  23. package/dist/common/errorHandling.js +11 -16
  24. package/dist/common/versionChecker.js +1 -1
  25. package/dist/constants/constants.js +24 -2
  26. package/dist/constants/locales.js +31 -36
  27. package/dist/constants.js +20 -0
  28. package/dist/lambda/analytics.js +11 -0
  29. package/dist/lambda/lambda.js +35 -4
  30. package/dist/lambda/types.js +13 -0
  31. package/dist/scaAnalysis/common/formatMessage.js +35 -0
  32. package/dist/scaAnalysis/common/treeUpload.js +29 -0
  33. package/dist/scaAnalysis/go/goAnalysis.js +17 -0
  34. package/dist/scaAnalysis/go/goParseDeps.js +158 -0
  35. package/dist/scaAnalysis/go/goReadDepFile.js +23 -0
  36. package/dist/scaAnalysis/java/analysis.js +105 -0
  37. package/dist/scaAnalysis/java/index.js +18 -0
  38. package/dist/scaAnalysis/java/javaBuildDepsParser.js +339 -0
  39. package/dist/scaAnalysis/python/analysis.js +41 -0
  40. package/dist/scaAnalysis/python/index.js +10 -0
  41. package/dist/scaAnalysis/ruby/analysis.js +226 -0
  42. package/dist/scaAnalysis/ruby/index.js +10 -0
  43. package/dist/scan/autoDetection.js +50 -1
  44. package/dist/scan/fileUtils.js +80 -1
  45. package/dist/scan/formatScanOutput.js +213 -0
  46. package/dist/scan/help.js +3 -1
  47. package/dist/scan/models/groupedResultsModel.js +2 -1
  48. package/dist/scan/models/scanResultsModel.js +3 -1
  49. package/dist/scan/populateProjectIdAndProjectName.js +2 -1
  50. package/dist/scan/scan.js +6 -99
  51. package/dist/scan/scanConfig.js +6 -1
  52. package/dist/scan/scanController.js +26 -7
  53. package/dist/scan/scanResults.js +20 -20
  54. package/dist/utils/commonApi.js +4 -1
  55. package/dist/utils/oraWrapper.js +5 -1
  56. package/package.json +12 -7
  57. package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
  58. package/src/audit/catalogueApplication/catalogueApplication.js +3 -16
  59. package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +11 -8
  60. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +2 -1
  61. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +2 -1
  62. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +2 -1
  63. package/src/audit/languageAnalysisEngine/languageAnalysisFactory.js +17 -5
  64. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +76 -3
  65. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +122 -40
  66. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +3 -3
  67. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +15 -11
  68. package/src/audit/languageAnalysisEngine/report/models/reportOutputModel.ts +29 -0
  69. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +12 -3
  70. package/src/audit/languageAnalysisEngine/report/models/severityCountModel.ts +16 -0
  71. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +3 -3
  72. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +87 -65
  73. package/src/audit/languageAnalysisEngine/sendSnapshot.js +78 -25
  74. package/src/commands/audit/auditConfig.ts +12 -3
  75. package/src/commands/audit/auditController.ts +9 -3
  76. package/src/commands/audit/processAudit.ts +4 -1
  77. package/src/commands/scan/processScan.js +10 -4
  78. package/src/commands/scan/sca/scaAnalysis.js +83 -0
  79. package/src/common/HTTPClient.js +65 -25
  80. package/src/common/errorHandling.ts +14 -22
  81. package/src/common/versionChecker.ts +1 -1
  82. package/src/constants/constants.js +24 -2
  83. package/src/constants/locales.js +33 -50
  84. package/src/constants.js +22 -0
  85. package/src/lambda/analytics.ts +9 -0
  86. package/src/lambda/arn.ts +2 -1
  87. package/src/lambda/lambda.ts +37 -17
  88. package/src/lambda/types.ts +35 -0
  89. package/src/lambda/utils.ts +2 -7
  90. package/src/scaAnalysis/common/formatMessage.js +38 -0
  91. package/src/scaAnalysis/common/treeUpload.js +30 -0
  92. package/src/scaAnalysis/go/goAnalysis.js +19 -0
  93. package/src/scaAnalysis/go/goParseDeps.js +203 -0
  94. package/src/scaAnalysis/go/goReadDepFile.js +32 -0
  95. package/src/scaAnalysis/java/analysis.js +142 -0
  96. package/src/scaAnalysis/java/index.js +21 -0
  97. package/src/scaAnalysis/java/javaBuildDepsParser.js +404 -0
  98. package/src/scaAnalysis/python/analysis.js +48 -0
  99. package/src/scaAnalysis/python/index.js +11 -0
  100. package/src/scaAnalysis/ruby/analysis.js +282 -0
  101. package/src/scaAnalysis/ruby/index.js +11 -0
  102. package/src/scan/autoDetection.js +58 -1
  103. package/src/scan/fileUtils.js +99 -1
  104. package/src/scan/formatScanOutput.ts +249 -0
  105. package/src/scan/help.js +3 -1
  106. package/src/scan/models/groupedResultsModel.ts +7 -5
  107. package/src/scan/models/resultContentModel.ts +2 -2
  108. package/src/scan/models/scanResultsModel.ts +5 -2
  109. package/src/scan/populateProjectIdAndProjectName.js +3 -1
  110. package/src/scan/scan.ts +8 -136
  111. package/src/scan/scanConfig.js +5 -1
  112. package/src/scan/scanController.js +30 -10
  113. package/src/scan/scanResults.js +31 -18
  114. package/src/utils/commonApi.js +4 -1
  115. package/src/utils/oraWrapper.js +6 -1
@@ -0,0 +1,404 @@
1
+ const i18n = require('i18n')
2
+ const StringBuilder = require('string-builder')
3
+ let sb = new StringBuilder()
4
+
5
+ const parseBuildDeps = (config, input) => {
6
+ const { mvnDependancyTreeOutput, projectType } = input
7
+ try {
8
+ return parseGradle(mvnDependancyTreeOutput, config, projectType)
9
+ } catch (err) {
10
+ throw new Error(i18n.__('javaParseProjectFile') + `${err.message}`)
11
+ }
12
+ }
13
+
14
+ const preParser = shavedOutput => {
15
+ let obj = []
16
+ for (let dep in shavedOutput) {
17
+ obj.push(
18
+ shavedOutput[dep]
19
+ .replace('+-', '+---')
20
+ .replace('[INFO]', '')
21
+ .replace('\\-', '\\---')
22
+ .replace(':jar:', ':')
23
+ .replace(':test', '')
24
+ .replace(':compile', '')
25
+ .replace(' +', '+')
26
+ .replace(' |', '|')
27
+ .replace(' \\', '\\')
28
+ .replace(':runtime', '')
29
+ )
30
+ }
31
+
32
+ let depTree = []
33
+ for (let x in obj) {
34
+ let nodeLevel = computeRelationToLastElement(obj[x])
35
+
36
+ let notLastLevel =
37
+ obj[x].startsWith('|') ||
38
+ obj[x].startsWith('+') ||
39
+ obj[x].startsWith('\\')
40
+
41
+ if (notLastLevel) {
42
+ if (nodeLevel === 0) {
43
+ depTree.push(obj[x])
44
+ } else {
45
+ let level = computeLevel(nodeLevel)
46
+ let validatedLevel = addIndentation(nodeLevel === 2 ? 5 : level, obj[x])
47
+ depTree.push(validatedLevel)
48
+ }
49
+ } else {
50
+ let level = computeLevel(nodeLevel)
51
+ let validatedLevel = addIndentation(nodeLevel === 3 ? 5 : level, obj[x])
52
+ depTree.push(validatedLevel)
53
+ }
54
+ }
55
+
56
+ return depTree
57
+ }
58
+
59
+ const shaveOutput = (gradleDependencyTreeOutput, projectType) => {
60
+ let shavedOutput = gradleDependencyTreeOutput.split('\n')
61
+
62
+ // console.log(projectType)
63
+
64
+ if (projectType === 'maven') {
65
+ shavedOutput = preParser(shavedOutput)
66
+ }
67
+
68
+ let obj = []
69
+ for (let key in shavedOutput) {
70
+ if (shavedOutput[key].includes('project :')) {
71
+ //skip
72
+ } else if (
73
+ shavedOutput[key].includes('+---') ||
74
+ shavedOutput[key].includes('\\---')
75
+ ) {
76
+ obj.push(shavedOutput[key])
77
+ }
78
+ }
79
+ return obj
80
+ }
81
+
82
+ const computeIndentation = element => {
83
+ let hasPlus = element.includes('+')
84
+ let hasSlash = element.includes('\\')
85
+ if (hasPlus) {
86
+ return element.substring(element.indexOf('+'))
87
+ }
88
+ if (hasSlash) {
89
+ return element.substring(element.indexOf('\\'))
90
+ }
91
+ }
92
+
93
+ const computeLevel = nodeLevel => {
94
+ let num = [5, 8, 11, 14, 17, 20]
95
+ for (let z in num) {
96
+ if (num[z] === nodeLevel) {
97
+ let n = parseInt(z)
98
+ return 5 * (n + 2)
99
+ }
100
+ }
101
+ }
102
+
103
+ const addIndentation = (number, str) => {
104
+ str = computeIndentation(str)
105
+ sb.clear() // need to clear so each dep doesn't append to the string
106
+ for (let j = 0; j < number; j++) {
107
+ sb.append(' ')
108
+ }
109
+ sb.append(str)
110
+ return sb.toString()
111
+ }
112
+
113
+ const computeRelationToLastElement = element => {
114
+ let hasPlus = element.includes('+---')
115
+ let hasSlash = element.includes('\\---')
116
+ if (hasPlus) {
117
+ return element.split('+---')[0].length
118
+ }
119
+ if (hasSlash) {
120
+ return element.split('\\---')[0].length
121
+ }
122
+ }
123
+
124
+ const stripElement = element => {
125
+ return element
126
+ .replace(/[|]/g, '')
127
+ .replace('+---', '')
128
+ .replace('\\---', '')
129
+ .replace(/[' ']/g, '')
130
+ .replace('(c)', '')
131
+ .replace('->', '@')
132
+ .replace('(*)', '')
133
+ }
134
+
135
+ const checkVersion = element => {
136
+ let version = element.split(':')
137
+ return version[version.length - 1]
138
+ }
139
+
140
+ const createElement = (element, isRoot) => {
141
+ let tree
142
+ let cleanElement = stripElement(element)
143
+ let splitGroupName = cleanElement.split(':')
144
+
145
+ let validateVersion = false
146
+ if (!element.includes('->')) {
147
+ validateVersion = true
148
+ }
149
+
150
+ tree = {
151
+ artifactID: splitGroupName[1],
152
+ group: splitGroupName[0],
153
+ version: validateVersion
154
+ ? checkVersion(cleanElement)
155
+ : splitGroupName[splitGroupName.length - 1],
156
+ scope: 'compile',
157
+ type: isRoot ? 'direct' : 'transitive',
158
+ edges: {}
159
+ }
160
+ return tree
161
+ }
162
+
163
+ const getElementHeader = element => {
164
+ let elementHeader = stripElement(element)
165
+ elementHeader = elementHeader.replace(':', '/')
166
+ elementHeader = elementHeader.replace(':', '@')
167
+
168
+ return elementHeader
169
+ }
170
+
171
+ const buildElement = (element, rootElement, parentOfCurrent, tree, isRoot) => {
172
+ let childElement = createElement(element, isRoot)
173
+ let elementHeader = getElementHeader(element)
174
+ let levelsArray = [rootElement, parentOfCurrent]
175
+ const treeNode = getNestedObject(tree, levelsArray)
176
+ const rootNode = getNestedObject(tree, [rootElement])
177
+
178
+ // eslint-disable-next-line
179
+ if (!rootNode.hasOwnProperty(elementHeader)) {
180
+ tree[rootElement][elementHeader] = childElement
181
+ }
182
+ treeNode.edges[elementHeader] = elementHeader
183
+ }
184
+
185
+ const hasChildren = (nextNodeLevel, nodeLevel) => {
186
+ if (nextNodeLevel > nodeLevel) {
187
+ return true
188
+ }
189
+ }
190
+
191
+ const lastChild = (nextNodeLevel, nodeLevel) => {
192
+ if (nextNodeLevel < nodeLevel) {
193
+ return true
194
+ }
195
+ }
196
+
197
+ const calculateLevels = (nextNodeLevel, nodeLevel) => {
198
+ return (nodeLevel - nextNodeLevel) / 5
199
+ }
200
+
201
+ const buildTree = shavedOutput => {
202
+ let tree = {}
203
+ let rootElement
204
+ let levelNodes = []
205
+
206
+ shavedOutput.forEach((element, index) => {
207
+ if (index === 0) {
208
+ // console.log(element, index)
209
+ let cleanElement = stripElement(element)
210
+ let elementHeader = getElementHeader(cleanElement)
211
+ let splitElement = element.split(' ')
212
+ let splitGroupName = splitElement[1].split(':')
213
+
214
+ let validateVersion = false
215
+ if (!element.includes('->')) {
216
+ validateVersion = true
217
+ }
218
+
219
+ tree[splitGroupName[0]] = {}
220
+ tree[splitGroupName[0]][elementHeader] = {
221
+ artifactID: splitGroupName[1],
222
+ group: splitGroupName[0],
223
+ version: validateVersion
224
+ ? checkVersion(cleanElement)
225
+ : splitElement[splitElement.length - 1],
226
+ scope: 'compile',
227
+ type: 'direct',
228
+ edges: {}
229
+ }
230
+
231
+ rootElement = splitGroupName[0]
232
+ levelNodes.push(elementHeader)
233
+ }
234
+
235
+ if (shavedOutput.length - 1 === index) {
236
+ // console.log(element, index)
237
+ const parentOfCurrent = levelNodes[levelNodes.length - 1]
238
+ let nodeLevel = computeRelationToLastElement(element)
239
+
240
+ let validateVersion = false
241
+ if (!element.includes('->')) {
242
+ validateVersion = true
243
+ }
244
+
245
+ if (nodeLevel === 0) {
246
+ let cleanElement = stripElement(element)
247
+ let elementHeader = getElementHeader(cleanElement)
248
+ let splitElement = element.split(' ')
249
+ let splitGroupName = splitElement[1].split(':')
250
+ tree[rootElement][elementHeader] = {
251
+ artifactID: splitGroupName[1],
252
+ group: splitGroupName[0],
253
+ version: validateVersion
254
+ ? checkVersion(cleanElement)
255
+ : splitElement[splitElement.length - 1],
256
+ scope: 'compile',
257
+ type: 'direct',
258
+ edges: {}
259
+ }
260
+ } else {
261
+ buildElement(element, rootElement, parentOfCurrent, tree)
262
+ }
263
+ }
264
+
265
+ if (index >= 1 && index < shavedOutput.length - 1) {
266
+ let nodeLevel = computeRelationToLastElement(element)
267
+ let nextNodeLevel = computeRelationToLastElement(shavedOutput[index + 1])
268
+ const parentOfCurrent = levelNodes[levelNodes.length - 1]
269
+
270
+ let isRoot = false
271
+ if (nodeLevel === 0) {
272
+ isRoot = true
273
+ }
274
+
275
+ // useful for debugging
276
+ // console.log(
277
+ // element,
278
+ // index,
279
+ // 'nodeLevel:',
280
+ // nodeLevel,
281
+ // 'nextNodeLevel:',
282
+ // nextNodeLevel,
283
+ // 'parentofCurrent:',
284
+ // parentOfCurrent
285
+ // )
286
+
287
+ if (isRoot) {
288
+ let cleanElement = stripElement(element)
289
+ let elementHeader = getElementHeader(cleanElement)
290
+ let splitElement = element.split(' ')
291
+ let splitGroupName = splitElement[1].split(':')
292
+
293
+ let validateVersion = false
294
+ if (!element.includes('->')) {
295
+ validateVersion = true
296
+ }
297
+
298
+ tree[rootElement][elementHeader] = {
299
+ artifactID: splitGroupName[1],
300
+ group: splitGroupName[0],
301
+ version: validateVersion
302
+ ? checkVersion(cleanElement)
303
+ : splitElement[splitElement.length - 1],
304
+ scope: 'compile',
305
+ type: 'direct',
306
+ edges: {}
307
+ }
308
+ levelNodes.push(elementHeader)
309
+ return
310
+ }
311
+
312
+ let elementHeader = getElementHeader(element)
313
+ buildElement(element, rootElement, parentOfCurrent, tree, isRoot)
314
+
315
+ if (hasChildren(nextNodeLevel, nodeLevel)) {
316
+ buildElement(element, rootElement, parentOfCurrent, tree, isRoot)
317
+ levelNodes.push(elementHeader)
318
+ }
319
+
320
+ if (lastChild(nextNodeLevel, nodeLevel)) {
321
+ let levelDifference = calculateLevels(nextNodeLevel, nodeLevel)
322
+ if (levelDifference === 0) {
323
+ levelNodes.pop()
324
+ } else {
325
+ let i
326
+ for (i = 0; i < levelDifference; i++) {
327
+ levelNodes.pop()
328
+ }
329
+ }
330
+ }
331
+ }
332
+ })
333
+
334
+ return tree
335
+ }
336
+
337
+ const getNestedObject = (nestedObj, pathArr) => {
338
+ return pathArr.reduce(
339
+ (obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined),
340
+ nestedObj
341
+ )
342
+ }
343
+
344
+ // emit any "+--- project :" within the tree
345
+ const parseSubProject = shavedOutput => {
346
+ let obj = []
347
+ for (let key in shavedOutput) {
348
+ if (!shavedOutput[key].includes('project')) {
349
+ obj.push(shavedOutput[key])
350
+ }
351
+ }
352
+ return obj
353
+ }
354
+
355
+ const validateIndentation = shavedOutput => {
356
+ let validatedTree = []
357
+ shavedOutput.forEach((element, index) => {
358
+ let nextNodeLevel
359
+ let nodeLevel = computeRelationToLastElement(element)
360
+ if (shavedOutput[index + 1] !== undefined) {
361
+ nextNodeLevel = computeRelationToLastElement(shavedOutput[index + 1])
362
+ }
363
+ if (index === 0) {
364
+ validatedTree.push(shavedOutput[index])
365
+ validatedTree.push(shavedOutput[index + 1])
366
+ } else if (nextNodeLevel > nodeLevel + 5) {
367
+ return
368
+ } else {
369
+ validatedTree.push(shavedOutput[index + 1])
370
+ }
371
+ })
372
+ validatedTree.pop()
373
+ return validatedTree
374
+ }
375
+
376
+ const parseGradle = (gradleDependencyTreeOutput, config, projectType) => {
377
+ let shavedOutput = shaveOutput(gradleDependencyTreeOutput, projectType)
378
+
379
+ if (config.subProject) {
380
+ let subProject = parseSubProject(shavedOutput)
381
+ let validatedOutput = validateIndentation(subProject)
382
+ return buildTree(validatedOutput)
383
+ } else {
384
+ let validatedOutput = validateIndentation(shavedOutput)
385
+ return buildTree(validatedOutput)
386
+ }
387
+ }
388
+
389
+ module.exports = {
390
+ parseBuildDeps,
391
+ shaveOutput,
392
+ validateIndentation,
393
+ calculateLevels,
394
+ lastChild,
395
+ hasChildren,
396
+ getElementHeader,
397
+ createElement,
398
+ stripElement,
399
+ checkVersion,
400
+ computeRelationToLastElement,
401
+ addIndentation,
402
+ computeLevel,
403
+ computeIndentation
404
+ }
@@ -0,0 +1,48 @@
1
+ const multiReplace = require('string-multiple-replace')
2
+ const fs = require('fs')
3
+
4
+ const readAndParseProjectFile = projectPath => {
5
+ const filePath = filePathForWindows(projectPath + '/Pipfile')
6
+ const pipFile = fs.readFileSync(filePath, 'utf8')
7
+
8
+ const matcherObj = { '"': '' }
9
+ const sequencer = ['"']
10
+ const parsedPipfile = multiReplace(pipFile, matcherObj, sequencer)
11
+
12
+ const pythonArray = parsedPipfile.split('\n')
13
+
14
+ return pythonArray.filter(element => element !== '' && !element.includes('#'))
15
+ }
16
+
17
+ const readAndParseLockFile = projectPath => {
18
+ const filePath = filePathForWindows(projectPath + '/Pipfile.lock')
19
+ const lockFile = fs.readFileSync(filePath, 'utf8')
20
+ let parsedPipLock = JSON.parse(lockFile)
21
+ parsedPipLock['defaults'] = parsedPipLock['default']
22
+ return parsedPipLock
23
+ }
24
+
25
+ const getPythonDeps = config => {
26
+ try {
27
+ const parseProject = readAndParseProjectFile(config.projectPath)
28
+ const parsePip = readAndParseLockFile(config.projectPath)
29
+
30
+ return { pipfileLock: parseProject, pipfilDependanceies: parsePip }
31
+ } catch (err) {
32
+ console.log(err.message.toString())
33
+ process.exit(1)
34
+ }
35
+ }
36
+
37
+ const filePathForWindows = path => {
38
+ if (process.platform === 'win32') {
39
+ path = path.replace(/\//g, '\\')
40
+ }
41
+ return path
42
+ }
43
+
44
+ module.exports = {
45
+ getPythonDeps,
46
+ readAndParseProjectFile,
47
+ readAndParseLockFile
48
+ }
@@ -0,0 +1,11 @@
1
+ const { createPythonTSMessage } = require('../common/formatMessage')
2
+ const { getPythonDeps } = require('./analysis')
3
+
4
+ const pythonAnalysis = (config, languageFiles) => {
5
+ const pythonDeps = getPythonDeps(config, languageFiles.PYTHON)
6
+ return createPythonTSMessage(pythonDeps)
7
+ }
8
+
9
+ module.exports = {
10
+ pythonAnalysis
11
+ }