@contrast/contrast 1.0.0 → 1.0.1

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 (201) hide show
  1. package/.prettierignore +2 -0
  2. package/README.md +120 -47
  3. package/dist/audit/AnalysisEngine.js +37 -0
  4. package/dist/audit/catalogueApplication/catalogueApplication.js +36 -0
  5. package/dist/audit/dotnetAnalysisEngine/index.js +25 -0
  6. package/dist/audit/dotnetAnalysisEngine/parseLockFileContents.js +35 -0
  7. package/dist/audit/dotnetAnalysisEngine/parseProjectFileContents.js +15 -0
  8. package/dist/audit/dotnetAnalysisEngine/readLockFileContents.js +18 -0
  9. package/dist/audit/dotnetAnalysisEngine/readProjectFileContents.js +14 -0
  10. package/dist/audit/dotnetAnalysisEngine/sanitizer.js +9 -0
  11. package/dist/audit/goAnalysisEngine/index.js +17 -0
  12. package/dist/audit/goAnalysisEngine/parseProjectFileContents.js +164 -0
  13. package/dist/audit/goAnalysisEngine/readProjectFileContents.js +21 -0
  14. package/dist/audit/goAnalysisEngine/sanitizer.js +5 -0
  15. package/dist/audit/javaAnalysisEngine/index.js +34 -0
  16. package/dist/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +153 -0
  17. package/dist/audit/javaAnalysisEngine/parseProjectFileContents.js +353 -0
  18. package/dist/audit/javaAnalysisEngine/readProjectFileContents.js +98 -0
  19. package/dist/audit/javaAnalysisEngine/sanitizer.js +5 -0
  20. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +24 -0
  21. package/dist/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +24 -0
  22. package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +35 -0
  23. package/dist/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +23 -0
  24. package/dist/audit/languageAnalysisEngine/commonApi.js +18 -0
  25. package/dist/audit/languageAnalysisEngine/constants.js +20 -0
  26. package/dist/audit/languageAnalysisEngine/filterProjectPath.js +20 -0
  27. package/dist/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +25 -0
  28. package/dist/audit/languageAnalysisEngine/getProjectRootFilenames.js +39 -0
  29. package/dist/audit/languageAnalysisEngine/index.js +39 -0
  30. package/dist/audit/languageAnalysisEngine/langugageAnalysisFactory.js +70 -0
  31. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +121 -0
  32. package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +17 -0
  33. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +257 -0
  34. package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +81 -0
  35. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +133 -0
  36. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +41 -0
  37. package/dist/audit/languageAnalysisEngine/util/capabilities.js +11 -0
  38. package/dist/audit/languageAnalysisEngine/util/generalAPI.js +39 -0
  39. package/dist/audit/languageAnalysisEngine/util/requestUtils.js +14 -0
  40. package/dist/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +40 -0
  41. package/dist/audit/nodeAnalysisEngine/index.js +31 -0
  42. package/dist/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +18 -0
  43. package/dist/audit/nodeAnalysisEngine/parseYarn2LockFileContents.js +51 -0
  44. package/dist/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +18 -0
  45. package/dist/audit/nodeAnalysisEngine/readNPMLockFileContents.js +17 -0
  46. package/dist/audit/nodeAnalysisEngine/readProjectFileContents.js +14 -0
  47. package/dist/audit/nodeAnalysisEngine/readYarnLockFileContents.js +24 -0
  48. package/dist/audit/nodeAnalysisEngine/sanitizer.js +9 -0
  49. package/dist/audit/phpAnalysisEngine/index.js +23 -0
  50. package/dist/audit/phpAnalysisEngine/parseLockFileContents.js +52 -0
  51. package/dist/audit/phpAnalysisEngine/readLockFileContents.js +13 -0
  52. package/dist/audit/phpAnalysisEngine/readProjectFileContents.js +16 -0
  53. package/dist/audit/phpAnalysisEngine/sanitizer.js +5 -0
  54. package/dist/audit/pythonAnalysisEngine/index.js +25 -0
  55. package/dist/audit/pythonAnalysisEngine/parsePipfileLockContents.js +17 -0
  56. package/dist/audit/pythonAnalysisEngine/parseProjectFileContents.js +21 -0
  57. package/dist/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +13 -0
  58. package/dist/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +14 -0
  59. package/dist/audit/pythonAnalysisEngine/sanitizer.js +7 -0
  60. package/dist/audit/rubyAnalysisEngine/index.js +25 -0
  61. package/dist/audit/rubyAnalysisEngine/parseGemfileLockContents.js +176 -0
  62. package/dist/audit/rubyAnalysisEngine/parsedGemfile.js +22 -0
  63. package/dist/audit/rubyAnalysisEngine/readGemfileContents.js +14 -0
  64. package/dist/audit/rubyAnalysisEngine/readGemfileLockContents.js +14 -0
  65. package/dist/audit/rubyAnalysisEngine/sanitizer.js +6 -0
  66. package/dist/commands/audit/auditConfig.js +25 -0
  67. package/dist/commands/audit/auditController.js +31 -0
  68. package/dist/commands/audit/help.js +52 -0
  69. package/dist/commands/audit/processAudit.js +18 -0
  70. package/dist/commands/auth/auth.js +1 -1
  71. package/dist/commands/scan/processScan.js +19 -5
  72. package/dist/common/HTTPClient.js +101 -13
  73. package/dist/common/errorHandling.js +49 -1
  74. package/dist/common/findLatestCLIVersion.js +23 -0
  75. package/dist/constants/constants.js +1 -1
  76. package/dist/constants/lambda.js +32 -4
  77. package/dist/constants/locales.js +39 -16
  78. package/dist/constants.js +148 -20
  79. package/dist/index.js +7 -1
  80. package/dist/lambda/aws.js +14 -11
  81. package/dist/lambda/help.js +4 -0
  82. package/dist/lambda/lambda.js +50 -27
  83. package/dist/lambda/lambdaUtils.js +72 -0
  84. package/dist/lambda/logUtils.js +11 -1
  85. package/dist/lambda/scanDetailCompletion.js +4 -4
  86. package/dist/lambda/scanRequest.js +11 -5
  87. package/dist/lambda/utils.js +110 -53
  88. package/dist/scan/autoDetection.js +0 -32
  89. package/dist/scan/fileUtils.js +1 -1
  90. package/dist/scan/help.js +12 -40
  91. package/dist/scan/populateProjectIdAndProjectName.js +4 -0
  92. package/dist/scan/saveResults.js +15 -0
  93. package/dist/scan/scan.js +77 -42
  94. package/dist/scan/scanConfig.js +20 -0
  95. package/dist/scan/scanController.js +13 -15
  96. package/dist/scan/scanResults.js +18 -16
  97. package/dist/utils/commonApi.js +3 -3
  98. package/dist/utils/fileUtils.js +31 -0
  99. package/dist/utils/paramsUtil/commandlineParams.js +1 -20
  100. package/dist/utils/paramsUtil/genericCommandLineParams.js +12 -0
  101. package/dist/utils/paramsUtil/paramHandler.js +3 -6
  102. package/dist/utils/parsedCLIOptions.js +14 -8
  103. package/package.json +26 -21
  104. package/src/audit/AnalysisEngine.js +103 -0
  105. package/src/audit/catalogueApplication/catalogueApplication.js +42 -0
  106. package/src/audit/dotnetAnalysisEngine/index.js +26 -0
  107. package/src/audit/dotnetAnalysisEngine/parseLockFileContents.js +47 -0
  108. package/src/audit/dotnetAnalysisEngine/parseProjectFileContents.js +29 -0
  109. package/src/audit/dotnetAnalysisEngine/readLockFileContents.js +30 -0
  110. package/src/audit/dotnetAnalysisEngine/readProjectFileContents.js +26 -0
  111. package/src/audit/dotnetAnalysisEngine/sanitizer.js +11 -0
  112. package/src/audit/goAnalysisEngine/index.js +18 -0
  113. package/src/audit/goAnalysisEngine/parseProjectFileContents.js +209 -0
  114. package/src/audit/goAnalysisEngine/readProjectFileContents.js +31 -0
  115. package/src/audit/goAnalysisEngine/sanitizer.js +7 -0
  116. package/src/audit/javaAnalysisEngine/index.js +41 -0
  117. package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +222 -0
  118. package/src/audit/javaAnalysisEngine/parseProjectFileContents.js +420 -0
  119. package/src/audit/javaAnalysisEngine/readProjectFileContents.js +141 -0
  120. package/src/audit/javaAnalysisEngine/sanitizer.js +6 -0
  121. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +35 -0
  122. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +41 -0
  123. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +54 -0
  124. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +32 -0
  125. package/src/audit/languageAnalysisEngine/commonApi.js +20 -0
  126. package/src/audit/languageAnalysisEngine/constants.js +23 -0
  127. package/src/audit/languageAnalysisEngine/filterProjectPath.js +21 -0
  128. package/src/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +41 -0
  129. package/src/audit/languageAnalysisEngine/getProjectRootFilenames.js +72 -0
  130. package/src/audit/languageAnalysisEngine/index.js +45 -0
  131. package/src/audit/languageAnalysisEngine/langugageAnalysisFactory.js +94 -0
  132. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +177 -0
  133. package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +27 -0
  134. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +303 -0
  135. package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +124 -0
  136. package/src/audit/languageAnalysisEngine/report/reportingFeature.js +190 -0
  137. package/src/audit/languageAnalysisEngine/sendSnapshot.js +51 -0
  138. package/src/audit/languageAnalysisEngine/util/capabilities.js +12 -0
  139. package/src/audit/languageAnalysisEngine/util/generalAPI.js +43 -0
  140. package/src/audit/languageAnalysisEngine/util/requestUtils.js +17 -0
  141. package/src/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +49 -0
  142. package/src/audit/nodeAnalysisEngine/index.js +35 -0
  143. package/src/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +20 -0
  144. package/src/audit/nodeAnalysisEngine/parseYarn2LockFileContents.js +63 -0
  145. package/src/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +26 -0
  146. package/src/audit/nodeAnalysisEngine/readNPMLockFileContents.js +23 -0
  147. package/src/audit/nodeAnalysisEngine/readProjectFileContents.js +27 -0
  148. package/src/audit/nodeAnalysisEngine/readYarnLockFileContents.js +36 -0
  149. package/src/audit/nodeAnalysisEngine/sanitizer.js +11 -0
  150. package/src/audit/phpAnalysisEngine/index.js +27 -0
  151. package/src/audit/phpAnalysisEngine/parseLockFileContents.js +60 -0
  152. package/src/audit/phpAnalysisEngine/readLockFileContents.js +14 -0
  153. package/src/audit/phpAnalysisEngine/readProjectFileContents.js +25 -0
  154. package/src/audit/phpAnalysisEngine/sanitizer.js +4 -0
  155. package/src/audit/pythonAnalysisEngine/index.js +55 -0
  156. package/src/audit/pythonAnalysisEngine/parsePipfileLockContents.js +23 -0
  157. package/src/audit/pythonAnalysisEngine/parseProjectFileContents.js +33 -0
  158. package/src/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +16 -0
  159. package/src/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +22 -0
  160. package/src/audit/pythonAnalysisEngine/sanitizer.js +9 -0
  161. package/src/audit/rubyAnalysisEngine/index.js +30 -0
  162. package/src/audit/rubyAnalysisEngine/parseGemfileLockContents.js +215 -0
  163. package/src/audit/rubyAnalysisEngine/parsedGemfile.js +39 -0
  164. package/src/audit/rubyAnalysisEngine/readGemfileContents.js +18 -0
  165. package/src/audit/rubyAnalysisEngine/readGemfileLockContents.js +17 -0
  166. package/src/audit/rubyAnalysisEngine/sanitizer.js +8 -0
  167. package/src/commands/audit/auditConfig.ts +30 -0
  168. package/src/commands/audit/auditController.ts +31 -0
  169. package/src/commands/audit/help.ts +48 -0
  170. package/src/commands/audit/processAudit.ts +19 -0
  171. package/src/commands/auth/auth.js +1 -1
  172. package/src/commands/scan/processScan.js +20 -5
  173. package/src/common/HTTPClient.js +136 -14
  174. package/src/common/errorHandling.ts +56 -1
  175. package/src/common/findLatestCLIVersion.ts +27 -0
  176. package/src/constants/constants.js +1 -1
  177. package/src/constants/lambda.js +45 -4
  178. package/src/constants/locales.js +48 -20
  179. package/src/constants.js +168 -22
  180. package/src/index.ts +9 -2
  181. package/src/lambda/aws.ts +13 -12
  182. package/src/lambda/help.ts +4 -0
  183. package/src/lambda/lambda.ts +53 -34
  184. package/src/lambda/lambdaUtils.ts +111 -0
  185. package/src/lambda/logUtils.ts +19 -1
  186. package/src/lambda/scanDetailCompletion.ts +4 -4
  187. package/src/lambda/scanRequest.ts +13 -11
  188. package/src/lambda/utils.ts +149 -81
  189. package/src/scan/autoDetection.js +0 -29
  190. package/src/scan/fileUtils.js +1 -1
  191. package/src/scan/help.js +12 -45
  192. package/src/scan/populateProjectIdAndProjectName.js +4 -0
  193. package/src/scan/saveResults.js +15 -0
  194. package/src/scan/scan.js +95 -59
  195. package/src/scan/scanConfig.js +29 -0
  196. package/src/scan/scanController.js +13 -13
  197. package/src/scan/scanResults.js +21 -19
  198. package/src/utils/commonApi.js +2 -3
  199. package/src/utils/paramsUtil/commandlineParams.js +1 -26
  200. package/src/utils/paramsUtil/paramHandler.js +3 -7
  201. package/src/utils/parsedCLIOptions.js +11 -9
@@ -24,8 +24,7 @@ const sendScanPostRequest = async (
24
24
  const client = getHttpClient(config)
25
25
 
26
26
  if (showProgress) {
27
- // prettier-ignore
28
- log(`${logSymbols.success} Sending Lambda Function scan request to Contrast`)
27
+ log(i18n.__('sendingScanRequest', { icon: logSymbols.success }))
29
28
  }
30
29
 
31
30
  const res = await client.postFunctionScan(config, params, functionsEvent)
@@ -33,7 +32,7 @@ const sendScanPostRequest = async (
33
32
 
34
33
  if (statusCode === 201) {
35
34
  if (showProgress) {
36
- log(`${logSymbols.success} Scan requested successfully`)
35
+ log(i18n.__('scanRequestedSuccessfully', { icon: logSymbols.success }))
37
36
  }
38
37
 
39
38
  return body?.data?.scanId
@@ -45,11 +44,10 @@ const sendScanPostRequest = async (
45
44
  let description = ''
46
45
  switch (errorCode) {
47
46
  case 'not_supported_runtime':
48
- description = i18n.__(
49
- errorCode,
50
- data?.runtime,
51
- data?.supportedRuntimes.sort().join(' | ')
52
- )
47
+ description = i18n.__(errorCode, {
48
+ runtime: data?.runtime,
49
+ supportedRuntimes: data?.supportedRuntimes.sort().join(' | ')
50
+ })
53
51
  errorCode = false
54
52
  break
55
53
  }
@@ -88,8 +86,12 @@ const requestScanFunctionPost = async (
88
86
  const lambdaClient = getLambdaClient(lambdaOptions)
89
87
 
90
88
  if (!jsonOutput) {
91
- // prettier-ignore
92
- log(`${logSymbols.success} Fetching configuration and policies for Lambda Function ${chalk.bold(functionName)}`)
89
+ log(
90
+ i18n.__('fetchingConfiguration', {
91
+ icon: logSymbols.success,
92
+ functionName: chalk.bold(functionName)
93
+ })
94
+ )
93
95
  }
94
96
 
95
97
  const lambdaConfig = await getLambdaFunctionConfiguration(
@@ -125,7 +127,7 @@ const requestScanFunctionPost = async (
125
127
  }
126
128
 
127
129
  if (verbose) {
128
- log(`${logSymbols.success} Fetched configuration from AWS:`)
130
+ log(i18n.__('fetchedConfiguration', { icon: logSymbols.success }))
129
131
  prettyPrintJson(functionEvent)
130
132
  }
131
133
 
@@ -1,112 +1,182 @@
1
1
  import chalk from 'chalk'
2
- import { capitalize, groupBy, minBy, sortBy } from 'lodash'
2
+ import { groupBy, sortBy, capitalize, minBy } from 'lodash'
3
+ import i18n from 'i18n'
3
4
  import { log } from './logUtils'
4
5
 
6
+ // fix for using `plural`
7
+ // https://github.com/mashpie/i18n-node/issues/429
8
+ i18n.setLocale('en')
9
+
10
+ class PrintVulnerability {
11
+ index: number
12
+ vulnerability: any
13
+ group?: any[]
14
+ title: string
15
+ severity: string
16
+ remediation: string
17
+ description: string
18
+ recommendation: string
19
+ whatHappened: string
20
+
21
+ constructor(index: number, vulnerability: any, group?: any[]) {
22
+ const {
23
+ severityText,
24
+ title,
25
+ description,
26
+ remediation,
27
+ categoryText
28
+ } = vulnerability
29
+
30
+ this.group = group
31
+ this.vulnerability = vulnerability
32
+ this.index = index
33
+ this.title = title
34
+ this.severity = capitalize(severityText)
35
+ this.description = underlineLinks(description)
36
+ this.remediation = remediation?.description
37
+ this.recommendation = ''
38
+ this.whatHappened = ''
39
+
40
+ if (categoryText === 'PERMISSIONS') {
41
+ this.formatPermissions()
42
+ } else if (categoryText === 'DEPENDENCIES') {
43
+ this.formatDependencies()
44
+ }
45
+ }
46
+
47
+ formatPermissions() {
48
+ const { leastPrivilege, comment } = this.vulnerability.evidence
49
+ const violatingPolicies = leastPrivilege?.violatingPolicies || []
50
+
51
+ const filteredPolicies = violatingPolicies
52
+ .filter((vp: any) => vp?.suggestedPolicy?.suggestedPolicyCode?.length)
53
+ .map((vp: any) => vp?.suggestedPolicy)
54
+
55
+ const shouldNumerate = filteredPolicies.length > 1
56
+ filteredPolicies.forEach((policies: any, i: number) => {
57
+ const { suggestedPolicyCode, description } = policies
58
+
59
+ suggestedPolicyCode.forEach((policy: any) => {
60
+ const { snippet, title } = policy
61
+ this.recommendation += shouldNumerate
62
+ ? ` ${i + 1}. ${description}\n`
63
+ : `${description}\n`
64
+
65
+ if (title !== 'DELETE POLICY') {
66
+ this.recommendation += `${snippet}\n`
67
+ }
68
+ })
69
+ })
70
+
71
+ if (comment?.length) {
72
+ const splitComment = (comment: string) => {
73
+ const [policy, description] = comment.split(':').map(c => c.trim())
74
+ return { policy, description }
75
+ }
76
+ const groupByPolicy = groupBy(comment, c => splitComment(c).policy)
77
+
78
+ Object.entries(groupByPolicy).forEach(([policy, commentArr]) => {
79
+ const comments = commentArr
80
+ .map(splitComment)
81
+ .map(({ description }) => ` - ${description}`)
82
+ .join('\n')
83
+ this.whatHappened += i18n.__('whatHappenedItem', { policy, comments })
84
+ })
85
+ }
86
+ }
87
+
88
+ formatDependencies() {
89
+ if (!this.group?.length) {
90
+ this.recommendation = this.vulnerability?.remediation?.description
91
+ return
92
+ }
93
+
94
+ const maxSeverity = minBy(this.group, 'severity')
95
+ this.title = i18n.__('vulnerableDependency')
96
+ this.severity = capitalize(maxSeverity.severityText)
97
+ this.recommendation = maxSeverity.remediation?.description
98
+
99
+ const library = groupByDependency({ title: this.vulnerability.title })
100
+ const [packageName, version] = library.split(':')
101
+ const allCves = this.group.map(groupByCVE)
102
+
103
+ this.description = i18n.__mf('vulnerableDependencyDescriptions', {
104
+ NUM: this.group.length,
105
+ packageName,
106
+ version,
107
+ cves: allCves.join(' | ')
108
+ })
109
+ }
110
+
111
+ print() {
112
+ log(`${this.index}.`)
113
+ // prettier-ignore
114
+ log(`${chalk.bold(this.severity)} | ${chalk.bold(this.title)} ${this.description}`)
115
+
116
+ if (this.whatHappened) {
117
+ log(`\n${chalk.bold(i18n.__('whatHappenedTitle'))}\n${this.whatHappened}`)
118
+ }
119
+
120
+ if (this.recommendation) {
121
+ log(`${chalk.bold(i18n.__('recommendation'))}\n${this.recommendation}`)
122
+ }
123
+
124
+ log('')
125
+ }
126
+ }
127
+
5
128
  const groupByCVE = ({ title }: any) =>
6
129
  title.substring(0, title.indexOf('[') - 1)
7
130
 
8
131
  const groupByDependency = ({ title }: any) =>
9
132
  title.substring(title.indexOf('[') + 1, title.indexOf(']'))
10
133
 
11
- const prettyPrintResults = (results: any[]) => {
12
- log('')
13
-
134
+ const printResults = (results: any[]) => {
14
135
  //filter out any vulnerabs which is not least privilege or dependencies- cli does not handle other vulnerabs yet
15
136
  const vulnerabs = results.filter(r => r.category === 1 || r.category === 4)
16
137
  const sortBySeverity = sortBy(vulnerabs, ['severity', 'title'])
17
138
  const notDependencies = sortBySeverity.filter(r => r.category !== 1)
18
139
  const dependencies = sortBySeverity.filter(r => r.category === 1)
19
140
  const dependenciesByLibrary = groupBy(dependencies, groupByDependency)
20
- const dependenciesCount = Object.keys(dependenciesByLibrary).length
21
141
 
22
- notDependencies.forEach(printVulnerability)
142
+ log('')
23
143
 
144
+ notDependencies.forEach((vulnerability: any, index: number) => {
145
+ const printVulnerab = new PrintVulnerability(index + 1, vulnerability)
146
+ printVulnerab.print()
147
+ })
24
148
  const prevIndex = notDependencies.length + 1
25
- Object.entries(dependenciesByLibrary).forEach(([library, group], i) => {
26
- const maxSeverity = minBy(group, 'severity')
27
- const allCves = group.map(groupByCVE)
28
-
29
- log(prevIndex + i)
30
- log(
31
- `${chalk.bold(capitalize(maxSeverity.severityText))} | ${chalk.bold(
32
- 'Vulnerable dependency'
33
- )} ${library} has ${group.length} known CVEs`
34
- )
35
- log(allCves.join(', '))
36
- if (maxSeverity.remediation?.description) {
37
- log(
38
- `${chalk.bold('Recommendation:')} ${
39
- maxSeverity.remediation.description
40
- }`
41
- )
42
- }
43
- log('')
149
+ Object.entries(dependenciesByLibrary).forEach(([, group], i) => {
150
+ const printVulnerab = new PrintVulnerability(prevIndex + i, group[0], group)
151
+ printVulnerab.print()
44
152
  })
45
153
 
154
+ const dependenciesCount = Object.keys(dependenciesByLibrary).length
46
155
  const resultCount = notDependencies.length + dependenciesCount
156
+ log(i18n.__n('foundVulnerabilities', resultCount), { bold: true })
157
+
158
+ const counters = getNotDependenciesCounters(notDependencies)
159
+ if (dependenciesCount) {
160
+ counters.push(i18n.__n('dependenciesCount', dependenciesCount))
161
+ }
162
+ log(counters.join(' | '), { bold: true })
163
+ }
164
+
165
+ const getNotDependenciesCounters = (notDependencies: any[]) => {
47
166
  const groupByType = groupBy(notDependencies, ['categoryText'])
48
- const summary = Object.values(groupByType).map(
167
+ return Object.values(groupByType).map(
49
168
  group => `${group.length} ${capitalize(group[0].categoryText)}`
50
169
  )
51
- log(`Found ${resultCount} vulnerabilities`, { bold: true })
52
- summary.push(`${dependenciesCount} Dependencies`)
53
-
54
- log(chalk.bold(summary.join(' | ')))
55
170
  }
56
171
 
57
172
  const underlineLinks = (text: string) => {
58
173
  if (!text) {
59
174
  return text
60
175
  }
61
-
62
176
  const urlRegex = /(https?:\/\/[^\s]+)/g
63
177
  return text.replace(urlRegex, chalk.underline('$1'))
64
178
  }
65
179
 
66
- const printVulnerability = (vulnerability: any, index: number) => {
67
- log(index + 1)
68
- const descriptionWithLinks = underlineLinks(vulnerability.description)
69
- log(
70
- `${chalk.bold(capitalize(vulnerability.severityText))} | ${chalk.bold(
71
- vulnerability.title
72
- )} ${descriptionWithLinks}`
73
- )
74
-
75
- const category = vulnerability?.categoryText
76
- switch (category) {
77
- case 'PERMISSIONS':
78
- printLeastPrivilegeRemediation(vulnerability)
79
- break
80
- default:
81
- printRemediation(vulnerability)
82
- }
83
- log('')
84
- }
85
-
86
- const printLeastPrivilegeRemediation = (vulnerability: any) => {
87
- log(
88
- `${chalk.bold(
89
- 'Recommendation:'
90
- )} Replace the existing policies with the following`
91
- )
92
-
93
- const violatingPolicies =
94
- vulnerability?.evidence?.leastPrivilege?.violatingPolicies || []
95
-
96
- violatingPolicies
97
- .filter((vp: any) => vp?.suggestedPolicy?.suggestedPolicyCode?.length)
98
- .map((vp: any) => vp?.suggestedPolicy?.suggestedPolicyCode)
99
- .forEach((policies: any) => {
100
- policies.forEach((policy: any) => {
101
- console.log(policy.snippet)
102
- })
103
- })
104
- }
105
-
106
- const printRemediation = (vulnerability: any) => {
107
- log(`Remediation - ${vulnerability?.remediation?.description || 'Unknown'}`)
108
- }
109
-
110
180
  function toLowerKeys(obj: Record<string, unknown>) {
111
181
  return Object.keys(obj).reduce((accumulator, key) => {
112
182
  const new_key = `${key[0].toLowerCase()}${key.slice(1)}`
@@ -115,11 +185,9 @@ function toLowerKeys(obj: Record<string, unknown>) {
115
185
  }, {} as Record<string, unknown>)
116
186
  }
117
187
 
118
- export { toLowerKeys, prettyPrintResults }
119
-
188
+ export { toLowerKeys, printResults }
120
189
  export const exportedForTesting = {
121
- printLeastPrivilegeRemediation,
122
- printRemediation,
123
- printVulnerability,
124
- underlineLinks
190
+ underlineLinks,
191
+ printResults,
192
+ PrintVulnerability
125
193
  }
@@ -1,7 +1,5 @@
1
1
  const i18n = require('i18n')
2
- const { zipValidator } = require('./scan')
3
2
  const fileFinder = require('./fileUtils')
4
- const { supportedLanguages } = require('../constants/constants')
5
3
 
6
4
  const autoDetectFileAndLanguage = async configToUse => {
7
5
  const entries = await fileFinder.findFile()
@@ -18,8 +16,6 @@ const autoDetectFileAndLanguage = async configToUse => {
18
16
  if (configToUse.name === undefined) {
19
17
  configToUse.name = entries[0]
20
18
  }
21
- zipValidator(configToUse)
22
- assignLanguage(entries, configToUse)
23
19
  } else {
24
20
  errorOnFileDetection(entries)
25
21
  }
@@ -46,32 +42,7 @@ const errorOnFileDetection = entries => {
46
42
  process.exit(1)
47
43
  }
48
44
 
49
- const assignLanguage = (entries, configToUse) => {
50
- let split = entries[0].split('.')
51
- const fileType = split[split.length - 1]
52
- if (fileType === 'war' || fileType === 'jar') {
53
- console.log('Language is Java')
54
- configToUse.language = 'JAVA'
55
- } else if (fileType === 'dll') {
56
- console.log('Language is Dotnet')
57
- configToUse.language = 'DOTNET'
58
- } else if (fileType === 'js') {
59
- console.log('Language is Javascript')
60
- configToUse.language = supportedLanguages.JAVASCRIPT
61
- } else if (fileType === 'zip') {
62
- if (configToUse.language !== supportedLanguages.JAVASCRIPT) {
63
- console.log(i18n.__('zipErrorScan'))
64
- process.exit(1)
65
- }
66
- console.log('Language is Javascript within zip file')
67
- } else {
68
- console.log(i18n.__('unknownFileErrorScan'))
69
- process.exit(1)
70
- }
71
- }
72
-
73
45
  module.exports = {
74
46
  autoDetectFileAndLanguage,
75
- assignLanguage,
76
47
  errorOnFileDetection
77
48
  }
@@ -4,7 +4,7 @@ const i18n = require('i18n')
4
4
 
5
5
  const findFile = async () => {
6
6
  console.log(i18n.__('searchingScanFileDirectory', process.cwd()))
7
- return fg(['**/*.jar', '**/*.war', '**/*.zip', '**/*.dll'], {
7
+ return fg(['**/*.jar', '**/*.war', '**/*.zip', '**/*.dll', '**/*.exe'], {
8
8
  dot: false,
9
9
  deep: 3,
10
10
  onlyFiles: true
package/src/scan/help.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const commandLineUsage = require('command-line-usage')
2
2
  const i18n = require('i18n')
3
+ const constants = require('../constants')
3
4
 
4
5
  const scanUsageGuide = commandLineUsage([
5
6
  {
@@ -17,51 +18,17 @@ const scanUsageGuide = commandLineUsage([
17
18
  },
18
19
  {
19
20
  header: i18n.__('constantsScanOptions'),
20
- content: [
21
- {
22
- name: i18n.__('scanOptionsFileName'),
23
- summary:
24
- '{italic ' +
25
- i18n.__('constantsOptional') +
26
- '}: ' +
27
- i18n.__('scanOptionsFileNameSummary')
28
- },
29
- {
30
- name: i18n.__('scanOptionsLanguage'),
31
- summary:
32
- '{italic ' +
33
- i18n.__('constantsOptional') +
34
- '}: ' +
35
- i18n.__('scanOptionsLanguageSummaryOptional') +
36
- '{italic ' +
37
- i18n.__('constantsRequired') +
38
- '}: ' +
39
- i18n.__('scanOptionsLanguageSummaryRequired')
40
- },
41
- {
42
- name: i18n.__('scanOptionsName'),
43
- summary:
44
- '{italic ' +
45
- i18n.__('constantsOptional') +
46
- '}: ' +
47
- i18n.__('scanOptionsNameSummary')
48
- },
49
- {
50
- name: i18n.__('scanOptionsTimeout'),
51
- summary:
52
- '{italic ' +
53
- i18n.__('constantsOptional') +
54
- '}: ' +
55
- i18n.__('scanOptionsTimeoutSummary')
56
- },
57
- {
58
- name: i18n.__('scanOptionsVerbose'),
59
- summary:
60
- '{italic ' +
61
- i18n.__('constantsOptional') +
62
- '}: ' +
63
- i18n.__('scanOptionsVerboseSummary')
64
- }
21
+ optionList: constants.commandLineDefinitions.scanOptionDefinitions,
22
+ hide: [
23
+ 'project-id',
24
+ 'language',
25
+ 'organization-id',
26
+ 'api-key',
27
+ 'authorization',
28
+ 'host',
29
+ 'proxy',
30
+ 'ff',
31
+ 'ignore-cert-errors'
65
32
  ]
66
33
  },
67
34
  {
@@ -21,6 +21,10 @@ const createProjectId = async (config, client) => {
21
21
  console.log(i18n.__('foundExistingProjectScan'))
22
22
  return
23
23
  }
24
+ if (res.statusCode === 403) {
25
+ console.log(i18n.__('permissionsError'))
26
+ return
27
+ }
24
28
 
25
29
  if (res.statusCode === 201) {
26
30
  console.log(i18n.__('projectCreatedScan'))
@@ -0,0 +1,15 @@
1
+ const fs = require('fs')
2
+
3
+ const writeResultsToFile = (responseBody, name = 'results.sarif') => {
4
+ fs.writeFile(name, JSON.stringify(responseBody, null, 2), err => {
5
+ if (err) {
6
+ console.log('Error writing Scan Results to file')
7
+ } else {
8
+ console.log(`Scan Results saved to ${name}`)
9
+ }
10
+ })
11
+ }
12
+
13
+ module.exports = {
14
+ writeResultsToFile
15
+ }
package/src/scan/scan.js CHANGED
@@ -1,10 +1,9 @@
1
1
  const commonApi = require('../utils/commonApi.js')
2
2
  const fileUtils = require('../scan/fileUtils')
3
- const allowedFileTypes = ['.jar', '.war', '.js', '.zip']
3
+ const allowedFileTypes = ['.jar', '.war', '.js', '.zip', '.exe']
4
4
  const i18n = require('i18n')
5
- const AdmZip = require('adm-zip')
6
5
  const oraWrapper = require('../utils/oraWrapper')
7
- const { supportedLanguages } = require('../constants/constants')
6
+ const chalk = require('chalk')
8
7
 
9
8
  const isFileAllowed = scanOption => {
10
9
  let valid = false
@@ -16,6 +15,14 @@ const isFileAllowed = scanOption => {
16
15
  return valid
17
16
  }
18
17
 
18
+ const stripMustacheTags = oldString => {
19
+ return oldString
20
+ .replace(/\n/g, ' ')
21
+ .replace(/{{.*?}}/g, '\n')
22
+ .replace(/\s+/g, ' ')
23
+ .trim()
24
+ }
25
+
19
26
  const sendScan = async config => {
20
27
  if (!isFileAllowed(config.file)) {
21
28
  console.log(i18n.__('scanErrorFileMessage'))
@@ -40,10 +47,17 @@ const sendScan = async config => {
40
47
  }
41
48
  return res.body.id
42
49
  } else {
50
+ if (config.debug) {
51
+ console.log(res.statusCode)
52
+ console.log(config)
53
+ }
43
54
  oraWrapper.failSpinner(
44
55
  startUploadSpinner,
45
56
  i18n.__('uploadingScanFail')
46
57
  )
58
+ if (res.statusCode === 403) {
59
+ console.log(i18n.__('permissionsError'))
60
+ }
47
61
  process.exit(1)
48
62
  }
49
63
  })
@@ -53,74 +67,96 @@ const sendScan = async config => {
53
67
  }
54
68
  }
55
69
 
56
- const zipValidator = configToUse => {
57
- if (configToUse.file.endsWith('.zip')) {
58
- let zipFileName = configToUse.file.split('/').pop()
59
- try {
60
- let zip = new AdmZip(configToUse.file)
61
- let zipEntries = zip.getEntries()
62
- zipEntries.forEach(function(zipEntry) {
63
- if (
64
- !zipEntry.entryName.includes('._') &&
65
- !zipEntry.entryName.includes('/.')
66
- ) {
67
- if (!zipEntry.isDirectory) {
68
- if (!zipEntry.entryName.endsWith('.js')) {
69
- console.log(i18n.__('scanZipError', zipFileName))
70
- process.exit(1)
71
- }
72
- }
73
- }
74
- })
75
- configToUse.language = supportedLanguages.JAVASCRIPT
76
- } catch {
77
- console.log(i18n.__('zipFileException'))
78
- }
79
- }
80
- }
81
-
82
70
  const formatScanOutput = (overview, results) => {
83
71
  console.log()
84
- console.log('Here are your top priorities to fix')
85
- console.log()
72
+ //check for no vulnerabilities and show a different message
86
73
 
87
- results.content.forEach(entry => {
88
- console.log(entry.severity, 'ID:', entry.id)
89
- console.log(
90
- entry.ruleId,
91
- 'in',
92
- entry.locations[0]?.physicalLocation.artifactLocation.uri,
93
- '@',
94
- entry.codeFlows[0]?.threadFlows[0]?.locations[0]?.location
95
- ?.physicalLocation?.region?.startLine
96
- )
74
+ if (results.content.length === 0) {
75
+ console.log(i18n.__('scanNoVulnerabilitiesFound'))
76
+ } else {
77
+ console.log(chalk.bold('Here are your top priorities to fix'))
97
78
  console.log()
98
- })
99
79
 
100
- const totalVulnerabilities =
101
- overview.critical +
102
- overview.high +
103
- overview.medium +
104
- overview.low +
105
- overview.note
80
+ const groups = getGroups(results.content)
81
+ groups.forEach(entry => {
82
+ console.log(
83
+ chalk.bold(
84
+ `${entry.severity} | ${entry.ruleId} (${entry.lineInfoSet.size})`
85
+ )
86
+ )
87
+ let count = 1
88
+ entry.lineInfoSet.forEach(lineInfo => {
89
+ console.log(`\t ${count}. ${lineInfo}`)
90
+ count++
91
+ })
92
+ console.log(chalk.bold('How to fix:'))
93
+ console.log(entry.recommendation)
94
+ console.log()
95
+ })
106
96
 
107
- console.log(`Found ${totalVulnerabilities} vulnerabilities`)
108
- console.log(
109
- i18n.__(
110
- 'foundDetailedVulnerabilities',
111
- overview.critical,
112
- overview.high,
113
- overview.medium,
114
- overview.low,
97
+ const totalVulnerabilities =
98
+ overview.critical +
99
+ overview.high +
100
+ overview.medium +
101
+ overview.low +
115
102
  overview.note
103
+
104
+ console.log(chalk.bold(`Found ${totalVulnerabilities} vulnerabilities`))
105
+ console.log(
106
+ i18n.__(
107
+ 'foundDetailedVulnerabilities',
108
+ overview.critical,
109
+ overview.high,
110
+ overview.medium,
111
+ overview.low,
112
+ overview.note
113
+ )
116
114
  )
117
- )
115
+ }
116
+ }
117
+
118
+ const getGroups = content => {
119
+ const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId))
120
+ let groupTypeResults = []
121
+ groupTypeSet.forEach(groupName => {
122
+ let groupResultsObj = {
123
+ ruleId: groupName,
124
+ lineInfoSet: new Set(),
125
+ recommendation: '',
126
+ severity: ''
127
+ }
128
+ content.forEach(resultEntry => {
129
+ if (resultEntry.ruleId === groupName) {
130
+ groupResultsObj.severity = resultEntry.severity
131
+ groupResultsObj.recommendation = resultEntry.recommendation
132
+ ? stripMustacheTags(resultEntry.recommendation)
133
+ : ''
134
+ groupResultsObj.lineInfoSet.add(formattedCodeLine(resultEntry))
135
+ }
136
+ })
137
+ groupTypeResults.push(groupResultsObj)
138
+ })
139
+ return groupTypeResults
140
+ }
141
+
142
+ const formattedCodeLine = resultEntry => {
143
+ let lineUri = resultEntry.locations[0]?.physicalLocation.artifactLocation.uri
144
+ return lineUri + ' @ ' + setLineNumber(resultEntry)
145
+ }
146
+
147
+ const setLineNumber = resultEntry => {
148
+ return resultEntry.codeFlows?.[0]?.threadFlows[0]?.locations[0]?.location
149
+ ?.physicalLocation?.region?.startLine
150
+ ? resultEntry.codeFlows[0]?.threadFlows[0]?.locations[0]?.location
151
+ ?.physicalLocation?.region?.startLine
152
+ : resultEntry.locations[0]?.physicalLocation?.region?.startLine
118
153
  }
119
154
 
120
155
  module.exports = {
121
156
  sendScan: sendScan,
157
+ getGroups: getGroups,
122
158
  allowedFileTypes: allowedFileTypes,
123
159
  isFileAllowed: isFileAllowed,
124
- formatScanOutput: formatScanOutput,
125
- zipValidator: zipValidator
160
+ stripMustacheTags: stripMustacheTags,
161
+ formatScanOutput: formatScanOutput
126
162
  }