@contrast/contrast 1.0.13 → 1.0.14
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/commands/scan/sca/scaAnalysis.js +0 -1
- package/dist/common/errorHandling.js +1 -2
- package/dist/constants/constants.js +1 -1
- package/dist/constants/locales.js +15 -7
- package/dist/constants.js +2 -2
- package/dist/index.js +4 -2
- package/dist/lambda/lambda.js +1 -1
- package/dist/scaAnalysis/common/auditReport.js +78 -0
- package/dist/scaAnalysis/common/scaServicesUpload.js +12 -11
- package/dist/scan/formatScanOutput.js +4 -29
- 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/commands/scan/sca/scaAnalysis.js +3 -4
- package/src/common/errorHandling.ts +1 -2
- package/src/constants/constants.js +1 -1
- package/src/constants/locales.js +16 -7
- package/src/constants.js +2 -2
- package/src/index.ts +4 -6
- package/src/lambda/lambda.ts +1 -1
- package/src/lambda/lambdaUtils.ts +1 -1
- package/src/scaAnalysis/common/auditReport.js +108 -0
- package/src/scaAnalysis/common/scaServicesUpload.js +17 -15
- package/src/scan/formatScanOutput.ts +5 -42
- package/src/audit/report/commonReportingFunctions.ts +0 -355
|
@@ -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) {
|
|
@@ -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 }
|
|
@@ -105,9 +104,9 @@ const processSca = async config => {
|
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
// if (config.experimental) {
|
|
108
|
-
// await scaUpload.scaTreeUpload(messageToSend, config)
|
|
109
|
-
//
|
|
110
|
-
// {
|
|
107
|
+
// // const reports = await scaUpload.scaTreeUpload(messageToSend, config)
|
|
108
|
+
// auditReport.processAuditReport(config, 'reports')
|
|
109
|
+
// } else {
|
|
111
110
|
console.log('') //empty log for space before spinner
|
|
112
111
|
//send message to TS
|
|
113
112
|
const reportSpinner = returnOra(i18n.__('auditSCAAnalysisBegins'))
|
|
@@ -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
|
}
|
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]',
|
|
@@ -317,7 +319,17 @@ const en_locales = () => {
|
|
|
317
319
|
scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
|
|
318
320
|
authSuccessMessage: 'Authentication successful',
|
|
319
321
|
runAuthSuccessMessage:
|
|
320
|
-
|
|
322
|
+
chalk.bold('CodeSec by Contrast') +
|
|
323
|
+
'\nScan, secure and ship your code in minutes for FREE. \n' +
|
|
324
|
+
chalk.bold('\nRun\n') +
|
|
325
|
+
chalk.bold('\ncontrast scan') +
|
|
326
|
+
" to run Contrast's industry leading SAST scanner. \nSupports Java, JavaScript and .Net \n" +
|
|
327
|
+
chalk.bold('\ncontrast audit') +
|
|
328
|
+
' to find vulnerabilities in your open source dependencies.\nSupports Java, .NET, Node, Ruby, Python, Go and PHP \n' +
|
|
329
|
+
chalk.bold('\ncontrast lambda') +
|
|
330
|
+
' to secure your AWS serverless functions. \nSupports Java and Python \n' +
|
|
331
|
+
chalk.bold('\ncontrast help') +
|
|
332
|
+
' to learn more about the capabilities.',
|
|
321
333
|
authWaitingMessage: 'Waiting for auth...',
|
|
322
334
|
authTimedOutMessage: 'Auth Timed out, try again',
|
|
323
335
|
zipErrorScan:
|
|
@@ -325,10 +337,7 @@ const en_locales = () => {
|
|
|
325
337
|
unknownFileErrorScan: 'Unsupported file selected for Scan.',
|
|
326
338
|
foundScanFile: 'Found: %s',
|
|
327
339
|
foundDetailedVulnerabilities:
|
|
328
|
-
chalk.bold('%s
|
|
329
|
-
' | ' +
|
|
330
|
-
chalk.bold('%s High') +
|
|
331
|
-
' | %s Medium | %s Low | %s Note',
|
|
340
|
+
chalk.bold('%s') + ' | ' + chalk.bold('%s') + ' | %s | %s | %s ',
|
|
332
341
|
requiredParams: 'All required parameters are not present.',
|
|
333
342
|
timeoutScan: 'Timeout set to 5 minutes.',
|
|
334
343
|
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',
|
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[]
|
|
@@ -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,7 +7,7 @@ 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
|
|
@@ -27,26 +27,28 @@ const scaTreeUpload = async (analysis, config) => {
|
|
|
27
27
|
.catch(err => {
|
|
28
28
|
throw err
|
|
29
29
|
})
|
|
30
|
+
console.log(' polling report')
|
|
30
31
|
|
|
31
32
|
let keepChecking = true
|
|
33
|
+
let res
|
|
32
34
|
while (keepChecking) {
|
|
33
|
-
|
|
34
|
-
.
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
res = await client.scaServiceReportStatus(config, reportID).then(res => {
|
|
36
|
+
console.log(res.statusCode)
|
|
37
|
+
console.log(res.body)
|
|
38
|
+
if (res.body.status == 'COMPLETED') {
|
|
39
|
+
keepChecking = false
|
|
40
|
+
return client.scaServiceReport(config, reportID).then(res => {
|
|
41
|
+
return res.body
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
})
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
})
|
|
46
|
+
if (!keepChecking) {
|
|
47
|
+
return res
|
|
48
|
+
}
|
|
48
49
|
await requestUtils.sleep(5000)
|
|
49
50
|
}
|
|
51
|
+
return res
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
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) {
|