@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.
- package/.prettierignore +4 -0
- package/README.md +24 -16
- package/dist/audit/autodetection/autoDetectLanguage.js +32 -0
- package/dist/audit/catalogueApplication/catalogueApplication.js +2 -11
- package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +30 -13
- package/dist/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +25 -0
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +51 -237
- 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 +2 -14
- package/dist/commands/audit/auditConfig.js +8 -2
- package/dist/commands/audit/auditController.js +14 -5
- 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 +13 -27
- package/dist/commands/scan/sca/scaAnalysis.js +44 -0
- package/dist/common/HTTPClient.js +29 -26
- package/dist/common/errorHandling.js +15 -39
- package/dist/common/versionChecker.js +32 -0
- package/dist/constants/constants.js +16 -2
- package/dist/constants/lambda.js +3 -1
- package/dist/constants/locales.js +58 -48
- package/dist/constants.js +59 -3
- package/dist/index.js +48 -30
- package/dist/lambda/help.js +22 -14
- package/dist/lambda/lambda.js +6 -0
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scaAnalysis/common/formatMessage.js +11 -0
- package/dist/scaAnalysis/common/treeUpload.js +30 -0
- package/dist/scaAnalysis/java/analysis.js +116 -0
- package/dist/scaAnalysis/java/index.js +18 -0
- package/dist/scaAnalysis/java/javaBuildDepsParser.js +326 -0
- package/dist/scan/autoDetection.js +46 -1
- package/dist/scan/fileUtils.js +73 -1
- package/dist/scan/formatScanOutput.js +212 -0
- package/dist/scan/help.js +6 -2
- package/dist/scan/models/groupedResultsModel.js +11 -0
- package/dist/scan/models/resultContentModel.js +2 -0
- package/dist/scan/models/scanResultsModel.js +11 -0
- package/dist/scan/populateProjectIdAndProjectName.js +1 -0
- package/dist/scan/saveResults.js +9 -10
- package/dist/scan/scan.js +26 -101
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +8 -4
- package/dist/scan/scanResults.js +8 -17
- package/dist/utils/getConfig.js +3 -0
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +3 -2
- package/src/audit/autodetection/autoDetectLanguage.ts +40 -0
- package/src/audit/catalogueApplication/catalogueApplication.js +4 -16
- package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +41 -19
- package/src/audit/languageAnalysisEngine/reduceIdentifiedLanguages.js +71 -0
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +105 -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 +2 -22
- package/src/commands/audit/auditConfig.ts +12 -3
- package/src/commands/audit/auditController.ts +21 -5
- package/src/commands/audit/processAudit.ts +3 -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 +15 -31
- package/src/commands/scan/sca/scaAnalysis.js +73 -0
- package/src/common/HTTPClient.js +42 -36
- package/src/common/errorHandling.ts +17 -48
- package/src/common/versionChecker.ts +41 -0
- package/src/constants/constants.js +17 -4
- package/src/constants/lambda.js +3 -1
- package/src/constants/locales.js +69 -63
- package/src/constants.js +66 -3
- package/src/index.ts +62 -36
- package/src/lambda/help.ts +22 -14
- package/src/lambda/lambda.ts +8 -0
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scaAnalysis/common/formatMessage.js +10 -0
- package/src/scaAnalysis/common/treeUpload.js +34 -0
- package/src/scaAnalysis/java/analysis.js +159 -0
- package/src/scaAnalysis/java/index.js +21 -0
- package/src/scaAnalysis/java/javaBuildDepsParser.js +391 -0
- package/src/scan/autoDetection.js +54 -1
- package/src/scan/fileUtils.js +91 -1
- package/src/scan/formatScanOutput.ts +241 -0
- package/src/scan/help.js +6 -2
- package/src/scan/models/groupedResultsModel.ts +20 -0
- package/src/scan/models/resultContentModel.ts +86 -0
- package/src/scan/models/scanResultsModel.ts +52 -0
- package/src/scan/populateProjectIdAndProjectName.js +1 -0
- package/src/scan/saveResults.js +8 -9
- package/src/scan/scan.ts +62 -0
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +12 -4
- package/src/scan/scanResults.js +19 -17
- package/src/utils/getConfig.ts +12 -0
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
- package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
- package/dist/common/findLatestCLIVersion.js +0 -23
- 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/common/findLatestCLIVersion.ts +0 -27
- 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
|
+
}
|
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.ts
ADDED
|
@@ -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
|
+
}
|
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(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(
|
|
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
|
-
|
|
63
|
-
|
|
68
|
+
console.log(
|
|
69
|
+
`----- Scan completed in ${(scanDurationMs / 1000).toFixed(2)}s -----`
|
|
70
|
+
)
|
|
71
|
+
return { scanDetail, scanResultsInstances }
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
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
|
|
@@ -46,8 +47,23 @@ const returnScanResults = async (
|
|
|
46
47
|
}
|
|
47
48
|
if (result.body.status === 'FAILED') {
|
|
48
49
|
complete = true
|
|
49
|
-
oraFunctions.failSpinner(
|
|
50
|
-
|
|
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
|
}
|