@contrast/contrast 1.0.4 → 1.0.5
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 +2 -0
- package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
- package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
- package/dist/audit/languageAnalysisEngine/languageAnalysisFactory.js +4 -2
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +25 -0
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +3 -17
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +1 -1
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +2 -16
- package/dist/commands/audit/auditConfig.js +8 -2
- package/dist/commands/audit/auditController.js +8 -2
- package/dist/commands/scan/processScan.js +6 -3
- package/dist/commands/scan/sca/scaAnalysis.js +44 -0
- package/dist/common/HTTPClient.js +0 -1
- package/dist/common/errorHandling.js +7 -17
- package/dist/constants/constants.js +14 -2
- package/dist/constants/locales.js +28 -35
- package/dist/constants.js +20 -0
- package/dist/scaAnalysis/common/formatMessage.js +11 -0
- package/dist/scaAnalysis/common/treeUpload.js +30 -0
- package/dist/scaAnalysis/java/analysis.js +116 -0
- package/dist/scaAnalysis/java/index.js +18 -0
- package/dist/scaAnalysis/java/javaBuildDepsParser.js +326 -0
- package/dist/scan/autoDetection.js +46 -1
- package/dist/scan/fileUtils.js +73 -1
- package/dist/scan/formatScanOutput.js +212 -0
- package/dist/scan/help.js +3 -1
- package/dist/scan/models/groupedResultsModel.js +2 -1
- package/dist/scan/scan.js +1 -96
- package/dist/scan/scanController.js +1 -2
- package/dist/scan/scanResults.js +3 -17
- package/package.json +2 -1
- package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
- package/src/audit/catalogueApplication/catalogueApplication.js +4 -16
- package/src/audit/languageAnalysisEngine/languageAnalysisFactory.js +9 -5
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +71 -0
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +3 -25
- package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +1 -1
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +2 -24
- package/src/commands/audit/auditConfig.ts +12 -3
- package/src/commands/audit/auditController.ts +9 -2
- package/src/commands/audit/processAudit.ts +3 -0
- package/src/commands/scan/processScan.js +8 -3
- package/src/commands/scan/sca/scaAnalysis.js +73 -0
- package/src/common/HTTPClient.js +1 -1
- package/src/common/errorHandling.ts +7 -24
- package/src/constants/constants.js +14 -2
- package/src/constants/locales.js +30 -49
- package/src/constants.js +22 -0
- package/src/scaAnalysis/common/formatMessage.js +10 -0
- package/src/scaAnalysis/common/treeUpload.js +34 -0
- package/src/scaAnalysis/java/analysis.js +159 -0
- package/src/scaAnalysis/java/index.js +21 -0
- package/src/scaAnalysis/java/javaBuildDepsParser.js +391 -0
- package/src/scan/autoDetection.js +54 -1
- package/src/scan/fileUtils.js +91 -1
- package/src/scan/formatScanOutput.ts +241 -0
- package/src/scan/help.js +3 -1
- package/src/scan/models/groupedResultsModel.ts +7 -5
- package/src/scan/models/resultContentModel.ts +2 -2
- package/src/scan/scan.ts +0 -130
- package/src/scan/scanController.js +1 -2
- package/src/scan/scanResults.js +9 -17
package/src/scan/fileUtils.js
CHANGED
|
@@ -11,6 +11,90 @@ const findFile = async () => {
|
|
|
11
11
|
})
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const findFilesJava = async languagesFound => {
|
|
15
|
+
const result = await fg(
|
|
16
|
+
['**/pom.xml', '**/build.gradle', '**/build.gradle.kts'],
|
|
17
|
+
{
|
|
18
|
+
dot: false,
|
|
19
|
+
deep: 1,
|
|
20
|
+
onlyFiles: true
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if (result.length > 0) {
|
|
25
|
+
return languagesFound.push({ java: result })
|
|
26
|
+
}
|
|
27
|
+
return languagesFound
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const findFilesJavascript = async languagesFound => {
|
|
31
|
+
const result = await fg(
|
|
32
|
+
['**/package.json', '**/yarn.lock', '**/package.lock.json'],
|
|
33
|
+
{
|
|
34
|
+
dot: false,
|
|
35
|
+
deep: 1,
|
|
36
|
+
onlyFiles: true
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if (result.length > 0) {
|
|
41
|
+
return languagesFound.push({ javascript: result })
|
|
42
|
+
}
|
|
43
|
+
return languagesFound
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const findFilesPython = async languagesFound => {
|
|
47
|
+
const result = await fg(['**/Pipfile.lock', '**/Pipfile'], {
|
|
48
|
+
dot: false,
|
|
49
|
+
deep: 3,
|
|
50
|
+
onlyFiles: true
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (result.length > 0) {
|
|
54
|
+
return languagesFound.push({ python: result })
|
|
55
|
+
}
|
|
56
|
+
return languagesFound
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const findFilesGo = async languagesFound => {
|
|
60
|
+
const result = await fg(['**/go.mod'], {
|
|
61
|
+
dot: false,
|
|
62
|
+
deep: 3,
|
|
63
|
+
onlyFiles: true
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (result.length > 0) {
|
|
67
|
+
return languagesFound.push({ go: result })
|
|
68
|
+
}
|
|
69
|
+
return languagesFound
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const findFilesRuby = async languagesFound => {
|
|
73
|
+
const result = await fg(['**/Gemfile', '**/Gemfile.lock'], {
|
|
74
|
+
dot: false,
|
|
75
|
+
deep: 3,
|
|
76
|
+
onlyFiles: true
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
if (result.length > 0) {
|
|
80
|
+
return languagesFound.push({ ruby: result })
|
|
81
|
+
}
|
|
82
|
+
return languagesFound
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const findFilesPhp = async languagesFound => {
|
|
86
|
+
const result = await fg(['**/composer.json', '**/composer.lock'], {
|
|
87
|
+
dot: false,
|
|
88
|
+
deep: 3,
|
|
89
|
+
onlyFiles: true
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (result.length > 0) {
|
|
93
|
+
return languagesFound.push({ php: result })
|
|
94
|
+
}
|
|
95
|
+
return languagesFound
|
|
96
|
+
}
|
|
97
|
+
|
|
14
98
|
const checkFilePermissions = file => {
|
|
15
99
|
let readableFile = false
|
|
16
100
|
try {
|
|
@@ -29,5 +113,11 @@ const fileExists = path => {
|
|
|
29
113
|
module.exports = {
|
|
30
114
|
findFile,
|
|
31
115
|
fileExists,
|
|
32
|
-
checkFilePermissions
|
|
116
|
+
checkFilePermissions,
|
|
117
|
+
findFilesJava,
|
|
118
|
+
findFilesJavascript,
|
|
119
|
+
findFilesPython,
|
|
120
|
+
findFilesGo,
|
|
121
|
+
findFilesPhp,
|
|
122
|
+
findFilesRuby
|
|
33
123
|
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { ProjectOverview, ScanResultsModel } from './models/scanResultsModel'
|
|
2
|
+
import i18n from 'i18n'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import { ResultContent } from './models/resultContentModel'
|
|
5
|
+
import { GroupedResultsModel } from './models/groupedResultsModel'
|
|
6
|
+
import { sortBy } from 'lodash'
|
|
7
|
+
import Table from 'cli-table3'
|
|
8
|
+
import {
|
|
9
|
+
CRITICAL_COLOUR,
|
|
10
|
+
HIGH_COLOUR,
|
|
11
|
+
LOW_COLOUR,
|
|
12
|
+
MEDIUM_COLOUR,
|
|
13
|
+
NOTE_COLOUR
|
|
14
|
+
} from '../constants/constants'
|
|
15
|
+
|
|
16
|
+
export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
17
|
+
const { scanResultsInstances } = scanResults
|
|
18
|
+
|
|
19
|
+
let projectOverview = getProjectOverview(scanResultsInstances.content)
|
|
20
|
+
if (scanResultsInstances.content.length === 0) {
|
|
21
|
+
console.log(i18n.__('scanNoVulnerabilitiesFound'))
|
|
22
|
+
console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
|
|
23
|
+
console.log(i18n.__('scanNoVulnerabilitiesFoundGoodWork'))
|
|
24
|
+
} else {
|
|
25
|
+
const message =
|
|
26
|
+
projectOverview.critical || projectOverview.high
|
|
27
|
+
? 'Here are your top priorities to fix'
|
|
28
|
+
: "No major issues, here's what we found"
|
|
29
|
+
console.log(chalk.bold(message))
|
|
30
|
+
console.log()
|
|
31
|
+
|
|
32
|
+
let defaultView = getDefaultView(scanResultsInstances.content)
|
|
33
|
+
|
|
34
|
+
let count = defaultView.length
|
|
35
|
+
defaultView.forEach(entry => {
|
|
36
|
+
let table = new Table({
|
|
37
|
+
chars: {
|
|
38
|
+
top: '',
|
|
39
|
+
'top-mid': '',
|
|
40
|
+
'top-left': '',
|
|
41
|
+
'top-right': '',
|
|
42
|
+
bottom: '',
|
|
43
|
+
'bottom-mid': '',
|
|
44
|
+
'bottom-left': '',
|
|
45
|
+
'bottom-right': '',
|
|
46
|
+
left: '',
|
|
47
|
+
'left-mid': '',
|
|
48
|
+
mid: '',
|
|
49
|
+
'mid-mid': '',
|
|
50
|
+
right: '',
|
|
51
|
+
'right-mid': '',
|
|
52
|
+
middle: ' '
|
|
53
|
+
},
|
|
54
|
+
style: { 'padding-left': 0, 'padding-right': 0 },
|
|
55
|
+
colAligns: ['right'],
|
|
56
|
+
wordWrap: true,
|
|
57
|
+
colWidths: [12, 1, 100]
|
|
58
|
+
})
|
|
59
|
+
let learnRow: string[] = []
|
|
60
|
+
let adviceRow = []
|
|
61
|
+
let headerRow = [
|
|
62
|
+
chalk
|
|
63
|
+
.hex(entry.colour)
|
|
64
|
+
.bold(`CONTRAST-${count.toString().padStart(3, '0')}`),
|
|
65
|
+
chalk.hex(entry.colour).bold('-'),
|
|
66
|
+
chalk.hex(entry.colour).bold(`[${entry.severity}] ${entry.ruleId}`) +
|
|
67
|
+
entry.message
|
|
68
|
+
]
|
|
69
|
+
let codeRow = [
|
|
70
|
+
chalk.hex('#F6F5F5').bold(`Code`),
|
|
71
|
+
chalk.hex('#F6F5F5').bold(`:`),
|
|
72
|
+
chalk.hex('#F6F5F5').bold(`${entry.codePath}`)
|
|
73
|
+
]
|
|
74
|
+
let issueRow = [chalk.bold(`Issue`), chalk.bold(`:`), `${entry.issue}`]
|
|
75
|
+
|
|
76
|
+
table.push(headerRow, codeRow, issueRow)
|
|
77
|
+
|
|
78
|
+
if (entry?.advice) {
|
|
79
|
+
adviceRow = [
|
|
80
|
+
chalk.bold('Advice'),
|
|
81
|
+
chalk.bold(`:`),
|
|
82
|
+
stripTags(entry.advice)
|
|
83
|
+
]
|
|
84
|
+
table.push(adviceRow)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (entry?.learn && entry?.learn.length > 0) {
|
|
88
|
+
learnRow = [
|
|
89
|
+
chalk.bold('Learn'),
|
|
90
|
+
chalk.bold(`:`),
|
|
91
|
+
chalk.hex('#97f7f7').bold.underline(entry.learn[0])
|
|
92
|
+
]
|
|
93
|
+
table.push(learnRow)
|
|
94
|
+
}
|
|
95
|
+
count--
|
|
96
|
+
console.log(table.toString())
|
|
97
|
+
console.log()
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
printVulnInfo(projectOverview)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function printVulnInfo(projectOverview: any) {
|
|
104
|
+
const totalVulnerabilities = projectOverview.total
|
|
105
|
+
|
|
106
|
+
const vulMessage =
|
|
107
|
+
totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
|
|
108
|
+
console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
|
|
109
|
+
console.log(
|
|
110
|
+
i18n.__(
|
|
111
|
+
'foundDetailedVulnerabilities',
|
|
112
|
+
String(projectOverview.critical),
|
|
113
|
+
String(projectOverview.high),
|
|
114
|
+
String(projectOverview.medium),
|
|
115
|
+
String(projectOverview.low),
|
|
116
|
+
String(projectOverview.note)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getProjectOverview(content: ResultContent[]) {
|
|
122
|
+
let acc: any = {
|
|
123
|
+
critical: 0,
|
|
124
|
+
high: 0,
|
|
125
|
+
medium: 0,
|
|
126
|
+
low: 0,
|
|
127
|
+
note: 0,
|
|
128
|
+
total: 0
|
|
129
|
+
}
|
|
130
|
+
content.forEach((i: ResultContent) => {
|
|
131
|
+
acc[i.severity.toLowerCase()] += 1
|
|
132
|
+
acc.total += 1
|
|
133
|
+
return acc
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
return acc
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function formatLinks(objName: string, entry: any[]) {
|
|
140
|
+
let line = chalk.bold(objName + ' : ')
|
|
141
|
+
if (entry.length === 1) {
|
|
142
|
+
console.log(line + chalk.hex('#97DCF7').bold.underline(entry[0]))
|
|
143
|
+
} else {
|
|
144
|
+
console.log(line)
|
|
145
|
+
entry.forEach(link => {
|
|
146
|
+
console.log(chalk.hex('#97DCF7').bold.underline(link))
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function getDefaultView(content: ResultContent[]) {
|
|
152
|
+
const groupTypeResults = [] as GroupedResultsModel[]
|
|
153
|
+
|
|
154
|
+
content.forEach(resultEntry => {
|
|
155
|
+
const groupResultsObj = new GroupedResultsModel(resultEntry.ruleId)
|
|
156
|
+
groupResultsObj.severity = resultEntry.severity
|
|
157
|
+
groupResultsObj.ruleId = resultEntry.ruleId
|
|
158
|
+
groupResultsObj.issue = stripTags(resultEntry.issue)
|
|
159
|
+
groupResultsObj.advice = resultEntry.advice
|
|
160
|
+
groupResultsObj.learn = resultEntry.learn
|
|
161
|
+
groupResultsObj.message = resultEntry.message?.text
|
|
162
|
+
? editVulName(resultEntry.message.text) +
|
|
163
|
+
':' +
|
|
164
|
+
getSourceLineNumber(resultEntry)
|
|
165
|
+
: ''
|
|
166
|
+
groupResultsObj.codePath = getLocationsSyncInfo(resultEntry)
|
|
167
|
+
groupTypeResults.push(groupResultsObj)
|
|
168
|
+
assignBySeverity(resultEntry, groupResultsObj)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
return sortBy(groupTypeResults, ['priority']).reverse()
|
|
172
|
+
}
|
|
173
|
+
export function editVulName(message: string) {
|
|
174
|
+
return message.substring(message.indexOf(' in '))
|
|
175
|
+
}
|
|
176
|
+
export function getLocationsSyncInfo(resultEntry: ResultContent) {
|
|
177
|
+
const locationsMessage =
|
|
178
|
+
resultEntry.locations[0]?.physicalLocation?.artifactLocation?.uri || ''
|
|
179
|
+
const locationsLineNumber =
|
|
180
|
+
resultEntry.locations[0]?.physicalLocation?.region?.startLine || ''
|
|
181
|
+
|
|
182
|
+
if (!locationsLineNumber) {
|
|
183
|
+
return '@' + locationsMessage
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return '@' + locationsMessage + ':' + locationsLineNumber
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function getSourceLineNumber(resultEntry: ResultContent) {
|
|
190
|
+
const locationsLineNumber =
|
|
191
|
+
resultEntry.locations[0]?.physicalLocation?.region?.startLine || ''
|
|
192
|
+
let codeFlowLineNumber = getCodeFlowInfo(resultEntry)
|
|
193
|
+
|
|
194
|
+
return codeFlowLineNumber ? codeFlowLineNumber : locationsLineNumber
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function getCodeFlowInfo(resultEntry: ResultContent) {
|
|
198
|
+
let result: any
|
|
199
|
+
resultEntry.codeFlows[0]?.threadFlows.forEach((i: { locations: any[] }) => {
|
|
200
|
+
return (result = i.locations.find(
|
|
201
|
+
(locations: { importance: string }) =>
|
|
202
|
+
locations.importance === 'essential'
|
|
203
|
+
))
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
return result?.location?.physicalLocation?.region?.startLine
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function stripTags(oldString: string) {
|
|
210
|
+
return oldString
|
|
211
|
+
.replace(/\n/g, ' ')
|
|
212
|
+
.replace(/\s+/g, ' ')
|
|
213
|
+
.trim()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function assignBySeverity(
|
|
217
|
+
entry: ResultContent,
|
|
218
|
+
assignedObj: GroupedResultsModel
|
|
219
|
+
) {
|
|
220
|
+
if (entry.severity.toUpperCase() === 'CRITICAL') {
|
|
221
|
+
assignedObj.priority = 1
|
|
222
|
+
assignedObj.colour = CRITICAL_COLOUR
|
|
223
|
+
return assignedObj
|
|
224
|
+
} else if (entry.severity.toUpperCase() === 'HIGH') {
|
|
225
|
+
assignedObj.priority = 2
|
|
226
|
+
assignedObj.colour = HIGH_COLOUR
|
|
227
|
+
return assignedObj
|
|
228
|
+
} else if (entry.severity.toUpperCase() === 'MEDIUM') {
|
|
229
|
+
assignedObj.priority = 3
|
|
230
|
+
assignedObj.colour = MEDIUM_COLOUR
|
|
231
|
+
return assignedObj
|
|
232
|
+
} else if (entry.severity.toUpperCase() === 'LOW') {
|
|
233
|
+
assignedObj.priority = 4
|
|
234
|
+
assignedObj.colour = LOW_COLOUR
|
|
235
|
+
return assignedObj
|
|
236
|
+
} else if (entry.severity.toUpperCase() === 'NOTE') {
|
|
237
|
+
assignedObj.priority = 5
|
|
238
|
+
assignedObj.colour = NOTE_COLOUR
|
|
239
|
+
return assignedObj
|
|
240
|
+
}
|
|
241
|
+
}
|
package/src/scan/help.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
export class GroupedResultsModel {
|
|
2
2
|
ruleId: string
|
|
3
|
-
|
|
3
|
+
codePathSet: Set<string>
|
|
4
4
|
cwe?: string[]
|
|
5
|
-
owasp?: string[]
|
|
6
5
|
reference?: string[]
|
|
7
|
-
recommendation?: string
|
|
8
6
|
severity?: string
|
|
9
7
|
advice?: string
|
|
10
8
|
learn?: string[]
|
|
11
9
|
issue?: string
|
|
12
|
-
|
|
10
|
+
priority?: number
|
|
11
|
+
message?: string | undefined
|
|
12
|
+
colour: string;
|
|
13
|
+
codePath?: string;
|
|
13
14
|
|
|
14
15
|
constructor(ruleId: string) {
|
|
15
16
|
this.ruleId = ruleId
|
|
16
|
-
this.
|
|
17
|
+
this.colour = '#999999'
|
|
18
|
+
this.codePathSet = new Set<string>
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -5,7 +5,7 @@ interface ArtifactLocation {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
interface Region {
|
|
8
|
-
startLine:
|
|
8
|
+
startLine: string
|
|
9
9
|
snippet: Snippet
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -52,7 +52,7 @@ export interface CodeFlow {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export interface ResultContent {
|
|
55
|
-
message
|
|
55
|
+
message?: {text :string};
|
|
56
56
|
id: string
|
|
57
57
|
organizationId: string
|
|
58
58
|
projectId: string
|
package/src/scan/scan.ts
CHANGED
|
@@ -2,10 +2,6 @@ import commonApi from '../utils/commonApi.js'
|
|
|
2
2
|
import fileUtils from '../scan/fileUtils'
|
|
3
3
|
import i18n from 'i18n'
|
|
4
4
|
import oraWrapper from '../utils/oraWrapper'
|
|
5
|
-
import chalk from 'chalk'
|
|
6
|
-
import { ProjectOverview, ScanResultsModel } from './models/scanResultsModel'
|
|
7
|
-
import { Location, ResultContent } from './models/resultContentModel'
|
|
8
|
-
import { GroupedResultsModel } from './models/groupedResultsModel'
|
|
9
5
|
|
|
10
6
|
export const allowedFileTypes = ['.jar', '.war', '.js', '.zip', '.exe']
|
|
11
7
|
|
|
@@ -64,129 +60,3 @@ export const sendScan = async (config: any) => {
|
|
|
64
60
|
})
|
|
65
61
|
}
|
|
66
62
|
}
|
|
67
|
-
|
|
68
|
-
export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
69
|
-
const { projectOverview, scanResultsInstances } = scanResults
|
|
70
|
-
|
|
71
|
-
if (scanResultsInstances.content.length === 0) {
|
|
72
|
-
console.log(i18n.__('scanNoVulnerabilitiesFound'))
|
|
73
|
-
} else {
|
|
74
|
-
const message =
|
|
75
|
-
projectOverview.critical || projectOverview.high
|
|
76
|
-
? 'Here are your top priorities to fix'
|
|
77
|
-
: "No major issues, here's what we found"
|
|
78
|
-
console.log(chalk.bold(message))
|
|
79
|
-
console.log()
|
|
80
|
-
|
|
81
|
-
const groups = getGroups(scanResultsInstances.content)
|
|
82
|
-
|
|
83
|
-
groups.forEach(entry => {
|
|
84
|
-
console.log(
|
|
85
|
-
chalk.bold(
|
|
86
|
-
`[ ${entry.severity} ] | ${entry.ruleId} (${entry.lineInfoSet.size}) - ` +
|
|
87
|
-
`${entry.message}`
|
|
88
|
-
)
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
let count = 1
|
|
92
|
-
entry.lineInfoSet.forEach(lineInfo => {
|
|
93
|
-
console.log(`\t ${count}. ${lineInfo}`)
|
|
94
|
-
count++
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
if (entry?.issue) {
|
|
98
|
-
console.log(chalk.bold('Issue' + ': ') + entry.issue)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (entry?.advice) {
|
|
102
|
-
console.log(chalk.bold('Advice' + ': ') + entry.advice)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (entry?.learn && entry?.learn.length > 0) {
|
|
106
|
-
formatLinks('Learn', entry.learn)
|
|
107
|
-
}
|
|
108
|
-
console.log()
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
printVulnInfo(projectOverview)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function printVulnInfo(projectOverview: ProjectOverview) {
|
|
116
|
-
const totalVulnerabilities = getTotalVulns(projectOverview)
|
|
117
|
-
|
|
118
|
-
const vulMessage =
|
|
119
|
-
totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
|
|
120
|
-
console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
|
|
121
|
-
console.log(
|
|
122
|
-
i18n.__(
|
|
123
|
-
'foundDetailedVulnerabilities',
|
|
124
|
-
String(projectOverview.critical),
|
|
125
|
-
String(projectOverview.high),
|
|
126
|
-
String(projectOverview.medium),
|
|
127
|
-
String(projectOverview.low),
|
|
128
|
-
String(projectOverview.note)
|
|
129
|
-
)
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function getTotalVulns(projectOverview: ProjectOverview) {
|
|
134
|
-
return (
|
|
135
|
-
projectOverview.critical +
|
|
136
|
-
projectOverview.high +
|
|
137
|
-
projectOverview.medium +
|
|
138
|
-
projectOverview.low +
|
|
139
|
-
projectOverview.note
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function formatLinks(objName: string, entry: any[]) {
|
|
144
|
-
console.log(chalk.bold(objName + ':'))
|
|
145
|
-
entry.forEach(link => {
|
|
146
|
-
console.log(link)
|
|
147
|
-
})
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function getGroups(content: ResultContent[]) {
|
|
151
|
-
const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId))
|
|
152
|
-
const groupTypeResults = [] as GroupedResultsModel[]
|
|
153
|
-
|
|
154
|
-
groupTypeSet.forEach(groupName => {
|
|
155
|
-
const groupResultsObj = new GroupedResultsModel(groupName)
|
|
156
|
-
|
|
157
|
-
content.forEach(resultEntry => {
|
|
158
|
-
if (resultEntry.ruleId === groupName) {
|
|
159
|
-
groupResultsObj.severity = resultEntry.severity
|
|
160
|
-
groupResultsObj.issue = stripMustacheTags(resultEntry.issue)
|
|
161
|
-
groupResultsObj.advice = resultEntry.advice
|
|
162
|
-
groupResultsObj.learn = resultEntry.learn
|
|
163
|
-
groupResultsObj.message = resultEntry.message?.text
|
|
164
|
-
|
|
165
|
-
groupResultsObj.lineInfoSet.add(getMessage(resultEntry.locations))
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
groupTypeResults.push(groupResultsObj)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
return groupTypeResults
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function getMessage(locations: Location[]) {
|
|
175
|
-
const message = locations[0]?.physicalLocation?.artifactLocation?.uri || ''
|
|
176
|
-
const lineNumber = locations[0]?.physicalLocation?.region?.startLine || ''
|
|
177
|
-
|
|
178
|
-
if (!lineNumber) {
|
|
179
|
-
return '@' + message
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return '@' + message + ':' + lineNumber
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export function stripMustacheTags(oldString: string) {
|
|
186
|
-
return oldString
|
|
187
|
-
.replace(/\n/g, ' ')
|
|
188
|
-
.replace(/{{.*?}}/g, '\n')
|
|
189
|
-
.replace(/\$\$LINK_DELIM\$\$/g, '\n')
|
|
190
|
-
.replace(/\s+/g, ' ')
|
|
191
|
-
.trim()
|
|
192
|
-
}
|
|
@@ -68,8 +68,7 @@ const startScan = async configToUse => {
|
|
|
68
68
|
console.log(
|
|
69
69
|
`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
70
70
|
)
|
|
71
|
-
|
|
72
|
-
return { projectOverview, scanDetail, scanResultsInstances }
|
|
71
|
+
return { scanDetail, scanResultsInstances }
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
|
package/src/scan/scanResults.js
CHANGED
|
@@ -47,8 +47,14 @@ const returnScanResults = async (
|
|
|
47
47
|
}
|
|
48
48
|
if (result.body.status === 'FAILED') {
|
|
49
49
|
complete = true
|
|
50
|
-
oraFunctions.failSpinner(
|
|
51
|
-
|
|
50
|
+
oraFunctions.failSpinner(
|
|
51
|
+
startScanSpinner,
|
|
52
|
+
i18n.__(
|
|
53
|
+
'scanNotCompleted',
|
|
54
|
+
'https://docs.contrastsecurity.com/en/binary-package-preparation.html'
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
result.body.errorMessage ? console.log(result.body.errorMessage) : ''
|
|
52
58
|
if (
|
|
53
59
|
result.body.errorMessage ===
|
|
54
60
|
'Unable to determine language for code artifact'
|
|
@@ -87,23 +93,9 @@ const returnScanResultsInstances = async (config, scanId) => {
|
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
const returnScanProjectById = async config => {
|
|
91
|
-
const client = commonApi.getHttpClient(config)
|
|
92
|
-
let result
|
|
93
|
-
try {
|
|
94
|
-
result = await client.getScanProjectById(config)
|
|
95
|
-
if (JSON.stringify(result.statusCode) == 200) {
|
|
96
|
-
return result.body
|
|
97
|
-
}
|
|
98
|
-
} catch (e) {
|
|
99
|
-
console.log(e.message.toString())
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
96
|
module.exports = {
|
|
104
97
|
getScanId: getScanId,
|
|
105
98
|
returnScanResults: returnScanResults,
|
|
106
99
|
pollScanResults: pollScanResults,
|
|
107
|
-
returnScanResultsInstances: returnScanResultsInstances
|
|
108
|
-
returnScanProjectById: returnScanProjectById
|
|
100
|
+
returnScanResultsInstances: returnScanResultsInstances
|
|
109
101
|
}
|