@contrast/contrast 1.0.2 → 1.0.3
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 +6 -4
- package/dist/audit/languageAnalysisEngine/langugageAnalysisFactory.js +25 -0
- package/dist/commands/audit/saveFile.js +11 -0
- package/dist/commands/auth/auth.js +19 -1
- package/dist/commands/config/config.js +19 -8
- package/dist/commands/scan/processScan.js +4 -22
- package/dist/common/HTTPClient.js +11 -0
- package/dist/common/errorHandling.js +9 -23
- package/dist/common/{findLatestCLIVersion.js → versionChecker.js} +10 -3
- package/dist/constants/constants.js +4 -2
- package/dist/constants/locales.js +17 -7
- package/dist/constants.js +34 -2
- package/dist/index.js +48 -30
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scan/help.js +4 -2
- package/dist/scan/populateProjectIdAndProjectName.js +1 -0
- package/dist/scan/saveResults.js +9 -10
- package/dist/scan/scan.js +27 -2
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +7 -2
- package/dist/scan/scanResults.js +5 -0
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +1 -1
- package/src/audit/languageAnalysisEngine/langugageAnalysisFactory.js +32 -0
- package/src/commands/audit/processAudit.ts +0 -1
- package/src/commands/audit/saveFile.ts +6 -0
- package/src/commands/auth/auth.js +25 -1
- package/src/commands/config/config.js +22 -8
- package/src/commands/scan/processScan.js +4 -23
- package/src/common/HTTPClient.js +13 -0
- package/src/common/errorHandling.ts +11 -24
- package/src/common/versionChecker.ts +39 -0
- package/src/constants/constants.js +5 -4
- package/src/constants/locales.js +23 -8
- package/src/constants.js +37 -2
- package/src/index.ts +63 -36
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scan/help.js +4 -2
- package/src/scan/populateProjectIdAndProjectName.js +1 -0
- package/src/scan/saveResults.js +8 -9
- package/src/scan/scan.js +30 -2
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +9 -2
- package/src/scan/scanResults.js +10 -0
- package/src/utils/getConfig.ts +2 -0
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/src/common/findLatestCLIVersion.ts +0 -27
package/src/constants/locales.js
CHANGED
|
@@ -172,13 +172,19 @@ const en_locales = () => {
|
|
|
172
172
|
constantsSeverity:
|
|
173
173
|
'Combined with the --report command, allows the user to report libraries with vulnerabilities above a chosen severity level. For example, cve_severity medium only reports libraries with vulnerabilities at medium or higher severity. Values for level are high, medium or low.',
|
|
174
174
|
constantsCount: "The number of CVE's that must be exceeded to fail a build",
|
|
175
|
-
constantsHeader: 'Contrast
|
|
175
|
+
constantsHeader: 'CodeSec by Contrast Security',
|
|
176
176
|
constantsPrerequisitesContentScanLanguages: 'Java & JavaScript supported',
|
|
177
177
|
constantsContrastContent:
|
|
178
|
-
'Use the Contrast CLI,
|
|
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.',
|
|
179
179
|
constantsUsageGuideContentRecommendation:
|
|
180
180
|
'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
181
|
constantsPrerequisitesHeader: 'Pre-requisites',
|
|
182
|
+
constantsAuthUsageHeader: 'Usage',
|
|
183
|
+
constantsAuthUsageContents: 'contrast auth',
|
|
184
|
+
constantsAuthHeaderContents:
|
|
185
|
+
'Authorize with external identity provider to perform scans on code',
|
|
186
|
+
configHeader: 'Config',
|
|
187
|
+
constantsConfigUsageContents: 'view / clear the configuration',
|
|
182
188
|
constantsPrerequisitesContent:
|
|
183
189
|
'To scan a Java project you will need a .jar or .war file for analysis\n' +
|
|
184
190
|
'To scan a Javascript project you will need a .js or.zip file for analysis\n' +
|
|
@@ -186,7 +192,7 @@ const en_locales = () => {
|
|
|
186
192
|
constantsUsage: 'Usage',
|
|
187
193
|
constantsUsageCommandExample: 'contrast [command] [options]',
|
|
188
194
|
constantsUsageCommandInfo:
|
|
189
|
-
'The file argument is optional. If no file is given, Contrast will search for a .jar, .war, .
|
|
195
|
+
'The file argument is optional. If no file is given, Contrast will search for a .jar, .war, .exe or .zip file in the working directory.\n',
|
|
190
196
|
constantsUsageCommandInfo24Hours:
|
|
191
197
|
'Submitted files are encrypted during upload and deleted in 24 hours.',
|
|
192
198
|
constantsAnd: 'AND',
|
|
@@ -296,8 +302,7 @@ const en_locales = () => {
|
|
|
296
302
|
'We only accept the following file types: \nJava - .jar, .war \nJavaScript - .js or .zip files',
|
|
297
303
|
helpAuthSummary:
|
|
298
304
|
'Authenticate Contrast using your Github or Google account',
|
|
299
|
-
helpScanSummary:
|
|
300
|
-
'Searches for a .jar, .war, .js or .zip file in the working directory, uploads for analysis and returns the results',
|
|
305
|
+
helpScanSummary: 'Perform static analysis on binaries / code artifacts',
|
|
301
306
|
helpLambdaSummary: 'Perform scan on AWS Lambda functions',
|
|
302
307
|
helpVersionSummary: 'Displays version of Contrast CLI',
|
|
303
308
|
helpConfigSummary: 'Displays stored credentials',
|
|
@@ -308,6 +313,7 @@ const en_locales = () => {
|
|
|
308
313
|
versionName: 'version',
|
|
309
314
|
configName: 'config',
|
|
310
315
|
helpName: 'help',
|
|
316
|
+
scanOptionsLanguageSummary: 'Valid values are JAVA, JAVASCRIPT and DOTNET',
|
|
311
317
|
scanOptionsLanguageSummaryOptional:
|
|
312
318
|
'Language of file to send for analysis. ',
|
|
313
319
|
scanOptionsLanguageSummaryRequired:
|
|
@@ -315,7 +321,7 @@ const en_locales = () => {
|
|
|
315
321
|
scanOptionsTimeoutSummary:
|
|
316
322
|
'Time in seconds to wait for scan to complete. Default value is 300 seconds.',
|
|
317
323
|
scanOptionsFileNameSummary:
|
|
318
|
-
'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .
|
|
324
|
+
'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.',
|
|
319
325
|
scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
|
|
320
326
|
authSuccessMessage: 'Authentication successful',
|
|
321
327
|
runAuthSuccessMessage:
|
|
@@ -326,7 +332,7 @@ const en_locales = () => {
|
|
|
326
332
|
zipErrorScan:
|
|
327
333
|
'We only support zip files for JAVASCRIPT language, please set the flag --language JAVASCRIPT',
|
|
328
334
|
unknownFileErrorScan: 'Unsupported file selected for Scan.',
|
|
329
|
-
foundScanFile: '
|
|
335
|
+
foundScanFile: 'Found: %s',
|
|
330
336
|
foundDetailedVulnerabilities:
|
|
331
337
|
chalk.bold('%s Critical') +
|
|
332
338
|
' | ' +
|
|
@@ -336,6 +342,7 @@ const en_locales = () => {
|
|
|
336
342
|
timeoutScan: 'Timeout set to 5 minutes.',
|
|
337
343
|
searchingScanFileDirectory: 'Searching for file to scan from %s...',
|
|
338
344
|
scanHeader: 'Contrast Scan CLI',
|
|
345
|
+
authHeader: 'Auth',
|
|
339
346
|
lambdaHeader: 'Contrast lambda help',
|
|
340
347
|
lambdaSummary:
|
|
341
348
|
'Performs static security scan on an AWS Lambda Function.\nProduces CVE (Vulnerable Dependencies) and Least Privilege violations/remediation results.',
|
|
@@ -406,7 +413,15 @@ const en_locales = () => {
|
|
|
406
413
|
'saves the output in specified format Txt text, sbom',
|
|
407
414
|
scanNoVulnerabilitiesFound: '👏 No vulnerabilities found',
|
|
408
415
|
scanNoFiletypeSpecifiedForSave:
|
|
409
|
-
'Please specify file type to save results to',
|
|
416
|
+
'Please specify file type to save results to, accepted value is SARIF',
|
|
417
|
+
auditSBOMSaveSuccess:
|
|
418
|
+
'\n Software Bill of Materials (SBOM) saved successfully',
|
|
419
|
+
auditNoFiletypeSpecifiedForSave: `\n ${chalk.yellow.bold(
|
|
420
|
+
'No file type specified for --save option to save audit results to. Use audit --help to see valid --save options.'
|
|
421
|
+
)}`,
|
|
422
|
+
auditBadFiletypeSpecifiedForSave: `\n ${chalk.yellow.bold(
|
|
423
|
+
'Bad file type specified for --save option. Use audit --help to see valid --save options.'
|
|
424
|
+
)}`,
|
|
410
425
|
...lambda
|
|
411
426
|
}
|
|
412
427
|
}
|
package/src/constants.js
CHANGED
|
@@ -20,6 +20,15 @@ const scanOptionDefinitions = [
|
|
|
20
20
|
'}: ' +
|
|
21
21
|
i18n.__('constantsProjectName')
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
name: 'language',
|
|
25
|
+
alias: 'l',
|
|
26
|
+
description:
|
|
27
|
+
'{bold ' +
|
|
28
|
+
i18n.__('constantsOptional') +
|
|
29
|
+
'}: ' +
|
|
30
|
+
i18n.__('scanOptionsLanguageSummary')
|
|
31
|
+
},
|
|
23
32
|
{
|
|
24
33
|
name: 'file',
|
|
25
34
|
alias: 'f',
|
|
@@ -75,7 +84,6 @@ const scanOptionDefinitions = [
|
|
|
75
84
|
},
|
|
76
85
|
{
|
|
77
86
|
name: 'host',
|
|
78
|
-
alias: 'h',
|
|
79
87
|
description:
|
|
80
88
|
'{bold ' +
|
|
81
89
|
i18n.__('constantsRequired') +
|
|
@@ -126,6 +134,7 @@ const scanOptionDefinitions = [
|
|
|
126
134
|
},
|
|
127
135
|
{
|
|
128
136
|
name: 'help',
|
|
137
|
+
alias: 'h',
|
|
129
138
|
type: Boolean
|
|
130
139
|
},
|
|
131
140
|
{
|
|
@@ -135,6 +144,29 @@ const scanOptionDefinitions = [
|
|
|
135
144
|
}
|
|
136
145
|
]
|
|
137
146
|
|
|
147
|
+
const authOptionDefinitions = [
|
|
148
|
+
{
|
|
149
|
+
name: 'help',
|
|
150
|
+
alias: 'h',
|
|
151
|
+
type: Boolean
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
const configOptionDefinitions = [
|
|
156
|
+
{
|
|
157
|
+
name: 'help',
|
|
158
|
+
alias: 'h',
|
|
159
|
+
type: Boolean,
|
|
160
|
+
description: 'Help text'
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'clear',
|
|
164
|
+
alias: 'c',
|
|
165
|
+
type: Boolean,
|
|
166
|
+
description: 'Clear the currently stored config'
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
|
|
138
170
|
const auditOptionDefinitions = [
|
|
139
171
|
{
|
|
140
172
|
name: 'application-id',
|
|
@@ -291,6 +323,7 @@ const mainUsageGuide = commandLineUsage([
|
|
|
291
323
|
header: i18n.__('constantsCommands'),
|
|
292
324
|
content: [
|
|
293
325
|
{ name: i18n.__('authName'), summary: i18n.__('helpAuthSummary') },
|
|
326
|
+
{ name: i18n.__('scanName'), summary: i18n.__('helpScanSummary') },
|
|
294
327
|
{ name: i18n.__('lambdaName'), summary: i18n.__('helpLambdaSummary') },
|
|
295
328
|
{ name: i18n.__('versionName'), summary: i18n.__('helpVersionSummary') },
|
|
296
329
|
{ name: i18n.__('configName'), summary: i18n.__('helpConfigSummary') },
|
|
@@ -309,6 +342,8 @@ module.exports = {
|
|
|
309
342
|
mainUsageGuide,
|
|
310
343
|
mainDefinition,
|
|
311
344
|
scanOptionDefinitions,
|
|
312
|
-
auditOptionDefinitions
|
|
345
|
+
auditOptionDefinitions,
|
|
346
|
+
authOptionDefinitions,
|
|
347
|
+
configOptionDefinitions
|
|
313
348
|
}
|
|
314
349
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,8 +7,11 @@ import constants from './constants'
|
|
|
7
7
|
import { APP_NAME, APP_VERSION } from './constants/constants'
|
|
8
8
|
import { processLambda } from './lambda/lambda'
|
|
9
9
|
import { localConfig } from './utils/getConfig'
|
|
10
|
-
import
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
findLatestCLIVersion,
|
|
12
|
+
isCorrectNodeVersion
|
|
13
|
+
} from './common/versionChecker'
|
|
14
|
+
import { findCommandOnError } from './common/errorHandling'
|
|
12
15
|
|
|
13
16
|
const {
|
|
14
17
|
commandLineDefinitions: { mainUsageGuide, mainDefinition }
|
|
@@ -31,49 +34,73 @@ const getMainOption = () => {
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
const start = async () => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
if (await isCorrectNodeVersion(process.version)) {
|
|
38
|
+
const { mainOptions, argv: argvMain } = getMainOption()
|
|
39
|
+
const command =
|
|
40
|
+
mainOptions.command != undefined ? mainOptions.command.toLowerCase() : ''
|
|
41
|
+
if (
|
|
42
|
+
command === 'version' ||
|
|
43
|
+
argvMain.includes('--v') ||
|
|
44
|
+
argvMain.includes('--version')
|
|
45
|
+
) {
|
|
46
|
+
console.log(APP_VERSION)
|
|
47
|
+
await findLatestCLIVersion()
|
|
48
|
+
return
|
|
49
|
+
}
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
config.set('numOfRuns', config.get('numOfRuns') + 1)
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
if (config.get('numOfRuns') >= 5) {
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
await findLatestCLIVersion()
|
|
58
|
+
config.set('numOfRuns', 0)
|
|
59
|
+
}
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
if (command === 'config') {
|
|
62
|
+
return processConfig(argvMain, config)
|
|
63
|
+
}
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
if (command === 'auth') {
|
|
66
|
+
return await processAuth(argvMain, config)
|
|
67
|
+
}
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
if (command === 'lambda') {
|
|
70
|
+
return await processLambda(argvMain)
|
|
71
|
+
}
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
if (command === 'scan') {
|
|
74
|
+
return await processScan(argvMain)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (command === 'audit') {
|
|
78
|
+
return await processAudit(argvMain)
|
|
79
|
+
}
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
if (
|
|
82
|
+
command === 'help' ||
|
|
83
|
+
argvMain.includes('--help') ||
|
|
84
|
+
Object.keys(mainOptions).length === 0
|
|
85
|
+
) {
|
|
86
|
+
console.log(mainUsageGuide)
|
|
87
|
+
} else if (mainOptions._unknown !== undefined) {
|
|
88
|
+
const foundCommand = findCommandOnError(mainOptions._unknown)
|
|
89
|
+
|
|
90
|
+
foundCommand
|
|
91
|
+
? console.log(
|
|
92
|
+
`Unknown Command: Did you mean "${foundCommand}"? \nUse "${foundCommand} --help" for the full list of options`
|
|
93
|
+
)
|
|
94
|
+
: console.log(
|
|
95
|
+
`Unknown Command: ${command} \nUse --help for the full list`
|
|
96
|
+
)
|
|
97
|
+
} else {
|
|
98
|
+
console.log(`Unknown Command: ${command} \nUse --help for the full list`)
|
|
99
|
+
}
|
|
100
|
+
process.exit(9)
|
|
74
101
|
} else {
|
|
75
102
|
console.log(
|
|
76
|
-
'
|
|
103
|
+
'Contrast supports Node versions >=16.13.2 <17. Please use one of those versions.'
|
|
77
104
|
)
|
|
78
105
|
process.exit(9)
|
|
79
106
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getHttpClient } from '../utils/commonApi'
|
|
2
|
+
|
|
3
|
+
export default function generateSbom(config: any) {
|
|
4
|
+
const client = getHttpClient(config)
|
|
5
|
+
return client
|
|
6
|
+
.getSbom(config)
|
|
7
|
+
.then((res: { statusCode: number; body: any }) => {
|
|
8
|
+
if (res.statusCode === 200) {
|
|
9
|
+
return res.body
|
|
10
|
+
} else {
|
|
11
|
+
console.log('Unable to retrieve Software Bill of Materials (SBOM)')
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
.catch((err: any) => {
|
|
15
|
+
console.log(err)
|
|
16
|
+
})
|
|
17
|
+
}
|
package/src/scan/help.js
CHANGED
|
@@ -21,14 +21,16 @@ const scanUsageGuide = commandLineUsage([
|
|
|
21
21
|
optionList: constants.commandLineDefinitions.scanOptionDefinitions,
|
|
22
22
|
hide: [
|
|
23
23
|
'project-id',
|
|
24
|
-
'language',
|
|
25
24
|
'organization-id',
|
|
26
25
|
'api-key',
|
|
27
26
|
'authorization',
|
|
28
27
|
'host',
|
|
29
28
|
'proxy',
|
|
29
|
+
'help',
|
|
30
30
|
'ff',
|
|
31
|
-
'ignore-cert-errors'
|
|
31
|
+
'ignore-cert-errors',
|
|
32
|
+
'verbose',
|
|
33
|
+
'debug'
|
|
32
34
|
]
|
|
33
35
|
},
|
|
34
36
|
{
|
package/src/scan/saveResults.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
|
|
3
|
-
const writeResultsToFile = (responseBody, name = 'results.sarif') => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
})
|
|
3
|
+
const writeResultsToFile = async (responseBody, name = 'results.sarif') => {
|
|
4
|
+
try {
|
|
5
|
+
fs.writeFileSync(name, JSON.stringify(responseBody, null, 2))
|
|
6
|
+
console.log(`Scan Results saved to ${name}`)
|
|
7
|
+
} catch (err) {
|
|
8
|
+
console.log('Error writing Scan Results to file')
|
|
9
|
+
}
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
module.exports = {
|
|
14
|
-
writeResultsToFile
|
|
13
|
+
writeResultsToFile: writeResultsToFile
|
|
15
14
|
}
|
package/src/scan/scan.js
CHANGED
|
@@ -58,6 +58,7 @@ const sendScan = async config => {
|
|
|
58
58
|
)
|
|
59
59
|
if (res.statusCode === 403) {
|
|
60
60
|
console.log(i18n.__('permissionsError'))
|
|
61
|
+
process.exit(1)
|
|
61
62
|
}
|
|
62
63
|
console.log(i18n.__('genericServiceError', res.statusCode))
|
|
63
64
|
process.exit(1)
|
|
@@ -94,6 +95,16 @@ const formatScanOutput = (overview, results) => {
|
|
|
94
95
|
console.log(`\t ${count}. ${lineInfo}`)
|
|
95
96
|
count++
|
|
96
97
|
})
|
|
98
|
+
|
|
99
|
+
if (entry?.cwe && entry?.cwe.length > 0) {
|
|
100
|
+
formatLinks('cwe', entry.cwe)
|
|
101
|
+
}
|
|
102
|
+
if (entry?.reference && entry?.reference.length > 0) {
|
|
103
|
+
formatLinks('reference', entry.reference)
|
|
104
|
+
}
|
|
105
|
+
if (entry?.owasp && entry?.owasp.length > 0) {
|
|
106
|
+
formatLinks('owasp', entry.owasp)
|
|
107
|
+
}
|
|
97
108
|
console.log(chalk.bold('How to fix:'))
|
|
98
109
|
console.log(entry.recommendation)
|
|
99
110
|
console.log()
|
|
@@ -106,7 +117,9 @@ const formatScanOutput = (overview, results) => {
|
|
|
106
117
|
overview.low +
|
|
107
118
|
overview.note
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
let vulMessage =
|
|
121
|
+
totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
|
|
122
|
+
console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
|
|
110
123
|
console.log(
|
|
111
124
|
i18n.__(
|
|
112
125
|
'foundDetailedVulnerabilities',
|
|
@@ -120,6 +133,14 @@ const formatScanOutput = (overview, results) => {
|
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
135
|
|
|
136
|
+
const formatLinks = (objName, entry) => {
|
|
137
|
+
console.log(chalk.bold(objName + ':'))
|
|
138
|
+
entry.forEach(link => {
|
|
139
|
+
console.log(link)
|
|
140
|
+
})
|
|
141
|
+
console.log()
|
|
142
|
+
}
|
|
143
|
+
|
|
123
144
|
const getGroups = content => {
|
|
124
145
|
const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId))
|
|
125
146
|
let groupTypeResults = []
|
|
@@ -127,12 +148,18 @@ const getGroups = content => {
|
|
|
127
148
|
let groupResultsObj = {
|
|
128
149
|
ruleId: groupName,
|
|
129
150
|
lineInfoSet: new Set(),
|
|
151
|
+
cwe: '',
|
|
152
|
+
owasp: '',
|
|
153
|
+
reference: '',
|
|
130
154
|
recommendation: '',
|
|
131
155
|
severity: ''
|
|
132
156
|
}
|
|
133
157
|
content.forEach(resultEntry => {
|
|
134
158
|
if (resultEntry.ruleId === groupName) {
|
|
135
159
|
groupResultsObj.severity = resultEntry.severity
|
|
160
|
+
groupResultsObj.cwe = resultEntry.cwe
|
|
161
|
+
groupResultsObj.owasp = resultEntry.owasp
|
|
162
|
+
groupResultsObj.reference = resultEntry.reference
|
|
136
163
|
groupResultsObj.recommendation = resultEntry.recommendation
|
|
137
164
|
? stripMustacheTags(resultEntry.recommendation)
|
|
138
165
|
: 'No Recommendations Data Found'
|
|
@@ -163,5 +190,6 @@ module.exports = {
|
|
|
163
190
|
allowedFileTypes: allowedFileTypes,
|
|
164
191
|
isFileAllowed: isFileAllowed,
|
|
165
192
|
stripMustacheTags: stripMustacheTags,
|
|
166
|
-
formatScanOutput: formatScanOutput
|
|
193
|
+
formatScanOutput: formatScanOutput,
|
|
194
|
+
formatLinks: formatLinks
|
|
167
195
|
}
|
package/src/scan/scanConfig.js
CHANGED
|
@@ -2,14 +2,34 @@ const paramHandler = require('../utils/paramsUtil/paramHandler')
|
|
|
2
2
|
const constants = require('../../src/constants.js')
|
|
3
3
|
const parsedCLIOptions = require('../../src/utils/parsedCLIOptions')
|
|
4
4
|
const path = require('path')
|
|
5
|
+
const {
|
|
6
|
+
supportedLanguages
|
|
7
|
+
} = require('../audit/languageAnalysisEngine/constants')
|
|
8
|
+
const i18n = require('i18n')
|
|
9
|
+
const { scanUsageGuide } = require('./help')
|
|
5
10
|
|
|
6
11
|
const getScanConfig = argv => {
|
|
7
12
|
let scanParams = parsedCLIOptions.getCommandLineArgsCustom(
|
|
8
13
|
argv,
|
|
9
14
|
constants.commandLineDefinitions.scanOptionDefinitions
|
|
10
15
|
)
|
|
16
|
+
|
|
17
|
+
if (scanParams.help) {
|
|
18
|
+
printHelpMessage()
|
|
19
|
+
process.exit(0)
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
const paramsAuth = paramHandler.getAuth(scanParams)
|
|
12
23
|
|
|
24
|
+
if (scanParams.language) {
|
|
25
|
+
scanParams.language = scanParams.language.toUpperCase()
|
|
26
|
+
if (!Object.values(supportedLanguages).includes(scanParams.language)) {
|
|
27
|
+
console.log(`Did not recognise --language ${scanParams.language}`)
|
|
28
|
+
console.log(i18n.__('constantsHowToRunDev3'))
|
|
29
|
+
process.exit(0)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
13
33
|
// if no name, take the full file path and use it as the project name
|
|
14
34
|
if (!scanParams.name && scanParams.file) {
|
|
15
35
|
scanParams.name = getFileName(scanParams.file)
|
|
@@ -23,7 +43,12 @@ const getFileName = file => {
|
|
|
23
43
|
return file.split(path.sep).pop()
|
|
24
44
|
}
|
|
25
45
|
|
|
46
|
+
const printHelpMessage = () => {
|
|
47
|
+
console.log(scanUsageGuide)
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
module.exports = {
|
|
27
51
|
getScanConfig,
|
|
28
|
-
getFileName
|
|
52
|
+
getFileName,
|
|
53
|
+
printHelpMessage
|
|
29
54
|
}
|
|
@@ -9,6 +9,7 @@ const scan = require('./scan')
|
|
|
9
9
|
const scanResults = require('./scanResults')
|
|
10
10
|
const autoDetection = require('./autoDetection')
|
|
11
11
|
const fileFunctions = require('./fileUtils')
|
|
12
|
+
const { performance } = require('perf_hooks')
|
|
12
13
|
|
|
13
14
|
const getTimeout = config => {
|
|
14
15
|
if (config.timeout) {
|
|
@@ -25,7 +26,7 @@ const fileAndLanguageLogic = async configToUse => {
|
|
|
25
26
|
if (configToUse.file) {
|
|
26
27
|
if (!fileFunctions.fileExists(configToUse.file)) {
|
|
27
28
|
console.log(i18n.__('fileNotExist'))
|
|
28
|
-
process.exit(
|
|
29
|
+
process.exit(1)
|
|
29
30
|
}
|
|
30
31
|
return configToUse
|
|
31
32
|
} else {
|
|
@@ -36,6 +37,7 @@ const fileAndLanguageLogic = async configToUse => {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
const startScan = async configToUse => {
|
|
40
|
+
const startTime = performance.now()
|
|
39
41
|
await fileAndLanguageLogic(configToUse)
|
|
40
42
|
|
|
41
43
|
if (!configToUse.projectId) {
|
|
@@ -46,7 +48,7 @@ const startScan = async configToUse => {
|
|
|
46
48
|
const codeArtifactId = await scan.sendScan(configToUse)
|
|
47
49
|
|
|
48
50
|
if (!configToUse.ff) {
|
|
49
|
-
const startScanSpinner = returnOra('Contrast Scan started')
|
|
51
|
+
const startScanSpinner = returnOra('🚀 Contrast Scan started')
|
|
50
52
|
startSpinner(startScanSpinner)
|
|
51
53
|
const scanDetail = await scanResults.returnScanResults(
|
|
52
54
|
configToUse,
|
|
@@ -58,7 +60,12 @@ const startScan = async configToUse => {
|
|
|
58
60
|
configToUse,
|
|
59
61
|
scanDetail.id
|
|
60
62
|
)
|
|
63
|
+
const endTime = performance.now()
|
|
64
|
+
const scanDurationMs = endTime - startTime
|
|
61
65
|
succeedSpinner(startScanSpinner, 'Contrast Scan complete')
|
|
66
|
+
console.log(
|
|
67
|
+
`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
68
|
+
)
|
|
62
69
|
const projectOverview = await scanResults.returnScanProjectById(configToUse)
|
|
63
70
|
return { projectOverview, scanDetail, scanResultsInstances }
|
|
64
71
|
}
|
package/src/scan/scanResults.js
CHANGED
|
@@ -2,6 +2,7 @@ const commonApi = require('../utils/commonApi')
|
|
|
2
2
|
const requestUtils = require('../../src/utils/requestUtils')
|
|
3
3
|
const oraFunctions = require('../utils/oraWrapper')
|
|
4
4
|
const _ = require('lodash')
|
|
5
|
+
const i18n = require('i18n')
|
|
5
6
|
|
|
6
7
|
const getScanId = async (config, codeArtifactId, client) => {
|
|
7
8
|
return client
|
|
@@ -48,6 +49,15 @@ const returnScanResults = async (
|
|
|
48
49
|
complete = true
|
|
49
50
|
oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.')
|
|
50
51
|
console.log(result.body.errorMessage)
|
|
52
|
+
if (
|
|
53
|
+
result.body.errorMessage ===
|
|
54
|
+
'Unable to determine language for code artifact'
|
|
55
|
+
) {
|
|
56
|
+
console.log(
|
|
57
|
+
'Try scanning again using --language param. ',
|
|
58
|
+
i18n.__('scanOptionsLanguageSummary')
|
|
59
|
+
)
|
|
60
|
+
}
|
|
51
61
|
process.exit(1)
|
|
52
62
|
}
|
|
53
63
|
}
|
package/src/utils/getConfig.ts
CHANGED
|
@@ -6,6 +6,7 @@ type ContrastConfOptions = Partial<{
|
|
|
6
6
|
apiKey: string
|
|
7
7
|
orgId: string
|
|
8
8
|
authHeader: string
|
|
9
|
+
numOfRuns: number
|
|
9
10
|
}>
|
|
10
11
|
|
|
11
12
|
type ContrastConf = Conf<ContrastConfOptions>
|
|
@@ -15,6 +16,7 @@ const localConfig = (name: string, version: string) => {
|
|
|
15
16
|
configName: name
|
|
16
17
|
})
|
|
17
18
|
config.set('version', version)
|
|
19
|
+
|
|
18
20
|
if (!config.has('host')) {
|
|
19
21
|
config.set('host', 'https://ce.contrastsecurity.com/')
|
|
20
22
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { SARIF_FILE } = require('../constants/constants')
|
|
2
|
+
const commonApi = require('./commonApi')
|
|
3
|
+
const saveResults = require('../scan/saveResults')
|
|
4
|
+
const i18n = require('i18n')
|
|
5
|
+
|
|
6
|
+
const saveScanFile = async (config, scanResults) => {
|
|
7
|
+
if (config.save === null || config.save.toUpperCase() === SARIF_FILE) {
|
|
8
|
+
const scanId = scanResults.scanDetail.id
|
|
9
|
+
const client = commonApi.getHttpClient(config)
|
|
10
|
+
const rawResults = await client.getSpecificScanResultSarif(config, scanId)
|
|
11
|
+
await saveResults.writeResultsToFile(rawResults?.body)
|
|
12
|
+
} else {
|
|
13
|
+
console.log(i18n.__('scanNoFiletypeSpecifiedForSave'))
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
saveScanFile: saveScanFile
|
|
19
|
+
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import latestVersion from 'latest-version'
|
|
2
|
-
import { APP_VERSION } from '../constants/constants'
|
|
3
|
-
import boxen from 'boxen'
|
|
4
|
-
import chalk from 'chalk'
|
|
5
|
-
import semver from 'semver'
|
|
6
|
-
|
|
7
|
-
export default async function findLatestCLIVersion() {
|
|
8
|
-
const latestCLIVersion = await latestVersion('@contrast/contrast')
|
|
9
|
-
|
|
10
|
-
if (semver.lt(APP_VERSION, latestCLIVersion)) {
|
|
11
|
-
const updateAvailableMessage = `Update available ${chalk.yellow(
|
|
12
|
-
APP_VERSION
|
|
13
|
-
)} → ${chalk.green(latestCLIVersion)}`
|
|
14
|
-
|
|
15
|
-
const updateAvailableCommand = `Run ${chalk.cyan(
|
|
16
|
-
'npm i @contrast/contrast'
|
|
17
|
-
)} to update`
|
|
18
|
-
|
|
19
|
-
console.log(
|
|
20
|
-
boxen(`${updateAvailableMessage}\n${updateAvailableCommand}`, {
|
|
21
|
-
margin: 1,
|
|
22
|
-
padding: 1,
|
|
23
|
-
align: 'center'
|
|
24
|
-
})
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
}
|