@contrast/contrast 1.0.2 → 1.0.5

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.
Files changed (113) hide show
  1. package/.prettierignore +4 -0
  2. package/README.md +24 -16
  3. package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
  4. package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
  5. package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +30 -13
  6. package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +25 -0
  7. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +51 -237
  8. package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
  9. package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
  10. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
  11. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
  12. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
  13. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +2 -14
  14. package/dist/commands/audit/auditConfig.js +8 -2
  15. package/dist/commands/audit/auditController.js +14 -5
  16. package/dist/commands/audit/saveFile.js +11 -0
  17. package/dist/commands/auth/auth.js +19 -1
  18. package/dist/commands/config/config.js +19 -8
  19. package/dist/commands/scan/processScan.js +13 -27
  20. package/dist/commands/scan/sca/scaAnalysis.js +44 -0
  21. package/dist/common/HTTPClient.js +29 -26
  22. package/dist/common/errorHandling.js +15 -39
  23. package/dist/common/versionChecker.js +32 -0
  24. package/dist/constants/constants.js +16 -2
  25. package/dist/constants/lambda.js +3 -1
  26. package/dist/constants/locales.js +58 -48
  27. package/dist/constants.js +59 -3
  28. package/dist/index.js +48 -30
  29. package/dist/lambda/help.js +22 -14
  30. package/dist/lambda/lambda.js +6 -0
  31. package/dist/sbom/generateSbom.js +20 -0
  32. package/dist/scaAnalysis/common/formatMessage.js +11 -0
  33. package/dist/scaAnalysis/common/treeUpload.js +30 -0
  34. package/dist/scaAnalysis/java/analysis.js +116 -0
  35. package/dist/scaAnalysis/java/index.js +18 -0
  36. package/dist/scaAnalysis/java/javaBuildDepsParser.js +326 -0
  37. package/dist/scan/autoDetection.js +46 -1
  38. package/dist/scan/fileUtils.js +73 -1
  39. package/dist/scan/formatScanOutput.js +212 -0
  40. package/dist/scan/help.js +6 -2
  41. package/dist/scan/models/groupedResultsModel.js +11 -0
  42. package/dist/scan/models/resultContentModel.js +2 -0
  43. package/dist/scan/models/scanResultsModel.js +11 -0
  44. package/dist/scan/populateProjectIdAndProjectName.js +1 -0
  45. package/dist/scan/saveResults.js +9 -10
  46. package/dist/scan/scan.js +26 -101
  47. package/dist/scan/scanConfig.js +20 -1
  48. package/dist/scan/scanController.js +8 -4
  49. package/dist/scan/scanResults.js +8 -17
  50. package/dist/utils/getConfig.js +3 -0
  51. package/dist/utils/requestUtils.js +1 -1
  52. package/dist/utils/saveFile.js +19 -0
  53. package/package.json +3 -2
  54. package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
  55. package/src/audit/catalogueApplication/catalogueApplication.js +4 -16
  56. package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +41 -19
  57. package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +71 -0
  58. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +105 -0
  59. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
  60. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
  61. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
  62. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
  63. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
  64. package/src/audit/languageAnalysisEngine/sendSnapshot.js +2 -22
  65. package/src/commands/audit/auditConfig.ts +12 -3
  66. package/src/commands/audit/auditController.ts +21 -5
  67. package/src/commands/audit/processAudit.ts +3 -1
  68. package/src/commands/audit/saveFile.ts +6 -0
  69. package/src/commands/auth/auth.js +25 -1
  70. package/src/commands/config/config.js +22 -8
  71. package/src/commands/scan/processScan.js +15 -31
  72. package/src/commands/scan/sca/scaAnalysis.js +73 -0
  73. package/src/common/HTTPClient.js +42 -36
  74. package/src/common/errorHandling.ts +17 -48
  75. package/src/common/versionChecker.ts +41 -0
  76. package/src/constants/constants.js +17 -4
  77. package/src/constants/lambda.js +3 -1
  78. package/src/constants/locales.js +69 -63
  79. package/src/constants.js +66 -3
  80. package/src/index.ts +62 -36
  81. package/src/lambda/help.ts +22 -14
  82. package/src/lambda/lambda.ts +8 -0
  83. package/src/sbom/generateSbom.ts +17 -0
  84. package/src/scaAnalysis/common/formatMessage.js +10 -0
  85. package/src/scaAnalysis/common/treeUpload.js +34 -0
  86. package/src/scaAnalysis/java/analysis.js +159 -0
  87. package/src/scaAnalysis/java/index.js +21 -0
  88. package/src/scaAnalysis/java/javaBuildDepsParser.js +391 -0
  89. package/src/scan/autoDetection.js +54 -1
  90. package/src/scan/fileUtils.js +91 -1
  91. package/src/scan/formatScanOutput.ts +241 -0
  92. package/src/scan/help.js +6 -2
  93. package/src/scan/models/groupedResultsModel.ts +20 -0
  94. package/src/scan/models/resultContentModel.ts +86 -0
  95. package/src/scan/models/scanResultsModel.ts +52 -0
  96. package/src/scan/populateProjectIdAndProjectName.js +1 -0
  97. package/src/scan/saveResults.js +8 -9
  98. package/src/scan/scan.ts +62 -0
  99. package/src/scan/scanConfig.js +26 -1
  100. package/src/scan/scanController.js +12 -4
  101. package/src/scan/scanResults.js +19 -17
  102. package/src/utils/getConfig.ts +12 -0
  103. package/src/utils/requestUtils.js +1 -1
  104. package/src/utils/saveFile.js +19 -0
  105. package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
  106. package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
  107. package/dist/common/findLatestCLIVersion.js +0 -23
  108. package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
  109. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
  110. package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
  111. package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
  112. package/src/common/findLatestCLIVersion.ts +0 -27
  113. package/src/scan/scan.js +0 -167
@@ -0,0 +1,241 @@
1
+ import { ProjectOverview, ScanResultsModel } from './models/scanResultsModel'
2
+ import i18n from 'i18n'
3
+ import chalk from 'chalk'
4
+ import { ResultContent } from './models/resultContentModel'
5
+ import { GroupedResultsModel } from './models/groupedResultsModel'
6
+ import { sortBy } from 'lodash'
7
+ import Table from 'cli-table3'
8
+ import {
9
+ CRITICAL_COLOUR,
10
+ HIGH_COLOUR,
11
+ LOW_COLOUR,
12
+ MEDIUM_COLOUR,
13
+ NOTE_COLOUR
14
+ } from '../constants/constants'
15
+
16
+ export function formatScanOutput(scanResults: ScanResultsModel) {
17
+ const { scanResultsInstances } = scanResults
18
+
19
+ let projectOverview = getProjectOverview(scanResultsInstances.content)
20
+ if (scanResultsInstances.content.length === 0) {
21
+ console.log(i18n.__('scanNoVulnerabilitiesFound'))
22
+ console.log(i18n.__('scanNoVulnerabilitiesFoundSecureCode'))
23
+ console.log(i18n.__('scanNoVulnerabilitiesFoundGoodWork'))
24
+ } else {
25
+ const message =
26
+ projectOverview.critical || projectOverview.high
27
+ ? 'Here are your top priorities to fix'
28
+ : "No major issues, here's what we found"
29
+ console.log(chalk.bold(message))
30
+ console.log()
31
+
32
+ let defaultView = getDefaultView(scanResultsInstances.content)
33
+
34
+ let count = defaultView.length
35
+ defaultView.forEach(entry => {
36
+ let table = new Table({
37
+ chars: {
38
+ top: '',
39
+ 'top-mid': '',
40
+ 'top-left': '',
41
+ 'top-right': '',
42
+ bottom: '',
43
+ 'bottom-mid': '',
44
+ 'bottom-left': '',
45
+ 'bottom-right': '',
46
+ left: '',
47
+ 'left-mid': '',
48
+ mid: '',
49
+ 'mid-mid': '',
50
+ right: '',
51
+ 'right-mid': '',
52
+ middle: ' '
53
+ },
54
+ style: { 'padding-left': 0, 'padding-right': 0 },
55
+ colAligns: ['right'],
56
+ wordWrap: true,
57
+ colWidths: [12, 1, 100]
58
+ })
59
+ let learnRow: string[] = []
60
+ let adviceRow = []
61
+ let headerRow = [
62
+ chalk
63
+ .hex(entry.colour)
64
+ .bold(`CONTRAST-${count.toString().padStart(3, '0')}`),
65
+ chalk.hex(entry.colour).bold('-'),
66
+ chalk.hex(entry.colour).bold(`[${entry.severity}] ${entry.ruleId}`) +
67
+ entry.message
68
+ ]
69
+ let codeRow = [
70
+ chalk.hex('#F6F5F5').bold(`Code`),
71
+ chalk.hex('#F6F5F5').bold(`:`),
72
+ chalk.hex('#F6F5F5').bold(`${entry.codePath}`)
73
+ ]
74
+ let issueRow = [chalk.bold(`Issue`), chalk.bold(`:`), `${entry.issue}`]
75
+
76
+ table.push(headerRow, codeRow, issueRow)
77
+
78
+ if (entry?.advice) {
79
+ adviceRow = [
80
+ chalk.bold('Advice'),
81
+ chalk.bold(`:`),
82
+ stripTags(entry.advice)
83
+ ]
84
+ table.push(adviceRow)
85
+ }
86
+
87
+ if (entry?.learn && entry?.learn.length > 0) {
88
+ learnRow = [
89
+ chalk.bold('Learn'),
90
+ chalk.bold(`:`),
91
+ chalk.hex('#97f7f7').bold.underline(entry.learn[0])
92
+ ]
93
+ table.push(learnRow)
94
+ }
95
+ count--
96
+ console.log(table.toString())
97
+ console.log()
98
+ })
99
+ }
100
+ printVulnInfo(projectOverview)
101
+ }
102
+
103
+ function printVulnInfo(projectOverview: any) {
104
+ const totalVulnerabilities = projectOverview.total
105
+
106
+ const vulMessage =
107
+ totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
108
+ console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
109
+ console.log(
110
+ i18n.__(
111
+ 'foundDetailedVulnerabilities',
112
+ String(projectOverview.critical),
113
+ String(projectOverview.high),
114
+ String(projectOverview.medium),
115
+ String(projectOverview.low),
116
+ String(projectOverview.note)
117
+ )
118
+ )
119
+ }
120
+
121
+ export function getProjectOverview(content: ResultContent[]) {
122
+ let acc: any = {
123
+ critical: 0,
124
+ high: 0,
125
+ medium: 0,
126
+ low: 0,
127
+ note: 0,
128
+ total: 0
129
+ }
130
+ content.forEach((i: ResultContent) => {
131
+ acc[i.severity.toLowerCase()] += 1
132
+ acc.total += 1
133
+ return acc
134
+ })
135
+
136
+ return acc
137
+ }
138
+
139
+ export function formatLinks(objName: string, entry: any[]) {
140
+ let line = chalk.bold(objName + ' : ')
141
+ if (entry.length === 1) {
142
+ console.log(line + chalk.hex('#97DCF7').bold.underline(entry[0]))
143
+ } else {
144
+ console.log(line)
145
+ entry.forEach(link => {
146
+ console.log(chalk.hex('#97DCF7').bold.underline(link))
147
+ })
148
+ }
149
+ }
150
+
151
+ export function getDefaultView(content: ResultContent[]) {
152
+ const groupTypeResults = [] as GroupedResultsModel[]
153
+
154
+ content.forEach(resultEntry => {
155
+ const groupResultsObj = new GroupedResultsModel(resultEntry.ruleId)
156
+ groupResultsObj.severity = resultEntry.severity
157
+ groupResultsObj.ruleId = resultEntry.ruleId
158
+ groupResultsObj.issue = stripTags(resultEntry.issue)
159
+ groupResultsObj.advice = resultEntry.advice
160
+ groupResultsObj.learn = resultEntry.learn
161
+ groupResultsObj.message = resultEntry.message?.text
162
+ ? editVulName(resultEntry.message.text) +
163
+ ':' +
164
+ getSourceLineNumber(resultEntry)
165
+ : ''
166
+ groupResultsObj.codePath = getLocationsSyncInfo(resultEntry)
167
+ groupTypeResults.push(groupResultsObj)
168
+ assignBySeverity(resultEntry, groupResultsObj)
169
+ })
170
+
171
+ return sortBy(groupTypeResults, ['priority']).reverse()
172
+ }
173
+ export function editVulName(message: string) {
174
+ return message.substring(message.indexOf(' in '))
175
+ }
176
+ export function getLocationsSyncInfo(resultEntry: ResultContent) {
177
+ const locationsMessage =
178
+ resultEntry.locations[0]?.physicalLocation?.artifactLocation?.uri || ''
179
+ const locationsLineNumber =
180
+ resultEntry.locations[0]?.physicalLocation?.region?.startLine || ''
181
+
182
+ if (!locationsLineNumber) {
183
+ return '@' + locationsMessage
184
+ }
185
+
186
+ return '@' + locationsMessage + ':' + locationsLineNumber
187
+ }
188
+
189
+ export function getSourceLineNumber(resultEntry: ResultContent) {
190
+ const locationsLineNumber =
191
+ resultEntry.locations[0]?.physicalLocation?.region?.startLine || ''
192
+ let codeFlowLineNumber = getCodeFlowInfo(resultEntry)
193
+
194
+ return codeFlowLineNumber ? codeFlowLineNumber : locationsLineNumber
195
+ }
196
+
197
+ export function getCodeFlowInfo(resultEntry: ResultContent) {
198
+ let result: any
199
+ resultEntry.codeFlows[0]?.threadFlows.forEach((i: { locations: any[] }) => {
200
+ return (result = i.locations.find(
201
+ (locations: { importance: string }) =>
202
+ locations.importance === 'essential'
203
+ ))
204
+ })
205
+
206
+ return result?.location?.physicalLocation?.region?.startLine
207
+ }
208
+
209
+ export function stripTags(oldString: string) {
210
+ return oldString
211
+ .replace(/\n/g, ' ')
212
+ .replace(/\s+/g, ' ')
213
+ .trim()
214
+ }
215
+
216
+ export function assignBySeverity(
217
+ entry: ResultContent,
218
+ assignedObj: GroupedResultsModel
219
+ ) {
220
+ if (entry.severity.toUpperCase() === 'CRITICAL') {
221
+ assignedObj.priority = 1
222
+ assignedObj.colour = CRITICAL_COLOUR
223
+ return assignedObj
224
+ } else if (entry.severity.toUpperCase() === 'HIGH') {
225
+ assignedObj.priority = 2
226
+ assignedObj.colour = HIGH_COLOUR
227
+ return assignedObj
228
+ } else if (entry.severity.toUpperCase() === 'MEDIUM') {
229
+ assignedObj.priority = 3
230
+ assignedObj.colour = MEDIUM_COLOUR
231
+ return assignedObj
232
+ } else if (entry.severity.toUpperCase() === 'LOW') {
233
+ assignedObj.priority = 4
234
+ assignedObj.colour = LOW_COLOUR
235
+ return assignedObj
236
+ } else if (entry.severity.toUpperCase() === 'NOTE') {
237
+ assignedObj.priority = 5
238
+ assignedObj.colour = NOTE_COLOUR
239
+ return assignedObj
240
+ }
241
+ }
package/src/scan/help.js CHANGED
@@ -21,14 +21,18 @@ 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',
34
+ 'experimental',
35
+ 'application-name'
32
36
  ]
33
37
  },
34
38
  {
@@ -0,0 +1,20 @@
1
+ export class GroupedResultsModel {
2
+ ruleId: string
3
+ codePathSet: Set<string>
4
+ cwe?: string[]
5
+ reference?: string[]
6
+ severity?: string
7
+ advice?: string
8
+ learn?: string[]
9
+ issue?: string
10
+ priority?: number
11
+ message?: string | undefined
12
+ colour: string;
13
+ codePath?: string;
14
+
15
+ constructor(ruleId: string) {
16
+ this.ruleId = ruleId
17
+ this.colour = '#999999'
18
+ this.codePathSet = new Set<string>
19
+ }
20
+ }
@@ -0,0 +1,86 @@
1
+ type Importance = 'important' | 'essential'
2
+
3
+ interface ArtifactLocation {
4
+ uri: string
5
+ }
6
+
7
+ interface Region {
8
+ startLine: string
9
+ snippet: Snippet
10
+ }
11
+
12
+ interface Snippet {
13
+ text: string
14
+ rendered: Rendered
15
+ }
16
+
17
+ interface Rendered {
18
+ text: string
19
+ }
20
+
21
+ interface PhysicalLocation {
22
+ artifactLocation: ArtifactLocation
23
+ region: Region
24
+ }
25
+
26
+ interface LogicalLocation {
27
+ fullyQualifiedName: string
28
+ name: string
29
+ }
30
+
31
+ export interface Location {
32
+ physicalLocation: PhysicalLocation
33
+ logicalLocations?: LogicalLocation[]
34
+ }
35
+
36
+ export interface ThreadFlowLocation {
37
+ importance: Importance
38
+ location: Location
39
+ }
40
+
41
+ interface ThreadFlow {
42
+ locations: ThreadFlowLocation[]
43
+ }
44
+
45
+ interface Message {
46
+ text: string
47
+ }
48
+
49
+ export interface CodeFlow {
50
+ message: Message
51
+ threadFlows: ThreadFlow[]
52
+ }
53
+
54
+ export interface ResultContent {
55
+ message?: {text :string};
56
+ id: string
57
+ organizationId: string
58
+ projectId: string
59
+ firstCreatedTime: string
60
+ ruleId: string
61
+ codeFlows: CodeFlow[]
62
+ lastSeenTime: string
63
+ locations: Location[]
64
+ name: string
65
+ description: string
66
+ recommendation: string | null
67
+ risk: string | null
68
+ category: string
69
+ confidence: string
70
+ standards: { [key: string]: string[] }
71
+ cwe: string[]
72
+ owasp: string[]
73
+ reference: string[]
74
+ sink: string
75
+ detailsTrigger: string
76
+ type: RuleType
77
+ source: string
78
+ severity: Severity
79
+ advice: string
80
+ learn: string[]
81
+ issue: string
82
+ }
83
+
84
+ export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'note'
85
+
86
+ export type RuleType = 'DATA_FLOW' | 'CRYPTO' | 'CONFIG' | 'DEFAULT'
@@ -0,0 +1,52 @@
1
+ import { ResultContent } from './resultContentModel'
2
+
3
+ export class ScanResultsModel {
4
+ projectOverview: ProjectOverview
5
+ scanDetail: ScanDetail
6
+ scanResultsInstances: ScanResultsInstances
7
+
8
+ constructor(scan: any) {
9
+ this.projectOverview = scan.projectOverview as ProjectOverview
10
+ this.scanDetail = scan.scanDetail as ScanDetail
11
+ this.scanResultsInstances = scan.scanResultsInstances as ScanResultsInstances
12
+ }
13
+ }
14
+
15
+ export interface ProjectOverview {
16
+ id: string
17
+ organizationId: string
18
+ name: string
19
+ archived: boolean
20
+ language: string
21
+ critical: number
22
+ high: number
23
+ medium: number
24
+ low: number
25
+ note: number
26
+ lastScanTime: string
27
+ completedScans: number
28
+ lastScanId: string
29
+ }
30
+
31
+ export interface ScanDetail {
32
+ critical: number
33
+ high: number
34
+ medium: number
35
+ low: number
36
+ note: number
37
+ id: string
38
+ organizationId: string
39
+ projectId: string
40
+ codeArtifactId: string
41
+ status: string
42
+ createdTime: string
43
+ startedTime: string
44
+ completedTime: string
45
+ language: string
46
+ label: string
47
+ errorMessage: string
48
+ }
49
+
50
+ export interface ScanResultsInstances {
51
+ content: ResultContent[]
52
+ }
@@ -23,6 +23,7 @@ const createProjectId = async (config, client) => {
23
23
  }
24
24
  if (res.statusCode === 403) {
25
25
  console.log(i18n.__('permissionsError'))
26
+ process.exit(1)
26
27
  return
27
28
  }
28
29
 
@@ -1,15 +1,14 @@
1
1
  const fs = require('fs')
2
2
 
3
- const writeResultsToFile = (responseBody, name = 'results.sarif') => {
4
- fs.writeFile(name, JSON.stringify(responseBody, null, 2), err => {
5
- if (err) {
6
- console.log('Error writing Scan Results to file')
7
- } else {
8
- console.log(`Scan Results saved to ${name}`)
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
  }
@@ -0,0 +1,62 @@
1
+ import commonApi from '../utils/commonApi.js'
2
+ import fileUtils from '../scan/fileUtils'
3
+ import i18n from 'i18n'
4
+ import oraWrapper from '../utils/oraWrapper'
5
+
6
+ export const allowedFileTypes = ['.jar', '.war', '.js', '.zip', '.exe']
7
+
8
+ export const isFileAllowed = (scanOption: string) => {
9
+ let valid = false
10
+ allowedFileTypes.forEach(fileType => {
11
+ if (scanOption.endsWith(fileType)) {
12
+ valid = true
13
+ }
14
+ })
15
+ return valid
16
+ }
17
+
18
+ export const sendScan = async (config: any) => {
19
+ if (!isFileAllowed(config.file)) {
20
+ console.log(i18n.__('scanErrorFileMessage'))
21
+ process.exit(9)
22
+ } else {
23
+ fileUtils.checkFilePermissions(config.file)
24
+ const client = commonApi.getHttpClient(config)
25
+
26
+ const startUploadSpinner = oraWrapper.returnOra(i18n.__('uploadingScan'))
27
+ oraWrapper.startSpinner(startUploadSpinner)
28
+
29
+ return await client
30
+ .sendArtifact(config)
31
+ .then(res => {
32
+ if (res.statusCode === 201) {
33
+ oraWrapper.succeedSpinner(
34
+ startUploadSpinner,
35
+ i18n.__('uploadingScanSuccessful')
36
+ )
37
+ if (config.verbose) {
38
+ console.log(i18n.__('responseMessage', res.body))
39
+ }
40
+ return res.body.id
41
+ } else {
42
+ if (config.debug) {
43
+ console.log(res.statusCode)
44
+ console.log(config)
45
+ }
46
+ oraWrapper.failSpinner(
47
+ startUploadSpinner,
48
+ i18n.__('uploadingScanFail')
49
+ )
50
+ if (res.statusCode === 403) {
51
+ console.log(i18n.__('permissionsError'))
52
+ process.exit(1)
53
+ }
54
+ console.log(i18n.__('genericServiceError', res.statusCode))
55
+ process.exit(1)
56
+ }
57
+ })
58
+ .catch(err => {
59
+ console.log(err)
60
+ })
61
+ }
62
+ }
@@ -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(1)
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(0)
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,
@@ -54,13 +56,19 @@ const startScan = async configToUse => {
54
56
  getTimeout(configToUse),
55
57
  startScanSpinner
56
58
  )
59
+
57
60
  const scanResultsInstances = await scanResults.returnScanResultsInstances(
58
61
  configToUse,
59
62
  scanDetail.id
60
63
  )
64
+
65
+ const endTime = performance.now()
66
+ const scanDurationMs = endTime - startTime
61
67
  succeedSpinner(startScanSpinner, 'Contrast Scan complete')
62
- const projectOverview = await scanResults.returnScanProjectById(configToUse)
63
- return { projectOverview, scanDetail, scanResultsInstances }
68
+ console.log(
69
+ `----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
70
+ )
71
+ return { scanDetail, scanResultsInstances }
64
72
  }
65
73
  }
66
74
 
@@ -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
@@ -46,8 +47,23 @@ const returnScanResults = async (
46
47
  }
47
48
  if (result.body.status === 'FAILED') {
48
49
  complete = true
49
- oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.')
50
- console.log(result.body.errorMessage)
50
+ oraFunctions.failSpinner(
51
+ startScanSpinner,
52
+ i18n.__(
53
+ 'scanNotCompleted',
54
+ 'https://docs.contrastsecurity.com/en/binary-package-preparation.html'
55
+ )
56
+ )
57
+ result.body.errorMessage ? console.log(result.body.errorMessage) : ''
58
+ if (
59
+ result.body.errorMessage ===
60
+ 'Unable to determine language for code artifact'
61
+ ) {
62
+ console.log(
63
+ 'Try scanning again using --language param. ',
64
+ i18n.__('scanOptionsLanguageSummary')
65
+ )
66
+ }
51
67
  process.exit(1)
52
68
  }
53
69
  }
@@ -77,23 +93,9 @@ const returnScanResultsInstances = async (config, scanId) => {
77
93
  }
78
94
  }
79
95
 
80
- const returnScanProjectById = async config => {
81
- const client = commonApi.getHttpClient(config)
82
- let result
83
- try {
84
- result = await client.getScanProjectById(config)
85
- if (JSON.stringify(result.statusCode) == 200) {
86
- return result.body
87
- }
88
- } catch (e) {
89
- console.log(e.message.toString())
90
- }
91
- }
92
-
93
96
  module.exports = {
94
97
  getScanId: getScanId,
95
98
  returnScanResults: returnScanResults,
96
99
  pollScanResults: pollScanResults,
97
- returnScanResultsInstances: returnScanResultsInstances,
98
- returnScanProjectById: returnScanProjectById
100
+ returnScanResultsInstances: returnScanResultsInstances
99
101
  }