@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
package/src/common/HTTPClient.js
CHANGED
|
@@ -246,6 +246,13 @@ HTTPClient.prototype.scaServiceReportStatus = function scaServiceReport(
|
|
|
246
246
|
return requestUtils.sendRequest({ method: 'get', options })
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
HTTPClient.prototype.scaServiceIngests = function scaServiceIngests(config) {
|
|
250
|
+
const options = _.cloneDeep(this.requestOptions)
|
|
251
|
+
let url = createScaServiceIngestsURL(config)
|
|
252
|
+
options.url = url
|
|
253
|
+
return requestUtils.sendRequest({ method: 'get', options })
|
|
254
|
+
}
|
|
255
|
+
|
|
249
256
|
HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
|
|
250
257
|
const options = _.cloneDeep(this.requestOptions)
|
|
251
258
|
if (config.ignoreDev) {
|
|
@@ -370,6 +377,12 @@ HTTPClient.prototype.getSbom = function getSbom(config, type) {
|
|
|
370
377
|
return requestUtils.sendRequest({ method: 'get', options })
|
|
371
378
|
}
|
|
372
379
|
|
|
380
|
+
HTTPClient.prototype.getSCASbom = function getSbom(config, type, reportId) {
|
|
381
|
+
const options = _.cloneDeep(this.requestOptions)
|
|
382
|
+
options.url = createSCASbomUrl(config, type, reportId)
|
|
383
|
+
return requestUtils.sendRequest({ method: 'get', options })
|
|
384
|
+
}
|
|
385
|
+
|
|
373
386
|
HTTPClient.prototype.getLatestVersion = function getLatestVersion() {
|
|
374
387
|
const options = _.cloneDeep(this.requestOptions)
|
|
375
388
|
options.url =
|
|
@@ -447,15 +460,21 @@ function createSnapshotURL(config) {
|
|
|
447
460
|
}
|
|
448
461
|
|
|
449
462
|
function createScaServiceReportURL(config, reportId) {
|
|
450
|
-
return
|
|
463
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/reports/${reportId}`
|
|
451
464
|
}
|
|
452
465
|
|
|
453
466
|
function createScaServiceReportStatusURL(config, reportId) {
|
|
454
|
-
return
|
|
467
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/${reportId}/status`
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function createScaServiceIngestsURL(config) {
|
|
471
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests`
|
|
455
472
|
}
|
|
456
473
|
|
|
457
474
|
function createScaServiceIngestURL(config) {
|
|
458
|
-
|
|
475
|
+
let baseUrl = `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/ingests/tree`
|
|
476
|
+
baseUrl = config.track ? baseUrl.concat('?persist=true') : baseUrl
|
|
477
|
+
return baseUrl
|
|
459
478
|
}
|
|
460
479
|
|
|
461
480
|
const createAppCreateURL = config => {
|
|
@@ -492,6 +511,10 @@ function createSbomUrl(config, type) {
|
|
|
492
511
|
return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/${type}`
|
|
493
512
|
}
|
|
494
513
|
|
|
514
|
+
function createSCASbomUrl(config, type, reportId) {
|
|
515
|
+
return `${config.host}/Contrast/api/sca/organizations/${config.organizationId}/libraries/applications/${config.applicationId}/sbom/${reportId}?toolType=${type}`
|
|
516
|
+
}
|
|
517
|
+
|
|
495
518
|
function createTelemetryEventUrl(config) {
|
|
496
519
|
return `${config.host}/Contrast/api/sast/organizations/${config.organizationId}/cli`
|
|
497
520
|
}
|
|
@@ -38,8 +38,7 @@ const reportFailureError = () => {
|
|
|
38
38
|
console.log(i18n.__('auditReportFailureMessage'))
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const genericError = (
|
|
42
|
-
console.log(missingCliOption)
|
|
41
|
+
const genericError = () => {
|
|
43
42
|
console.error(i18n.__('genericErrorMessage'))
|
|
44
43
|
process.exit(1)
|
|
45
44
|
}
|
|
@@ -14,7 +14,7 @@ const HIGH = 'HIGH'
|
|
|
14
14
|
const CRITICAL = 'CRITICAL'
|
|
15
15
|
// App
|
|
16
16
|
const APP_NAME = 'contrast'
|
|
17
|
-
const APP_VERSION = '1.0.
|
|
17
|
+
const APP_VERSION = '1.0.15'
|
|
18
18
|
const TIMEOUT = 120000
|
|
19
19
|
const HIGH_COLOUR = '#ff9900'
|
|
20
20
|
const CRITICAL_COLOUR = '#e35858'
|
|
@@ -34,6 +34,12 @@ const SBOM_CYCLONE_DX_FILE = 'cyclonedx'
|
|
|
34
34
|
const SBOM_SPDX_FILE = 'spdx'
|
|
35
35
|
const CE_URL = 'https://ce.contrastsecurity.com'
|
|
36
36
|
|
|
37
|
+
//configuration
|
|
38
|
+
const SAAS = 'SAAS'
|
|
39
|
+
const EOP = 'EOP'
|
|
40
|
+
const MODE_BUILD = 'BUILD'
|
|
41
|
+
const MODE_REPO = 'REPO'
|
|
42
|
+
|
|
37
43
|
module.exports = {
|
|
38
44
|
supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
|
|
39
45
|
supportedLanguagesScan: { JAVASCRIPT, DOTNET, JAVA },
|
|
@@ -59,5 +65,9 @@ module.exports = {
|
|
|
59
65
|
LOW_PRIORITY,
|
|
60
66
|
NOTE_PRIORITY,
|
|
61
67
|
SBOM_CYCLONE_DX_FILE,
|
|
62
|
-
SBOM_SPDX_FILE
|
|
68
|
+
SBOM_SPDX_FILE,
|
|
69
|
+
SAAS,
|
|
70
|
+
EOP,
|
|
71
|
+
MODE_BUILD,
|
|
72
|
+
MODE_REPO
|
|
63
73
|
}
|
package/src/constants/locales.js
CHANGED
|
@@ -122,6 +122,8 @@ const en_locales = () => {
|
|
|
122
122
|
'Provide this if you want to catalogue an application',
|
|
123
123
|
failOptionErrorMessage:
|
|
124
124
|
' FAIL - CVEs have been detected that match at least the cve_severity option specified.',
|
|
125
|
+
failOptionMessage:
|
|
126
|
+
' Use with contrast scan or contrast audit. Detects failures based on the severity level specified with the --severity command. For example, "contrast scan --fail --severity high". Returns all failures if no severity level is specified.',
|
|
125
127
|
constantsLanguage:
|
|
126
128
|
'Valid values are JAVA, DOTNET, NODE, PYTHON and RUBY. If there are multiple project configuration files in the project_path, language is also required. Also, provide this when cataloguing an application',
|
|
127
129
|
constantsFilePath: `Specify a directory or the file where dependencies are declared. (By default, CodeSec will search for project files in the current directory.)`,
|
|
@@ -147,7 +149,7 @@ const en_locales = () => {
|
|
|
147
149
|
failSeverityOptionErrorMessage:
|
|
148
150
|
' FAIL - Results detected vulnerabilities over accepted severity level',
|
|
149
151
|
constantsSeverity:
|
|
150
|
-
'
|
|
152
|
+
'Use with "contrast scan --fail --severity high" or "contrast audit --fail --severity high". Set the severity level to detect vulnerabilities or dependencies. Severity levels are critical, high, medium, low or note.',
|
|
151
153
|
constantsCount: 'The number of CVEs that must be exceeded to fail a build',
|
|
152
154
|
constantsHeader: 'CodeSec by Contrast Security',
|
|
153
155
|
configHeader2: 'Config options',
|
|
@@ -170,7 +172,7 @@ const en_locales = () => {
|
|
|
170
172
|
constantsConfigUsageContents: 'view / clear the configuration',
|
|
171
173
|
constantsPrerequisitesContent:
|
|
172
174
|
'To scan a Java project you will need a .jar or .war file for analysis\n' +
|
|
173
|
-
'To scan a Javascript project you will need a .js or.zip
|
|
175
|
+
'To scan a Javascript project you will need a single .js or a .zip of multiple .js files\n' +
|
|
174
176
|
'To scan a .NET c# webforms project you will need a .exe or a .zip file for analysis\n',
|
|
175
177
|
constantsUsage: 'Usage',
|
|
176
178
|
constantsUsageCommandExample: 'contrast [command] [options]',
|
|
@@ -315,9 +317,22 @@ const en_locales = () => {
|
|
|
315
317
|
scanOptionsFileNameSummary:
|
|
316
318
|
'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .exe or .zip file in the working directory.',
|
|
317
319
|
scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
|
|
320
|
+
auditOptionsTrackSummary: ' Save the results to the UI.',
|
|
321
|
+
auditOptionsBranchSummary:
|
|
322
|
+
' Set the branch name to associate the library results to.',
|
|
318
323
|
authSuccessMessage: 'Authentication successful',
|
|
319
324
|
runAuthSuccessMessage:
|
|
320
|
-
|
|
325
|
+
chalk.bold('CodeSec by Contrast') +
|
|
326
|
+
'\nScan, secure and ship your code in minutes for FREE. \n' +
|
|
327
|
+
chalk.bold('\nRun\n') +
|
|
328
|
+
chalk.bold('\ncontrast scan') +
|
|
329
|
+
" to run Contrast's industry leading SAST scanner. \nSupports Java, JavaScript and .Net \n" +
|
|
330
|
+
chalk.bold('\ncontrast audit') +
|
|
331
|
+
' to find vulnerabilities in your open source dependencies.\nSupports Java, .NET, Node, Ruby, Python, Go and PHP \n' +
|
|
332
|
+
chalk.bold('\ncontrast lambda') +
|
|
333
|
+
' to secure your AWS serverless functions. \nSupports Java and Python \n' +
|
|
334
|
+
chalk.bold('\ncontrast help') +
|
|
335
|
+
' to learn more about the capabilities.',
|
|
321
336
|
authWaitingMessage: 'Waiting for auth...',
|
|
322
337
|
authTimedOutMessage: 'Auth Timed out, try again',
|
|
323
338
|
zipErrorScan:
|
|
@@ -325,10 +340,7 @@ const en_locales = () => {
|
|
|
325
340
|
unknownFileErrorScan: 'Unsupported file selected for Scan.',
|
|
326
341
|
foundScanFile: 'Found: %s',
|
|
327
342
|
foundDetailedVulnerabilities:
|
|
328
|
-
chalk.bold('%s
|
|
329
|
-
' | ' +
|
|
330
|
-
chalk.bold('%s High') +
|
|
331
|
-
' | %s Medium | %s Low | %s Note',
|
|
343
|
+
chalk.bold('%s') + ' | ' + chalk.bold('%s') + ' | %s | %s | %s ',
|
|
332
344
|
requiredParams: 'All required parameters are not present.',
|
|
333
345
|
timeoutScan: 'Timeout set to 5 minutes.',
|
|
334
346
|
searchingScanFileDirectory: 'Searching for file to scan from %s...',
|
package/src/constants.js
CHANGED
|
@@ -115,7 +115,7 @@ const scanOptionDefinitions = [
|
|
|
115
115
|
'{bold ' +
|
|
116
116
|
i18n.__('constantsOptional') +
|
|
117
117
|
'}: ' +
|
|
118
|
-
i18n.__('
|
|
118
|
+
i18n.__('failOptionMessage')
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
name: 'severity',
|
|
@@ -247,7 +247,7 @@ const auditOptionDefinitions = [
|
|
|
247
247
|
'{bold ' +
|
|
248
248
|
i18n.__('constantsOptional') +
|
|
249
249
|
'}: ' +
|
|
250
|
-
i18n.__('
|
|
250
|
+
i18n.__('failOptionMessage')
|
|
251
251
|
},
|
|
252
252
|
{
|
|
253
253
|
name: 'severity',
|
|
@@ -384,6 +384,38 @@ const auditOptionDefinitions = [
|
|
|
384
384
|
name: 'help',
|
|
385
385
|
alias: 'h',
|
|
386
386
|
type: Boolean
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'debug',
|
|
390
|
+
alias: 'd',
|
|
391
|
+
type: Boolean
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'verbose',
|
|
395
|
+
alias: 'v',
|
|
396
|
+
type: Boolean,
|
|
397
|
+
description:
|
|
398
|
+
'{bold ' +
|
|
399
|
+
i18n.__('constantsOptional') +
|
|
400
|
+
'}:' +
|
|
401
|
+
i18n.__('scanOptionsVerboseSummary')
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: 'track',
|
|
405
|
+
type: Boolean,
|
|
406
|
+
description:
|
|
407
|
+
'{bold ' +
|
|
408
|
+
i18n.__('constantsOptional') +
|
|
409
|
+
'}:' +
|
|
410
|
+
i18n.__('auditOptionsTrackSummary')
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: 'branch',
|
|
414
|
+
description:
|
|
415
|
+
'{bold ' +
|
|
416
|
+
i18n.__('constantsOptional') +
|
|
417
|
+
'}:' +
|
|
418
|
+
i18n.__('auditOptionsBranchSummary')
|
|
387
419
|
}
|
|
388
420
|
]
|
|
389
421
|
|
package/src/index.ts
CHANGED
|
@@ -95,9 +95,8 @@ const start = async () => {
|
|
|
95
95
|
? console.log(
|
|
96
96
|
`Unknown Command: Did you mean "${foundCommand}"? \nUse "${foundCommand} --help" for the full list of options`
|
|
97
97
|
)
|
|
98
|
-
: console.log(
|
|
99
|
-
|
|
100
|
-
)
|
|
98
|
+
: console.log(`\nUnknown Command: ${command} \n`)
|
|
99
|
+
console.log(mainUsageGuide)
|
|
101
100
|
await sendTelemetryConfigAsConfObj(
|
|
102
101
|
config,
|
|
103
102
|
command,
|
|
@@ -106,9 +105,8 @@ const start = async () => {
|
|
|
106
105
|
'undefined'
|
|
107
106
|
)
|
|
108
107
|
} else {
|
|
109
|
-
console.log(
|
|
110
|
-
|
|
111
|
-
)
|
|
108
|
+
console.log(`\nUnknown Command: ${command}\n`)
|
|
109
|
+
console.log(mainUsageGuide)
|
|
112
110
|
await sendTelemetryConfigAsConfObj(
|
|
113
111
|
config,
|
|
114
112
|
command,
|
package/src/lambda/lambda.ts
CHANGED
|
@@ -126,7 +126,7 @@ const processLambda = async (argv: string[]) => {
|
|
|
126
126
|
|
|
127
127
|
const getAvailableFunctions = async (lambdaOptions: LambdaOptions) => {
|
|
128
128
|
const lambdas = await getAllLambdas(lambdaOptions)
|
|
129
|
-
printAvailableLambdas(lambdas, { runtimes: ['python', 'java'] })
|
|
129
|
+
printAvailableLambdas(lambdas, { runtimes: ['python', 'java', 'node'] })
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
const actualProcessLambda = async (lambdaOptions: LambdaOptions) => {
|
|
@@ -11,7 +11,7 @@ import ora from '../utils/oraWrapper'
|
|
|
11
11
|
import { LambdaOptions } from './lambda'
|
|
12
12
|
import { log, getReadableFileSize } from './logUtils'
|
|
13
13
|
|
|
14
|
-
type RuntimeLanguage = 'java' | 'python'
|
|
14
|
+
type RuntimeLanguage = 'java' | 'python' | 'node'
|
|
15
15
|
|
|
16
16
|
type FilterLambdas = {
|
|
17
17
|
runtimes: RuntimeLanguage[]
|
package/src/sbom/generateSbom.ts
CHANGED
|
@@ -15,3 +15,23 @@ export const generateSbom = (config: any, type: string) => {
|
|
|
15
15
|
console.log(err)
|
|
16
16
|
})
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export const generateSCASbom = (
|
|
20
|
+
config: any,
|
|
21
|
+
type: string,
|
|
22
|
+
reportId: string
|
|
23
|
+
) => {
|
|
24
|
+
const client = getHttpClient(config)
|
|
25
|
+
return client
|
|
26
|
+
.getSCASbom(config, type, reportId)
|
|
27
|
+
.then((res: { statusCode: number; body: any }) => {
|
|
28
|
+
if (res.statusCode === 200) {
|
|
29
|
+
return res.body
|
|
30
|
+
} else {
|
|
31
|
+
console.log('Unable to retrieve Software Bill of Materials (SBOM)')
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.catch((err: any) => {
|
|
35
|
+
console.log(err)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const {
|
|
2
|
+
getSeverityCounts,
|
|
3
|
+
createSummaryMessageTop,
|
|
4
|
+
printVulnInfo,
|
|
5
|
+
getReportTable,
|
|
6
|
+
getIssueRow,
|
|
7
|
+
printNoVulnFoundMsg
|
|
8
|
+
} = require('../../audit/report/commonReportingFunctions')
|
|
9
|
+
const { orderBy } = require('lodash')
|
|
10
|
+
const { assignBySeverity } = require('../../scan/formatScanOutput')
|
|
11
|
+
const chalk = require('chalk')
|
|
12
|
+
const { CE_URL } = require('../../constants/constants')
|
|
13
|
+
const common = require('../../common/fail')
|
|
14
|
+
|
|
15
|
+
const processAuditReport = (config, results) => {
|
|
16
|
+
let severityCounts = {}
|
|
17
|
+
if (results !== undefined) {
|
|
18
|
+
severityCounts = formatScaServicesReport(config, results)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (config.fail) {
|
|
22
|
+
common.processFail(config, severityCounts)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const formatScaServicesReport = (config, results) => {
|
|
26
|
+
const projectOverviewCount = getSeverityCounts(results)
|
|
27
|
+
|
|
28
|
+
if (projectOverviewCount.total === 0) {
|
|
29
|
+
printNoVulnFoundMsg()
|
|
30
|
+
return projectOverviewCount
|
|
31
|
+
} else {
|
|
32
|
+
let total = 0
|
|
33
|
+
const numberOfCves = results.length
|
|
34
|
+
const table = getReportTable()
|
|
35
|
+
let contrastHeaderNumCounter = 0
|
|
36
|
+
let assignPriorityToResults = results.map(result =>
|
|
37
|
+
assignBySeverity(result, result)
|
|
38
|
+
)
|
|
39
|
+
const numberOfVulns = results
|
|
40
|
+
.map(result => result.vulnerabilities)
|
|
41
|
+
.reduce((a, b) => {
|
|
42
|
+
return (total += b.length)
|
|
43
|
+
}, 0)
|
|
44
|
+
const outputOrderedByLowestSeverityAndLowestNumOfCvesFirst = orderBy(
|
|
45
|
+
assignPriorityToResults,
|
|
46
|
+
[
|
|
47
|
+
reportListItem => {
|
|
48
|
+
return reportListItem.priority
|
|
49
|
+
},
|
|
50
|
+
reportListItem => {
|
|
51
|
+
return reportListItem.vulnerabilities.length
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
['asc', 'desc']
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
for (const result of outputOrderedByLowestSeverityAndLowestNumOfCvesFirst) {
|
|
58
|
+
contrastHeaderNumCounter++
|
|
59
|
+
const cvesNum = result.vulnerabilities.length
|
|
60
|
+
const grammaticallyCorrectVul =
|
|
61
|
+
result.vulnerabilities.length > 1 ? 'vulnerabilities' : 'vulnerability'
|
|
62
|
+
|
|
63
|
+
const headerColour = chalk.hex(result.colour)
|
|
64
|
+
const headerRow = [
|
|
65
|
+
headerColour(
|
|
66
|
+
`CONTRAST-${contrastHeaderNumCounter.toString().padStart(3, '0')}`
|
|
67
|
+
),
|
|
68
|
+
headerColour(`-`),
|
|
69
|
+
headerColour(`[${result.severity}] `) +
|
|
70
|
+
headerColour.bold(`${result.artifactName}`) +
|
|
71
|
+
` introduces ${cvesNum} ${grammaticallyCorrectVul}`
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
const adviceRow = [
|
|
75
|
+
chalk.bold(`Advice`),
|
|
76
|
+
chalk.bold(`:`),
|
|
77
|
+
`Change to version ${result.remediationAdvice.latestStableVersion}`
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
let assignPriorityToVulns = result.vulnerabilities.map(result =>
|
|
81
|
+
assignBySeverity(result, result)
|
|
82
|
+
)
|
|
83
|
+
const issueRow = getIssueRow(assignPriorityToVulns)
|
|
84
|
+
|
|
85
|
+
table.push(headerRow, issueRow, adviceRow)
|
|
86
|
+
console.log()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log()
|
|
90
|
+
createSummaryMessageTop(numberOfCves, numberOfVulns)
|
|
91
|
+
console.log(table.toString() + '\n')
|
|
92
|
+
printVulnInfo(projectOverviewCount)
|
|
93
|
+
|
|
94
|
+
if (config.host !== CE_URL) {
|
|
95
|
+
console.log(
|
|
96
|
+
'\n' + chalk.bold('View your full dependency tree in Contrast:')
|
|
97
|
+
)
|
|
98
|
+
console.log(
|
|
99
|
+
`${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
return projectOverviewCount
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
module.exports = {
|
|
106
|
+
formatScaServicesReport,
|
|
107
|
+
processAuditReport
|
|
108
|
+
}
|
|
@@ -7,13 +7,17 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
7
7
|
applicationId: config.applicationId,
|
|
8
8
|
dependencyTree: analysis,
|
|
9
9
|
organizationId: config.organizationId,
|
|
10
|
-
language:
|
|
10
|
+
language: config.language,
|
|
11
11
|
tool: {
|
|
12
12
|
name: 'Contrast Codesec',
|
|
13
13
|
version: APP_VERSION
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
if (config.branch) {
|
|
18
|
+
requestBody.branchName = config.branch
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
const client = commonApi.getHttpClient(config)
|
|
18
22
|
const reportID = await client
|
|
19
23
|
.scaServiceIngest(requestBody, config)
|
|
@@ -27,26 +31,32 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
27
31
|
.catch(err => {
|
|
28
32
|
throw err
|
|
29
33
|
})
|
|
34
|
+
if (config.debug) {
|
|
35
|
+
console.log(' polling report', reportID)
|
|
36
|
+
}
|
|
30
37
|
|
|
31
38
|
let keepChecking = true
|
|
39
|
+
let res
|
|
32
40
|
while (keepChecking) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
res = await client.scaServiceReportStatus(config, reportID).then(res => {
|
|
42
|
+
if (config.debug) {
|
|
43
|
+
console.log(res.statusCode)
|
|
36
44
|
console.log(res.body)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
}
|
|
46
|
+
if (res.body.status === 'COMPLETED') {
|
|
47
|
+
keepChecking = false
|
|
48
|
+
return client.scaServiceReport(config, reportID).then(res => {
|
|
49
|
+
return [res.body, reportID]
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
})
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
})
|
|
54
|
+
if (!keepChecking) {
|
|
55
|
+
return [res, reportID]
|
|
56
|
+
}
|
|
48
57
|
await requestUtils.sleep(5000)
|
|
49
58
|
}
|
|
59
|
+
return [res, reportID]
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
module.exports = {
|
|
@@ -15,11 +15,15 @@ import {
|
|
|
15
15
|
MEDIUM_COLOUR,
|
|
16
16
|
NOTE_COLOUR
|
|
17
17
|
} from '../constants/constants'
|
|
18
|
+
import {
|
|
19
|
+
getSeverityCounts,
|
|
20
|
+
printVulnInfo
|
|
21
|
+
} from '../audit/report/commonReportingFunctions'
|
|
18
22
|
|
|
19
23
|
export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
20
24
|
const { scanResultsInstances } = scanResults
|
|
21
25
|
|
|
22
|
-
const projectOverview =
|
|
26
|
+
const projectOverview = getSeverityCounts(scanResultsInstances.content)
|
|
23
27
|
if (scanResultsInstances.content.length === 0) {
|
|
24
28
|
console.log(i18n.__('scanNoVulnerabilitiesFound'))
|
|
25
29
|
console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
|
|
@@ -108,47 +112,6 @@ export function formatScanOutput(scanResults: ScanResultsModel) {
|
|
|
108
112
|
return projectOverview
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
function printVulnInfo(projectOverview: any) {
|
|
112
|
-
const totalVulnerabilities = projectOverview.total
|
|
113
|
-
|
|
114
|
-
const vulMessage =
|
|
115
|
-
totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
|
|
116
|
-
console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
|
|
117
|
-
console.log(
|
|
118
|
-
i18n.__(
|
|
119
|
-
'foundDetailedVulnerabilities',
|
|
120
|
-
String(projectOverview.critical),
|
|
121
|
-
String(projectOverview.high),
|
|
122
|
-
String(projectOverview.medium),
|
|
123
|
-
String(projectOverview.low),
|
|
124
|
-
String(projectOverview.note)
|
|
125
|
-
)
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function getProjectOverview(scanResultsInstances: ScanResultsInstances) {
|
|
130
|
-
const acc: any = {
|
|
131
|
-
critical: 0,
|
|
132
|
-
high: 0,
|
|
133
|
-
medium: 0,
|
|
134
|
-
low: 0,
|
|
135
|
-
note: 0,
|
|
136
|
-
total: 0
|
|
137
|
-
}
|
|
138
|
-
if (
|
|
139
|
-
scanResultsInstances?.content &&
|
|
140
|
-
scanResultsInstances.content.length > 0
|
|
141
|
-
) {
|
|
142
|
-
scanResultsInstances.content.forEach((i: ResultContent) => {
|
|
143
|
-
acc[i.severity.toLowerCase()] += 1
|
|
144
|
-
acc.total += 1
|
|
145
|
-
return acc
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return acc
|
|
150
|
-
}
|
|
151
|
-
|
|
152
115
|
export function formatLinks(objName: string, entry: any[]) {
|
|
153
116
|
const line = chalk.bold(objName + ' : ')
|
|
154
117
|
if (entry.length === 1) {
|
package/src/utils/commonApi.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const commonApi = require('./commonApi')
|
|
2
|
+
const { getMode } = require('./generalAPI')
|
|
3
|
+
const { SAAS, MODE_BUILD } = require('../constants/constants')
|
|
4
|
+
|
|
5
|
+
const getSettings = async config => {
|
|
6
|
+
config.isEOP = (await getMode(config)).toUpperCase() === SAAS ? false : true
|
|
7
|
+
config.mode = MODE_BUILD
|
|
8
|
+
config.scaServices = await isSCAServicesAvailable(config)
|
|
9
|
+
return config
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const isSCAServicesAvailable = async config => {
|
|
13
|
+
const client = commonApi.getHttpClient(config)
|
|
14
|
+
return client
|
|
15
|
+
.scaServiceIngests(config)
|
|
16
|
+
.then(res => {
|
|
17
|
+
return res.statusCode !== 403
|
|
18
|
+
})
|
|
19
|
+
.catch(err => {
|
|
20
|
+
console.log(err)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
getSettings
|
|
26
|
+
}
|