@contrast/contrast 1.0.13 → 1.0.15
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 +175 -116
- package/dist/audit/report/models/reportSeverityModel.js +3 -3
- package/dist/audit/report/reportingFeature.js +1 -10
- package/dist/audit/report/utils/reportUtils.js +4 -4
- package/dist/audit/save.js +7 -2
- package/dist/commands/audit/help.js +3 -1
- package/dist/commands/scan/processScan.js +0 -6
- package/dist/commands/scan/sca/scaAnalysis.js +32 -15
- package/dist/common/HTTPClient.js +22 -3
- package/dist/common/errorHandling.js +1 -2
- package/dist/constants/constants.js +10 -2
- package/dist/constants/locales.js +17 -7
- package/dist/constants.js +31 -2
- package/dist/index.js +4 -2
- package/dist/lambda/lambda.js +1 -1
- package/dist/sbom/generateSbom.js +18 -1
- package/dist/scaAnalysis/common/auditReport.js +78 -0
- package/dist/scaAnalysis/common/scaServicesUpload.js +21 -13
- package/dist/scan/formatScanOutput.js +4 -29
- package/dist/utils/commonApi.js +1 -0
- package/dist/utils/settingsHelper.js +24 -0
- package/package.json +4 -1
- package/src/audit/report/commonReportingFunctions.js +432 -0
- package/src/audit/report/models/reportSeverityModel.ts +6 -6
- package/src/audit/report/reportingFeature.ts +2 -16
- package/src/audit/report/utils/reportUtils.ts +2 -8
- package/src/audit/save.js +14 -6
- package/src/commands/audit/help.ts +3 -1
- package/src/commands/scan/processScan.js +0 -8
- package/src/commands/scan/sca/scaAnalysis.js +52 -32
- package/src/common/HTTPClient.js +26 -3
- package/src/common/errorHandling.ts +1 -2
- package/src/constants/constants.js +12 -2
- package/src/constants/locales.js +19 -7
- package/src/constants.js +34 -2
- package/src/index.ts +4 -6
- package/src/lambda/lambda.ts +1 -1
- package/src/lambda/lambdaUtils.ts +1 -1
- package/src/sbom/generateSbom.ts +20 -0
- package/src/scaAnalysis/common/auditReport.js +108 -0
- package/src/scaAnalysis/common/scaServicesUpload.js +24 -14
- package/src/scan/formatScanOutput.ts +5 -42
- package/src/utils/commonApi.js +1 -0
- package/src/utils/settingsHelper.js +26 -0
- package/src/audit/report/commonReportingFunctions.ts +0 -355
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
const commonApi = require('../../utils/commonApi')
|
|
2
|
+
const {
|
|
3
|
+
ReportCompositeKey,
|
|
4
|
+
ReportList,
|
|
5
|
+
ReportModelStructure
|
|
6
|
+
} = require('./models/reportListModel')
|
|
7
|
+
const { orderBy } = require('lodash')
|
|
8
|
+
const chalk = require('chalk')
|
|
9
|
+
const {
|
|
10
|
+
countVulnerableLibrariesBySeverity,
|
|
11
|
+
orderByHighestPriority,
|
|
12
|
+
findHighestSeverityCVE,
|
|
13
|
+
findNameAndVersion,
|
|
14
|
+
severityCountAllCVEs,
|
|
15
|
+
findCVESeverity
|
|
16
|
+
} = require('./utils/reportUtils')
|
|
17
|
+
const { SeverityCountModel } = require('./models/severityCountModel')
|
|
18
|
+
const {
|
|
19
|
+
ReportOutputBodyModel,
|
|
20
|
+
ReportOutputHeaderModel,
|
|
21
|
+
ReportOutputModel
|
|
22
|
+
} = require('./models/reportOutputModel')
|
|
23
|
+
const {
|
|
24
|
+
CE_URL,
|
|
25
|
+
CRITICAL_COLOUR,
|
|
26
|
+
HIGH_COLOUR,
|
|
27
|
+
LOW_COLOUR,
|
|
28
|
+
MEDIUM_COLOUR,
|
|
29
|
+
NOTE_COLOUR
|
|
30
|
+
} = require('../../constants/constants')
|
|
31
|
+
const Table = require('cli-table3')
|
|
32
|
+
const { ReportGuidanceModel } = require('./models/reportGuidanceModel')
|
|
33
|
+
const i18n = require('i18n')
|
|
34
|
+
|
|
35
|
+
const createSummaryMessageTop = (numberOfVulnerableLibraries, numberOfCves) => {
|
|
36
|
+
numberOfVulnerableLibraries === 1
|
|
37
|
+
? console.log(`Found 1 vulnerable library containing ${numberOfCves} CVE`)
|
|
38
|
+
: console.log(
|
|
39
|
+
`Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVEs`
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const createSummaryMessageBottom = numberOfVulnerableLibraries => {
|
|
44
|
+
numberOfVulnerableLibraries === 1
|
|
45
|
+
? console.log(`Found 1 vulnerability`)
|
|
46
|
+
: console.log(`Found ${numberOfVulnerableLibraries} vulnerabilities`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const getReport = async (config, reportId) => {
|
|
50
|
+
const client = commonApi.getHttpClient(config)
|
|
51
|
+
return client
|
|
52
|
+
.getReportById(config, reportId)
|
|
53
|
+
.then(res => {
|
|
54
|
+
if (res.statusCode === 200) {
|
|
55
|
+
return res.body
|
|
56
|
+
} else {
|
|
57
|
+
console.log(JSON.stringify(res.statusCode))
|
|
58
|
+
commonApi.handleResponseErrors(res, 'report')
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.catch(err => {
|
|
62
|
+
console.log(err)
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const printVulnerabilityResponse = (
|
|
67
|
+
config,
|
|
68
|
+
vulnerableLibraries,
|
|
69
|
+
numberOfVulnerableLibraries,
|
|
70
|
+
numberOfCves,
|
|
71
|
+
guidance
|
|
72
|
+
) => {
|
|
73
|
+
let hasSomeVulnerabilitiesReported = false
|
|
74
|
+
printFormattedOutput(
|
|
75
|
+
config,
|
|
76
|
+
vulnerableLibraries,
|
|
77
|
+
numberOfVulnerableLibraries,
|
|
78
|
+
numberOfCves,
|
|
79
|
+
guidance
|
|
80
|
+
)
|
|
81
|
+
if (Object.keys(vulnerableLibraries).length > 0) {
|
|
82
|
+
hasSomeVulnerabilitiesReported = true
|
|
83
|
+
}
|
|
84
|
+
return hasSomeVulnerabilitiesReported
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const printFormattedOutput = (
|
|
88
|
+
config,
|
|
89
|
+
libraries,
|
|
90
|
+
numberOfVulnerableLibraries,
|
|
91
|
+
numberOfCves,
|
|
92
|
+
guidance
|
|
93
|
+
) => {
|
|
94
|
+
createSummaryMessageTop(numberOfVulnerableLibraries, numberOfCves)
|
|
95
|
+
console.log()
|
|
96
|
+
const report = new ReportList()
|
|
97
|
+
|
|
98
|
+
for (const library of libraries) {
|
|
99
|
+
const { name, version } = findNameAndVersion(library, config)
|
|
100
|
+
|
|
101
|
+
const newOutputModel = new ReportModelStructure(
|
|
102
|
+
new ReportCompositeKey(
|
|
103
|
+
name,
|
|
104
|
+
version,
|
|
105
|
+
findHighestSeverityCVE(library.cveArray),
|
|
106
|
+
severityCountAllCVEs(
|
|
107
|
+
library.cveArray,
|
|
108
|
+
new SeverityCountModel()
|
|
109
|
+
).getTotal
|
|
110
|
+
),
|
|
111
|
+
library.cveArray
|
|
112
|
+
)
|
|
113
|
+
report.reportOutputList.push(newOutputModel)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(
|
|
117
|
+
report.reportOutputList,
|
|
118
|
+
[
|
|
119
|
+
reportListItem => {
|
|
120
|
+
return reportListItem.compositeKey.highestSeverity.priority
|
|
121
|
+
},
|
|
122
|
+
reportListItem => {
|
|
123
|
+
return reportListItem.compositeKey.numberOfSeverities
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
['asc', 'desc']
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
let contrastHeaderNumCounter = 0
|
|
130
|
+
for (const reportModel of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
|
|
131
|
+
contrastHeaderNumCounter++
|
|
132
|
+
const { libraryName, libraryVersion, highestSeverity } =
|
|
133
|
+
reportModel.compositeKey
|
|
134
|
+
const numOfCVEs = reportModel.cveArray.length
|
|
135
|
+
|
|
136
|
+
const table = getReportTable()
|
|
137
|
+
|
|
138
|
+
const header = buildHeader(
|
|
139
|
+
highestSeverity,
|
|
140
|
+
contrastHeaderNumCounter,
|
|
141
|
+
libraryName,
|
|
142
|
+
libraryVersion,
|
|
143
|
+
numOfCVEs
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
const advice = gatherRemediationAdvice(
|
|
147
|
+
guidance,
|
|
148
|
+
libraryName,
|
|
149
|
+
libraryVersion
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const body = buildBody(reportModel.cveArray, advice)
|
|
153
|
+
|
|
154
|
+
const reportOutputModel = new ReportOutputModel(header, body)
|
|
155
|
+
|
|
156
|
+
table.push(
|
|
157
|
+
reportOutputModel.body.issueMessage,
|
|
158
|
+
reportOutputModel.body.adviceMessage
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
console.log(
|
|
162
|
+
reportOutputModel.header.vulnMessage,
|
|
163
|
+
reportOutputModel.header.introducesMessage
|
|
164
|
+
)
|
|
165
|
+
console.log(table.toString() + '\n')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
createSummaryMessageBottom(numberOfVulnerableLibraries)
|
|
169
|
+
const {
|
|
170
|
+
criticalMessage,
|
|
171
|
+
highMessage,
|
|
172
|
+
mediumMessage,
|
|
173
|
+
lowMessage,
|
|
174
|
+
noteMessage
|
|
175
|
+
} = buildFooter(outputOrderedByLowestSeverityAndLowestNumOfCvesFirst)
|
|
176
|
+
console.log(
|
|
177
|
+
`${criticalMessage} | ${highMessage} | ${mediumMessage} | ${lowMessage} | ${noteMessage}`
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if (config.host !== CE_URL) {
|
|
181
|
+
console.log(
|
|
182
|
+
'\n' + chalk.bold('View your full dependency tree in Contrast:')
|
|
183
|
+
)
|
|
184
|
+
console.log(
|
|
185
|
+
`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getReportTable() {
|
|
191
|
+
return new Table({
|
|
192
|
+
chars: {
|
|
193
|
+
top: '',
|
|
194
|
+
'top-mid': '',
|
|
195
|
+
'top-left': '',
|
|
196
|
+
'top-right': '',
|
|
197
|
+
bottom: '',
|
|
198
|
+
'bottom-mid': '',
|
|
199
|
+
'bottom-left': '',
|
|
200
|
+
'bottom-right': '',
|
|
201
|
+
left: '',
|
|
202
|
+
'left-mid': '',
|
|
203
|
+
mid: '',
|
|
204
|
+
'mid-mid': '',
|
|
205
|
+
right: '',
|
|
206
|
+
'right-mid': '',
|
|
207
|
+
middle: ' '
|
|
208
|
+
},
|
|
209
|
+
style: { 'padding-left': 0, 'padding-right': 0 },
|
|
210
|
+
colAligns: ['right'],
|
|
211
|
+
wordWrap: true,
|
|
212
|
+
colWidths: [12, 1, 100]
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
function buildHeader(
|
|
216
|
+
highestSeverity,
|
|
217
|
+
contrastHeaderNum,
|
|
218
|
+
libraryName,
|
|
219
|
+
version,
|
|
220
|
+
numOfCVEs
|
|
221
|
+
) {
|
|
222
|
+
const vulnerabilityPluralised =
|
|
223
|
+
numOfCVEs > 1 ? 'vulnerabilities' : 'vulnerability'
|
|
224
|
+
const formattedHeaderNum = buildFormattedHeaderNum(contrastHeaderNum)
|
|
225
|
+
|
|
226
|
+
const headerColour = chalk.hex(highestSeverity.colour)
|
|
227
|
+
const headerNumAndSeverity = headerColour(
|
|
228
|
+
`${formattedHeaderNum} - [${highestSeverity.severity}]`
|
|
229
|
+
)
|
|
230
|
+
const libraryNameAndVersion = headerColour.bold(`${libraryName}-${version}`)
|
|
231
|
+
const vulnMessage = `${headerNumAndSeverity} ${libraryNameAndVersion}`
|
|
232
|
+
|
|
233
|
+
const introducesMessage = `introduces ${numOfCVEs} ${vulnerabilityPluralised}`
|
|
234
|
+
|
|
235
|
+
return new ReportOutputHeaderModel(vulnMessage, introducesMessage)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function buildBody(cveArray, advice) {
|
|
239
|
+
let assignPriorityToVulns = cveArray.map(result => findCVESeverity(result))
|
|
240
|
+
|
|
241
|
+
const issueMessage = getIssueRow(assignPriorityToVulns)
|
|
242
|
+
|
|
243
|
+
//todo different advice based on remediationGuidance being available or now
|
|
244
|
+
// console.log(advice)
|
|
245
|
+
|
|
246
|
+
const minOrMax = advice.maximum ? advice.maximum : advice.minimum
|
|
247
|
+
const displayAdvice = minOrMax
|
|
248
|
+
? `Change to version ${chalk.bold(minOrMax)}`
|
|
249
|
+
: 'No recommendation is available according to our data. Upgrade to the latest stable is the best advice we can give.'
|
|
250
|
+
|
|
251
|
+
const adviceMessage = [chalk.bold('Advice'), ':', displayAdvice]
|
|
252
|
+
|
|
253
|
+
return new ReportOutputBodyModel(issueMessage, adviceMessage)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function getIssueRow(cveArray) {
|
|
257
|
+
orderByHighestPriority(cveArray)
|
|
258
|
+
const cveMessagesList = getIssueCveMsgList(cveArray)
|
|
259
|
+
const cveNumbers = getSeverityCounts(cveArray)
|
|
260
|
+
const numAndSeverityTypeDesc = getNumOfAndSeverityType(cveNumbers)
|
|
261
|
+
return [
|
|
262
|
+
chalk.bold('Issue'),
|
|
263
|
+
':',
|
|
264
|
+
`${numAndSeverityTypeDesc} ${cveMessagesList.join(', ')}`
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function gatherRemediationAdvice(guidance, libraryName, libraryVersion) {
|
|
269
|
+
const guidanceModel = new ReportGuidanceModel()
|
|
270
|
+
|
|
271
|
+
const data = guidance[libraryName + '@' + libraryVersion]
|
|
272
|
+
|
|
273
|
+
if (data) {
|
|
274
|
+
guidanceModel.minimum = data.minUpgradeVersion
|
|
275
|
+
guidanceModel.maximum = data.maxUpgradeVersion
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return guidanceModel
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function buildFormattedHeaderNum(contrastHeaderNum) {
|
|
282
|
+
return `CONTRAST-${contrastHeaderNum.toString().padStart(3, '0')}`
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function getNumOfAndSeverityType(cveNumbers) {
|
|
286
|
+
const { critical, high, medium, low, note } = cveNumbers
|
|
287
|
+
|
|
288
|
+
const criticalMsg = critical > 0 ? `${critical} Critical | ` : ''
|
|
289
|
+
const highMsg = high > 0 ? `${high} High | ` : ''
|
|
290
|
+
const mediumMsg = medium > 0 ? `${medium} Medium | ` : ''
|
|
291
|
+
const lowMsg = low > 0 ? `${low} Low | ` : ''
|
|
292
|
+
const noteMsg = note > 0 ? `${note} Note` : ''
|
|
293
|
+
|
|
294
|
+
//removes/trims whitespace to single spaces
|
|
295
|
+
return `${criticalMsg} ${highMsg} ${mediumMsg} ${lowMsg} ${noteMsg}`
|
|
296
|
+
.replace(/\s+/g, ' ')
|
|
297
|
+
.trim()
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const buildFooter = reportModelStructure => {
|
|
301
|
+
const { critical, high, medium, low, note } =
|
|
302
|
+
countVulnerableLibrariesBySeverity(reportModelStructure)
|
|
303
|
+
|
|
304
|
+
const criticalMessage = chalk
|
|
305
|
+
.hex(CRITICAL_COLOUR)
|
|
306
|
+
.bold(`${critical} Critical`)
|
|
307
|
+
const highMessage = chalk.hex(HIGH_COLOUR).bold(`${high} High`)
|
|
308
|
+
const mediumMessage = chalk.hex(MEDIUM_COLOUR).bold(`${medium} Medium`)
|
|
309
|
+
const lowMessage = chalk.hex(LOW_COLOUR).bold(`${low} Low`)
|
|
310
|
+
const noteMessage = chalk.hex(NOTE_COLOUR).bold(`${note} Note`)
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
criticalMessage,
|
|
314
|
+
highMessage,
|
|
315
|
+
mediumMessage,
|
|
316
|
+
lowMessage,
|
|
317
|
+
noteMessage
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const getIssueCveMsgList = results => {
|
|
322
|
+
const cveMessages = []
|
|
323
|
+
|
|
324
|
+
results.forEach(reportSeverityModel => {
|
|
325
|
+
// @ts-ignore
|
|
326
|
+
const { colour, severity, name } = reportSeverityModel
|
|
327
|
+
|
|
328
|
+
const severityShorthand = chalk
|
|
329
|
+
.hex(colour)
|
|
330
|
+
.bold(`[${severity.charAt(0).toUpperCase()}]`)
|
|
331
|
+
|
|
332
|
+
const builtMessage = severityShorthand + name
|
|
333
|
+
cveMessages.push(builtMessage)
|
|
334
|
+
})
|
|
335
|
+
return cveMessages
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const getSeverityCounts = results => {
|
|
339
|
+
const acc = {
|
|
340
|
+
critical: 0,
|
|
341
|
+
high: 0,
|
|
342
|
+
medium: 0,
|
|
343
|
+
low: 0,
|
|
344
|
+
note: 0,
|
|
345
|
+
total: 0
|
|
346
|
+
}
|
|
347
|
+
if (results && results.length > 0) {
|
|
348
|
+
results.forEach(i => {
|
|
349
|
+
acc[i.severity.toLowerCase()] += 1
|
|
350
|
+
acc.total += 1
|
|
351
|
+
return acc
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return acc
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const printNoVulnFoundMsg = () => {
|
|
359
|
+
console.log(i18n.__('scanNoVulnerabilitiesFound'))
|
|
360
|
+
console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
|
|
361
|
+
console.log(i18n.__('scanNoVulnerabilitiesFoundGoodWork'))
|
|
362
|
+
console.log(chalk.bold(`Found 0 vulnerabilities`))
|
|
363
|
+
console.log(
|
|
364
|
+
i18n.__(
|
|
365
|
+
'foundDetailedVulnerabilities',
|
|
366
|
+
String(0),
|
|
367
|
+
String(0),
|
|
368
|
+
String(0),
|
|
369
|
+
String(0),
|
|
370
|
+
String(0)
|
|
371
|
+
)
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
const printVulnInfo = projectOverview => {
|
|
375
|
+
const totalVulnerabilities = projectOverview.total
|
|
376
|
+
|
|
377
|
+
createSummaryMessageBottom(totalVulnerabilities)
|
|
378
|
+
const formattedValues = severityFormatted(projectOverview)
|
|
379
|
+
console.log(
|
|
380
|
+
i18n.__(
|
|
381
|
+
'foundDetailedVulnerabilities',
|
|
382
|
+
String(formattedValues.criticalFormatted),
|
|
383
|
+
String(formattedValues.highFormatted),
|
|
384
|
+
String(formattedValues.mediumFormatted),
|
|
385
|
+
String(formattedValues.lowFormatted),
|
|
386
|
+
String(formattedValues.noteFormatted)
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const severityFormatted = projectOverview => {
|
|
392
|
+
const criticalFormatted = chalk
|
|
393
|
+
.hex(CRITICAL_COLOUR)
|
|
394
|
+
.bold(`${projectOverview.critical} Critical`)
|
|
395
|
+
const highFormatted = chalk
|
|
396
|
+
.hex(HIGH_COLOUR)
|
|
397
|
+
.bold(`${projectOverview.high} High`)
|
|
398
|
+
const mediumFormatted = chalk
|
|
399
|
+
.hex(MEDIUM_COLOUR)
|
|
400
|
+
.bold(`${projectOverview.medium} Medium`)
|
|
401
|
+
const lowFormatted = chalk.hex(LOW_COLOUR).bold(`${projectOverview.low} Low`)
|
|
402
|
+
const noteFormatted = chalk
|
|
403
|
+
.hex(NOTE_COLOUR)
|
|
404
|
+
.bold(`${projectOverview.note} Note`)
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
criticalFormatted,
|
|
408
|
+
highFormatted,
|
|
409
|
+
mediumFormatted,
|
|
410
|
+
lowFormatted,
|
|
411
|
+
noteFormatted
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
module.exports = {
|
|
416
|
+
createSummaryMessageTop,
|
|
417
|
+
getReport,
|
|
418
|
+
createSummaryMessageBottom,
|
|
419
|
+
printVulnerabilityResponse,
|
|
420
|
+
printFormattedOutput,
|
|
421
|
+
getReportTable,
|
|
422
|
+
buildHeader,
|
|
423
|
+
buildBody,
|
|
424
|
+
getIssueRow,
|
|
425
|
+
gatherRemediationAdvice,
|
|
426
|
+
buildFormattedHeaderNum,
|
|
427
|
+
getNumOfAndSeverityType,
|
|
428
|
+
getIssueCveMsgList,
|
|
429
|
+
getSeverityCounts,
|
|
430
|
+
printNoVulnFoundMsg,
|
|
431
|
+
printVulnInfo
|
|
432
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export class ReportSeverityModel {
|
|
2
2
|
severity: string
|
|
3
3
|
priority: number
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
colour: string
|
|
5
|
+
name: string
|
|
6
6
|
|
|
7
7
|
constructor(
|
|
8
8
|
severity: string,
|
|
9
9
|
priority: number,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
colour: string,
|
|
11
|
+
name: string
|
|
12
12
|
) {
|
|
13
13
|
this.severity = severity
|
|
14
14
|
this.priority = priority
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
15
|
+
this.colour = colour
|
|
16
|
+
this.name = name
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getReport,
|
|
3
|
+
printNoVulnFoundMsg,
|
|
3
4
|
printVulnerabilityResponse
|
|
4
5
|
} from './commonReportingFunctions'
|
|
5
6
|
import {
|
|
@@ -58,22 +59,7 @@ export function formatVulnerabilityOutput(
|
|
|
58
59
|
const numberOfVulnerableLibraries = vulnerableLibraries.length
|
|
59
60
|
|
|
60
61
|
if (numberOfVulnerableLibraries === 0) {
|
|
61
|
-
|
|
62
|
-
console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
|
|
63
|
-
console.log(i18n.__('scanNoVulnerabilitiesFoundGoodWork'))
|
|
64
|
-
console.log(
|
|
65
|
-
chalk.bold(`Found ${numberOfVulnerableLibraries} vulnerabilities`)
|
|
66
|
-
)
|
|
67
|
-
console.log(
|
|
68
|
-
i18n.__(
|
|
69
|
-
'foundDetailedVulnerabilities',
|
|
70
|
-
String(0),
|
|
71
|
-
String(0),
|
|
72
|
-
String(0),
|
|
73
|
-
String(0),
|
|
74
|
-
String(0)
|
|
75
|
-
)
|
|
76
|
-
)
|
|
62
|
+
printNoVulnFoundMsg()
|
|
77
63
|
return [false, 0, [new SeverityCountModel()]]
|
|
78
64
|
} else {
|
|
79
65
|
let numberOfCves = 0
|
|
@@ -30,14 +30,8 @@ export function findHighestSeverityCVE(cveArray: ReportCVEModel[]) {
|
|
|
30
30
|
return orderBy(mappedToReportSeverityModels, cve => cve?.priority)[0]
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export function
|
|
34
|
-
cves
|
|
35
|
-
) {
|
|
36
|
-
return orderBy(
|
|
37
|
-
cves.map(cve => findCVESeverity(cve)),
|
|
38
|
-
['priority'],
|
|
39
|
-
['asc']
|
|
40
|
-
)
|
|
33
|
+
export function orderByHighestPriority(cves: ReportCVEModel[]) {
|
|
34
|
+
return orderBy(cves, ['priority'], ['asc'])
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
export function findCVESeverity(cve: ReportCVEModel) {
|
package/src/audit/save.js
CHANGED
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
SBOM_SPDX_FILE
|
|
9
9
|
} = require('../constants/constants')
|
|
10
10
|
|
|
11
|
-
async function auditSave(config) {
|
|
11
|
+
async function auditSave(config, reportId) {
|
|
12
12
|
let fileFormat
|
|
13
13
|
switch (config.save) {
|
|
14
14
|
case null:
|
|
@@ -23,11 +23,19 @@ async function auditSave(config) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (fileFormat) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if (config.experimental) {
|
|
27
|
+
save.saveFile(
|
|
28
|
+
config,
|
|
29
|
+
fileFormat,
|
|
30
|
+
await sbom.generateSCASbom(config, fileFormat, reportId)
|
|
31
|
+
)
|
|
32
|
+
} else {
|
|
33
|
+
save.saveFile(
|
|
34
|
+
config,
|
|
35
|
+
fileFormat,
|
|
36
|
+
await sbom.generateSbom(config, fileFormat)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
31
39
|
const filename = `${config.applicationId}-sbom-${fileFormat}.json`
|
|
32
40
|
if (fs.existsSync(filename)) {
|
|
33
41
|
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`)
|
|
@@ -3,21 +3,13 @@ const { startScan } = require('../../scan/scanController')
|
|
|
3
3
|
const { saveScanFile } = require('../../utils/saveFile')
|
|
4
4
|
const { ScanResultsModel } = require('../../scan/models/scanResultsModel')
|
|
5
5
|
const { formatScanOutput } = require('../../scan/formatScanOutput')
|
|
6
|
-
const { processSca } = require('./sca/scaAnalysis')
|
|
7
6
|
const common = require('../../common/fail')
|
|
8
7
|
const { sendTelemetryConfigAsObject } = require('../../telemetry/telemetry')
|
|
9
8
|
const chalk = require('chalk')
|
|
10
|
-
const generalAPI = require('../../utils/generalAPI')
|
|
11
9
|
|
|
12
10
|
const processScan = async (contrastConf, argv) => {
|
|
13
11
|
let config = await scanConfig.getScanConfig(contrastConf, 'scan', argv)
|
|
14
12
|
let output = undefined
|
|
15
|
-
config.mode = await generalAPI.getMode(config)
|
|
16
|
-
|
|
17
|
-
//try SCA analysis first
|
|
18
|
-
if (config.experimental) {
|
|
19
|
-
await processSca(config, argv)
|
|
20
|
-
}
|
|
21
13
|
|
|
22
14
|
let scanResults = new ScanResultsModel(await startScan(config))
|
|
23
15
|
await sendTelemetryConfigAsObject(
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const autoDetection = require('../../../scan/autoDetection')
|
|
2
2
|
const javaAnalysis = require('../../../scaAnalysis/java')
|
|
3
3
|
const treeUpload = require('../../../scaAnalysis/common/treeUpload')
|
|
4
|
-
const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload')
|
|
5
4
|
const auditController = require('../../audit/auditController')
|
|
6
5
|
const {
|
|
7
6
|
supportedLanguages: { JAVA, GO, PYTHON, RUBY, JAVASCRIPT, NODE, PHP, DOTNET }
|
|
@@ -28,10 +27,13 @@ const { dotNetAnalysis } = require('../../../scaAnalysis/dotnet')
|
|
|
28
27
|
const { auditUsageGuide } = require('../../audit/help')
|
|
29
28
|
const rootFile = require('../../../audit/languageAnalysisEngine/getProjectRootFilenames')
|
|
30
29
|
const path = require('path')
|
|
31
|
-
const
|
|
30
|
+
const auditReport = require('../../../scaAnalysis/common/auditReport')
|
|
31
|
+
const scaUpload = require('../../../scaAnalysis/common/scaServicesUpload')
|
|
32
|
+
const settingsHelper = require('../../../utils/settingsHelper')
|
|
32
33
|
|
|
33
34
|
const processSca = async config => {
|
|
34
|
-
|
|
35
|
+
//checks to see whether to use old TS / new SCA path
|
|
36
|
+
config = await settingsHelper.getSettings(config)
|
|
35
37
|
|
|
36
38
|
const startTime = performance.now()
|
|
37
39
|
let filesFound
|
|
@@ -104,38 +106,56 @@ const processSca = async config => {
|
|
|
104
106
|
config.applicationId = await auditController.dealWithNoAppId(config)
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const snapshotResponse = await treeUpload.commonSendSnapShot(
|
|
116
|
-
messageToSend,
|
|
117
|
-
config
|
|
118
|
-
)
|
|
109
|
+
if (config.experimental) {
|
|
110
|
+
console.log('') //empty log for space before spinner
|
|
111
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
|
|
112
|
+
startSpinner(reportSpinner)
|
|
113
|
+
const [reports, reportId] = await scaUpload.scaTreeUpload(
|
|
114
|
+
messageToSend,
|
|
115
|
+
config
|
|
116
|
+
)
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
config,
|
|
123
|
-
snapshotResponse.id,
|
|
124
|
-
reportSpinner
|
|
125
|
-
)
|
|
126
|
-
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
|
|
118
|
+
auditReport.processAuditReport(config, reports[0])
|
|
119
|
+
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
|
|
127
120
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
const endTime = performance.now() - startTime
|
|
133
|
-
const scanDurationMs = endTime - startTime
|
|
121
|
+
if (config.save !== undefined) {
|
|
122
|
+
await auditSave.auditSave(config, reportId)
|
|
123
|
+
}
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
const endTime = performance.now() - startTime
|
|
126
|
+
const scanDurationMs = endTime - startTime
|
|
127
|
+
console.log(
|
|
128
|
+
`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
129
|
+
)
|
|
130
|
+
} else {
|
|
131
|
+
console.log('') //empty log for space before spinner
|
|
132
|
+
//send message to TS
|
|
133
|
+
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
|
|
134
|
+
startSpinner(reportSpinner)
|
|
135
|
+
const snapshotResponse = await treeUpload.commonSendSnapShot(
|
|
136
|
+
messageToSend,
|
|
137
|
+
config
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
//poll for completion
|
|
141
|
+
await pollForSnapshotCompletition(
|
|
142
|
+
config,
|
|
143
|
+
snapshotResponse.id,
|
|
144
|
+
reportSpinner
|
|
145
|
+
)
|
|
146
|
+
succeedSpinner(reportSpinner, i18n.__('auditSCAAnalysisComplete'))
|
|
147
|
+
|
|
148
|
+
await vulnerabilityReportV2(config, snapshotResponse.id)
|
|
149
|
+
if (config.save !== undefined) {
|
|
150
|
+
await auditSave.auditSave(config)
|
|
151
|
+
}
|
|
152
|
+
const endTime = performance.now() - startTime
|
|
153
|
+
const scanDurationMs = endTime - startTime
|
|
154
|
+
|
|
155
|
+
console.log(
|
|
156
|
+
`----- completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
157
|
+
)
|
|
158
|
+
}
|
|
139
159
|
} else {
|
|
140
160
|
if (filesFound.length === 0) {
|
|
141
161
|
console.log(i18n.__('languageAnalysisNoLanguage'))
|