@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.
Files changed (213) hide show
  1. package/.prettierignore +3 -0
  2. package/README.md +115 -78
  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 +95 -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/audit/saveFile.js +11 -0
  71. package/dist/commands/auth/auth.js +20 -2
  72. package/dist/commands/config/config.js +19 -8
  73. package/dist/commands/scan/processScan.js +9 -13
  74. package/dist/common/HTTPClient.js +112 -13
  75. package/dist/common/errorHandling.js +65 -1
  76. package/dist/common/versionChecker.js +30 -0
  77. package/dist/constants/constants.js +4 -2
  78. package/dist/constants/lambda.js +32 -4
  79. package/dist/constants/locales.js +60 -21
  80. package/dist/constants.js +181 -21
  81. package/dist/index.js +50 -23
  82. package/dist/lambda/aws.js +14 -11
  83. package/dist/lambda/help.js +4 -0
  84. package/dist/lambda/lambda.js +50 -27
  85. package/dist/lambda/lambdaUtils.js +72 -0
  86. package/dist/lambda/logUtils.js +11 -1
  87. package/dist/lambda/scanDetailCompletion.js +4 -4
  88. package/dist/lambda/scanRequest.js +11 -5
  89. package/dist/lambda/utils.js +110 -53
  90. package/dist/sbom/generateSbom.js +20 -0
  91. package/dist/scan/autoDetection.js +0 -32
  92. package/dist/scan/fileUtils.js +1 -1
  93. package/dist/scan/help.js +14 -40
  94. package/dist/scan/populateProjectIdAndProjectName.js +5 -0
  95. package/dist/scan/saveResults.js +14 -0
  96. package/dist/scan/scan.js +105 -40
  97. package/dist/scan/scanConfig.js +39 -0
  98. package/dist/scan/scanController.js +19 -16
  99. package/dist/scan/scanResults.js +24 -16
  100. package/dist/utils/commonApi.js +3 -3
  101. package/dist/utils/paramsUtil/commandlineParams.js +1 -20
  102. package/dist/utils/paramsUtil/paramHandler.js +3 -6
  103. package/dist/utils/parsedCLIOptions.js +14 -8
  104. package/dist/utils/requestUtils.js +1 -1
  105. package/dist/utils/saveFile.js +19 -0
  106. package/package.json +26 -21
  107. package/src/audit/AnalysisEngine.js +103 -0
  108. package/src/audit/catalogueApplication/catalogueApplication.js +42 -0
  109. package/src/audit/dotnetAnalysisEngine/index.js +26 -0
  110. package/src/audit/dotnetAnalysisEngine/parseLockFileContents.js +47 -0
  111. package/src/audit/dotnetAnalysisEngine/parseProjectFileContents.js +29 -0
  112. package/src/audit/dotnetAnalysisEngine/readLockFileContents.js +30 -0
  113. package/src/audit/dotnetAnalysisEngine/readProjectFileContents.js +26 -0
  114. package/src/audit/dotnetAnalysisEngine/sanitizer.js +11 -0
  115. package/src/audit/goAnalysisEngine/index.js +18 -0
  116. package/src/audit/goAnalysisEngine/parseProjectFileContents.js +209 -0
  117. package/src/audit/goAnalysisEngine/readProjectFileContents.js +31 -0
  118. package/src/audit/goAnalysisEngine/sanitizer.js +7 -0
  119. package/src/audit/javaAnalysisEngine/index.js +41 -0
  120. package/src/audit/javaAnalysisEngine/parseMavenProjectFileContents.js +222 -0
  121. package/src/audit/javaAnalysisEngine/parseProjectFileContents.js +420 -0
  122. package/src/audit/javaAnalysisEngine/readProjectFileContents.js +141 -0
  123. package/src/audit/javaAnalysisEngine/sanitizer.js +6 -0
  124. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedLanguages.js +35 -0
  125. package/src/audit/languageAnalysisEngine/checkForMultipleIdentifiedProjectFiles.js +41 -0
  126. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasLockFile.js +54 -0
  127. package/src/audit/languageAnalysisEngine/checkIdentifiedLanguageHasProjectFile.js +32 -0
  128. package/src/audit/languageAnalysisEngine/commonApi.js +20 -0
  129. package/src/audit/languageAnalysisEngine/constants.js +23 -0
  130. package/src/audit/languageAnalysisEngine/filterProjectPath.js +21 -0
  131. package/src/audit/languageAnalysisEngine/getIdentifiedLanguageInfo.js +41 -0
  132. package/src/audit/languageAnalysisEngine/getProjectRootFilenames.js +72 -0
  133. package/src/audit/languageAnalysisEngine/index.js +45 -0
  134. package/src/audit/languageAnalysisEngine/langugageAnalysisFactory.js +126 -0
  135. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +177 -0
  136. package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +27 -0
  137. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +303 -0
  138. package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +124 -0
  139. package/src/audit/languageAnalysisEngine/report/reportingFeature.js +190 -0
  140. package/src/audit/languageAnalysisEngine/sendSnapshot.js +51 -0
  141. package/src/audit/languageAnalysisEngine/util/capabilities.js +12 -0
  142. package/src/audit/languageAnalysisEngine/util/generalAPI.js +43 -0
  143. package/src/audit/languageAnalysisEngine/util/requestUtils.js +17 -0
  144. package/src/audit/nodeAnalysisEngine/handleNPMLockFileV2.js +49 -0
  145. package/src/audit/nodeAnalysisEngine/index.js +35 -0
  146. package/src/audit/nodeAnalysisEngine/parseNPMLockFileContents.js +20 -0
  147. package/src/audit/nodeAnalysisEngine/parseYarn2LockFileContents.js +63 -0
  148. package/src/audit/nodeAnalysisEngine/parseYarnLockFileContents.js +26 -0
  149. package/src/audit/nodeAnalysisEngine/readNPMLockFileContents.js +23 -0
  150. package/src/audit/nodeAnalysisEngine/readProjectFileContents.js +27 -0
  151. package/src/audit/nodeAnalysisEngine/readYarnLockFileContents.js +36 -0
  152. package/src/audit/nodeAnalysisEngine/sanitizer.js +11 -0
  153. package/src/audit/phpAnalysisEngine/index.js +27 -0
  154. package/src/audit/phpAnalysisEngine/parseLockFileContents.js +60 -0
  155. package/src/audit/phpAnalysisEngine/readLockFileContents.js +14 -0
  156. package/src/audit/phpAnalysisEngine/readProjectFileContents.js +25 -0
  157. package/src/audit/phpAnalysisEngine/sanitizer.js +4 -0
  158. package/src/audit/pythonAnalysisEngine/index.js +55 -0
  159. package/src/audit/pythonAnalysisEngine/parsePipfileLockContents.js +23 -0
  160. package/src/audit/pythonAnalysisEngine/parseProjectFileContents.js +33 -0
  161. package/src/audit/pythonAnalysisEngine/readPipfileLockFileContents.js +16 -0
  162. package/src/audit/pythonAnalysisEngine/readPythonProjectFileContents.js +22 -0
  163. package/src/audit/pythonAnalysisEngine/sanitizer.js +9 -0
  164. package/src/audit/rubyAnalysisEngine/index.js +30 -0
  165. package/src/audit/rubyAnalysisEngine/parseGemfileLockContents.js +215 -0
  166. package/src/audit/rubyAnalysisEngine/parsedGemfile.js +39 -0
  167. package/src/audit/rubyAnalysisEngine/readGemfileContents.js +18 -0
  168. package/src/audit/rubyAnalysisEngine/readGemfileLockContents.js +17 -0
  169. package/src/audit/rubyAnalysisEngine/sanitizer.js +8 -0
  170. package/src/commands/audit/auditConfig.ts +30 -0
  171. package/src/commands/audit/auditController.ts +31 -0
  172. package/src/commands/audit/help.ts +48 -0
  173. package/src/commands/audit/processAudit.ts +18 -0
  174. package/src/commands/audit/saveFile.ts +6 -0
  175. package/src/commands/auth/auth.js +26 -2
  176. package/src/commands/config/config.js +22 -8
  177. package/src/commands/scan/processScan.js +9 -13
  178. package/src/common/HTTPClient.js +149 -14
  179. package/src/common/errorHandling.ts +85 -2
  180. package/src/common/versionChecker.ts +39 -0
  181. package/src/constants/constants.js +5 -4
  182. package/src/constants/lambda.js +45 -4
  183. package/src/constants/locales.js +76 -26
  184. package/src/constants.js +204 -23
  185. package/src/index.ts +67 -27
  186. package/src/lambda/aws.ts +13 -12
  187. package/src/lambda/help.ts +4 -0
  188. package/src/lambda/lambda.ts +53 -34
  189. package/src/lambda/lambdaUtils.ts +111 -0
  190. package/src/lambda/logUtils.ts +19 -1
  191. package/src/lambda/scanDetailCompletion.ts +4 -4
  192. package/src/lambda/scanRequest.ts +13 -11
  193. package/src/lambda/utils.ts +149 -81
  194. package/src/sbom/generateSbom.ts +17 -0
  195. package/src/scan/autoDetection.js +0 -29
  196. package/src/scan/fileUtils.js +1 -1
  197. package/src/scan/help.js +14 -45
  198. package/src/scan/populateProjectIdAndProjectName.js +5 -0
  199. package/src/scan/saveResults.js +14 -0
  200. package/src/scan/scan.js +127 -58
  201. package/src/scan/scanConfig.js +54 -0
  202. package/src/scan/scanController.js +22 -15
  203. package/src/scan/scanResults.js +32 -19
  204. package/src/utils/commonApi.js +2 -3
  205. package/src/utils/getConfig.ts +2 -0
  206. package/src/utils/paramsUtil/commandlineParams.js +1 -26
  207. package/src/utils/paramsUtil/paramHandler.js +3 -7
  208. package/src/utils/parsedCLIOptions.js +11 -9
  209. package/src/utils/requestUtils.js +1 -1
  210. package/src/utils/saveFile.js +19 -0
  211. package/dist/lambda/scanDetail.js +0 -30
  212. package/dist/scan/fileFinder.js +0 -15
  213. package/dist/utils/paramsUtil/yamlParams.js +0 -6
@@ -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
  }
@@ -0,0 +1,17 @@
1
+ import { getHttpClient } from '../utils/commonApi'
2
+
3
+ export default function generateSbom(config: any) {
4
+ const client = getHttpClient(config)
5
+ return client
6
+ .getSbom(config)
7
+ .then((res: { statusCode: number; body: any }) => {
8
+ if (res.statusCode === 200) {
9
+ return res.body
10
+ } else {
11
+ console.log('Unable to retrieve Software Bill of Materials (SBOM)')
12
+ }
13
+ })
14
+ .catch((err: any) => {
15
+ console.log(err)
16
+ })
17
+ }
@@ -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,19 @@ 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
+ 'organization-id',
25
+ 'api-key',
26
+ 'authorization',
27
+ 'host',
28
+ 'proxy',
29
+ 'help',
30
+ 'ff',
31
+ 'ignore-cert-errors',
32
+ 'verbose',
33
+ 'debug'
65
34
  ]
66
35
  },
67
36
  {
@@ -21,6 +21,11 @@ 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
+ process.exit(1)
27
+ return
28
+ }
24
29
 
25
30
  if (res.statusCode === 201) {
26
31
  console.log(i18n.__('projectCreatedScan'))
@@ -0,0 +1,14 @@
1
+ const fs = require('fs')
2
+
3
+ const writeResultsToFile = async (responseBody, name = 'results.sarif') => {
4
+ try {
5
+ fs.writeFileSync(name, JSON.stringify(responseBody, null, 2))
6
+ console.log(`Scan Results saved to ${name}`)
7
+ } catch (err) {
8
+ console.log('Error writing Scan Results to file')
9
+ }
10
+ }
11
+
12
+ module.exports = {
13
+ writeResultsToFile: writeResultsToFile
14
+ }
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,15 @@ 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(/\$\$LINK_DELIM\$\$/g, '\n')
23
+ .replace(/\s+/g, ' ')
24
+ .trim()
25
+ }
26
+
19
27
  const sendScan = async config => {
20
28
  if (!isFileAllowed(config.file)) {
21
29
  console.log(i18n.__('scanErrorFileMessage'))
@@ -40,10 +48,19 @@ const sendScan = async config => {
40
48
  }
41
49
  return res.body.id
42
50
  } else {
51
+ if (config.debug) {
52
+ console.log(res.statusCode)
53
+ console.log(config)
54
+ }
43
55
  oraWrapper.failSpinner(
44
56
  startUploadSpinner,
45
57
  i18n.__('uploadingScanFail')
46
58
  )
59
+ if (res.statusCode === 403) {
60
+ console.log(i18n.__('permissionsError'))
61
+ process.exit(1)
62
+ }
63
+ console.log(i18n.__('genericServiceError', res.statusCode))
47
64
  process.exit(1)
48
65
  }
49
66
  })
@@ -53,74 +70,126 @@ const sendScan = async config => {
53
70
  }
54
71
  }
55
72
 
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
- }
73
+ const formatScanOutput = (overview, results) => {
74
+ console.log()
75
+
76
+ if (results.content.length === 0) {
77
+ console.log(i18n.__('scanNoVulnerabilitiesFound'))
78
+ } else {
79
+ let message =
80
+ overview.critical || overview.high
81
+ ? 'Here are your top priorities to fix'
82
+ : "No major issues, here's what we found"
83
+ console.log(chalk.bold(message))
84
+ console.log()
85
+
86
+ const groups = getGroups(results.content)
87
+ groups.forEach(entry => {
88
+ console.log(
89
+ chalk.bold(
90
+ `${entry.severity} | ${entry.ruleId} (${entry.lineInfoSet.size})`
91
+ )
92
+ )
93
+ let count = 1
94
+ entry.lineInfoSet.forEach(lineInfo => {
95
+ console.log(`\t ${count}. ${lineInfo}`)
96
+ count++
74
97
  })
75
- configToUse.language = supportedLanguages.JAVASCRIPT
76
- } catch {
77
- console.log(i18n.__('zipFileException'))
78
- }
98
+
99
+ if (entry?.cwe && entry?.cwe.length > 0) {
100
+ formatLinks('cwe', entry.cwe)
101
+ }
102
+ if (entry?.reference && entry?.reference.length > 0) {
103
+ formatLinks('reference', entry.reference)
104
+ }
105
+ if (entry?.owasp && entry?.owasp.length > 0) {
106
+ formatLinks('owasp', entry.owasp)
107
+ }
108
+ console.log(chalk.bold('How to fix:'))
109
+ console.log(entry.recommendation)
110
+ console.log()
111
+ })
112
+
113
+ const totalVulnerabilities =
114
+ overview.critical +
115
+ overview.high +
116
+ overview.medium +
117
+ overview.low +
118
+ overview.note
119
+
120
+ let vulMessage =
121
+ totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
122
+ console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
123
+ console.log(
124
+ i18n.__(
125
+ 'foundDetailedVulnerabilities',
126
+ overview.critical,
127
+ overview.high,
128
+ overview.medium,
129
+ overview.low,
130
+ overview.note
131
+ )
132
+ )
79
133
  }
80
134
  }
81
135
 
82
- const formatScanOutput = (overview, results) => {
83
- console.log()
84
- console.log('Here are your top priorities to fix')
136
+ const formatLinks = (objName, entry) => {
137
+ console.log(chalk.bold(objName + ':'))
138
+ entry.forEach(link => {
139
+ console.log(link)
140
+ })
85
141
  console.log()
142
+ }
86
143
 
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
- )
97
- console.log()
144
+ const getGroups = content => {
145
+ const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId))
146
+ let groupTypeResults = []
147
+ groupTypeSet.forEach(groupName => {
148
+ let groupResultsObj = {
149
+ ruleId: groupName,
150
+ lineInfoSet: new Set(),
151
+ cwe: '',
152
+ owasp: '',
153
+ reference: '',
154
+ recommendation: '',
155
+ severity: ''
156
+ }
157
+ content.forEach(resultEntry => {
158
+ if (resultEntry.ruleId === groupName) {
159
+ groupResultsObj.severity = resultEntry.severity
160
+ groupResultsObj.cwe = resultEntry.cwe
161
+ groupResultsObj.owasp = resultEntry.owasp
162
+ groupResultsObj.reference = resultEntry.reference
163
+ groupResultsObj.recommendation = resultEntry.recommendation
164
+ ? stripMustacheTags(resultEntry.recommendation)
165
+ : 'No Recommendations Data Found'
166
+ groupResultsObj.lineInfoSet.add(formattedCodeLine(resultEntry))
167
+ }
168
+ })
169
+ groupTypeResults.push(groupResultsObj)
98
170
  })
171
+ return groupTypeResults
172
+ }
99
173
 
100
- const totalVulnerabilities =
101
- overview.critical +
102
- overview.high +
103
- overview.medium +
104
- overview.low +
105
- overview.note
106
-
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,
115
- overview.note
116
- )
117
- )
174
+ const formattedCodeLine = resultEntry => {
175
+ let lineUri = resultEntry.locations[0]?.physicalLocation.artifactLocation.uri
176
+ return lineUri + ' @ ' + setLineNumber(resultEntry)
177
+ }
178
+
179
+ const setLineNumber = resultEntry => {
180
+ return resultEntry.codeFlows?.[0]?.threadFlows[0]?.locations[0]?.location
181
+ ?.physicalLocation?.region?.startLine
182
+ ? resultEntry.codeFlows[0]?.threadFlows[0]?.locations[0]?.location
183
+ ?.physicalLocation?.region?.startLine
184
+ : resultEntry.locations[0]?.physicalLocation?.region?.startLine
118
185
  }
119
186
 
120
187
  module.exports = {
121
188
  sendScan: sendScan,
189
+ getGroups: getGroups,
122
190
  allowedFileTypes: allowedFileTypes,
123
191
  isFileAllowed: isFileAllowed,
192
+ stripMustacheTags: stripMustacheTags,
124
193
  formatScanOutput: formatScanOutput,
125
- zipValidator: zipValidator
194
+ formatLinks: formatLinks
126
195
  }