@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.
Files changed (62) hide show
  1. package/.prettierignore +1 -0
  2. package/README.md +20 -14
  3. package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +2 -12
  4. package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +62 -234
  5. package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
  6. package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
  7. package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
  8. package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
  9. package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
  10. package/dist/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
  11. package/dist/commands/audit/auditController.js +6 -3
  12. package/dist/commands/scan/processScan.js +4 -3
  13. package/dist/common/HTTPClient.js +19 -26
  14. package/dist/common/versionChecker.js +14 -12
  15. package/dist/constants/constants.js +1 -1
  16. package/dist/constants/lambda.js +3 -1
  17. package/dist/constants/locales.js +17 -10
  18. package/dist/constants.js +5 -1
  19. package/dist/index.js +2 -2
  20. package/dist/lambda/help.js +22 -14
  21. package/dist/lambda/lambda.js +6 -0
  22. package/dist/scan/models/groupedResultsModel.js +10 -0
  23. package/dist/scan/models/resultContentModel.js +2 -0
  24. package/dist/scan/models/scanResultsModel.js +11 -0
  25. package/dist/scan/scan.js +90 -95
  26. package/dist/scan/scanConfig.js +1 -1
  27. package/dist/utils/getConfig.js +3 -0
  28. package/package.json +2 -2
  29. package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +2 -16
  30. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +127 -0
  31. package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
  32. package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
  33. package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
  34. package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
  35. package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
  36. package/src/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
  37. package/src/commands/audit/auditController.ts +12 -3
  38. package/src/commands/scan/processScan.js +4 -6
  39. package/src/common/HTTPClient.js +31 -38
  40. package/src/common/errorHandling.ts +0 -1
  41. package/src/common/versionChecker.ts +24 -22
  42. package/src/constants/constants.js +1 -1
  43. package/src/constants/lambda.js +3 -1
  44. package/src/constants/locales.js +20 -10
  45. package/src/constants.js +7 -1
  46. package/src/index.ts +2 -3
  47. package/src/lambda/help.ts +22 -14
  48. package/src/lambda/lambda.ts +8 -0
  49. package/src/scan/models/groupedResultsModel.ts +18 -0
  50. package/src/scan/models/resultContentModel.ts +86 -0
  51. package/src/scan/models/scanResultsModel.ts +52 -0
  52. package/src/scan/scan.ts +192 -0
  53. package/src/scan/scanConfig.js +1 -1
  54. package/src/scan/scanController.js +2 -0
  55. package/src/utils/getConfig.ts +10 -0
  56. package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
  57. package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
  58. package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
  59. package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
  60. package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
  61. package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
  62. package/src/scan/scan.js +0 -195
@@ -0,0 +1,86 @@
1
+ type Importance = 'important' | 'essential'
2
+
3
+ interface ArtifactLocation {
4
+ uri: string
5
+ }
6
+
7
+ interface Region {
8
+ startLine: number
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
+ }
@@ -0,0 +1,192 @@
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
+ import chalk from 'chalk'
6
+ import { ProjectOverview, ScanResultsModel } from './models/scanResultsModel'
7
+ import { Location, ResultContent } from './models/resultContentModel'
8
+ import { GroupedResultsModel } from './models/groupedResultsModel'
9
+
10
+ export const allowedFileTypes = ['.jar', '.war', '.js', '.zip', '.exe']
11
+
12
+ export const isFileAllowed = (scanOption: string) => {
13
+ let valid = false
14
+ allowedFileTypes.forEach(fileType => {
15
+ if (scanOption.endsWith(fileType)) {
16
+ valid = true
17
+ }
18
+ })
19
+ return valid
20
+ }
21
+
22
+ export const sendScan = async (config: any) => {
23
+ if (!isFileAllowed(config.file)) {
24
+ console.log(i18n.__('scanErrorFileMessage'))
25
+ process.exit(9)
26
+ } else {
27
+ fileUtils.checkFilePermissions(config.file)
28
+ const client = commonApi.getHttpClient(config)
29
+
30
+ const startUploadSpinner = oraWrapper.returnOra(i18n.__('uploadingScan'))
31
+ oraWrapper.startSpinner(startUploadSpinner)
32
+
33
+ return await client
34
+ .sendArtifact(config)
35
+ .then(res => {
36
+ if (res.statusCode === 201) {
37
+ oraWrapper.succeedSpinner(
38
+ startUploadSpinner,
39
+ i18n.__('uploadingScanSuccessful')
40
+ )
41
+ if (config.verbose) {
42
+ console.log(i18n.__('responseMessage', res.body))
43
+ }
44
+ return res.body.id
45
+ } else {
46
+ if (config.debug) {
47
+ console.log(res.statusCode)
48
+ console.log(config)
49
+ }
50
+ oraWrapper.failSpinner(
51
+ startUploadSpinner,
52
+ i18n.__('uploadingScanFail')
53
+ )
54
+ if (res.statusCode === 403) {
55
+ console.log(i18n.__('permissionsError'))
56
+ process.exit(1)
57
+ }
58
+ console.log(i18n.__('genericServiceError', res.statusCode))
59
+ process.exit(1)
60
+ }
61
+ })
62
+ .catch(err => {
63
+ console.log(err)
64
+ })
65
+ }
66
+ }
67
+
68
+ export function formatScanOutput(scanResults: ScanResultsModel) {
69
+ const { projectOverview, scanResultsInstances } = scanResults
70
+
71
+ if (scanResultsInstances.content.length === 0) {
72
+ console.log(i18n.__('scanNoVulnerabilitiesFound'))
73
+ } else {
74
+ const message =
75
+ projectOverview.critical || projectOverview.high
76
+ ? 'Here are your top priorities to fix'
77
+ : "No major issues, here's what we found"
78
+ console.log(chalk.bold(message))
79
+ console.log()
80
+
81
+ const groups = getGroups(scanResultsInstances.content)
82
+
83
+ groups.forEach(entry => {
84
+ console.log(
85
+ chalk.bold(
86
+ `[ ${entry.severity} ] | ${entry.ruleId} (${entry.lineInfoSet.size}) - ` +
87
+ `${entry.message}`
88
+ )
89
+ )
90
+
91
+ let count = 1
92
+ entry.lineInfoSet.forEach(lineInfo => {
93
+ console.log(`\t ${count}. ${lineInfo}`)
94
+ count++
95
+ })
96
+
97
+ if (entry?.issue) {
98
+ console.log(chalk.bold('Issue' + ': ') + entry.issue)
99
+ }
100
+
101
+ if (entry?.advice) {
102
+ console.log(chalk.bold('Advice' + ': ') + entry.advice)
103
+ }
104
+
105
+ if (entry?.learn && entry?.learn.length > 0) {
106
+ formatLinks('Learn', entry.learn)
107
+ }
108
+ console.log()
109
+ })
110
+
111
+ printVulnInfo(projectOverview)
112
+ }
113
+ }
114
+
115
+ function printVulnInfo(projectOverview: ProjectOverview) {
116
+ const totalVulnerabilities = getTotalVulns(projectOverview)
117
+
118
+ const vulMessage =
119
+ totalVulnerabilities === 1 ? `vulnerability` : `vulnerabilities`
120
+ console.log(chalk.bold(`Found ${totalVulnerabilities} ${vulMessage}`))
121
+ console.log(
122
+ i18n.__(
123
+ 'foundDetailedVulnerabilities',
124
+ String(projectOverview.critical),
125
+ String(projectOverview.high),
126
+ String(projectOverview.medium),
127
+ String(projectOverview.low),
128
+ String(projectOverview.note)
129
+ )
130
+ )
131
+ }
132
+
133
+ function getTotalVulns(projectOverview: ProjectOverview) {
134
+ return (
135
+ projectOverview.critical +
136
+ projectOverview.high +
137
+ projectOverview.medium +
138
+ projectOverview.low +
139
+ projectOverview.note
140
+ )
141
+ }
142
+
143
+ export function formatLinks(objName: string, entry: any[]) {
144
+ console.log(chalk.bold(objName + ':'))
145
+ entry.forEach(link => {
146
+ console.log(link)
147
+ })
148
+ }
149
+
150
+ export function getGroups(content: ResultContent[]) {
151
+ const groupTypeSet = new Set(content.map(({ ruleId }) => ruleId))
152
+ const groupTypeResults = [] as GroupedResultsModel[]
153
+
154
+ groupTypeSet.forEach(groupName => {
155
+ const groupResultsObj = new GroupedResultsModel(groupName)
156
+
157
+ content.forEach(resultEntry => {
158
+ if (resultEntry.ruleId === groupName) {
159
+ groupResultsObj.severity = resultEntry.severity
160
+ groupResultsObj.issue = stripMustacheTags(resultEntry.issue)
161
+ groupResultsObj.advice = resultEntry.advice
162
+ groupResultsObj.learn = resultEntry.learn
163
+ groupResultsObj.message = resultEntry.message?.text
164
+
165
+ groupResultsObj.lineInfoSet.add(getMessage(resultEntry.locations))
166
+ }
167
+ })
168
+ groupTypeResults.push(groupResultsObj)
169
+ })
170
+
171
+ return groupTypeResults
172
+ }
173
+
174
+ export function getMessage(locations: Location[]) {
175
+ const message = locations[0]?.physicalLocation?.artifactLocation?.uri || ''
176
+ const lineNumber = locations[0]?.physicalLocation?.region?.startLine || ''
177
+
178
+ if (!lineNumber) {
179
+ return '@' + message
180
+ }
181
+
182
+ return '@' + message + ':' + lineNumber
183
+ }
184
+
185
+ export function stripMustacheTags(oldString: string) {
186
+ return oldString
187
+ .replace(/\n/g, ' ')
188
+ .replace(/{{.*?}}/g, '\n')
189
+ .replace(/\$\$LINK_DELIM\$\$/g, '\n')
190
+ .replace(/\s+/g, ' ')
191
+ .trim()
192
+ }
@@ -26,7 +26,7 @@ const getScanConfig = argv => {
26
26
  if (!Object.values(supportedLanguages).includes(scanParams.language)) {
27
27
  console.log(`Did not recognise --language ${scanParams.language}`)
28
28
  console.log(i18n.__('constantsHowToRunDev3'))
29
- process.exit(0)
29
+ process.exit(1)
30
30
  }
31
31
  }
32
32
 
@@ -56,10 +56,12 @@ const startScan = async configToUse => {
56
56
  getTimeout(configToUse),
57
57
  startScanSpinner
58
58
  )
59
+
59
60
  const scanResultsInstances = await scanResults.returnScanResultsInstances(
60
61
  configToUse,
61
62
  scanDetail.id
62
63
  )
64
+
63
65
  const endTime = performance.now()
64
66
  const scanDurationMs = endTime - startTime
65
67
  succeedSpinner(startScanSpinner, 'Contrast Scan complete')
@@ -7,6 +7,7 @@ type ContrastConfOptions = Partial<{
7
7
  orgId: string
8
8
  authHeader: string
9
9
  numOfRuns: number
10
+ updateMessageHidden: boolean
10
11
  }>
11
12
 
12
13
  type ContrastConf = Conf<ContrastConfOptions>
@@ -17,6 +18,15 @@ const localConfig = (name: string, version: string) => {
17
18
  })
18
19
  config.set('version', version)
19
20
 
21
+ if (process.env.CONTRAST_CODSEC_DISABLE_UPDATE_MESSAGE) {
22
+ config.set(
23
+ 'updateMessageHidden',
24
+ JSON.parse(
25
+ process.env.CONTRAST_CODSEC_DISABLE_UPDATE_MESSAGE.toLowerCase()
26
+ )
27
+ )
28
+ }
29
+
20
30
  if (!config.has('host')) {
21
31
  config.set('host', 'https://ce.contrastsecurity.com/')
22
32
  }
@@ -1,17 +0,0 @@
1
- "use strict";
2
- const { getGlobalProperties, getFeatures, isFeatureEnabled } = require('../util/generalAPI');
3
- const { CLI_IGNORE_DEV_DEPS } = require('../util/capabilities');
4
- const checkDevDeps = async (config) => {
5
- const shouldIgnoreDev = config.ignoreDev;
6
- const globalProperties = await getGlobalProperties();
7
- const features = getFeatures(globalProperties.internal_version);
8
- const isfeatureEnabled = isFeatureEnabled(features, CLI_IGNORE_DEV_DEPS);
9
- let ignoreDevUrl = false;
10
- if (shouldIgnoreDev) {
11
- ignoreDevUrl = isfeatureEnabled;
12
- }
13
- return ignoreDevUrl;
14
- };
15
- module.exports = {
16
- checkDevDeps
17
- };
@@ -1,81 +0,0 @@
1
- "use strict";
2
- const commonReport = require('./commonReportingFunctions');
3
- const { handleResponseErrors } = require('../commonApi');
4
- const { getHttpClient } = require('../../../utils/commonApi');
5
- const vulnReportWithoutDevDep = async (analysis, applicationId, snapshotId, config) => {
6
- if (config.report) {
7
- const reportResponse = await getSpecReport(snapshotId, config);
8
- if (reportResponse !== undefined) {
9
- const severity = config.cveSeverity;
10
- const id = applicationId;
11
- const name = config.applicationName;
12
- const hasSomeVulnerabilitiesReported = formatVulnerabilityOutput(reportResponse.vulnerabilities, severity, id, name, config);
13
- commonReport.analyseReportOptions(hasSomeVulnerabilitiesReported);
14
- }
15
- }
16
- };
17
- const getSpecReport = async (reportId, config) => {
18
- const client = getHttpClient(config);
19
- return client
20
- .getSpecificReport(config, reportId)
21
- .then(res => {
22
- if (res.statusCode === 200) {
23
- commonReport.displaySuccessMessageReport();
24
- return res.body;
25
- }
26
- else {
27
- handleResponseErrors(res, 'report');
28
- }
29
- })
30
- .catch(err => {
31
- console.log(err);
32
- });
33
- };
34
- const countSeverity = vulnerabilities => {
35
- const severityCount = {
36
- critical: 0,
37
- high: 0,
38
- medium: 0,
39
- low: 0
40
- };
41
- for (const key of Object.keys(vulnerabilities)) {
42
- vulnerabilities[key].forEach(vuln => {
43
- if (vuln.severityCode === 'HIGH') {
44
- severityCount['high'] += 1;
45
- }
46
- else if (vuln.severityCode === 'MEDIUM') {
47
- severityCount['medium'] += 1;
48
- }
49
- else if (vuln.severityCode === 'LOW') {
50
- severityCount['low'] += 1;
51
- }
52
- else if (vuln.severityCode === 'CRITICAL') {
53
- severityCount['critical'] += 1;
54
- }
55
- });
56
- }
57
- return severityCount;
58
- };
59
- const formatVulnerabilityOutput = (vulnerabilities, severity, id, name, config) => {
60
- const numberOfVulnerableLibraries = Object.keys(vulnerabilities).length;
61
- let numberOfCves = 0;
62
- for (const key of Object.keys(vulnerabilities)) {
63
- numberOfCves += vulnerabilities[key].length;
64
- }
65
- commonReport.createLibraryHeader(id, numberOfVulnerableLibraries, numberOfCves, name);
66
- const severityCount = countSeverity(vulnerabilities);
67
- const filteredVulns = commonReport.filterVulnerabilitiesBySeverity(severity, vulnerabilities);
68
- let hasSomeVulnerabilitiesReported;
69
- hasSomeVulnerabilitiesReported = commonReport.printVulnerabilityResponse(severity, filteredVulns, vulnerabilities);
70
- console.log('\n **************************' +
71
- ` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's ` +
72
- '************************** ');
73
- console.log(' \n Please go to the Contrast UI to view your dependency tree: \n' +
74
- ` \n ${config.host}/Contrast/static/ng/index.html#/${config.organizationId}/applications/${config.applicationId}/libs/dependency-tree`);
75
- return [hasSomeVulnerabilitiesReported, numberOfCves, severityCount];
76
- };
77
- module.exports = {
78
- vulnReportWithoutDevDep: vulnReportWithoutDevDep,
79
- formatVulnerabilityOutput: formatVulnerabilityOutput,
80
- getSpecReport: getSpecReport
81
- };
@@ -1,27 +0,0 @@
1
- const {
2
- getGlobalProperties,
3
- getFeatures,
4
- isFeatureEnabled
5
- } = require('../util/generalAPI')
6
- const { CLI_IGNORE_DEV_DEPS } = require('../util/capabilities')
7
-
8
- const checkDevDeps = async config => {
9
- const shouldIgnoreDev = config.ignoreDev
10
- const globalProperties = await getGlobalProperties()
11
-
12
- // returns [ 'CLI_IGNORE_DEV_DEPS' ] if teamserver version is above 3.8.1
13
- const features = getFeatures(globalProperties.internal_version)
14
-
15
- // providing user is on version >= 3.8.1, isfeatureEnabled will always return true,
16
- // therefore shouldIgnoreDev flag (from params) is needed to disable ignore dev deps
17
- const isfeatureEnabled = isFeatureEnabled(features, CLI_IGNORE_DEV_DEPS)
18
- let ignoreDevUrl = false
19
- if (shouldIgnoreDev) {
20
- ignoreDevUrl = isfeatureEnabled
21
- }
22
- return ignoreDevUrl
23
- }
24
-
25
- module.exports = {
26
- checkDevDeps
27
- }