@contrast/contrast 1.0.0 → 1.0.3
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 +3 -0
- package/README.md +115 -78
- package/dist/audit/AnalysisEngine.js +37 -0
- package/dist/audit/catalogueApplication/catalogueApplication.js +36 -0
- package/dist/audit/dotnetAnalysisEngine/index.js +25 -0
- package/dist/audit/dotnetAnalysisEngine/parseLockFileContents.js +35 -0
- package/dist/audit/dotnetAnalysisEngine/parseProjectFileContents.js +15 -0
- package/dist/audit/dotnetAnalysisEngine/readLockFileContents.js +18 -0
- package/dist/audit/dotnetAnalysisEngine/readProjectFileContents.js +14 -0
- package/dist/audit/dotnetAnalysisEngine/sanitizer.js +9 -0
- package/dist/audit/goAnalysisEngine/index.js +17 -0
- package/dist/audit/goAnalysisEngine/parseProjectFileContents.js +164 -0
- package/dist/audit/goAnalysisEngine/readProjectFileContents.js +21 -0
- package/dist/audit/goAnalysisEngine/sanitizer.js +5 -0
- package/dist/audit/javaAnalysisEngine/index.js +34 -0
- package/dist/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +153 -0
- package/dist/audit/javaAnalysisEngine/parseProjectFileContents.js +353 -0
- package/dist/audit/javaAnalysisEngine/readProjectFileContents.js +98 -0
- package/dist/audit/javaAnalysisEngine/sanitizer.js +5 -0
- package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +24 -0
- package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +24 -0
- package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +35 -0
- package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +23 -0
- package/dist/audit/languageAnalysisEngine/commonApi.js +18 -0
- package/dist/audit/languageAnalysisEngine/constants.js +20 -0
- package/dist/audit/languageAnalysisEngine/filterProjectPath.js +20 -0
- package/dist/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +25 -0
- package/dist/audit/languageAnalysisEngine/getProjectRootFilenames.js +39 -0
- package/dist/audit/languageAnalysisEngine/index.js +39 -0
- package/dist/audit/languageAnalysisEngine/langugageAnalysisFactory.js +95 -0
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +121 -0
- package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +17 -0
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +257 -0
- package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +81 -0
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +133 -0
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +41 -0
- package/dist/audit/languageAnalysisEngine/util/capabilities.js +11 -0
- package/dist/audit/languageAnalysisEngine/util/generalAPI.js +39 -0
- package/dist/audit/languageAnalysisEngine/util/requestUtils.js +14 -0
- package/dist/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +40 -0
- package/dist/audit/nodeAnalysisEngine/index.js +31 -0
- package/dist/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +18 -0
- package/dist/audit/nodeAnalysisEngine/parseYarn2LockFileContents.js +51 -0
- package/dist/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +18 -0
- package/dist/audit/nodeAnalysisEngine/readNPMLockFileContents.js +17 -0
- package/dist/audit/nodeAnalysisEngine/readProjectFileContents.js +14 -0
- package/dist/audit/nodeAnalysisEngine/readYarnLockFileContents.js +24 -0
- package/dist/audit/nodeAnalysisEngine/sanitizer.js +9 -0
- package/dist/audit/phpAnalysisEngine/index.js +23 -0
- package/dist/audit/phpAnalysisEngine/parseLockFileContents.js +52 -0
- package/dist/audit/phpAnalysisEngine/readLockFileContents.js +13 -0
- package/dist/audit/phpAnalysisEngine/readProjectFileContents.js +16 -0
- package/dist/audit/phpAnalysisEngine/sanitizer.js +5 -0
- package/dist/audit/pythonAnalysisEngine/index.js +25 -0
- package/dist/audit/pythonAnalysisEngine/parsePipfileLockContents.js +17 -0
- package/dist/audit/pythonAnalysisEngine/parseProjectFileContents.js +21 -0
- package/dist/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +13 -0
- package/dist/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +14 -0
- package/dist/audit/pythonAnalysisEngine/sanitizer.js +7 -0
- package/dist/audit/rubyAnalysisEngine/index.js +25 -0
- package/dist/audit/rubyAnalysisEngine/parseGemfileLockContents.js +176 -0
- package/dist/audit/rubyAnalysisEngine/parsedGemfile.js +22 -0
- package/dist/audit/rubyAnalysisEngine/readGemfileContents.js +14 -0
- package/dist/audit/rubyAnalysisEngine/readGemfileLockContents.js +14 -0
- package/dist/audit/rubyAnalysisEngine/sanitizer.js +6 -0
- package/dist/commands/audit/auditConfig.js +25 -0
- package/dist/commands/audit/auditController.js +31 -0
- package/dist/commands/audit/help.js +52 -0
- package/dist/commands/audit/processAudit.js +18 -0
- package/dist/commands/audit/saveFile.js +11 -0
- package/dist/commands/auth/auth.js +20 -2
- package/dist/commands/config/config.js +19 -8
- package/dist/commands/scan/processScan.js +9 -13
- package/dist/common/HTTPClient.js +112 -13
- package/dist/common/errorHandling.js +65 -1
- package/dist/common/versionChecker.js +30 -0
- package/dist/constants/constants.js +4 -2
- package/dist/constants/lambda.js +32 -4
- package/dist/constants/locales.js +60 -21
- package/dist/constants.js +181 -21
- package/dist/index.js +50 -23
- package/dist/lambda/aws.js +14 -11
- package/dist/lambda/help.js +4 -0
- package/dist/lambda/lambda.js +50 -27
- package/dist/lambda/lambdaUtils.js +72 -0
- package/dist/lambda/logUtils.js +11 -1
- package/dist/lambda/scanDetailCompletion.js +4 -4
- package/dist/lambda/scanRequest.js +11 -5
- package/dist/lambda/utils.js +110 -53
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scan/autoDetection.js +0 -32
- package/dist/scan/fileUtils.js +1 -1
- package/dist/scan/help.js +14 -40
- package/dist/scan/populateProjectIdAndProjectName.js +5 -0
- package/dist/scan/saveResults.js +14 -0
- package/dist/scan/scan.js +105 -40
- package/dist/scan/scanConfig.js +39 -0
- package/dist/scan/scanController.js +19 -16
- package/dist/scan/scanResults.js +24 -16
- package/dist/utils/commonApi.js +3 -3
- package/dist/utils/paramsUtil/commandlineParams.js +1 -20
- package/dist/utils/paramsUtil/paramHandler.js +3 -6
- package/dist/utils/parsedCLIOptions.js +14 -8
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +26 -21
- package/src/audit/AnalysisEngine.js +103 -0
- package/src/audit/catalogueApplication/catalogueApplication.js +42 -0
- package/src/audit/dotnetAnalysisEngine/index.js +26 -0
- package/src/audit/dotnetAnalysisEngine/parseLockFileContents.js +47 -0
- package/src/audit/dotnetAnalysisEngine/parseProjectFileContents.js +29 -0
- package/src/audit/dotnetAnalysisEngine/readLockFileContents.js +30 -0
- package/src/audit/dotnetAnalysisEngine/readProjectFileContents.js +26 -0
- package/src/audit/dotnetAnalysisEngine/sanitizer.js +11 -0
- package/src/audit/goAnalysisEngine/index.js +18 -0
- package/src/audit/goAnalysisEngine/parseProjectFileContents.js +209 -0
- package/src/audit/goAnalysisEngine/readProjectFileContents.js +31 -0
- package/src/audit/goAnalysisEngine/sanitizer.js +7 -0
- package/src/audit/javaAnalysisEngine/index.js +41 -0
- package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +222 -0
- package/src/audit/javaAnalysisEngine/parseProjectFileContents.js +420 -0
- package/src/audit/javaAnalysisEngine/readProjectFileContents.js +141 -0
- package/src/audit/javaAnalysisEngine/sanitizer.js +6 -0
- package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +35 -0
- package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +41 -0
- package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +54 -0
- package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +32 -0
- package/src/audit/languageAnalysisEngine/commonApi.js +20 -0
- package/src/audit/languageAnalysisEngine/constants.js +23 -0
- package/src/audit/languageAnalysisEngine/filterProjectPath.js +21 -0
- package/src/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +41 -0
- package/src/audit/languageAnalysisEngine/getProjectRootFilenames.js +72 -0
- package/src/audit/languageAnalysisEngine/index.js +45 -0
- package/src/audit/languageAnalysisEngine/langugageAnalysisFactory.js +126 -0
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +177 -0
- package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +27 -0
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +303 -0
- package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +124 -0
- package/src/audit/languageAnalysisEngine/report/reportingFeature.js +190 -0
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +51 -0
- package/src/audit/languageAnalysisEngine/util/capabilities.js +12 -0
- package/src/audit/languageAnalysisEngine/util/generalAPI.js +43 -0
- package/src/audit/languageAnalysisEngine/util/requestUtils.js +17 -0
- package/src/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +49 -0
- package/src/audit/nodeAnalysisEngine/index.js +35 -0
- package/src/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +20 -0
- package/src/audit/nodeAnalysisEngine/parseYarn2LockFileContents.js +63 -0
- package/src/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +26 -0
- package/src/audit/nodeAnalysisEngine/readNPMLockFileContents.js +23 -0
- package/src/audit/nodeAnalysisEngine/readProjectFileContents.js +27 -0
- package/src/audit/nodeAnalysisEngine/readYarnLockFileContents.js +36 -0
- package/src/audit/nodeAnalysisEngine/sanitizer.js +11 -0
- package/src/audit/phpAnalysisEngine/index.js +27 -0
- package/src/audit/phpAnalysisEngine/parseLockFileContents.js +60 -0
- package/src/audit/phpAnalysisEngine/readLockFileContents.js +14 -0
- package/src/audit/phpAnalysisEngine/readProjectFileContents.js +25 -0
- package/src/audit/phpAnalysisEngine/sanitizer.js +4 -0
- package/src/audit/pythonAnalysisEngine/index.js +55 -0
- package/src/audit/pythonAnalysisEngine/parsePipfileLockContents.js +23 -0
- package/src/audit/pythonAnalysisEngine/parseProjectFileContents.js +33 -0
- package/src/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +16 -0
- package/src/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +22 -0
- package/src/audit/pythonAnalysisEngine/sanitizer.js +9 -0
- package/src/audit/rubyAnalysisEngine/index.js +30 -0
- package/src/audit/rubyAnalysisEngine/parseGemfileLockContents.js +215 -0
- package/src/audit/rubyAnalysisEngine/parsedGemfile.js +39 -0
- package/src/audit/rubyAnalysisEngine/readGemfileContents.js +18 -0
- package/src/audit/rubyAnalysisEngine/readGemfileLockContents.js +17 -0
- package/src/audit/rubyAnalysisEngine/sanitizer.js +8 -0
- package/src/commands/audit/auditConfig.ts +30 -0
- package/src/commands/audit/auditController.ts +31 -0
- package/src/commands/audit/help.ts +48 -0
- package/src/commands/audit/processAudit.ts +18 -0
- package/src/commands/audit/saveFile.ts +6 -0
- package/src/commands/auth/auth.js +26 -2
- package/src/commands/config/config.js +22 -8
- package/src/commands/scan/processScan.js +9 -13
- package/src/common/HTTPClient.js +149 -14
- package/src/common/errorHandling.ts +85 -2
- package/src/common/versionChecker.ts +39 -0
- package/src/constants/constants.js +5 -4
- package/src/constants/lambda.js +45 -4
- package/src/constants/locales.js +76 -26
- package/src/constants.js +204 -23
- package/src/index.ts +67 -27
- package/src/lambda/aws.ts +13 -12
- package/src/lambda/help.ts +4 -0
- package/src/lambda/lambda.ts +53 -34
- package/src/lambda/lambdaUtils.ts +111 -0
- package/src/lambda/logUtils.ts +19 -1
- package/src/lambda/scanDetailCompletion.ts +4 -4
- package/src/lambda/scanRequest.ts +13 -11
- package/src/lambda/utils.ts +149 -81
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scan/autoDetection.js +0 -29
- package/src/scan/fileUtils.js +1 -1
- package/src/scan/help.js +14 -45
- package/src/scan/populateProjectIdAndProjectName.js +5 -0
- package/src/scan/saveResults.js +14 -0
- package/src/scan/scan.js +127 -58
- package/src/scan/scanConfig.js +54 -0
- package/src/scan/scanController.js +22 -15
- package/src/scan/scanResults.js +32 -19
- package/src/utils/commonApi.js +2 -3
- package/src/utils/getConfig.ts +2 -0
- package/src/utils/paramsUtil/commandlineParams.js +1 -26
- package/src/utils/paramsUtil/paramHandler.js +3 -7
- package/src/utils/parsedCLIOptions.js +11 -9
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/dist/lambda/scanDetail.js +0 -30
- package/dist/scan/fileFinder.js +0 -15
- package/dist/utils/paramsUtil/yamlParams.js +0 -6
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const child_process = require('child_process')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const i18n = require('i18n')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
module.exports = exports = (
|
|
7
|
+
{ language: { projectFilePath }, java },
|
|
8
|
+
next,
|
|
9
|
+
config
|
|
10
|
+
) => {
|
|
11
|
+
let cmdStdout
|
|
12
|
+
let cwd
|
|
13
|
+
let timeout
|
|
14
|
+
let javaProject = ''
|
|
15
|
+
let mvn_settings = ''
|
|
16
|
+
const maven = 'Maven'
|
|
17
|
+
const gradle = 'Gradle'
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
if (projectFilePath.includes('pom.xml')) {
|
|
21
|
+
javaProject = maven
|
|
22
|
+
cwd = projectFilePath.replace('pom.xml', '')
|
|
23
|
+
} else if (projectFilePath.includes('build.gradle')) {
|
|
24
|
+
javaProject = gradle
|
|
25
|
+
cwd = projectFilePath.replace('build.gradle', '')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// timeout is in milliseconds and 2.30 mintues was choses as when tested against
|
|
29
|
+
// Spring-boot (https://github.com/spring-projects/spring-boot) a complex project that was the
|
|
30
|
+
// average time for a first run when it had to download projects then build tree
|
|
31
|
+
timeout = 960000
|
|
32
|
+
|
|
33
|
+
// A sample of this output can be found
|
|
34
|
+
// in the java test data/mvnCmdResults.text
|
|
35
|
+
if (javaProject === maven) {
|
|
36
|
+
// Allow users to provide a custom location for their settings.xml
|
|
37
|
+
if (config.mavenSettingsPath) {
|
|
38
|
+
mvn_settings = ' -s ' + config.mavenSettingsPath
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (config.betaUnifiedJavaParser) {
|
|
42
|
+
cmdStdout = child_process.execSync(
|
|
43
|
+
'mvn dependency:tree -B' + mvn_settings,
|
|
44
|
+
{
|
|
45
|
+
cwd,
|
|
46
|
+
timeout
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
} else {
|
|
50
|
+
cmdStdout = child_process.execSync(
|
|
51
|
+
'mvn dependency:tree -DoutputType=dot -B' + mvn_settings,
|
|
52
|
+
{
|
|
53
|
+
cwd,
|
|
54
|
+
timeout
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
java.mvnDependancyTreeOutput = cmdStdout.toString()
|
|
59
|
+
} else if (javaProject === gradle) {
|
|
60
|
+
// path.sep is user here to either execute as "./gradlew" for UNIX/Linux/MacOS
|
|
61
|
+
// & ".\gradlew" for Windows
|
|
62
|
+
// Check if the user has specified a sub-project
|
|
63
|
+
if (config.subProject) {
|
|
64
|
+
cmdStdout = child_process.execSync(
|
|
65
|
+
'.' +
|
|
66
|
+
path.sep +
|
|
67
|
+
'gradlew :' +
|
|
68
|
+
config.subProject +
|
|
69
|
+
':dependencies --configuration runtimeClasspath',
|
|
70
|
+
{
|
|
71
|
+
cwd,
|
|
72
|
+
timeout
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
} else {
|
|
76
|
+
cmdStdout = child_process.execSync(
|
|
77
|
+
'.' +
|
|
78
|
+
path.sep +
|
|
79
|
+
'gradlew dependencies --configuration runtimeClasspath',
|
|
80
|
+
{
|
|
81
|
+
cwd,
|
|
82
|
+
timeout
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
if (
|
|
87
|
+
cmdStdout
|
|
88
|
+
.toString()
|
|
89
|
+
.includes(
|
|
90
|
+
"runtimeClasspath - Runtime classpath of source set 'main'.\n" +
|
|
91
|
+
'No dependencies'
|
|
92
|
+
)
|
|
93
|
+
) {
|
|
94
|
+
cmdStdout = child_process.execSync(
|
|
95
|
+
'.' + path.sep + 'gradlew dependencies',
|
|
96
|
+
{
|
|
97
|
+
cwd,
|
|
98
|
+
timeout
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
java.mvnDependancyTreeOutput = cmdStdout.toString()
|
|
103
|
+
}
|
|
104
|
+
next()
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (javaProject === maven) {
|
|
107
|
+
try {
|
|
108
|
+
child_process.execSync('mvn --version', {
|
|
109
|
+
cwd,
|
|
110
|
+
timeout
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
next(
|
|
114
|
+
new Error(
|
|
115
|
+
i18n.__('mavenDependencyTreeNonZero', cwd, `${err.message}`)
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
} catch (mvnErr) {
|
|
119
|
+
next(
|
|
120
|
+
new Error(i18n.__('mavenNotInstalledError', cwd, `${mvnErr.message}`))
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
} else if (javaProject === gradle) {
|
|
124
|
+
if (
|
|
125
|
+
fs.existsSync(cwd + 'gradlew') ||
|
|
126
|
+
fs.existsSync(cwd + 'gradlew.bat')
|
|
127
|
+
) {
|
|
128
|
+
next(
|
|
129
|
+
new Error(
|
|
130
|
+
i18n.__('gradleDependencyTreeNonZero', cwd, `${err.message}`)
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
} else {
|
|
134
|
+
next(
|
|
135
|
+
new Error(i18n.__('gradleWrapperUnavailable', cwd, `${err.message}`))
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const i18n = require('i18n')
|
|
2
|
+
/**
|
|
3
|
+
* Checks that the list of languages and files that has been reduced doesn't
|
|
4
|
+
* contain more than one identified language.
|
|
5
|
+
*/
|
|
6
|
+
module.exports = exports = (analysis, next) => {
|
|
7
|
+
const { languageAnalysis } = analysis
|
|
8
|
+
try {
|
|
9
|
+
checkForMultipleIdentifiedLanguages(languageAnalysis.identifiedLanguages)
|
|
10
|
+
} catch (err) {
|
|
11
|
+
next(err)
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
next()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const checkForMultipleIdentifiedLanguages = identifiedLanguages => {
|
|
18
|
+
if (Object.keys(identifiedLanguages).length > 1) {
|
|
19
|
+
// Handle the error case where multiple languages have been identified
|
|
20
|
+
let errMsg = i18n.__('languageAnalysisMultipleLanguages1')
|
|
21
|
+
|
|
22
|
+
for (const [language, { projectFilenames }] of Object.entries(
|
|
23
|
+
identifiedLanguages
|
|
24
|
+
)) {
|
|
25
|
+
errMsg += `\t${language}: ${projectFilenames.join(', ')}\n`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
errMsg += i18n.__('languageAnalysisMultipleLanguages2', "'project_path'")
|
|
29
|
+
|
|
30
|
+
throw new Error(errMsg)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//For testing purposes
|
|
35
|
+
exports.checkForMultipleIdentifiedLanguages = checkForMultipleIdentifiedLanguages
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const i18n = require('i18n')
|
|
2
|
+
/**
|
|
3
|
+
* Checks that the list of languages and files that has been reduced doesn't
|
|
4
|
+
* contain more than one project file for any identified language.
|
|
5
|
+
*/
|
|
6
|
+
module.exports = exports = (analysis, next) => {
|
|
7
|
+
const { languageAnalysis } = analysis
|
|
8
|
+
try {
|
|
9
|
+
checkForMultipleIdentifiedProjectFiles(languageAnalysis.identifiedLanguages)
|
|
10
|
+
} catch (err) {
|
|
11
|
+
next(err)
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
next()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const checkForMultipleIdentifiedProjectFiles = identifiedLanguages => {
|
|
18
|
+
// Handle the error case where only a single language has been identified...
|
|
19
|
+
if (Object.keys(identifiedLanguages).length == 1) {
|
|
20
|
+
let { projectFilenames } = Object.values(identifiedLanguages)[0]
|
|
21
|
+
|
|
22
|
+
// ...but multiple project files for that language have been found
|
|
23
|
+
if (projectFilenames.length > 1) {
|
|
24
|
+
const [language] = Object.keys(identifiedLanguages)
|
|
25
|
+
projectFilenames = projectFilenames.join(', ')
|
|
26
|
+
|
|
27
|
+
// NOTE : Quotation marks for language needs to be added back in (this includes tests)
|
|
28
|
+
throw new Error(
|
|
29
|
+
i18n.__(
|
|
30
|
+
'languageAnalysisProjectFiles',
|
|
31
|
+
language,
|
|
32
|
+
projectFilenames,
|
|
33
|
+
"'project_path'"
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//For testing purposes
|
|
41
|
+
exports.checkForMultipleIdentifiedProjectFiles = checkForMultipleIdentifiedProjectFiles
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const i18n = require('i18n')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks that a project has a lock file
|
|
5
|
+
*/
|
|
6
|
+
module.exports = exports = (analysis, next) => {
|
|
7
|
+
try {
|
|
8
|
+
const { languageAnalysis } = analysis
|
|
9
|
+
//.NET and NODE both need lock files. currently JAVA and GO do not
|
|
10
|
+
// need a lock file so if lang is JAVA / GO just go to next
|
|
11
|
+
if (
|
|
12
|
+
Object.getOwnPropertyNames(languageAnalysis.identifiedLanguages)[0] ===
|
|
13
|
+
'JAVA' ||
|
|
14
|
+
Object.getOwnPropertyNames(languageAnalysis.identifiedLanguages)[0] ===
|
|
15
|
+
'GO'
|
|
16
|
+
) {
|
|
17
|
+
next()
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
checkForLockFile(languageAnalysis.identifiedLanguages)
|
|
21
|
+
} catch (err) {
|
|
22
|
+
next(err)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
next()
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const checkForLockFile = identifiedLanguages => {
|
|
30
|
+
// Handle the error case where only a single language has been identified...
|
|
31
|
+
if (Object.keys(identifiedLanguages).length == 1) {
|
|
32
|
+
let { lockFilenames } = Object.values(identifiedLanguages)[0]
|
|
33
|
+
|
|
34
|
+
// ...but no lock files for that language have been found
|
|
35
|
+
if (lockFilenames.length == 0) {
|
|
36
|
+
const [language] = Object.keys(identifiedLanguages)
|
|
37
|
+
throw new Error(i18n.__('languageAnalysisHasNoLockFile', language))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (lockFilenames.length > 1) {
|
|
41
|
+
const [language] = Object.keys(identifiedLanguages)
|
|
42
|
+
throw new Error(
|
|
43
|
+
i18n.__(
|
|
44
|
+
'languageAnalysisHasMultipleLockFiles',
|
|
45
|
+
language,
|
|
46
|
+
String(lockFilenames)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//For testing purposes
|
|
54
|
+
exports.checkForLockFile = checkForLockFile
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const i18n = require('i18n')
|
|
2
|
+
/**
|
|
3
|
+
* Checks that a single identified language in the list of languages and files
|
|
4
|
+
* that has been reduced has a single project file. This is important in the
|
|
5
|
+
* (uncommon) case that a project has a lock file without a project file.
|
|
6
|
+
*/
|
|
7
|
+
module.exports = exports = (analysis, next) => {
|
|
8
|
+
const { languageAnalysis } = analysis
|
|
9
|
+
try {
|
|
10
|
+
checkIdentifiedLanguageHasProjectFile(languageAnalysis.identifiedLanguages)
|
|
11
|
+
} catch (err) {
|
|
12
|
+
next(err)
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
next()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const checkIdentifiedLanguageHasProjectFile = identifiedLanguages => {
|
|
19
|
+
// Handle the error case where only a single language has been identified...
|
|
20
|
+
if (Object.keys(identifiedLanguages).length == 1) {
|
|
21
|
+
let { projectFilenames } = Object.values(identifiedLanguages)[0]
|
|
22
|
+
|
|
23
|
+
// ...but no project files for that language have been found
|
|
24
|
+
if (projectFilenames.length == 0) {
|
|
25
|
+
const [language] = Object.keys(identifiedLanguages)
|
|
26
|
+
throw new Error(i18n.__('languageAnalysisProjectFileError', language))
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//For testing purposes
|
|
32
|
+
exports.checkIdentifiedLanguageHasProjectFile = checkIdentifiedLanguageHasProjectFile
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { getHttpClient } = require('../../utils/commonApi')
|
|
2
|
+
|
|
3
|
+
const returnAppId = async config => {
|
|
4
|
+
const client = getHttpClient(config)
|
|
5
|
+
let appId
|
|
6
|
+
|
|
7
|
+
await client.getAppId(config).then(res => {
|
|
8
|
+
if (res.body) {
|
|
9
|
+
let obj = res.body['applications']
|
|
10
|
+
if (obj) {
|
|
11
|
+
appId = obj.length === 0 ? '' : obj[0].app_id
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
return appId
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
returnAppId: returnAppId
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Language identifiers
|
|
2
|
+
const NODE = 'NODE'
|
|
3
|
+
const JAVASCRIPT = 'JAVASCRIPT'
|
|
4
|
+
const DOTNET = 'DOTNET'
|
|
5
|
+
const JAVA = 'JAVA'
|
|
6
|
+
const RUBY = 'RUBY'
|
|
7
|
+
const PYTHON = 'PYTHON'
|
|
8
|
+
const GO = 'GO'
|
|
9
|
+
// we set the langauge as Node instead of PHP since we're using the Node engine in TS
|
|
10
|
+
const PHP = 'PHP'
|
|
11
|
+
|
|
12
|
+
const LOW = 'LOW'
|
|
13
|
+
const MEDIUM = 'MEDIUM'
|
|
14
|
+
const HIGH = 'HIGH'
|
|
15
|
+
const CRITICAL = 'CRITICAL'
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
|
|
19
|
+
LOW: LOW,
|
|
20
|
+
MEDIUM: MEDIUM,
|
|
21
|
+
HIGH: HIGH,
|
|
22
|
+
CRITICAL: CRITICAL
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
function resolveFilePath(filepath) {
|
|
4
|
+
if (filepath[0] === '~') {
|
|
5
|
+
return path.join(process.env.HOME, filepath.slice(1))
|
|
6
|
+
}
|
|
7
|
+
return filepath
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const returnProjectPath = () => {
|
|
11
|
+
if (process.env.PWD !== (undefined || null || 'undefined')) {
|
|
12
|
+
return process.env.PWD
|
|
13
|
+
} else {
|
|
14
|
+
return process.argv[process.argv.indexOf('--project_path') + 1]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
returnProjectPath: returnProjectPath,
|
|
20
|
+
resolveFilePath: resolveFilePath
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Assemble analysis results into a common object to provide
|
|
5
|
+
* language, project file name and paths
|
|
6
|
+
*/
|
|
7
|
+
module.exports = exports = (analysis, next) => {
|
|
8
|
+
const { projectPath, languageAnalysis } = analysis
|
|
9
|
+
languageAnalysis.identifiedLanguageInfo = getIdentifiedLanguageInfo(
|
|
10
|
+
projectPath,
|
|
11
|
+
languageAnalysis.identifiedLanguages
|
|
12
|
+
)
|
|
13
|
+
next()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const getIdentifiedLanguageInfo = (projectPath, identifiedLanguages) => {
|
|
17
|
+
const [language] = Object.keys(identifiedLanguages)
|
|
18
|
+
const {
|
|
19
|
+
projectFilenames: [projectFilename],
|
|
20
|
+
lockFilenames: [lockFilename]
|
|
21
|
+
} = Object.values(identifiedLanguages)[0]
|
|
22
|
+
|
|
23
|
+
let identifiedLanguageInfo = {
|
|
24
|
+
language,
|
|
25
|
+
projectFilename,
|
|
26
|
+
projectFilePath: path.join(projectPath, projectFilename)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (lockFilename) {
|
|
30
|
+
identifiedLanguageInfo = {
|
|
31
|
+
...identifiedLanguageInfo,
|
|
32
|
+
lockFilename,
|
|
33
|
+
lockFilePath: path.join(projectPath, lockFilename)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return identifiedLanguageInfo
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//For testing purposes
|
|
41
|
+
exports.getIdentifiedLanguageInfo = getIdentifiedLanguageInfo
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const i18n = require('i18n')
|
|
4
|
+
/**
|
|
5
|
+
* Will get the filenames from the project path provided to the SCA CLI tool. If
|
|
6
|
+
* the project path points to a file and not a directory will return the
|
|
7
|
+
* filename in the same fashion as if a directory had been read.
|
|
8
|
+
*
|
|
9
|
+
* Will fail and throw for a manner of reasons when doing file/directory
|
|
10
|
+
* inspection.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} projectPath - The path to a projects root directory or a
|
|
13
|
+
* specific project file
|
|
14
|
+
*
|
|
15
|
+
* @return {string[]} List of filenames associated with a projects root
|
|
16
|
+
* directory or the name of the specific project file if that was provided to
|
|
17
|
+
* the 'projectPath' parameter
|
|
18
|
+
*
|
|
19
|
+
* @throws {Error} If the project path doesn't exist
|
|
20
|
+
* @throws {Error} If the project path information can't be collected
|
|
21
|
+
* @throws {Error} If a non-file or non-directory inspected
|
|
22
|
+
*/
|
|
23
|
+
module.exports = exports = (analysis, next) => {
|
|
24
|
+
const { projectPath, languageAnalysis } = analysis
|
|
25
|
+
try {
|
|
26
|
+
languageAnalysis.projectRootFilenames = getProjectRootFilenames(projectPath)
|
|
27
|
+
} catch (err) {
|
|
28
|
+
next(err)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
next()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getProjectRootFilenames = projectPath => {
|
|
35
|
+
let projectStats = null
|
|
36
|
+
try {
|
|
37
|
+
projectStats = fs.statSync(projectPath)
|
|
38
|
+
} catch (err) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
i18n.__('languageAnalysisProjectRootFileNameFailure', projectPath) +
|
|
41
|
+
`${err.message}`
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Return the contents of a directory...
|
|
46
|
+
if (projectStats.isDirectory()) {
|
|
47
|
+
try {
|
|
48
|
+
return fs.readdirSync(projectPath)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
i18n.__('languageAnalysisProjectRootFileNameReadError', projectPath) +
|
|
52
|
+
`${err.message}`
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// If we are working with a file return it in a list as we do when we work
|
|
58
|
+
// with a directory...
|
|
59
|
+
if (projectStats.isFile()) {
|
|
60
|
+
return [path.basename(projectPath)]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Error out if we are working with something like a socket file or some
|
|
64
|
+
// other craziness...
|
|
65
|
+
throw new Error(
|
|
66
|
+
i18n.__('languageAnalysisProjectRootFileNameMissingError'),
|
|
67
|
+
projectPath
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//For testing purposes
|
|
72
|
+
exports.getProjectRootFilenames = getProjectRootFilenames
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const AnalysisEngine = require('./../AnalysisEngine')
|
|
2
|
+
const i18n = require('i18n')
|
|
3
|
+
|
|
4
|
+
const getProjectRootFilenames = require('./getProjectRootFilenames')
|
|
5
|
+
const reduceIdentifiedLanguages = require('./reduceIdentifiedLanguages')
|
|
6
|
+
const checkForMultipleIdentifiedLanguages = require('./checkForMultipleIdentifiedLanguages')
|
|
7
|
+
const checkForMultipleIdentifiedProjectFiles = require('./checkForMultipleIdentifiedProjectFiles')
|
|
8
|
+
const checkIdentifiedLanguageHasProjectFile = require('./checkIdentifiedLanguageHasProjectFile')
|
|
9
|
+
const checkIdentifiedLanguageHasLockFile = require('./checkIdentifiedLanguageHasLockFile')
|
|
10
|
+
const getIdentifiedLanguageInfo = require('./getIdentifiedLanguageInfo')
|
|
11
|
+
const { libraryAnalysisError } = require('../../common/errorHandling')
|
|
12
|
+
|
|
13
|
+
module.exports = exports = (projectPath, callback, appId, config) => {
|
|
14
|
+
// Create an analysis engine to identify the project language
|
|
15
|
+
const ae = new AnalysisEngine({
|
|
16
|
+
projectPath,
|
|
17
|
+
appId,
|
|
18
|
+
languageAnalysis: { appId: appId },
|
|
19
|
+
config
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
ae.use([
|
|
23
|
+
getProjectRootFilenames,
|
|
24
|
+
reduceIdentifiedLanguages,
|
|
25
|
+
checkForMultipleIdentifiedLanguages,
|
|
26
|
+
checkForMultipleIdentifiedProjectFiles,
|
|
27
|
+
checkIdentifiedLanguageHasProjectFile,
|
|
28
|
+
checkIdentifiedLanguageHasLockFile,
|
|
29
|
+
getIdentifiedLanguageInfo
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
ae.analyze((err, analysis) => {
|
|
33
|
+
if (err) {
|
|
34
|
+
console.log(
|
|
35
|
+
'*******************' +
|
|
36
|
+
i18n.__('languageAnalysisFailureMessage') +
|
|
37
|
+
'****************'
|
|
38
|
+
)
|
|
39
|
+
console.error(`${err.message}`)
|
|
40
|
+
libraryAnalysisError()
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
callback(null, analysis)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const {
|
|
2
|
+
supportedLanguages: { DOTNET, NODE, JAVA, RUBY, PYTHON, GO, PHP }
|
|
3
|
+
} = require('../languageAnalysisEngine/constants')
|
|
4
|
+
const i18n = require('i18n')
|
|
5
|
+
const dotnetAE = require('../dotnetAnalysisEngine')
|
|
6
|
+
const nodeAE = require('../nodeAnalysisEngine')
|
|
7
|
+
const javaAE = require('../javaAnalysisEngine')
|
|
8
|
+
const rubyAE = require('../rubyAnalysisEngine')
|
|
9
|
+
const pythonAE = require('../pythonAnalysisEngine')
|
|
10
|
+
const phpAE = require('../phpAnalysisEngine')
|
|
11
|
+
const goAE = require('../goAnalysisEngine')
|
|
12
|
+
const { vulnerabilityReport } = require('./report/reportingFeature')
|
|
13
|
+
const { vulnReportWithoutDevDep } = require('./report/newReportingFeature')
|
|
14
|
+
const { checkDevDeps } = require('./report/checkIgnoreDevDep')
|
|
15
|
+
const { newSendSnapShot } = require('../languageAnalysisEngine/sendSnapshot')
|
|
16
|
+
const fs = require('fs')
|
|
17
|
+
const chalk = require('chalk')
|
|
18
|
+
const saveFile = require('../../commands/audit/saveFile').default
|
|
19
|
+
const generateSbom = require('../../sbom/generateSbom').default
|
|
20
|
+
|
|
21
|
+
module.exports = exports = (err, analysis) => {
|
|
22
|
+
const { identifiedLanguageInfo } = analysis.languageAnalysis
|
|
23
|
+
const catalogueAppId = analysis.languageAnalysis.appId
|
|
24
|
+
|
|
25
|
+
if (err) {
|
|
26
|
+
console.error(err)
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// this callback is the end of the chain
|
|
31
|
+
const langCallback = async (err, analysis) => {
|
|
32
|
+
const config = analysis.config
|
|
33
|
+
if (err) {
|
|
34
|
+
console.log()
|
|
35
|
+
console.log(
|
|
36
|
+
'***********' +
|
|
37
|
+
i18n.__('languageAnalysisFactoryFailureHeader') +
|
|
38
|
+
'****************'
|
|
39
|
+
)
|
|
40
|
+
console.log(identifiedLanguageInfo.language)
|
|
41
|
+
console.log()
|
|
42
|
+
console.error(
|
|
43
|
+
`${identifiedLanguageInfo.language}` +
|
|
44
|
+
i18n.__('languageAnalysisFailure') +
|
|
45
|
+
err
|
|
46
|
+
)
|
|
47
|
+
return process.exit(5)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('\n **************CONTRAST OSS ANALYSIS BEGINS**************')
|
|
51
|
+
const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId)
|
|
52
|
+
|
|
53
|
+
if (config.report) {
|
|
54
|
+
const ignoreDevUrl = await checkDevDeps(config)
|
|
55
|
+
if (ignoreDevUrl) {
|
|
56
|
+
await vulnReportWithoutDevDep(
|
|
57
|
+
analysis,
|
|
58
|
+
catalogueAppId,
|
|
59
|
+
snapshotResponse.id,
|
|
60
|
+
config
|
|
61
|
+
)
|
|
62
|
+
} else {
|
|
63
|
+
await vulnerabilityReport(analysis, catalogueAppId, config)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//should be moved to processAudit.ts once promises implemented
|
|
68
|
+
await auditSave(config)
|
|
69
|
+
|
|
70
|
+
console.log(
|
|
71
|
+
'\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n'
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (identifiedLanguageInfo.language === DOTNET) {
|
|
76
|
+
dotnetAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (identifiedLanguageInfo.language === NODE) {
|
|
80
|
+
nodeAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (identifiedLanguageInfo.language === JAVA) {
|
|
84
|
+
javaAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (identifiedLanguageInfo.language === RUBY) {
|
|
88
|
+
rubyAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (identifiedLanguageInfo.language === PYTHON) {
|
|
92
|
+
pythonAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (identifiedLanguageInfo.language === PHP) {
|
|
96
|
+
phpAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (identifiedLanguageInfo.language === GO) {
|
|
100
|
+
goAE(identifiedLanguageInfo, analysis.config, langCallback)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function auditSave(config) {
|
|
105
|
+
//should be moved to processAudit.ts once promises implemented
|
|
106
|
+
if (config.save) {
|
|
107
|
+
if (config.save.toLowerCase() === 'sbom') {
|
|
108
|
+
saveFile(config, await generateSbom(config))
|
|
109
|
+
|
|
110
|
+
const filename = `${config.applicationId}-sbom-cyclonedx.json`
|
|
111
|
+
if (fs.existsSync(filename)) {
|
|
112
|
+
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`)
|
|
113
|
+
} else {
|
|
114
|
+
console.log(
|
|
115
|
+
chalk.yellow.bold(
|
|
116
|
+
`\n Unable to save ${filename} Software Bill of Materials (SBOM)`
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
console.log(i18n.__('auditBadFiletypeSpecifiedForSave'))
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
console.log(i18n.__('auditNoFiletypeSpecifiedForSave'))
|
|
125
|
+
}
|
|
126
|
+
}
|