@contrast/contrast 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +1 -0
- package/README.md +20 -14
- package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +2 -12
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +62 -234
- package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
- package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
- package/dist/commands/audit/auditController.js +6 -3
- package/dist/commands/scan/processScan.js +4 -3
- package/dist/common/HTTPClient.js +19 -26
- package/dist/common/versionChecker.js +14 -12
- package/dist/constants/constants.js +1 -1
- package/dist/constants/lambda.js +3 -1
- package/dist/constants/locales.js +17 -10
- package/dist/constants.js +5 -1
- package/dist/index.js +2 -2
- package/dist/lambda/help.js +22 -14
- package/dist/lambda/lambda.js +6 -0
- package/dist/scan/models/groupedResultsModel.js +10 -0
- package/dist/scan/models/resultContentModel.js +2 -0
- package/dist/scan/models/scanResultsModel.js +11 -0
- package/dist/scan/scan.js +90 -95
- package/dist/scan/scanConfig.js +1 -1
- package/dist/utils/getConfig.js +3 -0
- package/package.json +2 -2
- package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +2 -16
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +127 -0
- package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
- package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
- package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
- package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
- package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
- package/src/commands/audit/auditController.ts +12 -3
- package/src/commands/scan/processScan.js +4 -6
- package/src/common/HTTPClient.js +31 -38
- package/src/common/errorHandling.ts +0 -1
- package/src/common/versionChecker.ts +24 -22
- package/src/constants/constants.js +1 -1
- package/src/constants/lambda.js +3 -1
- package/src/constants/locales.js +20 -10
- package/src/constants.js +7 -1
- package/src/index.ts +2 -3
- package/src/lambda/help.ts +22 -14
- package/src/lambda/lambda.ts +8 -0
- package/src/scan/models/groupedResultsModel.ts +18 -0
- package/src/scan/models/resultContentModel.ts +86 -0
- package/src/scan/models/scanResultsModel.ts +52 -0
- package/src/scan/scan.ts +192 -0
- package/src/scan/scanConfig.js +1 -1
- package/src/scan/scanController.js +2 -0
- package/src/utils/getConfig.ts +10 -0
- package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
- package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
- package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
- package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
- package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
- package/src/scan/scan.js +0 -195
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReportCVEModel,
|
|
3
|
+
ReportLibraryModel
|
|
4
|
+
} from '../models/reportLibraryModel'
|
|
5
|
+
import { ReportSeverityModel } from '../models/reportSeverityModel'
|
|
6
|
+
import languageAnalysisEngine from '../../../languageAnalysisEngine/constants'
|
|
7
|
+
const {
|
|
8
|
+
supportedLanguages: { GO }
|
|
9
|
+
} = languageAnalysisEngine
|
|
10
|
+
|
|
11
|
+
export function findHighestSeverityCVE(cveArray: ReportCVEModel[]) {
|
|
12
|
+
if (
|
|
13
|
+
cveArray.find(
|
|
14
|
+
cve =>
|
|
15
|
+
cve.cvss3SeverityCode === 'CRITICAL' || cve.severityCode === 'CRITICAL'
|
|
16
|
+
)
|
|
17
|
+
) {
|
|
18
|
+
return new ReportSeverityModel('CRITICAL', 1)
|
|
19
|
+
} else if (
|
|
20
|
+
cveArray.find(
|
|
21
|
+
cve => cve.cvss3SeverityCode === 'HIGH' || cve.severityCode === 'HIGH'
|
|
22
|
+
)
|
|
23
|
+
) {
|
|
24
|
+
return new ReportSeverityModel('HIGH', 2)
|
|
25
|
+
} else if (
|
|
26
|
+
cveArray.find(
|
|
27
|
+
cve => cve.cvss3SeverityCode === 'MEDIUM' || cve.severityCode === 'MEDIUM'
|
|
28
|
+
)
|
|
29
|
+
) {
|
|
30
|
+
return new ReportSeverityModel('MEDIUM', 3)
|
|
31
|
+
} else if (
|
|
32
|
+
cveArray.find(
|
|
33
|
+
cve => cve.cvss3SeverityCode === 'LOW' || cve.severityCode === 'LOW'
|
|
34
|
+
)
|
|
35
|
+
) {
|
|
36
|
+
return new ReportSeverityModel('LOW', 4)
|
|
37
|
+
} else if (
|
|
38
|
+
cveArray.find(
|
|
39
|
+
cve => cve.cvss3SeverityCode === 'NOTE' || cve.severityCode === 'NOTE'
|
|
40
|
+
)
|
|
41
|
+
) {
|
|
42
|
+
return new ReportSeverityModel('NOTE', 5)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function convertGenericToTypedLibraries(libraries: any) {
|
|
47
|
+
return Object.entries(libraries).map(([name, cveArray]) => {
|
|
48
|
+
return new ReportLibraryModel(name, cveArray as ReportCVEModel[])
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function severityCount(vulnerableLibraries: ReportLibraryModel[]) {
|
|
53
|
+
const severityCount = {
|
|
54
|
+
critical: 0,
|
|
55
|
+
high: 0,
|
|
56
|
+
medium: 0,
|
|
57
|
+
low: 0,
|
|
58
|
+
note: 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
vulnerableLibraries.forEach(lib => {
|
|
62
|
+
lib.cveArray.forEach(cve => {
|
|
63
|
+
if (
|
|
64
|
+
cve.cvss3SeverityCode === 'CRITICAL' ||
|
|
65
|
+
cve.severityCode === 'CRITICAL'
|
|
66
|
+
) {
|
|
67
|
+
severityCount['critical'] += 1
|
|
68
|
+
} else if (
|
|
69
|
+
cve.cvss3SeverityCode === 'HIGH' ||
|
|
70
|
+
cve.severityCode === 'HIGH'
|
|
71
|
+
) {
|
|
72
|
+
severityCount['high'] += 1
|
|
73
|
+
} else if (
|
|
74
|
+
cve.cvss3SeverityCode === 'MEDIUM' ||
|
|
75
|
+
cve.severityCode === 'MEDIUM'
|
|
76
|
+
) {
|
|
77
|
+
severityCount['medium'] += 1
|
|
78
|
+
} else if (
|
|
79
|
+
cve.cvss3SeverityCode === 'LOW' ||
|
|
80
|
+
cve.severityCode === 'LOW'
|
|
81
|
+
) {
|
|
82
|
+
severityCount['low'] += 1
|
|
83
|
+
} else if (
|
|
84
|
+
cve.cvss3SeverityCode === 'NOTE' ||
|
|
85
|
+
cve.severityCode === 'NOTE'
|
|
86
|
+
) {
|
|
87
|
+
severityCount['note'] += 1
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
return severityCount
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function findNameAndVersion(library: ReportLibraryModel, config: any) {
|
|
96
|
+
if (config.language.toUpperCase() === GO) {
|
|
97
|
+
const nameVersion = library.name.split('@')
|
|
98
|
+
const name = nameVersion[0]
|
|
99
|
+
const version = nameVersion[1]
|
|
100
|
+
|
|
101
|
+
return { name, version }
|
|
102
|
+
} else {
|
|
103
|
+
const splitLibraryName = library.name.split('/')
|
|
104
|
+
const nameVersion = splitLibraryName[1].split('@')
|
|
105
|
+
const name = nameVersion[0]
|
|
106
|
+
const version = nameVersion[1]
|
|
107
|
+
|
|
108
|
+
return { name, version }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -34,7 +34,9 @@ const newSendSnapShot = async (analysis, applicationId) => {
|
|
|
34
34
|
// console.log(prettyjson.render(requestBody))
|
|
35
35
|
// }
|
|
36
36
|
if (res.statusCode === 201) {
|
|
37
|
-
|
|
37
|
+
if (analysis.config.host !== 'https://ce.contrastsecurity.com/') {
|
|
38
|
+
displaySnapshotSuccessMessage(analysis.config)
|
|
39
|
+
}
|
|
38
40
|
return res.body
|
|
39
41
|
} else {
|
|
40
42
|
handleResponseErrors(res, 'snapshot')
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { catalogueApplication } from '../../audit/catalogueApplication/catalogueApplication'
|
|
2
2
|
import commonApi from '../../audit/languageAnalysisEngine/commonApi'
|
|
3
|
+
|
|
3
4
|
const identifyLanguageAE = require('./../../audit/languageAnalysisEngine')
|
|
4
|
-
const languageFactory = require('
|
|
5
|
+
const languageFactory = require('../../audit/languageAnalysisEngine/languageAnalysisFactory')
|
|
5
6
|
|
|
6
7
|
const dealWithNoAppId = async (config: { [x: string]: string }) => {
|
|
7
8
|
let appID
|
|
@@ -10,10 +11,18 @@ const dealWithNoAppId = async (config: { [x: string]: string }) => {
|
|
|
10
11
|
if (!appID && config.applicationName) {
|
|
11
12
|
return await catalogueApplication(config)
|
|
12
13
|
}
|
|
14
|
+
// @ts-ignore
|
|
13
15
|
} catch (e) {
|
|
14
|
-
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
if (e.toString().includes('tunneling socket could not be established')) {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
console.log(e.message)
|
|
20
|
+
console.log(
|
|
21
|
+
'There seems to be an issue with your proxy, please check and try again'
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
process.exit(1)
|
|
15
25
|
}
|
|
16
|
-
console.log(appID)
|
|
17
26
|
return appID
|
|
18
27
|
}
|
|
19
28
|
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
const { startScan } = require('../../scan/scanController')
|
|
2
|
-
const { formatScanOutput } = require('../../scan/scan')
|
|
3
2
|
const { scanUsageGuide } = require('../../scan/help')
|
|
4
3
|
const scanConfig = require('../../scan/scanConfig')
|
|
5
4
|
const { saveScanFile } = require('../../utils/saveFile')
|
|
5
|
+
const { ScanResultsModel } = require('../../scan/models/scanResultsModel')
|
|
6
|
+
const { formatScanOutput } = require('../../scan/scan')
|
|
6
7
|
|
|
7
8
|
const processScan = async argvMain => {
|
|
8
9
|
let config = scanConfig.getScanConfig(argvMain)
|
|
9
10
|
|
|
10
|
-
let scanResults = await startScan(config)
|
|
11
|
+
let scanResults = new ScanResultsModel(await startScan(config))
|
|
11
12
|
if (scanResults) {
|
|
12
|
-
formatScanOutput(
|
|
13
|
-
scanResults?.projectOverview,
|
|
14
|
-
scanResults?.scanResultsInstances
|
|
15
|
-
)
|
|
13
|
+
formatScanOutput(scanResults)
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
if (config.save !== undefined) {
|
package/src/common/HTTPClient.js
CHANGED
|
@@ -106,7 +106,9 @@ HTTPClient.prototype.getScanId = function getScanId(config, codeArtifactId) {
|
|
|
106
106
|
options.url = url
|
|
107
107
|
options.body = {
|
|
108
108
|
codeArtifactId: codeArtifactId,
|
|
109
|
-
label:
|
|
109
|
+
label: config.label
|
|
110
|
+
? config.label
|
|
111
|
+
: `Started by CLI tool at ${new Date().toString()}`
|
|
110
112
|
}
|
|
111
113
|
return requestUtils.sendRequest({ method: 'post', options })
|
|
112
114
|
}
|
|
@@ -188,6 +190,9 @@ HTTPClient.prototype.catalogueCommand = function catalogueCommand(config) {
|
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
|
|
193
|
+
if (config.language.toUpperCase() === 'RUBY') {
|
|
194
|
+
console.log('sendSnapshot requestBody', requestBody.snapshot.ruby)
|
|
195
|
+
}
|
|
191
196
|
const options = _.cloneDeep(this.requestOptions)
|
|
192
197
|
let url = createSnapshotURL(config)
|
|
193
198
|
options.url = url
|
|
@@ -195,32 +200,22 @@ HTTPClient.prototype.sendSnapshot = function sendSnapshot(requestBody, config) {
|
|
|
195
200
|
return requestUtils.sendRequest({ method: 'post', options })
|
|
196
201
|
}
|
|
197
202
|
|
|
198
|
-
HTTPClient.prototype.
|
|
199
|
-
const options = _.cloneDeep(this.requestOptions)
|
|
200
|
-
let url = createReportUrl(config)
|
|
201
|
-
options.url = url
|
|
202
|
-
|
|
203
|
-
return requestUtils.sendRequest({ method: 'get', options })
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
HTTPClient.prototype.getSpecificReport = function getSpecificReport(
|
|
207
|
-
config,
|
|
208
|
-
reportId
|
|
209
|
-
) {
|
|
203
|
+
HTTPClient.prototype.getReportById = function getReportById(config, reportId) {
|
|
210
204
|
const options = _.cloneDeep(this.requestOptions)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
205
|
+
if (config.ignoreDev) {
|
|
206
|
+
options.url = createSpecificReportWithProdUrl(config, reportId)
|
|
207
|
+
} else {
|
|
208
|
+
options.url = createSpecificReportUrl(config, reportId)
|
|
209
|
+
}
|
|
214
210
|
return requestUtils.sendRequest({ method: 'get', options })
|
|
215
211
|
}
|
|
216
212
|
|
|
217
213
|
HTTPClient.prototype.getLibraryVulnerabilities = function getLibraryVulnerabilities(
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
config,
|
|
215
|
+
requestBody
|
|
220
216
|
) {
|
|
221
217
|
const options = _.cloneDeep(this.requestOptions)
|
|
222
|
-
|
|
223
|
-
options.url = url
|
|
218
|
+
options.url = createLibraryVulnerabilitiesUrl(config)
|
|
224
219
|
options.body = requestBody
|
|
225
220
|
|
|
226
221
|
return requestUtils.sendRequest({ method: 'put', options })
|
|
@@ -233,16 +228,16 @@ HTTPClient.prototype.getAppId = function getAppId(config) {
|
|
|
233
228
|
return requestUtils.sendRequest({ method: 'get', options })
|
|
234
229
|
}
|
|
235
230
|
|
|
236
|
-
HTTPClient.prototype.getDependencyTree = function getReport(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
231
|
+
// HTTPClient.prototype.getDependencyTree = function getReport(
|
|
232
|
+
// orgUuid,
|
|
233
|
+
// appId,
|
|
234
|
+
// reportId
|
|
235
|
+
// ) {
|
|
236
|
+
// const options = _.cloneDeep(this.requestOptions)
|
|
237
|
+
// let url = createGetDependencyTree(options.uri, orgUuid, appId, reportId)
|
|
238
|
+
// options.url = url
|
|
239
|
+
// return requestUtils.sendRequest({ method: 'get', options })
|
|
240
|
+
// }
|
|
246
241
|
|
|
247
242
|
// serverless - lambda
|
|
248
243
|
function getServerlessHost(config = {}) {
|
|
@@ -379,22 +374,20 @@ function createLibraryVulnerabilitiesUrl(config) {
|
|
|
379
374
|
return `${config.host}/Contrast/api/ng/${config.organizationId}/libraries/artifactsByGroupNameVersion`
|
|
380
375
|
}
|
|
381
376
|
|
|
382
|
-
function
|
|
383
|
-
return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/reports`
|
|
377
|
+
function createSpecificReportUrl(config, reportId) {
|
|
378
|
+
return `${config.host}/Contrast/api/ng/sca/organizations/${config.organizationId}/applications/${config.applicationId}/reports/${reportId}`
|
|
384
379
|
}
|
|
385
380
|
|
|
386
|
-
function
|
|
387
|
-
return
|
|
381
|
+
function createSpecificReportWithProdUrl(config, reportId) {
|
|
382
|
+
return createSpecificReportUrl(config, reportId).concat(
|
|
383
|
+
`?nodesToInclude=PROD`
|
|
384
|
+
)
|
|
388
385
|
}
|
|
389
386
|
|
|
390
387
|
function createDataUrl() {
|
|
391
388
|
return `https://ardy.contrastsecurity.com/production`
|
|
392
389
|
}
|
|
393
390
|
|
|
394
|
-
const createGetDependencyTree = (protocol, orgUuid, appId, reportId) => {
|
|
395
|
-
return `${protocol}/Contrast/api/ng/sca/organizations/${orgUuid}/applications/${appId}/reports/${reportId}`
|
|
396
|
-
}
|
|
397
|
-
|
|
398
391
|
function createSbomCycloneDXUrl(config) {
|
|
399
392
|
return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/cyclonedx`
|
|
400
393
|
}
|
|
@@ -4,33 +4,35 @@ import boxen from 'boxen'
|
|
|
4
4
|
import chalk from 'chalk'
|
|
5
5
|
import semver from 'semver'
|
|
6
6
|
|
|
7
|
-
export async function findLatestCLIVersion() {
|
|
8
|
-
|
|
7
|
+
export async function findLatestCLIVersion(updateMessageHidden: boolean) {
|
|
8
|
+
if (!updateMessageHidden) {
|
|
9
|
+
const latestCLIVersion = await latestVersion('@contrast/contrast')
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
if (semver.lt(APP_VERSION, latestCLIVersion)) {
|
|
12
|
+
const updateAvailableMessage = `Update available ${chalk.yellow(
|
|
13
|
+
APP_VERSION
|
|
14
|
+
)} → ${chalk.green(latestCLIVersion)}`
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const npmUpdateAvailableCommand = `Run ${chalk.cyan(
|
|
17
|
+
'npm i @contrast/contrast -g'
|
|
18
|
+
)} to update via npm`
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const homebrewUpdateAvailableCommand = `Run ${chalk.cyan(
|
|
21
|
+
'brew install contrastsecurity/tap/contrast'
|
|
22
|
+
)} to update via brew`
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
console.log(
|
|
25
|
+
boxen(
|
|
26
|
+
`${updateAvailableMessage}\n${npmUpdateAvailableCommand}\n\n${homebrewUpdateAvailableCommand}`,
|
|
27
|
+
{
|
|
28
|
+
titleAlignment: 'center',
|
|
29
|
+
margin: 1,
|
|
30
|
+
padding: 1,
|
|
31
|
+
align: 'center'
|
|
32
|
+
}
|
|
33
|
+
)
|
|
32
34
|
)
|
|
33
|
-
|
|
35
|
+
}
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -13,7 +13,7 @@ const MEDIUM = 'MEDIUM'
|
|
|
13
13
|
const HIGH = 'HIGH'
|
|
14
14
|
const CRITICAL = 'CRITICAL'
|
|
15
15
|
const APP_NAME = 'contrast'
|
|
16
|
-
const APP_VERSION = '1.0.
|
|
16
|
+
const APP_VERSION = '1.0.4'
|
|
17
17
|
const TIMEOUT = 120000
|
|
18
18
|
|
|
19
19
|
const AUTH_UI_URL = 'https://cli-auth.contrastsecurity.com'
|
package/src/constants/lambda.js
CHANGED
|
@@ -36,11 +36,13 @@ const lambda = {
|
|
|
36
36
|
loadingFunctionList: 'Loading lambda function list',
|
|
37
37
|
functionsFound: '{{count}} functions found',
|
|
38
38
|
noFunctionsFound: 'No functions found',
|
|
39
|
-
failedToLoadFunctions: '
|
|
39
|
+
failedToLoadFunctions: 'Failed to load lambda functions',
|
|
40
40
|
availableForScan: '{{icon}} {{count}} available for scan',
|
|
41
41
|
runtimeCount: '----- {{runtime}} ({{count}}) -----',
|
|
42
42
|
|
|
43
43
|
// ====== print vulnerabilities ===== //
|
|
44
|
+
gatherResults: 'Gathering results...',
|
|
45
|
+
doneGatherResults: 'Done gathering results',
|
|
44
46
|
whatHappenedTitle: 'What happened:',
|
|
45
47
|
whatHappenedItem: '{{policy}} have:\n{{comments}}\n',
|
|
46
48
|
recommendation: 'Recommendation:',
|
package/src/constants/locales.js
CHANGED
|
@@ -14,8 +14,6 @@ const en_locales = () => {
|
|
|
14
14
|
vulnerabilitiesSuccessMessage: ' Vulnerability data successfully retrieved',
|
|
15
15
|
vulnerabilitiesFailureMessage:
|
|
16
16
|
' Unable to retrieve library vulnerabilities from Team Server.',
|
|
17
|
-
reportSuccessMessage: ' Report successfully retrieved',
|
|
18
|
-
reportFailureMessage: ' Unable to generate library report.',
|
|
19
17
|
catchErrorMessage: 'Contrast UI error: ',
|
|
20
18
|
dependenciesNote:
|
|
21
19
|
'Please Note: We currently only support projects with one .csproj AND *.package.lock.json',
|
|
@@ -175,7 +173,7 @@ const en_locales = () => {
|
|
|
175
173
|
constantsHeader: 'CodeSec by Contrast Security',
|
|
176
174
|
constantsPrerequisitesContentScanLanguages: 'Java & JavaScript supported',
|
|
177
175
|
constantsContrastContent:
|
|
178
|
-
'Use the Contrast CLI to run a scan(Java, JavaScript and .NET ) or lambda command (Java and Python) to find your vulnerabilities and start securing your code.',
|
|
176
|
+
'Use the Contrast CLI to run a scan (Java, JavaScript and .NET ) or lambda command (Java and Python) to find your vulnerabilities and start securing your code.',
|
|
179
177
|
constantsUsageGuideContentRecommendation:
|
|
180
178
|
'Our recommendation is that this is invoked as part of a CI pipeline so that running the cli is automated as part of your build process.',
|
|
181
179
|
constantsPrerequisitesHeader: 'Pre-requisites',
|
|
@@ -274,7 +272,9 @@ const en_locales = () => {
|
|
|
274
272
|
'Add the application code this application should use in the Contrast UI',
|
|
275
273
|
constantsIgnoreCertErrors:
|
|
276
274
|
' For EOP users with a local Teamserver install, this will bypass the SSL certificate and recognise a self signed certificate.',
|
|
277
|
-
constantsSave: ' Saves the Scan Results
|
|
275
|
+
constantsSave: ' Saves the Scan Results SARIF to file.',
|
|
276
|
+
scanLabel:
|
|
277
|
+
"adds a label to the scan - defaults to 'Started by CLI tool at current date'",
|
|
278
278
|
constantsIgnoreDev:
|
|
279
279
|
'Combined with the --report command excludes developer dependencies from the vulnerabilities report. By default all dependencies are included in a report.',
|
|
280
280
|
constantsCommands: 'Commands',
|
|
@@ -284,7 +284,7 @@ const en_locales = () => {
|
|
|
284
284
|
ignoreDevDep: 'No private libraries that are not scoped detected',
|
|
285
285
|
foundExistingProjectScan: 'Found existing project...',
|
|
286
286
|
projectCreatedScan: 'Project created',
|
|
287
|
-
uploadingScan: 'Uploading
|
|
287
|
+
uploadingScan: 'Uploading file to scan.',
|
|
288
288
|
uploadingScanSuccessful: 'Uploaded file successfully.',
|
|
289
289
|
uploadingScanFail: 'Unable to upload the file.',
|
|
290
290
|
waitingTimedOut: 'Timed out.',
|
|
@@ -296,6 +296,7 @@ const en_locales = () => {
|
|
|
296
296
|
'Java Scan requires a .war or .jar file. Javascript Scan requires a .js or .zip file.\nTo start a Scan enter "contrast scan -f <path-to-file>"',
|
|
297
297
|
populateProjectIdMessage: 'project ID is %s',
|
|
298
298
|
genericServiceError: 'returned with status code %s',
|
|
299
|
+
projectIdError: 'Your project ID is %s please check this is correct',
|
|
299
300
|
permissionsError:
|
|
300
301
|
'You do not have the correct permissions here. \n Contact support@contrastsecurity.com to get this fixed.',
|
|
301
302
|
scanErrorFileMessage:
|
|
@@ -343,14 +344,19 @@ const en_locales = () => {
|
|
|
343
344
|
searchingScanFileDirectory: 'Searching for file to scan from %s...',
|
|
344
345
|
scanHeader: 'Contrast Scan CLI',
|
|
345
346
|
authHeader: 'Auth',
|
|
346
|
-
lambdaHeader: 'Contrast
|
|
347
|
+
lambdaHeader: 'Contrast Lambda CLI',
|
|
347
348
|
lambdaSummary:
|
|
348
349
|
'Performs static security scan on an AWS Lambda Function.\nProduces CVE (Vulnerable Dependencies) and Least Privilege violations/remediation results.',
|
|
349
350
|
lambdaUsage: 'contrast lambda --function-name <function> [options]',
|
|
350
|
-
lambdaPrerequisitesContent: '
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
351
|
+
lambdaPrerequisitesContent: '',
|
|
352
|
+
lambdaPrerequisitesContentLambdaLanguages:
|
|
353
|
+
'Supported runtimes: Java & Python',
|
|
354
|
+
lambdaPrerequisitesContentLambdaDescriptionTitle: 'AWS Requirements\n',
|
|
355
|
+
lambdaPrerequisitesContentLambdaDescription:
|
|
356
|
+
'Make sure you have the AWS credentials configured on your local environment. \nYou need the following AWS permissions configured on your IAM user:\n - Lambda: GetFunction, GetLayerVersionֿ\n - IAM: GetRolePolicy, GetPolicy, GetPolicyVersion, ListRolePolicies, ListAttachedRolePolicies',
|
|
357
|
+
scanFileNameOption: '-f, --file',
|
|
358
|
+
lambdaFunctionNameOption: '-f, --function-name',
|
|
359
|
+
lambdaListFunctionsOption: '-l, --list-functions',
|
|
354
360
|
lambdaEndpointOption: '-e, --endpoint-url',
|
|
355
361
|
lambdaRegionOption: '-r, --region',
|
|
356
362
|
lambdaProfileOption: '-p, --profile',
|
|
@@ -422,6 +428,10 @@ const en_locales = () => {
|
|
|
422
428
|
auditBadFiletypeSpecifiedForSave: `\n ${chalk.yellow.bold(
|
|
423
429
|
'Bad file type specified for --save option. Use audit --help to see valid --save options.'
|
|
424
430
|
)}`,
|
|
431
|
+
auditReportWaiting: 'Waiting for report...',
|
|
432
|
+
auditReportFail: 'Report Retrieval Failed, please try again',
|
|
433
|
+
auditReportSuccessMessage: ' Report successfully retrieved',
|
|
434
|
+
auditReportFailureMessage: ' Unable to generate library report.',
|
|
425
435
|
...lambda
|
|
426
436
|
}
|
|
427
437
|
}
|
package/src/constants.js
CHANGED
|
@@ -132,6 +132,11 @@ const scanOptionDefinitions = [
|
|
|
132
132
|
description:
|
|
133
133
|
'{bold ' + i18n.__('constantsOptional') + '}:' + i18n.__('constantsSave')
|
|
134
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: 'label',
|
|
137
|
+
description:
|
|
138
|
+
'{bold ' + i18n.__('constantsOptional') + '}:' + i18n.__('scanLabel')
|
|
139
|
+
},
|
|
135
140
|
{
|
|
136
141
|
name: 'help',
|
|
137
142
|
alias: 'h',
|
|
@@ -331,7 +336,8 @@ const mainUsageGuide = commandLineUsage([
|
|
|
331
336
|
]
|
|
332
337
|
},
|
|
333
338
|
{
|
|
334
|
-
content:
|
|
339
|
+
content:
|
|
340
|
+
'{underline https://developer.contrastsecurity.com/} \n For technical support head to {underline https://support.contrastsecurity.com}'
|
|
335
341
|
}
|
|
336
342
|
])
|
|
337
343
|
|
package/src/index.ts
CHANGED
|
@@ -44,7 +44,7 @@ const start = async () => {
|
|
|
44
44
|
argvMain.includes('--version')
|
|
45
45
|
) {
|
|
46
46
|
console.log(APP_VERSION)
|
|
47
|
-
await findLatestCLIVersion()
|
|
47
|
+
await findLatestCLIVersion(config.get('updateMessageHidden') as boolean)
|
|
48
48
|
return
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -53,8 +53,7 @@ const start = async () => {
|
|
|
53
53
|
|
|
54
54
|
// @ts-ignore
|
|
55
55
|
if (config.get('numOfRuns') >= 5) {
|
|
56
|
-
|
|
57
|
-
await findLatestCLIVersion()
|
|
56
|
+
await findLatestCLIVersion(config.get('updateMessageHidden') as boolean)
|
|
58
57
|
config.set('numOfRuns', 0)
|
|
59
58
|
}
|
|
60
59
|
|
package/src/lambda/help.ts
CHANGED
|
@@ -8,7 +8,15 @@ const lambdaUsageGuide = commandLineUsage([
|
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
header: i18n.__('constantsPrerequisitesHeader'),
|
|
11
|
-
content: [
|
|
11
|
+
content: [
|
|
12
|
+
'{bold ' +
|
|
13
|
+
i18n.__('lambdaPrerequisitesContentLambdaLanguages') +
|
|
14
|
+
'}\n\n' +
|
|
15
|
+
'{bold ' +
|
|
16
|
+
i18n.__('lambdaPrerequisitesContentLambdaDescriptionTitle') +
|
|
17
|
+
'}' +
|
|
18
|
+
i18n.__('lambdaPrerequisitesContentLambdaDescription')
|
|
19
|
+
]
|
|
12
20
|
},
|
|
13
21
|
{
|
|
14
22
|
header: i18n.__('constantsUsage'),
|
|
@@ -18,49 +26,49 @@ const lambdaUsageGuide = commandLineUsage([
|
|
|
18
26
|
header: i18n.__('constantsOptions'),
|
|
19
27
|
content: [
|
|
20
28
|
{
|
|
21
|
-
name: i18n.__('lambdaFunctionNameOption'),
|
|
29
|
+
name: '{bold ' + i18n.__('lambdaFunctionNameOption') + '}',
|
|
22
30
|
summary: i18n.__('lambdaFunctionNameSummery')
|
|
23
31
|
},
|
|
24
32
|
{
|
|
25
|
-
name: i18n.__('lambdaListFunctionsOption'),
|
|
33
|
+
name: '{bold ' + i18n.__('lambdaListFunctionsOption') + '}',
|
|
26
34
|
summary: i18n.__('lambdaListFunctionsSummery')
|
|
27
35
|
},
|
|
28
36
|
{
|
|
29
|
-
name: i18n.__('lambdaEndpointOption'),
|
|
37
|
+
name: '{bold ' + i18n.__('lambdaEndpointOption') + '}',
|
|
30
38
|
summary:
|
|
31
|
-
'{
|
|
39
|
+
'{bold ' +
|
|
32
40
|
i18n.__('constantsOptional') +
|
|
33
41
|
'}: ' +
|
|
34
42
|
i18n.__('lambdaEndpointSummery')
|
|
35
43
|
},
|
|
36
44
|
{
|
|
37
|
-
name: i18n.__('lambdaRegionOption'),
|
|
45
|
+
name: '{bold ' + i18n.__('lambdaRegionOption') + '}',
|
|
38
46
|
summary:
|
|
39
|
-
'{
|
|
47
|
+
'{bold ' +
|
|
40
48
|
i18n.__('constantsOptional') +
|
|
41
49
|
'}: ' +
|
|
42
50
|
i18n.__('lambdaRegionSummery')
|
|
43
51
|
},
|
|
44
52
|
{
|
|
45
|
-
name: i18n.__('lambdaProfileOption'),
|
|
53
|
+
name: '{bold ' + i18n.__('lambdaProfileOption') + '}',
|
|
46
54
|
summary:
|
|
47
|
-
'{
|
|
55
|
+
'{bold ' +
|
|
48
56
|
i18n.__('constantsOptional') +
|
|
49
57
|
'}: ' +
|
|
50
58
|
i18n.__('lambdaProfileSummery')
|
|
51
59
|
},
|
|
52
60
|
{
|
|
53
|
-
name: i18n.__('lambdaJsonOption'),
|
|
61
|
+
name: '{bold ' + i18n.__('lambdaJsonOption') + '}',
|
|
54
62
|
summary:
|
|
55
|
-
'{
|
|
63
|
+
'{bold ' +
|
|
56
64
|
i18n.__('constantsOptional') +
|
|
57
65
|
'}: ' +
|
|
58
66
|
i18n.__('lambdaJsonSummery')
|
|
59
67
|
},
|
|
60
68
|
{
|
|
61
|
-
name: i18n.__('lambdaVerboseOption'),
|
|
69
|
+
name: '{bold ' + i18n.__('lambdaVerboseOption') + '}',
|
|
62
70
|
summary:
|
|
63
|
-
'{
|
|
71
|
+
'{bold ' +
|
|
64
72
|
i18n.__('constantsOptional') +
|
|
65
73
|
'}: ' +
|
|
66
74
|
i18n.__('lambdaVerbosSummery')
|
|
@@ -73,7 +81,7 @@ const lambdaUsageGuide = commandLineUsage([
|
|
|
73
81
|
]
|
|
74
82
|
},
|
|
75
83
|
{
|
|
76
|
-
content: '{underline https://www.contrastsecurity.com}'
|
|
84
|
+
content: '{underline https://www.contrastsecurity.com/developer/codesec}'
|
|
77
85
|
}
|
|
78
86
|
])
|
|
79
87
|
|
package/src/lambda/lambda.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { requestScanFunctionPost } from './scanRequest'
|
|
|
12
12
|
import { getScanResults } from './scanResults'
|
|
13
13
|
import { printResults } from './utils'
|
|
14
14
|
import { getAllLambdas, printAvailableLambdas } from './lambdaUtils'
|
|
15
|
+
import { sleep } from '../utils/requestUtils'
|
|
16
|
+
import ora from '../utils/oraWrapper'
|
|
15
17
|
|
|
16
18
|
type LambdaOptions = {
|
|
17
19
|
functionName?: string
|
|
@@ -123,6 +125,12 @@ const actualProcessLambda = async (lambdaOptions: LambdaOptions) => {
|
|
|
123
125
|
})
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
// Wait to make sure we will have all the results
|
|
129
|
+
const startGetherResultsSpinner = ora.returnOra(i18n.__('gatherResults'))
|
|
130
|
+
ora.startSpinner(startGetherResultsSpinner)
|
|
131
|
+
await sleep(15 * 1000)
|
|
132
|
+
ora.succeedSpinner(startGetherResultsSpinner, 'Done gathering results')
|
|
133
|
+
|
|
126
134
|
const resultsResponse = await getScanResults(
|
|
127
135
|
auth,
|
|
128
136
|
params,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class GroupedResultsModel {
|
|
2
|
+
ruleId: string
|
|
3
|
+
lineInfoSet: Set<string>
|
|
4
|
+
cwe?: string[]
|
|
5
|
+
owasp?: string[]
|
|
6
|
+
reference?: string[]
|
|
7
|
+
recommendation?: string
|
|
8
|
+
severity?: string
|
|
9
|
+
advice?: string
|
|
10
|
+
learn?: string[]
|
|
11
|
+
issue?: string
|
|
12
|
+
message?: string
|
|
13
|
+
|
|
14
|
+
constructor(ruleId: string) {
|
|
15
|
+
this.ruleId = ruleId
|
|
16
|
+
this.lineInfoSet = new Set<string>
|
|
17
|
+
}
|
|
18
|
+
}
|