@contrast/contrast 1.0.19 → 1.0.20
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/dist/audit/report/commonReportingFunctions.js +3 -4
- package/dist/audit/report/models/reportListModel.js +2 -1
- package/dist/audit/report/reportingFeature.js +1 -1
- package/dist/audit/report/utils/reportUtils.js +30 -11
- package/dist/commands/audit/auditConfig.js +1 -2
- package/dist/commands/scan/sca/scaAnalysis.js +4 -2
- package/dist/constants/constants.js +1 -1
- package/dist/constants/locales.js +6 -2
- package/dist/scaAnalysis/common/auditReport.js +16 -60
- package/dist/scaAnalysis/common/commonReportingFunctionsSca.js +154 -0
- package/dist/scaAnalysis/common/models/ScaReportModel.js +45 -0
- package/dist/scaAnalysis/common/scaServicesUpload.js +4 -3
- package/dist/scaAnalysis/common/utils/reportUtilsSca.js +76 -0
- package/dist/scaAnalysis/java/analysis.js +1 -28
- package/dist/scaAnalysis/java/index.js +1 -13
- package/dist/scan/formatScanOutput.js +19 -13
- package/dist/utils/paramsUtil/configStoreParams.js +1 -12
- package/dist/utils/paramsUtil/paramHandler.js +1 -7
- package/package.json +5 -1
- package/src/audit/report/commonReportingFunctions.js +7 -5
- package/src/audit/report/models/reportListModel.ts +12 -2
- package/src/audit/report/reportingFeature.ts +1 -1
- package/src/audit/report/utils/reportUtils.ts +4 -4
- package/src/commands/audit/auditConfig.js +1 -2
- package/src/commands/scan/sca/scaAnalysis.js +7 -2
- package/src/constants/constants.js +1 -1
- package/src/constants/locales.js +6 -2
- package/src/scaAnalysis/common/auditReport.js +25 -80
- package/src/scaAnalysis/common/commonReportingFunctionsSca.js +276 -0
- package/src/scaAnalysis/common/models/ScaReportModel.ts +81 -0
- package/src/scaAnalysis/common/scaServicesUpload.js +5 -3
- package/src/scaAnalysis/common/utils/reportUtilsSca.ts +123 -0
- package/src/scaAnalysis/java/analysis.js +1 -28
- package/src/scaAnalysis/java/index.js +1 -18
- package/src/scan/formatScanOutput.ts +28 -17
- package/src/utils/getConfig.ts +0 -1
- package/src/utils/paramsUtil/configStoreParams.js +1 -14
- package/src/utils/paramsUtil/paramHandler.js +1 -9
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
const {
|
|
2
|
+
ReportList,
|
|
3
|
+
ReportModelStructure,
|
|
4
|
+
ReportCompositeKey
|
|
5
|
+
} = require('../../audit/report/models/reportListModel')
|
|
6
|
+
const {
|
|
7
|
+
countVulnerableLibrariesBySeverity
|
|
8
|
+
} = require('../../audit/report/utils/reportUtils')
|
|
9
|
+
const {
|
|
10
|
+
SeverityCountModel
|
|
11
|
+
} = require('../../audit/report/models/severityCountModel')
|
|
12
|
+
const { orderBy } = require('lodash')
|
|
13
|
+
const {
|
|
14
|
+
ReportOutputModel,
|
|
15
|
+
ReportOutputHeaderModel,
|
|
16
|
+
ReportOutputBodyModel
|
|
17
|
+
} = require('../../audit/report/models/reportOutputModel')
|
|
18
|
+
const {
|
|
19
|
+
CE_URL,
|
|
20
|
+
CRITICAL_COLOUR,
|
|
21
|
+
HIGH_COLOUR,
|
|
22
|
+
MEDIUM_COLOUR,
|
|
23
|
+
LOW_COLOUR,
|
|
24
|
+
NOTE_COLOUR
|
|
25
|
+
} = require('../../constants/constants')
|
|
26
|
+
const chalk = require('chalk')
|
|
27
|
+
const Table = require('cli-table3')
|
|
28
|
+
const {
|
|
29
|
+
findHighestSeverityCVESca,
|
|
30
|
+
severityCountAllCVEsSca,
|
|
31
|
+
findCVESeveritySca,
|
|
32
|
+
orderByHighestPrioritySca
|
|
33
|
+
} = require('./utils/reportUtilsSca')
|
|
34
|
+
const {
|
|
35
|
+
buildFormattedHeaderNum
|
|
36
|
+
} = require('../../audit/report/commonReportingFunctions')
|
|
37
|
+
|
|
38
|
+
const createSummaryMessageTop = (numberOfVulnerableLibraries, numberOfCves) => {
|
|
39
|
+
numberOfVulnerableLibraries === 1
|
|
40
|
+
? console.log(
|
|
41
|
+
`\n\nFound 1 vulnerable library containing ${numberOfCves} CVE`
|
|
42
|
+
)
|
|
43
|
+
: console.log(
|
|
44
|
+
`\n\nFound ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs`
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const createSummaryMessageBottom = numberOfVulnerableLibraries => {
|
|
49
|
+
numberOfVulnerableLibraries === 1
|
|
50
|
+
? console.log(`Found 1 vulnerability`)
|
|
51
|
+
: console.log(`Found ${numberOfVulnerableLibraries} vulnerabilities`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const printFormattedOutputSca = (
|
|
55
|
+
config,
|
|
56
|
+
reportModelList,
|
|
57
|
+
numberOfVulnerableLibraries,
|
|
58
|
+
numberOfCves
|
|
59
|
+
) => {
|
|
60
|
+
createSummaryMessageTop(numberOfVulnerableLibraries, numberOfCves)
|
|
61
|
+
console.log()
|
|
62
|
+
const report = new ReportList()
|
|
63
|
+
|
|
64
|
+
for (const library of reportModelList) {
|
|
65
|
+
const { artifactName, version, vulnerabilities, remediationAdvice } =
|
|
66
|
+
library
|
|
67
|
+
|
|
68
|
+
const newOutputModel = new ReportModelStructure(
|
|
69
|
+
new ReportCompositeKey(
|
|
70
|
+
artifactName,
|
|
71
|
+
version,
|
|
72
|
+
findHighestSeverityCVESca(vulnerabilities),
|
|
73
|
+
severityCountAllCVEsSca(
|
|
74
|
+
vulnerabilities,
|
|
75
|
+
new SeverityCountModel()
|
|
76
|
+
).getTotal
|
|
77
|
+
),
|
|
78
|
+
vulnerabilities,
|
|
79
|
+
remediationAdvice
|
|
80
|
+
)
|
|
81
|
+
report.reportOutputList.push(newOutputModel)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(
|
|
85
|
+
report.reportOutputList,
|
|
86
|
+
[
|
|
87
|
+
reportListItem => {
|
|
88
|
+
return reportListItem.compositeKey.highestSeverity.priority
|
|
89
|
+
},
|
|
90
|
+
reportListItem => {
|
|
91
|
+
return reportListItem.compositeKey.numberOfSeverities
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
['asc', 'desc']
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
let contrastHeaderNumCounter = 0
|
|
98
|
+
for (const reportModel of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
|
|
99
|
+
contrastHeaderNumCounter++
|
|
100
|
+
const { libraryName, libraryVersion, highestSeverity } =
|
|
101
|
+
reportModel.compositeKey
|
|
102
|
+
|
|
103
|
+
const { cveArray, remediationAdvice } = reportModel
|
|
104
|
+
|
|
105
|
+
const numOfCVEs = reportModel.cveArray.length
|
|
106
|
+
|
|
107
|
+
const table = getReportTable()
|
|
108
|
+
|
|
109
|
+
const header = buildHeader(
|
|
110
|
+
highestSeverity,
|
|
111
|
+
contrastHeaderNumCounter,
|
|
112
|
+
libraryName,
|
|
113
|
+
libraryVersion,
|
|
114
|
+
numOfCVEs
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const body = buildBody(cveArray, remediationAdvice)
|
|
118
|
+
|
|
119
|
+
const reportOutputModel = new ReportOutputModel(header, body)
|
|
120
|
+
|
|
121
|
+
table.push(
|
|
122
|
+
reportOutputModel.body.issueMessage,
|
|
123
|
+
reportOutputModel.body.adviceMessage
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
console.log(
|
|
127
|
+
reportOutputModel.header.vulnMessage,
|
|
128
|
+
reportOutputModel.header.introducesMessage
|
|
129
|
+
)
|
|
130
|
+
console.log(table.toString() + '\n')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
createSummaryMessageBottom(numberOfVulnerableLibraries)
|
|
134
|
+
const {
|
|
135
|
+
criticalMessage,
|
|
136
|
+
highMessage,
|
|
137
|
+
mediumMessage,
|
|
138
|
+
lowMessage,
|
|
139
|
+
noteMessage
|
|
140
|
+
} = buildFooter(outputOrderedByLowestSeverityAndLowestNumOfCvesFirst)
|
|
141
|
+
console.log(
|
|
142
|
+
`${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if (config.host !== CE_URL) {
|
|
146
|
+
console.log(
|
|
147
|
+
'\n' + chalk.bold('View your full dependency tree in Contrast:')
|
|
148
|
+
)
|
|
149
|
+
console.log(
|
|
150
|
+
`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getReportTable() {
|
|
156
|
+
return new Table({
|
|
157
|
+
chars: {
|
|
158
|
+
top: '',
|
|
159
|
+
'top-mid': '',
|
|
160
|
+
'top-left': '',
|
|
161
|
+
'top-right': '',
|
|
162
|
+
bottom: '',
|
|
163
|
+
'bottom-mid': '',
|
|
164
|
+
'bottom-left': '',
|
|
165
|
+
'bottom-right': '',
|
|
166
|
+
left: '',
|
|
167
|
+
'left-mid': '',
|
|
168
|
+
mid: '',
|
|
169
|
+
'mid-mid': '',
|
|
170
|
+
right: '',
|
|
171
|
+
'right-mid': '',
|
|
172
|
+
middle: ' '
|
|
173
|
+
},
|
|
174
|
+
style: { 'padding-left': 0, 'padding-right': 0 },
|
|
175
|
+
colAligns: ['right'],
|
|
176
|
+
wordWrap: true,
|
|
177
|
+
colWidths: [12, 1, 100]
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function buildHeader(
|
|
182
|
+
highestSeverity,
|
|
183
|
+
contrastHeaderNum,
|
|
184
|
+
libraryName,
|
|
185
|
+
version,
|
|
186
|
+
numOfCVEs
|
|
187
|
+
) {
|
|
188
|
+
const vulnerabilityPluralised =
|
|
189
|
+
numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability'
|
|
190
|
+
const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum)
|
|
191
|
+
|
|
192
|
+
const headerColour = chalk.hex(highestSeverity.colour)
|
|
193
|
+
const headerNumAndSeverity = headerColour(
|
|
194
|
+
`${formattedHeaderNum} - [${highestSeverity.severity}]`
|
|
195
|
+
)
|
|
196
|
+
const libraryNameAndVersion = headerColour.bold(`${libraryName}-${version}`)
|
|
197
|
+
const vulnMessage = `${headerNumAndSeverity} ${libraryNameAndVersion}`
|
|
198
|
+
|
|
199
|
+
const introducesMessage = `introduces ${numOfCVEs} ${vulnerabilityPluralised}`
|
|
200
|
+
|
|
201
|
+
return new ReportOutputHeaderModel(vulnMessage, introducesMessage)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function buildBody(cveArray, advice) {
|
|
205
|
+
const orderedCvesWithSeverityAssigned = orderByHighestPrioritySca(
|
|
206
|
+
cveArray.map(cve => findCVESeveritySca(cve))
|
|
207
|
+
)
|
|
208
|
+
const issueMessage = getIssueRow(orderedCvesWithSeverityAssigned)
|
|
209
|
+
const adviceMessage = getAdviceRow(advice)
|
|
210
|
+
|
|
211
|
+
return new ReportOutputBodyModel(issueMessage, adviceMessage)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getIssueRow(cveArray) {
|
|
215
|
+
const cveMessagesList = getIssueCveMsgList(cveArray)
|
|
216
|
+
return [chalk.bold('Issue'), ':', `${cveMessagesList.join(', ')}`]
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function getAdviceRow(advice) {
|
|
220
|
+
const latestOrClosest = advice.latestStableVersion
|
|
221
|
+
? advice.latestStableVersion
|
|
222
|
+
: advice.closestStableVersion
|
|
223
|
+
const displayAdvice = latestOrClosest
|
|
224
|
+
? `Change to version ${chalk.bold(latestOrClosest)}`
|
|
225
|
+
: 'No recommendation is available according to our data. Upgrade to the latest stable is the best advice we can give.'
|
|
226
|
+
|
|
227
|
+
return [chalk.bold(`Advice`), chalk.bold(`:`), `${displayAdvice}`]
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const buildFooter = reportModelStructure => {
|
|
231
|
+
const { critical, high, medium, low, note } =
|
|
232
|
+
countVulnerableLibrariesBySeverity(reportModelStructure)
|
|
233
|
+
|
|
234
|
+
const criticalMessage = chalk
|
|
235
|
+
.hex(CRITICAL_COLOUR)
|
|
236
|
+
.bold(`${critical} Critical`)
|
|
237
|
+
const highMessage = chalk.hex(HIGH_COLOUR).bold(`${high} High`)
|
|
238
|
+
const mediumMessage = chalk.hex(MEDIUM_COLOUR).bold(`${medium} Medium`)
|
|
239
|
+
const lowMessage = chalk.hex(LOW_COLOUR).bold(`${low} Low`)
|
|
240
|
+
const noteMessage = chalk.hex(NOTE_COLOUR).bold(`${note} Note`)
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
criticalMessage,
|
|
244
|
+
highMessage,
|
|
245
|
+
mediumMessage,
|
|
246
|
+
lowMessage,
|
|
247
|
+
noteMessage
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const getIssueCveMsgList = reportSeverityModels => {
|
|
252
|
+
const cveMessages = []
|
|
253
|
+
reportSeverityModels.forEach(reportSeverityModel => {
|
|
254
|
+
const { colour, severity, name } = reportSeverityModel
|
|
255
|
+
|
|
256
|
+
const severityShorthand = chalk
|
|
257
|
+
.hex(colour)
|
|
258
|
+
.bold(`[${severity.charAt(0).toUpperCase()}]`)
|
|
259
|
+
|
|
260
|
+
const builtMessage = severityShorthand + name
|
|
261
|
+
cveMessages.push(builtMessage)
|
|
262
|
+
})
|
|
263
|
+
return cveMessages
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
createSummaryMessageTop,
|
|
268
|
+
createSummaryMessageBottom,
|
|
269
|
+
printFormattedOutputSca,
|
|
270
|
+
getReportTable,
|
|
271
|
+
buildHeader,
|
|
272
|
+
buildBody,
|
|
273
|
+
getIssueRow,
|
|
274
|
+
buildFormattedHeaderNum,
|
|
275
|
+
getIssueCveMsgList
|
|
276
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export class ScaReportModel {
|
|
2
|
+
uuid: string
|
|
3
|
+
groupName: string
|
|
4
|
+
artifactName: string
|
|
5
|
+
version: string
|
|
6
|
+
hash: string
|
|
7
|
+
fileName: string
|
|
8
|
+
libraryLanguage: string
|
|
9
|
+
vulnerable: boolean
|
|
10
|
+
privateLibrary: boolean
|
|
11
|
+
severity: string
|
|
12
|
+
releaseDate: string
|
|
13
|
+
latestVersionReleaseDate: string
|
|
14
|
+
latestVersion: string
|
|
15
|
+
versionsBehind: number
|
|
16
|
+
vulnerabilities: ScaReportVulnerabilityModel[]
|
|
17
|
+
remediationAdvice: ScaReportRemediationAdviceModel
|
|
18
|
+
|
|
19
|
+
constructor(library: any) {
|
|
20
|
+
this.uuid = library.uuid
|
|
21
|
+
this.groupName = library.groupName
|
|
22
|
+
this.artifactName = library.artifactName
|
|
23
|
+
this.version = library.version
|
|
24
|
+
this.hash = library.hash
|
|
25
|
+
this.fileName = library.fileName
|
|
26
|
+
this.libraryLanguage = library.libraryLanguage
|
|
27
|
+
this.vulnerable = library.vulnerable
|
|
28
|
+
this.privateLibrary = library.privateLibrary
|
|
29
|
+
this.severity = library.severity
|
|
30
|
+
this.releaseDate = library.releaseDate
|
|
31
|
+
this.latestVersionReleaseDate = library.latestVersionReleaseDate
|
|
32
|
+
this.latestVersion = library.latestVersion
|
|
33
|
+
this.versionsBehind = library.versionsBehind
|
|
34
|
+
this.vulnerabilities = library.vulnerabilities
|
|
35
|
+
this.remediationAdvice = library.remediationAdvice
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class ScaReportVulnerabilityModel {
|
|
40
|
+
name: string
|
|
41
|
+
description: string
|
|
42
|
+
cvss2Vector: string
|
|
43
|
+
severityValue: number
|
|
44
|
+
severity: string
|
|
45
|
+
cvss3Vector: string
|
|
46
|
+
cvss3SeverityValue: number
|
|
47
|
+
cvss3Severity: string
|
|
48
|
+
hasCvss3: boolean
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
name: string,
|
|
52
|
+
description: string,
|
|
53
|
+
cvss2Vector: string,
|
|
54
|
+
severityValue: number,
|
|
55
|
+
severity: string,
|
|
56
|
+
cvss3Vector: string,
|
|
57
|
+
cvss3SeverityValue: number,
|
|
58
|
+
cvss3Severity: string,
|
|
59
|
+
hasCvss3: boolean
|
|
60
|
+
) {
|
|
61
|
+
this.name = name
|
|
62
|
+
this.description = description
|
|
63
|
+
this.cvss2Vector = cvss2Vector
|
|
64
|
+
this.severityValue = severityValue
|
|
65
|
+
this.severity = severity
|
|
66
|
+
this.cvss3Vector = cvss3Vector
|
|
67
|
+
this.cvss3SeverityValue = cvss3SeverityValue
|
|
68
|
+
this.cvss3Severity = cvss3Severity
|
|
69
|
+
this.hasCvss3 = hasCvss3
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class ScaReportRemediationAdviceModel {
|
|
74
|
+
closestStableVersion: string
|
|
75
|
+
latestStableVersion: string
|
|
76
|
+
|
|
77
|
+
constructor(closestStableVersion: string, latestStableVersion: string) {
|
|
78
|
+
this.closestStableVersion = closestStableVersion
|
|
79
|
+
this.latestStableVersion = latestStableVersion
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -46,17 +46,19 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
46
46
|
if (res.body.status === 'COMPLETED') {
|
|
47
47
|
keepChecking = false
|
|
48
48
|
return client.scaServiceReport(config, reportID).then(res => {
|
|
49
|
-
|
|
49
|
+
const reportBody = res.body
|
|
50
|
+
return { reportBody, reportID }
|
|
50
51
|
})
|
|
51
52
|
}
|
|
52
53
|
})
|
|
53
54
|
|
|
54
55
|
if (!keepChecking) {
|
|
55
|
-
return
|
|
56
|
+
return { reportArray: res.reportBody, reportID }
|
|
56
57
|
}
|
|
57
58
|
await requestUtils.sleep(5000)
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
return { reportArray: res, reportID }
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
module.exports = {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { orderBy } from 'lodash'
|
|
2
|
+
import {
|
|
3
|
+
CRITICAL_COLOUR,
|
|
4
|
+
CRITICAL_PRIORITY,
|
|
5
|
+
HIGH_COLOUR,
|
|
6
|
+
HIGH_PRIORITY,
|
|
7
|
+
LOW_COLOUR,
|
|
8
|
+
LOW_PRIORITY,
|
|
9
|
+
MEDIUM_COLOUR,
|
|
10
|
+
MEDIUM_PRIORITY,
|
|
11
|
+
NOTE_COLOUR,
|
|
12
|
+
NOTE_PRIORITY
|
|
13
|
+
} from '../../../constants/constants'
|
|
14
|
+
import { ReportSeverityModel } from '../../../audit/report/models/reportSeverityModel'
|
|
15
|
+
import { SeverityCountModel } from '../../../audit/report/models/severityCountModel'
|
|
16
|
+
import {
|
|
17
|
+
ScaReportModel,
|
|
18
|
+
ScaReportVulnerabilityModel
|
|
19
|
+
} from '../models/ScaReportModel'
|
|
20
|
+
|
|
21
|
+
export function findHighestSeverityCVESca(
|
|
22
|
+
cveArray: ScaReportVulnerabilityModel[]
|
|
23
|
+
) {
|
|
24
|
+
const mappedToReportSeverityModels = cveArray.map(cve =>
|
|
25
|
+
findCVESeveritySca(cve)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
//order and get first
|
|
29
|
+
return orderBy(mappedToReportSeverityModels, cve => cve?.priority)[0]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function orderByHighestPrioritySca(
|
|
33
|
+
reportSeverityModel: ReportSeverityModel[]
|
|
34
|
+
) {
|
|
35
|
+
return orderBy(reportSeverityModel, ['priority'], ['asc'])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function findCVESeveritySca(
|
|
39
|
+
vulnerabilityModel: ScaReportVulnerabilityModel
|
|
40
|
+
) {
|
|
41
|
+
const { name } = vulnerabilityModel
|
|
42
|
+
|
|
43
|
+
if (
|
|
44
|
+
vulnerabilityModel.cvss3Severity === 'CRITICAL' ||
|
|
45
|
+
vulnerabilityModel.severity === 'CRITICAL'
|
|
46
|
+
) {
|
|
47
|
+
return new ReportSeverityModel(
|
|
48
|
+
'CRITICAL',
|
|
49
|
+
CRITICAL_PRIORITY,
|
|
50
|
+
CRITICAL_COLOUR,
|
|
51
|
+
name
|
|
52
|
+
)
|
|
53
|
+
} else if (
|
|
54
|
+
vulnerabilityModel.cvss3Severity === 'HIGH' ||
|
|
55
|
+
vulnerabilityModel.severity === 'HIGH'
|
|
56
|
+
) {
|
|
57
|
+
return new ReportSeverityModel('HIGH', HIGH_PRIORITY, HIGH_COLOUR, name)
|
|
58
|
+
} else if (
|
|
59
|
+
vulnerabilityModel.cvss3Severity === 'MEDIUM' ||
|
|
60
|
+
vulnerabilityModel.severity === 'MEDIUM'
|
|
61
|
+
) {
|
|
62
|
+
return new ReportSeverityModel(
|
|
63
|
+
'MEDIUM',
|
|
64
|
+
MEDIUM_PRIORITY,
|
|
65
|
+
MEDIUM_COLOUR,
|
|
66
|
+
name
|
|
67
|
+
)
|
|
68
|
+
} else if (
|
|
69
|
+
vulnerabilityModel.cvss3Severity === 'LOW' ||
|
|
70
|
+
vulnerabilityModel.severity === 'LOW'
|
|
71
|
+
) {
|
|
72
|
+
return new ReportSeverityModel('LOW', LOW_PRIORITY, LOW_COLOUR, name)
|
|
73
|
+
} else if (
|
|
74
|
+
vulnerabilityModel.cvss3Severity === 'NOTE' ||
|
|
75
|
+
vulnerabilityModel.severity === 'NOTE'
|
|
76
|
+
) {
|
|
77
|
+
return new ReportSeverityModel('NOTE', NOTE_PRIORITY, NOTE_COLOUR, name)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function convertGenericToTypedReportModelSca(reportArray: any) {
|
|
82
|
+
return reportArray.map((library: any) => {
|
|
83
|
+
return new ScaReportModel(library)
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function severityCountAllLibrariesSca(
|
|
88
|
+
vulnerableLibraries: ScaReportModel[],
|
|
89
|
+
severityCount: SeverityCountModel
|
|
90
|
+
) {
|
|
91
|
+
vulnerableLibraries.forEach(lib =>
|
|
92
|
+
severityCountAllCVEsSca(lib.vulnerabilities, severityCount)
|
|
93
|
+
)
|
|
94
|
+
return severityCount
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function severityCountAllCVEsSca(
|
|
98
|
+
cveArray: ScaReportVulnerabilityModel[],
|
|
99
|
+
severityCount: SeverityCountModel
|
|
100
|
+
) {
|
|
101
|
+
const severityCountInner = severityCount
|
|
102
|
+
cveArray.forEach(cve => severityCountSingleCVESca(cve, severityCountInner))
|
|
103
|
+
return severityCountInner
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function severityCountSingleCVESca(
|
|
107
|
+
cve: ScaReportVulnerabilityModel,
|
|
108
|
+
severityCount: SeverityCountModel
|
|
109
|
+
) {
|
|
110
|
+
if (cve.cvss3Severity === 'CRITICAL' || cve.severity === 'CRITICAL') {
|
|
111
|
+
severityCount.critical += 1
|
|
112
|
+
} else if (cve.cvss3Severity === 'HIGH' || cve.severity === 'HIGH') {
|
|
113
|
+
severityCount.high += 1
|
|
114
|
+
} else if (cve.cvss3Severity === 'MEDIUM' || cve.severity === 'MEDIUM') {
|
|
115
|
+
severityCount.medium += 1
|
|
116
|
+
} else if (cve.cvss3Severity === 'LOW' || cve.severity === 'LOW') {
|
|
117
|
+
severityCount.low += 1
|
|
118
|
+
} else if (cve.cvss3Severity === 'NOTE' || cve.severity === 'NOTE') {
|
|
119
|
+
severityCount.note += 1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return severityCount
|
|
123
|
+
}
|
|
@@ -147,34 +147,7 @@ const getJavaBuildDeps = (config, files) => {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
const agreementPrompt = async config => {
|
|
151
|
-
const rl = readLine.createInterface({
|
|
152
|
-
input: process.stdin,
|
|
153
|
-
output: process.stdout
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
return new Promise((resolve, reject) => {
|
|
157
|
-
rl.question('❔ Do you want to continue? Type Y or N', async input => {
|
|
158
|
-
if (input.toLowerCase() === 'yes' || input.toLowerCase() === 'y') {
|
|
159
|
-
config.javaAgreement = paramHandler.setAgreement(true)
|
|
160
|
-
rl.close()
|
|
161
|
-
resolve(config)
|
|
162
|
-
} else if (input.toLowerCase() === 'no' || input.toLowerCase() === 'n') {
|
|
163
|
-
rl.close()
|
|
164
|
-
resolve(process.exit(1))
|
|
165
|
-
} else {
|
|
166
|
-
rl.close()
|
|
167
|
-
console.log('Invalid Input: Exiting')
|
|
168
|
-
resolve(process.exit(1))
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
}).catch(e => {
|
|
172
|
-
throw e
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
|
|
176
150
|
module.exports = {
|
|
177
151
|
getJavaBuildDeps,
|
|
178
|
-
determineProjectTypeAndCwd
|
|
179
|
-
agreementPrompt
|
|
152
|
+
determineProjectTypeAndCwd
|
|
180
153
|
}
|
|
@@ -4,16 +4,12 @@ const { createJavaTSMessage } = require('../common/formatMessage')
|
|
|
4
4
|
const {
|
|
5
5
|
parseDependenciesForSCAServices
|
|
6
6
|
} = require('../common/scaParserForGoAndJava')
|
|
7
|
-
const chalk = require('chalk')
|
|
8
|
-
const _ = require('lodash')
|
|
9
7
|
|
|
10
8
|
const javaAnalysis = async (config, languageFiles) => {
|
|
11
9
|
languageFiles.JAVA.forEach(file => {
|
|
12
10
|
file.replace('build.gradle.kts', 'build.gradle')
|
|
13
11
|
})
|
|
14
12
|
|
|
15
|
-
await getAgreement(config)
|
|
16
|
-
|
|
17
13
|
const javaDeps = buildJavaTree(config, languageFiles.JAVA)
|
|
18
14
|
|
|
19
15
|
if (config.experimental) {
|
|
@@ -23,24 +19,11 @@ const javaAnalysis = async (config, languageFiles) => {
|
|
|
23
19
|
}
|
|
24
20
|
}
|
|
25
21
|
|
|
26
|
-
const getAgreement = async config => {
|
|
27
|
-
console.log(chalk.bold('Java project detected'))
|
|
28
|
-
console.log(
|
|
29
|
-
'Java analysis uses maven / gradle which are potentially susceptible to command injection. Be sure that the code you are running Contrast CLI on is trusted before continuing.'
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
if (!process.env.CI && !config?.javaAgreement) {
|
|
33
|
-
return await analysis.agreementPrompt(config)
|
|
34
|
-
}
|
|
35
|
-
return config
|
|
36
|
-
}
|
|
37
|
-
|
|
38
22
|
const buildJavaTree = (config, files) => {
|
|
39
23
|
const javaBuildDeps = analysis.getJavaBuildDeps(config, files)
|
|
40
24
|
return parseBuildDeps(config, javaBuildDeps)
|
|
41
25
|
}
|
|
42
26
|
|
|
43
27
|
module.exports = {
|
|
44
|
-
javaAnalysis
|
|
45
|
-
getAgreement
|
|
28
|
+
javaAnalysis
|
|
46
29
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ScanResultsInstances,
|
|
3
|
-
ScanResultsModel
|
|
4
|
-
} from './models/scanResultsModel'
|
|
1
|
+
import { ScanResultsModel } from './models/scanResultsModel'
|
|
5
2
|
import i18n from 'i18n'
|
|
6
3
|
import chalk from 'chalk'
|
|
7
4
|
import { ResultContent } from './models/resultContentModel'
|
|
@@ -13,7 +10,8 @@ import {
|
|
|
13
10
|
HIGH_COLOUR,
|
|
14
11
|
LOW_COLOUR,
|
|
15
12
|
MEDIUM_COLOUR,
|
|
16
|
-
NOTE_COLOUR
|
|
13
|
+
NOTE_COLOUR,
|
|
14
|
+
supportedLanguagesScan
|
|
17
15
|
} from '../constants/constants'
|
|
18
16
|
import {
|
|
19
17
|
getSeverityCounts,
|
|
@@ -21,27 +19,28 @@ import {
|
|
|
21
19
|
} from '../audit/report/commonReportingFunctions'
|
|
22
20
|
|
|
23
21
|
export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
24
|
-
const {
|
|
22
|
+
const { content } = scanResults.scanResultsInstances
|
|
23
|
+
const { language } = scanResults.scanDetail
|
|
25
24
|
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
25
|
+
const severityCounts = getSeverityCounts(content)
|
|
26
|
+
if (content.length === 0) {
|
|
28
27
|
console.log(i18n.__('scanNoVulnerabilitiesFound'))
|
|
29
28
|
console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
|
|
30
29
|
console.log(i18n.__('scanNoVulnerabilitiesFoundGoodWork'))
|
|
31
30
|
} else {
|
|
32
31
|
const message =
|
|
33
|
-
|
|
32
|
+
severityCounts.critical || severityCounts.high
|
|
34
33
|
? 'Here are your top priorities to fix'
|
|
35
34
|
: "No major issues, here's what we found"
|
|
36
35
|
console.log(chalk.bold(message))
|
|
37
36
|
console.log()
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
const defaultView = getDefaultView(content, language)
|
|
40
39
|
|
|
41
40
|
let count = 0
|
|
42
41
|
defaultView.forEach(entry => {
|
|
43
42
|
count++
|
|
44
|
-
|
|
43
|
+
const table = new Table({
|
|
45
44
|
chars: {
|
|
46
45
|
top: '',
|
|
47
46
|
'top-mid': '',
|
|
@@ -64,6 +63,7 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
64
63
|
wordWrap: true,
|
|
65
64
|
colWidths: [12, 1, 100]
|
|
66
65
|
})
|
|
66
|
+
|
|
67
67
|
let learnRow: string[] = []
|
|
68
68
|
let adviceRow = []
|
|
69
69
|
const headerColour = chalk.hex(entry.colour)
|
|
@@ -107,9 +107,9 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
107
107
|
console.log()
|
|
108
108
|
})
|
|
109
109
|
}
|
|
110
|
-
printVulnInfo(
|
|
110
|
+
printVulnInfo(severityCounts)
|
|
111
111
|
|
|
112
|
-
return
|
|
112
|
+
return severityCounts
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export function formatLinks(objName: string, entry: any[]) {
|
|
@@ -124,7 +124,7 @@ export function formatLinks(objName: string, entry: any[]) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
export function getDefaultView(content: ResultContent[]) {
|
|
127
|
+
export function getDefaultView(content: ResultContent[], language: string) {
|
|
128
128
|
const groupTypeResults = [] as GroupedResultsModel[]
|
|
129
129
|
|
|
130
130
|
content.forEach(resultEntry => {
|
|
@@ -136,8 +136,7 @@ export function getDefaultView(content: ResultContent[]) {
|
|
|
136
136
|
groupResultsObj.learn = resultEntry.learn
|
|
137
137
|
groupResultsObj.message = resultEntry.message?.text
|
|
138
138
|
? editVulName(resultEntry.message.text) +
|
|
139
|
-
|
|
140
|
-
getSourceLineNumber(resultEntry)
|
|
139
|
+
doAddSourceLineNumber(resultEntry, language)
|
|
141
140
|
: ''
|
|
142
141
|
groupResultsObj.codePath = getLocationsSyncInfo(resultEntry)
|
|
143
142
|
groupTypeResults.push(groupResultsObj)
|
|
@@ -146,9 +145,21 @@ export function getDefaultView(content: ResultContent[]) {
|
|
|
146
145
|
|
|
147
146
|
return sortBy(groupTypeResults, ['priority'])
|
|
148
147
|
}
|
|
148
|
+
|
|
149
|
+
export function doAddSourceLineNumber(
|
|
150
|
+
resultEntry: ResultContent,
|
|
151
|
+
language: string
|
|
152
|
+
) {
|
|
153
|
+
//only add source line num if not JS
|
|
154
|
+
return language !== supportedLanguagesScan.JAVASCRIPT
|
|
155
|
+
? ':' + getSourceLineNumber(resultEntry)
|
|
156
|
+
: ''
|
|
157
|
+
}
|
|
158
|
+
|
|
149
159
|
export function editVulName(message: string) {
|
|
150
160
|
return message.substring(message.indexOf(' in '))
|
|
151
161
|
}
|
|
162
|
+
|
|
152
163
|
export function getLocationsSyncInfo(resultEntry: ResultContent) {
|
|
153
164
|
const locationsMessage =
|
|
154
165
|
resultEntry.locations[0]?.physicalLocation?.artifactLocation?.uri || ''
|
|
@@ -165,7 +176,7 @@ export function getLocationsSyncInfo(resultEntry: ResultContent) {
|
|
|
165
176
|
export function getSourceLineNumber(resultEntry: ResultContent) {
|
|
166
177
|
const locationsLineNumber =
|
|
167
178
|
resultEntry.locations[0]?.physicalLocation?.region?.startLine || ''
|
|
168
|
-
|
|
179
|
+
const codeFlowLineNumber = getCodeFlowInfo(resultEntry)
|
|
169
180
|
|
|
170
181
|
return codeFlowLineNumber ? codeFlowLineNumber : locationsLineNumber
|
|
171
182
|
}
|