Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
edeab723
Commit
edeab723
authored
Apr 05, 2018
by
Filipa Lacerda
Committed by
Phil Hughes
Apr 05, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Groups reports in MR view and splits reports in CI view
parent
a990e800
Changes
52
Hide whitespace changes
Inline
Side-by-side
Showing
52 changed files
with
2190 additions
and
2466 deletions
+2190
-2466
app/assets/javascripts/pipelines/pipeline_details_bundle.js
app/assets/javascripts/pipelines/pipeline_details_bundle.js
+20
-46
app/assets/javascripts/pipelines/pipeline_details_mediator.js
...assets/javascripts/pipelines/pipeline_details_mediator.js
+0
-19
app/assets/javascripts/pipelines/stores/pipeline_store.js
app/assets/javascripts/pipelines/stores/pipeline_store.js
+0
-26
app/assets/stylesheets/pages/merge_requests.scss
app/assets/stylesheets/pages/merge_requests.scss
+4
-0
app/views/projects/merge_requests/show.html.haml
app/views/projects/merge_requests/show.html.haml
+5
-0
app/views/projects/pipelines/_with_tabs.html.haml
app/views/projects/pipelines/_with_tabs.html.haml
+3
-1
ee/app/assets/javascripts/pipelines/components/security_reports/report_summary_widget.vue
...nes/components/security_reports/report_summary_widget.vue
+109
-85
ee/app/assets/javascripts/pipelines/components/security_reports/security_report_app.vue
...lines/components/security_reports/security_report_app.vue
+0
-68
ee/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
...javascripts/vue_merge_request_widget/mr_widget_options.js
+63
-293
ee/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
...cripts/vue_merge_request_widget/stores/mr_widget_store.js
+38
-70
ee/app/assets/javascripts/vue_shared/security_reports/components/dast_issue_body.vue
...ue_shared/security_reports/components/dast_issue_body.vue
+25
-24
ee/app/assets/javascripts/vue_shared/security_reports/components/error_row.vue
...ipts/vue_shared/security_reports/components/error_row.vue
+0
-31
ee/app/assets/javascripts/vue_shared/security_reports/components/help_popover.vue
...s/vue_shared/security_reports/components/help_popover.vue
+20
-11
ee/app/assets/javascripts/vue_shared/security_reports/components/issues_list.vue
...ts/vue_shared/security_reports/components/issues_list.vue
+85
-0
ee/app/assets/javascripts/vue_shared/security_reports/components/loading_row.vue
...ts/vue_shared/security_reports/components/loading_row.vue
+0
-24
ee/app/assets/javascripts/vue_shared/security_reports/components/report_issues.vue
.../vue_shared/security_reports/components/report_issues.vue
+114
-114
ee/app/assets/javascripts/vue_shared/security_reports/components/report_link.vue
...ts/vue_shared/security_reports/components/report_link.vue
+8
-8
ee/app/assets/javascripts/vue_shared/security_reports/components/report_section.vue
...vue_shared/security_reports/components/report_section.vue
+157
-200
ee/app/assets/javascripts/vue_shared/security_reports/components/sast_container_info.vue
...hared/security_reports/components/sast_container_info.vue
+19
-0
ee/app/assets/javascripts/vue_shared/security_reports/components/sast_container_issue_body.vue
...security_reports/components/sast_container_issue_body.vue
+16
-16
ee/app/assets/javascripts/vue_shared/security_reports/components/sast_issue_body.vue
...ue_shared/security_reports/components/sast_issue_body.vue
+16
-16
ee/app/assets/javascripts/vue_shared/security_reports/components/summary_row.vue
...ts/vue_shared/security_reports/components/summary_row.vue
+12
-3
ee/app/assets/javascripts/vue_shared/security_reports/grouped_security_reports_app.vue
..._shared/security_reports/grouped_security_reports_app.vue
+254
-0
ee/app/assets/javascripts/vue_shared/security_reports/helpers/state.js
.../javascripts/vue_shared/security_reports/helpers/state.js
+0
-26
ee/app/assets/javascripts/vue_shared/security_reports/helpers/utils.js
.../javascripts/vue_shared/security_reports/helpers/utils.js
+0
-155
ee/app/assets/javascripts/vue_shared/security_reports/mixins/reports_mixin.js
...ripts/vue_shared/security_reports/mixins/reports_mixin.js
+19
-0
ee/app/assets/javascripts/vue_shared/security_reports/mixins/security_report_mixin.js
...e_shared/security_reports/mixins/security_report_mixin.js
+48
-133
ee/app/assets/javascripts/vue_shared/security_reports/split_security_reports_app.vue
...ue_shared/security_reports/split_security_reports_app.vue
+146
-0
ee/app/assets/javascripts/vue_shared/security_reports/store/actions.js
.../javascripts/vue_shared/security_reports/store/actions.js
+8
-8
ee/app/assets/javascripts/vue_shared/security_reports/store/constants.js
...avascripts/vue_shared/security_reports/store/constants.js
+4
-0
ee/app/assets/javascripts/vue_shared/security_reports/store/getters.js
.../javascripts/vue_shared/security_reports/store/getters.js
+95
-17
ee/app/assets/javascripts/vue_shared/security_reports/store/mutations.js
...avascripts/vue_shared/security_reports/store/mutations.js
+69
-101
ee/app/assets/javascripts/vue_shared/security_reports/store/utils.js
...ts/javascripts/vue_shared/security_reports/store/utils.js
+23
-16
ee/app/assets/stylesheets/pages/security_reports.scss
ee/app/assets/stylesheets/pages/security_reports.scss
+44
-3
ee/changelogs/unreleased/4310-security-reports-step-2.yml
ee/changelogs/unreleased/4310-security-reports-step-2.yml
+6
-0
ee/spec/features/projects/pipelines/pipeline_spec.rb
ee/spec/features/projects/pipelines/pipeline_spec.rb
+2
-2
spec/javascripts/pipelines/pipeline_details_mediator_spec.js
spec/javascripts/pipelines/pipeline_details_mediator_spec.js
+0
-26
spec/javascripts/pipelines/pipeline_store_spec.js
spec/javascripts/pipelines/pipeline_store_spec.js
+0
-8
spec/javascripts/pipelines/security_reports/report_summary_widget_spec.js
.../pipelines/security_reports/report_summary_widget_spec.js
+190
-0
spec/javascripts/pipelines/security_reports/sast_report_summary_widget_spec.js
...lines/security_reports/sast_report_summary_widget_spec.js
+0
-56
spec/javascripts/pipelines/security_reports/security_report_app_spec.js
...ts/pipelines/security_reports/security_report_app_spec.js
+0
-62
spec/javascripts/vue_mr_widget/ee_mr_widget_options_spec.js
spec/javascripts/vue_mr_widget/ee_mr_widget_options_spec.js
+184
-411
spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
.../javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+6
-88
spec/javascripts/vue_shared/security_reports/components/error_row_spec.js
.../vue_shared/security_reports/components/error_row_spec.js
+0
-25
spec/javascripts/vue_shared/security_reports/components/loading_row_spec.js
...ue_shared/security_reports/components/loading_row_spec.js
+0
-23
spec/javascripts/vue_shared/security_reports/components/report_section_spec.js
...shared/security_reports/components/report_section_spec.js
+12
-32
spec/javascripts/vue_shared/security_reports/grouped_security_reports_app_spec.js
...red/security_reports/grouped_security_reports_app_spec.js
+172
-0
spec/javascripts/vue_shared/security_reports/helpers/mixins_spec.js
...cripts/vue_shared/security_reports/helpers/mixins_spec.js
+0
-114
spec/javascripts/vue_shared/security_reports/helpers/utils_spec.js
...scripts/vue_shared/security_reports/helpers/utils_spec.js
+0
-73
spec/javascripts/vue_shared/security_reports/split_security_reports_app_spec.js
...hared/security_reports/split_security_reports_app_spec.js
+112
-0
spec/javascripts/vue_shared/security_reports/store/getters_spec.js
...scripts/vue_shared/security_reports/store/getters_spec.js
+71
-27
spec/javascripts/vue_shared/security_reports/store/utils_spec.js
...vascripts/vue_shared/security_reports/store/utils_spec.js
+11
-5
No files found.
app/assets/javascripts/pipelines/pipeline_details_bundle.js
View file @
edeab723
...
@@ -7,8 +7,9 @@ import pipelineGraph from './components/graph/graph_component.vue';
...
@@ -7,8 +7,9 @@ import pipelineGraph from './components/graph/graph_component.vue';
import
pipelineHeader
from
'
./components/header_component.vue
'
;
import
pipelineHeader
from
'
./components/header_component.vue
'
;
import
eventHub
from
'
./event_hub
'
;
import
eventHub
from
'
./event_hub
'
;
import
SecurityReportApp
from
'
ee/
pipelines/components/security_reports/security_report
_app.vue
'
;
// eslint-disable-line import/first
import
SecurityReportApp
from
'
ee/
vue_shared/security_reports/split_security_reports
_app.vue
'
;
// eslint-disable-line import/first
import
SastSummaryWidget
from
'
ee/pipelines/components/security_reports/report_summary_widget.vue
'
;
// eslint-disable-line import/first
import
SastSummaryWidget
from
'
ee/pipelines/components/security_reports/report_summary_widget.vue
'
;
// eslint-disable-line import/first
import
store
from
'
ee/vue_shared/security_reports/store
'
;
// eslint-disable-line import/first
Vue
.
use
(
Translate
);
Vue
.
use
(
Translate
);
...
@@ -99,54 +100,23 @@ export default () => {
...
@@ -99,54 +100,23 @@ export default () => {
const
blobPath
=
datasetOptions
.
blobPath
;
const
blobPath
=
datasetOptions
.
blobPath
;
const
dependencyScanningEndpoint
=
datasetOptions
.
dependencyScanningEndpoint
;
const
dependencyScanningEndpoint
=
datasetOptions
.
dependencyScanningEndpoint
;
if
(
endpoint
)
{
mediator
.
fetchSastReport
(
endpoint
,
blobPath
)
.
then
(()
=>
{
// update the badge
if
(
mediator
.
store
.
state
.
securityReports
.
sast
.
newIssues
.
length
)
{
updateBadgeCount
(
mediator
.
store
.
state
.
securityReports
.
sast
.
newIssues
.
length
);
}
})
.
catch
(()
=>
{
Flash
(
__
(
'
Something went wrong while fetching SAST.
'
));
});
}
if
(
dependencyScanningEndpoint
)
{
mediator
.
fetchDependencyScanningReport
(
dependencyScanningEndpoint
)
.
then
(()
=>
{
// update the badge
if
(
mediator
.
store
.
state
.
securityReports
.
dependencyScanning
.
newIssues
.
length
)
{
updateBadgeCount
(
mediator
.
store
.
state
.
securityReports
.
dependencyScanning
.
newIssues
.
length
,
);
}
})
.
catch
(()
=>
{
Flash
(
__
(
'
Something went wrong while fetching Dependency Scanning.
'
));
});
}
// Widget summary
// Widget summary
// eslint-disable-next-line no-new
// eslint-disable-next-line no-new
new
Vue
({
new
Vue
({
el
:
sastSummary
,
el
:
sastSummary
,
store
,
components
:
{
components
:
{
SastSummaryWidget
,
SastSummaryWidget
,
},
},
data
()
{
methods
:
{
return
{
updateBadge
(
count
)
{
mediator
,
updateBadgeCount
(
count
);
}
;
}
,
},
},
render
(
createElement
)
{
render
(
createElement
)
{
return
createElement
(
'
sast-summary-widget
'
,
{
return
createElement
(
'
sast-summary-widget
'
,
{
props
:
{
on
:
{
hasDependencyScanning
:
dependencyScanningEndpoint
!==
undefined
,
updateBadgeCount
:
this
.
updateBadge
,
hasSast
:
endpoint
!==
undefined
,
sastIssues
:
this
.
mediator
.
store
.
state
.
securityReports
.
sast
.
newIssues
.
length
,
dependencyScanningIssues
:
this
.
mediator
.
store
.
state
.
securityReports
.
dependencyScanning
.
newIssues
.
length
,
},
},
});
});
},
},
...
@@ -156,20 +126,24 @@ export default () => {
...
@@ -156,20 +126,24 @@ export default () => {
// eslint-disable-next-line no-new
// eslint-disable-next-line no-new
new
Vue
({
new
Vue
({
el
:
securityTab
,
el
:
securityTab
,
store
,
components
:
{
components
:
{
SecurityReportApp
,
SecurityReportApp
,
},
},
data
()
{
methods
:
{
return
{
updateBadge
(
count
)
{
mediator
,
updateBadgeCount
(
count
);
}
;
}
,
},
},
render
(
createElement
)
{
render
(
createElement
)
{
return
createElement
(
'
security-report-app
'
,
{
return
createElement
(
'
security-report-app
'
,
{
props
:
{
props
:
{
securityReports
:
this
.
mediator
.
store
.
state
.
securityReports
,
headBlobPath
:
blobPath
,
hasDependencyScanning
:
dependencyScanningEndpoint
!==
undefined
,
sastHeadPath
:
endpoint
,
hasSast
:
endpoint
!==
undefined
,
dependencyScanningHeadPath
:
dependencyScanningEndpoint
,
},
on
:
{
updateBadgeCount
:
this
.
updateBadge
,
},
},
});
});
},
},
...
...
app/assets/javascripts/pipelines/pipeline_details_mediator.js
View file @
edeab723
...
@@ -56,23 +56,4 @@ export default class pipelinesMediator {
...
@@ -56,23 +56,4 @@ export default class pipelinesMediator {
.
then
(
response
=>
this
.
successCallback
(
response
))
.
then
(
response
=>
this
.
successCallback
(
response
))
.
catch
(()
=>
this
.
errorCallback
());
.
catch
(()
=>
this
.
errorCallback
());
}
}
/**
* EE only
*/
fetchSastReport
(
endpoint
,
blobPath
)
{
return
PipelineService
.
getSecurityReport
(
endpoint
)
.
then
(
response
=>
response
.
json
())
.
then
((
data
)
=>
{
this
.
store
.
storeSastReport
(
data
,
blobPath
);
});
}
fetchDependencyScanningReport
(
endpoint
,
blobPath
)
{
return
PipelineService
.
getSecurityReport
(
endpoint
)
.
then
(
response
=>
response
.
json
())
.
then
((
data
)
=>
{
this
.
store
.
storeDependencyScanningReport
(
data
,
blobPath
);
});
}
}
}
app/assets/javascripts/pipelines/stores/pipeline_store.js
View file @
edeab723
import
securityState
from
'
ee/vue_shared/security_reports/helpers/state
'
;
import
{
setSastReport
,
}
from
'
ee/vue_shared/security_reports/helpers/utils
'
;
export
default
class
PipelineStore
{
export
default
class
PipelineStore
{
constructor
()
{
constructor
()
{
this
.
state
=
{};
this
.
state
=
{};
this
.
state
.
pipeline
=
{};
this
.
state
.
pipeline
=
{};
/* EE only */
this
.
state
.
securityReports
=
securityState
;
}
}
storePipeline
(
pipeline
=
{})
{
storePipeline
(
pipeline
=
{})
{
this
.
state
.
pipeline
=
pipeline
;
this
.
state
.
pipeline
=
pipeline
;
}
}
/**
* EE only
*/
storeSastReport
(
data
,
blobPath
)
{
Object
.
assign
(
this
.
state
.
securityReports
.
sast
,
setSastReport
({
head
:
data
,
headBlobPath
:
blobPath
}),
);
}
storeDependencyScanningReport
(
data
,
blobPath
)
{
Object
.
assign
(
this
.
state
.
securityReports
.
dependencyScanning
,
setSastReport
({
head
:
data
,
headBlobPath
:
blobPath
}),
);
}
}
}
app/assets/stylesheets/pages/merge_requests.scss
View file @
edeab723
...
@@ -28,6 +28,10 @@
...
@@ -28,6 +28,10 @@
border-top
:
solid
1px
$border-color
;
border-top
:
solid
1px
$border-color
;
}
}
.mr-widget-border-top
{
border-top
:
1px
solid
$border-color
;
}
.mr-widget-footer
{
.mr-widget-footer
{
padding
:
0
;
padding
:
0
;
}
}
...
...
app/views/projects/merge_requests/show.html.haml
View file @
edeab723
...
@@ -27,6 +27,11 @@
...
@@ -27,6 +27,11 @@
window
.
gl
.
mrWidgetData
.
enable_squash_before_merge
=
'
#{
@merge_request
.
project
.
feature_available?
(
:merge_request_squash
)
}
'
===
'
true
'
;
window
.
gl
.
mrWidgetData
.
enable_squash_before_merge
=
'
#{
@merge_request
.
project
.
feature_available?
(
:merge_request_squash
)
}
'
===
'
true
'
;
window
.
gl
.
mrWidgetData
.
squash_before_merge_help_path
=
'
#{
help_page_path
(
"user/project/merge_requests/squash_and_merge"
)
}
'
;
window
.
gl
.
mrWidgetData
.
squash_before_merge_help_path
=
'
#{
help_page_path
(
"user/project/merge_requests/squash_and_merge"
)
}
'
;
window
.
gl
.
mrWidgetData
.
sast_help_path
=
'
#{
help_page_path
(
"user/project/merge_requests/sast"
)
}
'
;
window
.
gl
.
mrWidgetData
.
sast_container_help_path
=
'
#{
help_page_path
(
"user/project/merge_requests/container_scanning"
)
}
'
;
window
.
gl
.
mrWidgetData
.
dast_help_path
=
'
#{
help_page_path
(
"user/project/merge_requests/dast"
)
}
'
;
window
.
gl
.
mrWidgetData
.
dependency_scanning_help_path
=
'
#{
help_page_path
(
"user/project/merge_requests/dependency_scanning"
)
}
'
;
#js-vue-mr-widget
.mr-widget
#js-vue-mr-widget
.mr-widget
.content-block.content-block-small.emoji-list-container.js-noteable-awards
.content-block.content-block-small.emoji-list-container.js-noteable-awards
...
...
app/views/projects/pipelines/_with_tabs.html.haml
View file @
edeab723
...
@@ -65,4 +65,6 @@
...
@@ -65,4 +65,6 @@
#js-tab-security
.build-security.tab-pane
#js-tab-security
.build-security.tab-pane
#js-security-report-app
{
data:
{
endpoint:
expose_sast_data
?
sast_artifact_url
(
@pipeline
)
:
nil
,
#js-security-report-app
{
data:
{
endpoint:
expose_sast_data
?
sast_artifact_url
(
@pipeline
)
:
nil
,
blob_path:
blob_path
,
blob_path:
blob_path
,
dependency_scanning_endpoint:
expose_dependency_data
?
dependency_scanning_artifact_url
(
@pipeline
)
:
nil
}
}
dependency_scanning_endpoint:
expose_dependency_data
?
dependency_scanning_artifact_url
(
@pipeline
)
:
nil
,
sast_help_path:
help_page_path
(
'user/project/merge_requests/sast'
),
dependency_scanning_help_path:
help_page_path
(
'user/project/merge_requests/dependency_scanning'
)}
}
ee/app/assets/javascripts/pipelines/components/security_reports/report_summary_widget.vue
View file @
edeab723
<
script
>
<
script
>
import
$
from
'
jquery
'
;
import
{
mapState
}
from
'
vuex
'
;
import
{
n__
,
s__
}
from
'
~/locale
'
;
import
$
from
'
jquery
'
;
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
{
n__
,
s__
}
from
'
~/locale
'
;
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
LoadingIcon
from
'
~/vue_shared/components/loading_icon.vue
'
;
export
default
{
export
default
{
name
:
'
SummaryReport
'
,
name
:
'
SummaryReport
'
,
components
:
{
components
:
{
CiIcon
,
CiIcon
,
LoadingIcon
,
},
computed
:
{
...
mapState
([
'
sast
'
,
'
dependencyScanning
'
]),
sastLink
()
{
return
this
.
link
(
this
.
sast
.
newIssues
.
length
);
},
},
props
:
{
dependencyScanningLink
()
{
sastIssues
:
{
return
this
.
link
(
this
.
dependencyScanning
.
newIssues
.
length
);
type
:
Number
,
required
:
false
,
default
:
0
,
},
dependencyScanningIssues
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
hasDependencyScanning
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
hasSast
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
},
computed
:
{
sastIcon
()
{
sastLink
()
{
return
this
.
statusIcon
(
this
.
hasSastError
,
this
.
sast
.
newIssues
.
length
);
return
this
.
link
(
this
.
sastIssues
);
},
dependencyScanningLink
()
{
return
this
.
link
(
this
.
dependencyScanningIssues
);
},
sastIcon
()
{
return
this
.
statusIcon
(
this
.
sastIssues
);
},
dependencyScanningIcon
()
{
return
this
.
statusIcon
(
this
.
dependencyScanningIssues
);
},
},
},
methods
:
{
dependencyScanningIcon
()
{
openTab
()
{
return
this
.
statusIcon
(
// This opens a tab outside of this Vue application
this
.
hasDependencyScanningError
,
// It opens the securty report tab in the pipelines page and updates the URL
this
.
dependencyScanning
.
newIssues
.
length
,
// This is needed because the tabs are built in haml+jquery
);
$
(
'
.pipelines-tabs a[data-action="security"]
'
).
tab
(
'
show
'
);
},
},
hasSast
()
{
link
(
issues
)
{
return
this
.
sast
.
paths
.
head
!==
null
;
if
(
issues
>
0
)
{
},
return
n__
(
hasDependencyScanning
()
{
'
%d vulnerability
'
,
return
this
.
dependencyScanning
.
paths
.
head
!==
null
;
'
%d vulnerabilities
'
,
},
issues
,
isLoadingSast
()
{
);
return
this
.
sast
.
isLoading
;
}
},
return
s__
(
'
ciReport|no vulnerabilities
'
);
isLoadingDependencyScanning
()
{
},
return
this
.
dependencyScanning
.
isLoading
;
statusIcon
(
issues
)
{
},
if
(
issues
>
0
)
{
hasSastError
()
{
return
{
return
this
.
sast
.
hasError
;
group
:
'
warning
'
,
},
icon
:
'
status_warning
'
,
hasDependencyScanningError
()
{
};
return
this
.
dependencyScanning
.
hasError
;
}
},
},
methods
:
{
openTab
()
{
// This opens a tab outside of this Vue application
// It opens the securty report tab in the pipelines page and updates the URL
// This is needed because the tabs are built in haml+jquery
$
(
'
.pipelines-tabs a[data-action="security"]
'
).
tab
(
'
show
'
);
},
link
(
issuesCount
=
0
)
{
if
(
issuesCount
>
0
)
{
return
n__
(
'
%d vulnerability
'
,
'
%d vulnerabilities
'
,
issuesCount
);
}
return
s__
(
'
ciReport|no vulnerabilities
'
);
},
statusIcon
(
failed
=
true
,
issuesCount
=
0
)
{
if
(
issuesCount
>
0
||
failed
)
{
return
{
return
{
group
:
'
success
'
,
group
:
'
warning
'
,
icon
:
'
status_
success
'
,
icon
:
'
status_
warning
'
,
};
};
},
}
return
{
group
:
'
success
'
,
icon
:
'
status_success
'
,
};
},
},
};
},
};
</
script
>
</
script
>
<
template
>
<
template
>
<div>
<div>
...
@@ -82,7 +81,12 @@
...
@@ -82,7 +81,12 @@
class=
"well-segment flex js-sast-summary"
class=
"well-segment flex js-sast-summary"
v-if=
"hasSast"
v-if=
"hasSast"
>
>
<loading-icon
v-if=
"isLoadingSast"
/>
<ci-icon
<ci-icon
v-else
:status=
"sastIcon"
:status=
"sastIcon"
class=
"flex flex-align-self-center"
class=
"flex flex-align-self-center"
/>
/>
...
@@ -90,21 +94,33 @@
...
@@ -90,21 +94,33 @@
<span
<span
class=
"prepend-left-10 flex flex-align-self-center"
class=
"prepend-left-10 flex flex-align-self-center"
>
>
{{
s__
(
'
ciReport|SAST detected
'
)
}}
<template
v-if=
"hasSastError"
>
<button
{{
s__
(
'
ciReport|SAST resulted in error while loading results
'
)
}}
type=
"button"
</
template
>
class=
"btn-link btn-blank prepend-left-5"
<
template
v-else-if=
"isLoadingSast"
>
@
click=
"openTab"
{{
s__
(
'
ciReport|SAST is loading
'
)
}}
>
</
template
>
{{
sastLink
}}
<
template
v-else
>
</button>
{{
s__
(
'
ciReport|SAST detected
'
)
}}
<button
type=
"button"
class=
"btn-link btn-blank prepend-left-5"
@
click=
"openTab"
>
{{
sastLink
}}
</button>
</
template
>
</span>
</span>
</div>
</div>
<div
<div
class=
"well-segment flex js-dss-summary"
class=
"well-segment flex js-dss-summary"
v-if=
"hasDependencyScanning"
v-if=
"hasDependencyScanning"
>
>
<loading-icon
v-if=
"dependencyScanning.isLoading"
/>
<ci-icon
<ci-icon
v-else
:status=
"dependencyScanningIcon"
:status=
"dependencyScanningIcon"
class=
"flex flex-align-self-center"
class=
"flex flex-align-self-center"
/>
/>
...
@@ -112,14 +128,22 @@
...
@@ -112,14 +128,22 @@
<span
<span
class=
"prepend-left-10 flex flex-align-self-center"
class=
"prepend-left-10 flex flex-align-self-center"
>
>
{{
s__
(
'
ciReport|Dependency scanning detected
'
)
}}
<
template
v-if=
"hasDependencyScanningError"
>
<button
{{
s__
(
'
ciReport|Dependency scanning resulted in error while loading results
'
)
}}
type=
"button"
</
template
>
class=
"btn-link btn-blank prepend-left-5"
<
template
v-else-if=
"isLoadingDependencyScanning"
>
@
click=
"openTab"
{{
s__
(
'
ciReport|Dependency scanning is loading
'
)
}}
>
</
template
>
{{
dependencyScanningLink
}}
<
template
v-else
>
</button>
{{
s__
(
'
ciReport|Dependency scanning detected
'
)
}}
<button
type=
"button"
class=
"btn-link btn-blank prepend-left-5"
@
click=
"openTab"
>
{{
dependencyScanningLink
}}
</button>
</
template
>
</span>
</span>
</div>
</div>
</div>
</div>
...
...
ee/app/assets/javascripts/pipelines/components/security_reports/security_report_app.vue
deleted
100644 → 0
View file @
a990e800
<
script
>
import
ReportSection
from
'
ee/vue_shared/security_reports/components/report_section.vue
'
;
import
securityMixin
from
'
ee/vue_shared/security_reports/mixins/security_report_mixin
'
;
import
LoadingIcon
from
'
~/vue_shared/components/loading_icon.vue
'
;
import
{
SAST
}
from
'
ee/vue_shared/security_reports/helpers/constants
'
;
export
default
{
name
:
'
SecurityReportTab
'
,
components
:
{
LoadingIcon
,
ReportSection
,
},
mixins
:
[
securityMixin
],
sast
:
SAST
,
props
:
{
securityReports
:
{
type
:
Object
,
required
:
true
,
},
hasDependencyScanning
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
hasSast
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
};
</
script
>
<
template
>
<div
class=
"pipeline-tab-content"
>
<report-section
v-if=
"hasSast"
class=
"js-sast-widget"
:type=
"$options.sast"
:status=
"checkReportStatus(securityReports.sast.isLoading, securityReports.sast.hasError)"
:loading-text=
"translateText('security').loading"
:error-text=
"translateText('security').error"
:success-text=
"sastText(securityReports.sast.newIssues, securityReports.sast.resolvedIssues)"
:unresolved-issues=
"securityReports.sast.newIssues"
:resolved-issues=
"securityReports.sast.resolvedIssues"
:all-issues=
"securityReports.sast.allIssues"
/>
<report-section
v-if=
"hasDependencyScanning"
class=
"js-dependency-scanning-widget"
:class=
"
{ 'prepend-top-20': hasSast }"
:type="$options.sast"
:status="checkReportStatus(
securityReports.dependencyScanning.isLoading,
securityReports.dependencyScanning.hasError
)"
:loading-text="translateText('dependency scanning').loading"
:error-text="translateText('dependency scanning').error"
:success-text="depedencyScanningText(
securityReports.dependencyScanning.newIssues,
securityReports.dependencyScanning.resolvedIssues
)"
:unresolved-issues="securityReports.dependencyScanning.newIssues"
:resolved-issues="securityReports.dependencyScanning.resolvedIssues"
:all-issues="securityReports.dependencyScanning.allIssues"
/>
</div>
</
template
>
ee/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
View file @
edeab723
import
{
n__
,
s__
,
__
}
from
'
~/locale
'
;
import
{
n__
,
s__
,
__
,
sprintf
}
from
'
~/locale
'
;
import
CEWidgetOptions
from
'
~/vue_merge_request_widget/mr_widget_options
'
;
import
CEWidgetOptions
from
'
~/vue_merge_request_widget/mr_widget_options
'
;
import
WidgetApprovals
from
'
./components/approvals/mr_widget_approvals
'
;
import
WidgetApprovals
from
'
./components/approvals/mr_widget_approvals
'
;
import
GeoSecondaryNode
from
'
./components/states/mr_widget_secondary_geo_node
'
;
import
GeoSecondaryNode
from
'
./components/states/mr_widget_secondary_geo_node
'
;
import
ReportSection
from
'
../vue_shared/security_reports/components/report_section.vue
'
;
import
ReportSection
from
'
../vue_shared/security_reports/components/report_section.vue
'
;
import
securityMixin
from
'
../vue_shared/security_reports/mixins/security_report_mixin
'
;
import
GroupedSecurityReportsApp
from
'
../vue_shared/security_reports/grouped_security_reports_app.vue
'
;
import
{
import
reportsMixin
from
'
../vue_shared/security_reports/mixins/reports_mixin
'
;
SAST
,
DAST
,
SAST_CONTAINER
,
}
from
'
../vue_shared/security_reports/helpers/constants
'
;
export
default
{
export
default
{
extends
:
CEWidgetOptions
,
extends
:
CEWidgetOptions
,
components
:
{
components
:
{
'
mr-widget-approvals
'
:
WidgetApprovals
,
'
mr-widget-approvals
'
:
WidgetApprovals
,
'
mr-widget-geo-secondary-node
'
:
GeoSecondaryNode
,
'
mr-widget-geo-secondary-node
'
:
GeoSecondaryNode
,
GroupedSecurityReportsApp
,
ReportSection
,
ReportSection
,
},
},
mixins
:
[
securityMixin
],
mixins
:
[
reportsMixin
],
dast
:
DAST
,
sast
:
SAST
,
sastContainer
:
SAST_CONTAINER
,
data
()
{
data
()
{
return
{
return
{
isLoadingCodequality
:
false
,
isLoadingCodequality
:
false
,
isLoadingPerformance
:
false
,
isLoadingPerformance
:
false
,
isLoadingSecurity
:
false
,
isLoadingDocker
:
false
,
isLoadingDast
:
false
,
isLoadingDependencyScanning
:
false
,
loadingCodequalityFailed
:
false
,
loadingCodequalityFailed
:
false
,
loadingPerformanceFailed
:
false
,
loadingPerformanceFailed
:
false
,
loadingSecurityFailed
:
false
,
loadingDockerFailed
:
false
,
loadingDastFailed
:
false
,
loadingDependencyScanningFailed
:
false
,
};
};
},
},
computed
:
{
computed
:
{
...
@@ -45,21 +31,34 @@ export default {
...
@@ -45,21 +31,34 @@ export default {
const
{
codeclimate
}
=
this
.
mr
;
const
{
codeclimate
}
=
this
.
mr
;
return
codeclimate
&&
codeclimate
.
head_path
&&
codeclimate
.
base_path
;
return
codeclimate
&&
codeclimate
.
head_path
&&
codeclimate
.
base_path
;
},
},
hasCodequalityIssues
()
{
return
(
this
.
mr
.
codeclimateMetrics
&&
((
this
.
mr
.
codeclimateMetrics
.
newIssues
&&
this
.
mr
.
codeclimateMetrics
.
newIssues
.
length
>
0
)
||
(
this
.
mr
.
codeclimateMetrics
.
resolvedIssues
&&
this
.
mr
.
codeclimateMetrics
.
resolvedIssues
.
length
>
0
))
);
},
hasPerformanceMetrics
()
{
return
(
this
.
mr
.
performanceMetrics
&&
((
this
.
mr
.
performanceMetrics
.
degraded
&&
this
.
mr
.
performanceMetrics
.
degraded
.
length
>
0
)
||
(
this
.
mr
.
performanceMetrics
.
improved
&&
this
.
mr
.
performanceMetrics
.
improved
.
length
>
0
)
||
(
this
.
mr
.
performanceMetrics
.
neutral
&&
this
.
mr
.
performanceMetrics
.
neutral
.
length
>
0
))
);
},
shouldRenderPerformance
()
{
shouldRenderPerformance
()
{
const
{
performance
}
=
this
.
mr
;
const
{
performance
}
=
this
.
mr
;
return
performance
&&
performance
.
head_path
&&
performance
.
base_path
;
return
performance
&&
performance
.
head_path
&&
performance
.
base_path
;
},
},
shouldRenderSecurityReport
()
{
shouldRenderSecurityReport
()
{
return
this
.
mr
.
sast
&&
this
.
mr
.
sast
.
head_path
;
return
(
},
(
this
.
mr
.
sast
&&
this
.
mr
.
sast
.
head_path
)
||
shouldRenderDockerReport
()
{
(
this
.
mr
.
sastContainer
&&
this
.
mr
.
sastContainer
.
head_path
)
||
return
this
.
mr
.
sastContainer
&&
this
.
mr
.
sastContainer
.
head_path
;
(
this
.
mr
.
dast
&&
this
.
mr
.
dast
.
head_path
)
||
},
(
this
.
mr
.
dependencyScanning
&&
this
.
mr
.
dependencyScanning
.
head_path
)
shouldRenderDastReport
()
{
);
return
this
.
mr
.
dast
&&
this
.
mr
.
dast
.
head_path
;
},
shouldRenderDependencyReport
()
{
return
this
.
mr
.
dependencyScanning
&&
this
.
mr
.
dependencyScanning
.
head_path
;
},
},
codequalityText
()
{
codequalityText
()
{
const
{
newIssues
,
resolvedIssues
}
=
this
.
mr
.
codeclimateMetrics
;
const
{
newIssues
,
resolvedIssues
}
=
this
.
mr
.
codeclimateMetrics
;
...
@@ -71,13 +70,7 @@ export default {
...
@@ -71,13 +70,7 @@ export default {
text
.
push
(
s__
(
'
ciReport|Code quality
'
));
text
.
push
(
s__
(
'
ciReport|Code quality
'
));
if
(
resolvedIssues
.
length
)
{
if
(
resolvedIssues
.
length
)
{
text
.
push
(
text
.
push
(
n__
(
'
improved on %d point
'
,
'
improved on %d points
'
,
resolvedIssues
.
length
));
n__
(
'
improved on %d point
'
,
'
improved on %d points
'
,
resolvedIssues
.
length
,
),
);
}
}
if
(
newIssues
.
length
>
0
&&
resolvedIssues
.
length
>
0
)
{
if
(
newIssues
.
length
>
0
&&
resolvedIssues
.
length
>
0
)
{
...
@@ -85,13 +78,7 @@ export default {
...
@@ -85,13 +78,7 @@ export default {
}
}
if
(
newIssues
.
length
)
{
if
(
newIssues
.
length
)
{
text
.
push
(
text
.
push
(
n__
(
'
degraded on %d point
'
,
'
degraded on %d points
'
,
newIssues
.
length
));
n__
(
'
degraded on %d point
'
,
'
degraded on %d points
'
,
newIssues
.
length
,
),
);
}
}
}
}
...
@@ -108,13 +95,7 @@ export default {
...
@@ -108,13 +95,7 @@ export default {
text
.
push
(
s__
(
'
ciReport|Performance metrics
'
));
text
.
push
(
s__
(
'
ciReport|Performance metrics
'
));
if
(
improved
.
length
)
{
if
(
improved
.
length
)
{
text
.
push
(
text
.
push
(
n__
(
'
improved on %d point
'
,
'
improved on %d points
'
,
improved
.
length
));
n__
(
'
improved on %d point
'
,
'
improved on %d points
'
,
improved
.
length
,
),
);
}
}
if
(
improved
.
length
>
0
&&
degraded
.
length
>
0
)
{
if
(
improved
.
length
>
0
&&
degraded
.
length
>
0
)
{
...
@@ -122,75 +103,19 @@ export default {
...
@@ -122,75 +103,19 @@ export default {
}
}
if
(
degraded
.
length
)
{
if
(
degraded
.
length
)
{
text
.
push
(
text
.
push
(
n__
(
'
degraded on %d point
'
,
'
degraded on %d points
'
,
degraded
.
length
));
n__
(
'
degraded on %d point
'
,
'
degraded on %d points
'
,
degraded
.
length
,
),
);
}
}
}
}
return
text
.
join
(
''
);
return
text
.
join
(
''
);
},
},
securityText
()
{
const
{
newIssues
,
resolvedIssues
,
allIssues
}
=
this
.
mr
.
securityReport
;
return
this
.
sastText
(
newIssues
,
resolvedIssues
,
allIssues
);
},
dependencyScanningText
()
{
const
{
newIssues
,
resolvedIssues
,
allIssues
}
=
this
.
mr
.
dependencyScanningReport
;
return
this
.
depedencyScanningText
(
newIssues
,
resolvedIssues
,
allIssues
);
},
dockerText
()
{
const
{
vulnerabilities
,
approved
,
unapproved
}
=
this
.
mr
.
dockerReport
;
return
this
.
sastContainerText
(
vulnerabilities
,
approved
,
unapproved
);
},
getDastText
()
{
return
this
.
dastText
(
this
.
mr
.
dastReport
);
},
codequalityStatus
()
{
codequalityStatus
()
{
return
this
.
checkReportStatus
(
return
this
.
checkReportStatus
(
this
.
isLoadingCodequality
,
this
.
loadingCodequalityFailed
);
this
.
isLoadingCodequality
,
this
.
loadingCodequalityFailed
,
);
},
},
performanceStatus
()
{
performanceStatus
()
{
return
this
.
checkReportStatus
(
return
this
.
checkReportStatus
(
this
.
isLoadingPerformance
,
this
.
loadingPerformanceFailed
);
this
.
isLoadingPerformance
,
this
.
loadingPerformanceFailed
,
);
},
securityStatus
()
{
return
this
.
checkReportStatus
(
this
.
isLoadingSecurity
,
this
.
loadingSecurityFailed
,
);
},
dockerStatus
()
{
return
this
.
checkReportStatus
(
this
.
isLoadingDocker
,
this
.
loadingDockerFailed
,
);
},
dastStatus
()
{
return
this
.
checkReportStatus
(
this
.
isLoadingDast
,
this
.
loadingDastFailed
);
},
dependencyScanningStatus
()
{
return
this
.
checkReportStatus
(
this
.
isLoadingDependencyScanning
,
this
.
loadingDependencyScanningFailed
,
);
},
},
},
},
methods
:
{
methods
:
{
...
@@ -199,10 +124,7 @@ export default {
...
@@ -199,10 +124,7 @@ export default {
this
.
isLoadingCodequality
=
true
;
this
.
isLoadingCodequality
=
true
;
Promise
.
all
([
Promise
.
all
([
this
.
service
.
fetchReport
(
head_path
),
this
.
service
.
fetchReport
(
base_path
)])
this
.
service
.
fetchReport
(
head_path
),
this
.
service
.
fetchReport
(
base_path
),
])
.
then
(
values
=>
{
.
then
(
values
=>
{
this
.
mr
.
compareCodeclimateMetrics
(
this
.
mr
.
compareCodeclimateMetrics
(
values
[
0
],
values
[
0
],
...
@@ -223,10 +145,7 @@ export default {
...
@@ -223,10 +145,7 @@ export default {
this
.
isLoadingPerformance
=
true
;
this
.
isLoadingPerformance
=
true
;
Promise
.
all
([
Promise
.
all
([
this
.
service
.
fetchReport
(
head_path
),
this
.
service
.
fetchReport
(
base_path
)])
this
.
service
.
fetchReport
(
head_path
),
this
.
service
.
fetchReport
(
base_path
),
])
.
then
(
values
=>
{
.
then
(
values
=>
{
this
.
mr
.
comparePerformanceMetrics
(
values
[
0
],
values
[
1
]);
this
.
mr
.
comparePerformanceMetrics
(
values
[
0
],
values
[
1
]);
this
.
isLoadingPerformance
=
false
;
this
.
isLoadingPerformance
=
false
;
...
@@ -236,122 +155,16 @@ export default {
...
@@ -236,122 +155,16 @@ export default {
this
.
loadingPerformanceFailed
=
true
;
this
.
loadingPerformanceFailed
=
true
;
});
});
},
},
/**
* Sast report can either have 2 reports or just 1
* When it has 2 we need to compare them
* When it has 1 we render the output given
*/
fetchSecurity
()
{
const
{
sast
}
=
this
.
mr
;
this
.
isLoadingSecurity
=
true
;
translateText
(
type
)
{
return
{
if
(
sast
.
base_path
&&
sast
.
head_path
)
{
error
:
sprintf
(
s__
(
'
ciReport|Failed to load %{reportName} report
'
),
{
Promise
.
all
([
reportName
:
type
,
this
.
service
.
fetchReport
(
sast
.
head_path
),
}),
this
.
service
.
fetchReport
(
sast
.
base_path
),
loading
:
sprintf
(
s__
(
'
ciReport|Loading %{reportName} report
'
),
{
])
reportName
:
type
,
.
then
(
values
=>
{
}),
this
.
handleSecuritySuccess
({
};
head
:
values
[
0
],
headBlobPath
:
this
.
mr
.
headBlobPath
,
base
:
values
[
1
],
baseBlobPath
:
this
.
mr
.
baseBlobPath
,
});
})
.
catch
(()
=>
this
.
handleSecurityError
());
}
else
if
(
sast
.
head_path
)
{
this
.
service
.
fetchReport
(
sast
.
head_path
)
.
then
(
data
=>
{
this
.
handleSecuritySuccess
({
head
:
data
,
headBlobPath
:
this
.
mr
.
headBlobPath
,
});
})
.
catch
(()
=>
this
.
handleSecurityError
());
}
},
fetchDependencyScanning
()
{
const
{
dependencyScanning
}
=
this
.
mr
;
this
.
isLoadingDependencyScanning
=
true
;
if
(
dependencyScanning
.
base_path
&&
dependencyScanning
.
head_path
)
{
Promise
.
all
([
this
.
service
.
fetchReport
(
dependencyScanning
.
head_path
),
this
.
service
.
fetchReport
(
dependencyScanning
.
base_path
),
])
.
then
(
values
=>
{
this
.
mr
.
setDependencyScanningReport
({
head
:
values
[
0
],
headBlobPath
:
this
.
mr
.
headBlobPath
,
base
:
values
[
1
],
baseBlobPath
:
this
.
mr
.
baseBlobPath
,
});
this
.
isLoadingDependencyScanning
=
false
;
})
.
catch
(()
=>
{
this
.
isLoadingDependencyScanning
=
false
;
this
.
loadingDependencyScanningFailed
=
true
;
});
}
else
if
(
dependencyScanning
.
head_path
)
{
this
.
service
.
fetchReport
(
dependencyScanning
.
head_path
)
.
then
(
data
=>
{
this
.
mr
.
setDependencyScanningReport
({
head
:
data
,
headBlobPath
:
this
.
mr
.
headBlobPath
,
});
this
.
isLoadingDependencyScanning
=
false
;
})
.
catch
(()
=>
{
this
.
isLoadingDependencyScanning
=
false
;
this
.
loadingDependencyScanningFailed
=
true
;
});
}
},
handleSecuritySuccess
(
data
)
{
this
.
mr
.
setSecurityReport
(
data
);
this
.
isLoadingSecurity
=
false
;
},
handleSecurityError
()
{
this
.
isLoadingSecurity
=
false
;
this
.
loadingSecurityFailed
=
true
;
},
fetchDockerReport
()
{
const
{
head_path
}
=
this
.
mr
.
sastContainer
;
this
.
isLoadingDocker
=
true
;
this
.
service
.
fetchReport
(
head_path
)
.
then
(
data
=>
{
this
.
mr
.
setDockerReport
(
data
);
this
.
isLoadingDocker
=
false
;
})
.
catch
(()
=>
{
this
.
isLoadingDocker
=
false
;
this
.
loadingDockerFailed
=
true
;
});
},
fetchDastReport
()
{
this
.
isLoadingDast
=
true
;
this
.
service
.
fetchReport
(
this
.
mr
.
dast
.
head_path
)
.
then
(
data
=>
{
this
.
mr
.
setDastReport
(
data
);
this
.
isLoadingDast
=
false
;
})
.
catch
(()
=>
{
this
.
isLoadingDast
=
false
;
this
.
loadingDastFailed
=
true
;
});
},
},
},
},
created
()
{
created
()
{
...
@@ -362,22 +175,6 @@ export default {
...
@@ -362,22 +175,6 @@ export default {
if
(
this
.
shouldRenderPerformance
)
{
if
(
this
.
shouldRenderPerformance
)
{
this
.
fetchPerformance
();
this
.
fetchPerformance
();
}
}
if
(
this
.
shouldRenderSecurityReport
)
{
this
.
fetchSecurity
();
}
if
(
this
.
shouldRenderDockerReport
)
{
this
.
fetchDockerReport
();
}
if
(
this
.
shouldRenderDastReport
)
{
this
.
fetchDastReport
();
}
if
(
this
.
shouldRenderDependencyReport
)
{
this
.
fetchDependencyScanning
();
}
},
},
template
:
`
template
:
`
<div class="mr-state-widget prepend-top-default">
<div class="mr-state-widget prepend-top-default">
...
@@ -397,7 +194,7 @@ export default {
...
@@ -397,7 +194,7 @@ export default {
v-if="shouldRenderApprovals"
v-if="shouldRenderApprovals"
:mr="mr"
:mr="mr"
:service="service"
:service="service"
/>
/>
<report-section
<report-section
class="js-codequality-widget"
class="js-codequality-widget"
v-if="shouldRenderCodeQuality"
v-if="shouldRenderCodeQuality"
...
@@ -408,6 +205,7 @@ export default {
...
@@ -408,6 +205,7 @@ export default {
:success-text="codequalityText"
:success-text="codequalityText"
:unresolved-issues="mr.codeclimateMetrics.newIssues"
:unresolved-issues="mr.codeclimateMetrics.newIssues"
:resolved-issues="mr.codeclimateMetrics.resolvedIssues"
:resolved-issues="mr.codeclimateMetrics.resolvedIssues"
:has-issues="hasCodequalityIssues"
/>
/>
<report-section
<report-section
class="js-performance-widget"
class="js-performance-widget"
...
@@ -420,52 +218,24 @@ export default {
...
@@ -420,52 +218,24 @@ export default {
:unresolved-issues="mr.performanceMetrics.degraded"
:unresolved-issues="mr.performanceMetrics.degraded"
:resolved-issues="mr.performanceMetrics.improved"
:resolved-issues="mr.performanceMetrics.improved"
:neutral-issues="mr.performanceMetrics.neutral"
:neutral-issues="mr.performanceMetrics.neutral"
:has-issues="hasPerformanceMetrics"
/>
/>
<report-section
<grouped-security-reports-app
class="js-sast-widget"
v-if="shouldRenderSecurityReport"
v-if="shouldRenderSecurityReport"
:type="$options.sast"
:head-blob-path="mr.headBlobPath"
:status="securityStatus"
:base-blob-path="mr.baseBlobPath"
:loading-text="translateText('security').loading"
:sast-head-path="mr.sast.head_path"
:error-text="translateText('security').error"
:sast-base-path="mr.sast.base_path"
:success-text="securityText"
:sast-help-path="mr.sastHelp"
:unresolved-issues="mr.securityReport.newIssues"
:dast-head-path="mr.dast.head_path"
:resolved-issues="mr.securityReport.resolvedIssues"
:dast-base-path="mr.dast.base_path"
:all-issues="mr.securityReport.allIssues"
:dast-help-path="mr.dastHelp"
/>
:sast-container-head-path="mr.sastContainer.head_path"
<report-section
:sast-container-base-path="mr.sastContainer.base_path"
class="js-dependency-scanning-widget"
:sast-container-help-path="mr.sastContainerHelp"
v-if="shouldRenderDependencyReport"
:dependency-scanning-head-path="mr.dependencyScanning.head_path"
:type="$options.sast"
:dependency-scanning-base-path="mr.dependencyScanning.base_path"
:status="dependencyScanningStatus"
:dependency-scanning-help-path="mr.dependencyScanningHelp"
:loading-text="translateText('dependency scanning').loading"
:error-text="translateText('dependency scanning').error"
:success-text="dependencyScanningText"
:unresolved-issues="mr.dependencyScanningReport.newIssues"
:resolved-issues="mr.dependencyScanningReport.resolvedIssues"
:all-issues="mr.dependencyScanningReport.allIssues"
/>
<report-section
class="js-docker-widget"
v-if="shouldRenderDockerReport"
:type="$options.sastContainer"
:status="dockerStatus"
:loading-text="translateText('sast:container').loading"
:error-text="translateText('sast:container').error"
:success-text="dockerText"
:unresolved-issues="mr.dockerReport.unapproved"
:neutral-issues="mr.dockerReport.approved"
:info-text="sastContainerInformationText()"
/>
<report-section
class="js-dast-widget"
v-if="shouldRenderDastReport"
:type="$options.dast"
:status="dastStatus"
:loading-text="translateText('DAST').loading"
:error-text="translateText('DAST').error"
:success-text="getDastText"
:unresolved-issues="mr.dastReport"
/>
/>
<div class="mr-widget-section">
<div class="mr-widget-section">
<component
<component
...
...
ee/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
View file @
edeab723
import
CEMergeRequestStore
from
'
~/vue_merge_request_widget/stores/mr_widget_store
'
;
import
CEMergeRequestStore
from
'
~/vue_merge_request_widget/stores/mr_widget_store
'
;
import
{
import
{
filterByKey
}
from
'
../../vue_shared/security_reports/store/utils
'
;
parseCodeclimateMetrics
,
filterByKey
,
setSastContainerReport
,
setSastReport
,
setDastReport
,
}
from
'
../../vue_shared/security_reports/helpers/utils
'
;
export
default
class
MergeRequestStore
extends
CEMergeRequestStore
{
export
default
class
MergeRequestStore
extends
CEMergeRequestStore
{
constructor
(
data
)
{
constructor
(
data
)
{
...
@@ -14,13 +8,17 @@ export default class MergeRequestStore extends CEMergeRequestStore {
...
@@ -14,13 +8,17 @@ export default class MergeRequestStore extends CEMergeRequestStore {
const
blobPath
=
data
.
blob_path
||
{};
const
blobPath
=
data
.
blob_path
||
{};
this
.
headBlobPath
=
blobPath
.
head_path
||
''
;
this
.
headBlobPath
=
blobPath
.
head_path
||
''
;
this
.
baseBlobPath
=
blobPath
.
base_path
||
''
;
this
.
baseBlobPath
=
blobPath
.
base_path
||
''
;
this
.
sast
=
data
.
sast
||
{};
this
.
sastContainer
=
data
.
sast_container
||
{};
this
.
dast
=
data
.
dast
||
{};
this
.
dependencyScanning
=
data
.
dependency_scanning
||
{};
this
.
sastHelp
=
data
.
sast_help_path
;
this
.
sastContainerHelp
=
data
.
sast_container_help_path
;
this
.
dastHelp
=
data
.
dast_help_path
;
this
.
dependencyScanningHelp
=
data
.
dependency_scanning_help_path
;
this
.
initCodeclimate
(
data
);
this
.
initCodeclimate
(
data
);
this
.
initPerformanceReport
(
data
);
this
.
initPerformanceReport
(
data
);
this
.
initSecurityReport
(
data
);
this
.
initDockerReport
(
data
);
this
.
initDastReport
(
data
);
this
.
initDependencyScanningReport
(
data
);
}
}
setData
(
data
)
{
setData
(
data
)
{
...
@@ -70,69 +68,13 @@ export default class MergeRequestStore extends CEMergeRequestStore {
...
@@ -70,69 +68,13 @@ export default class MergeRequestStore extends CEMergeRequestStore {
this
.
performanceMetrics
=
{
this
.
performanceMetrics
=
{
improved
:
[],
improved
:
[],
degraded
:
[],
degraded
:
[],
neutral
:
[],
};
};
}
}
initSecurityReport
(
data
)
{
this
.
sast
=
data
.
sast
;
this
.
securityReport
=
{
newIssues
:
[],
resolvedIssues
:
[],
allIssues
:
[],
};
}
initDockerReport
(
data
)
{
this
.
sastContainer
=
data
.
sast_container
;
this
.
dockerReport
=
{
approved
:
[],
unapproved
:
[],
vulnerabilities
:
[],
};
}
initDastReport
(
data
)
{
this
.
dast
=
data
.
dast
;
this
.
dastReport
=
[];
}
initDependencyScanningReport
(
data
)
{
this
.
dependencyScanning
=
data
.
dependency_scanning
;
this
.
dependencyScanningReport
=
{
newIssues
:
[],
resolvedIssues
:
[],
allIssues
:
[],
};
}
setSecurityReport
(
data
)
{
const
report
=
setSastReport
(
data
);
this
.
securityReport
.
newIssues
=
report
.
newIssues
;
this
.
securityReport
.
resolvedIssues
=
report
.
resolvedIssues
;
this
.
securityReport
.
allIssues
=
report
.
allIssues
;
}
setDockerReport
(
data
=
{})
{
const
report
=
setSastContainerReport
(
data
);
this
.
dockerReport
.
approved
=
report
.
approved
;
this
.
dockerReport
.
unapproved
=
report
.
unapproved
;
this
.
dockerReport
.
vulnerabilities
=
report
.
vulnerabilities
;
}
setDastReport
(
data
)
{
this
.
dastReport
=
setDastReport
(
data
);
}
setDependencyScanningReport
(
data
)
{
const
report
=
setSastReport
(
data
);
this
.
dependencyScanningReport
.
newIssues
=
report
.
newIssues
;
this
.
dependencyScanningReport
.
resolvedIssues
=
report
.
resolvedIssues
;
this
.
dependencyScanningReport
.
allIssues
=
report
.
allIssues
;
}
compareCodeclimateMetrics
(
headIssues
,
baseIssues
,
headBlobPath
,
baseBlobPath
)
{
compareCodeclimateMetrics
(
headIssues
,
baseIssues
,
headBlobPath
,
baseBlobPath
)
{
const
parsedHeadIssues
=
parseCodeclimateMetrics
(
headIssues
,
headBlobPath
);
const
parsedHeadIssues
=
MergeRequestStore
.
parseCodeclimateMetrics
(
headIssues
,
headBlobPath
);
const
parsedBaseIssues
=
parseCodeclimateMetrics
(
baseIssues
,
baseBlobPath
);
const
parsedBaseIssues
=
MergeRequestStore
.
parseCodeclimateMetrics
(
baseIssues
,
baseBlobPath
);
this
.
codeclimateMetrics
.
newIssues
=
filterByKey
(
this
.
codeclimateMetrics
.
newIssues
=
filterByKey
(
parsedHeadIssues
,
parsedHeadIssues
,
...
@@ -202,4 +144,30 @@ export default class MergeRequestStore extends CEMergeRequestStore {
...
@@ -202,4 +144,30 @@ export default class MergeRequestStore extends CEMergeRequestStore {
return
indexedSubjects
;
return
indexedSubjects
;
}
}
static
parseCodeclimateMetrics
(
issues
=
[],
path
=
''
)
{
return
issues
.
map
(
issue
=>
{
const
parsedIssue
=
{
...
issue
,
name
:
issue
.
description
,
};
if
(
issue
.
location
)
{
let
parseCodeQualityUrl
;
if
(
issue
.
location
.
path
)
{
parseCodeQualityUrl
=
`
${
path
}
/
${
issue
.
location
.
path
}
`
;
parsedIssue
.
path
=
issue
.
location
.
path
;
if
(
issue
.
location
.
lines
&&
issue
.
location
.
lines
.
begin
)
{
parsedIssue
.
line
=
issue
.
location
.
lines
.
begin
;
parseCodeQualityUrl
+=
`#L
${
issue
.
location
.
lines
.
begin
}
`
;
}
parsedIssue
.
urlPath
=
parseCodeQualityUrl
;
}
}
return
parsedIssue
;
});
}
}
}
ee/app/assets/javascripts/vue_shared/security_reports/components/dast_issue_body.vue
View file @
edeab723
<
script
>
<
script
>
/**
/**
* Renders DAST body text
* Renders DAST body text
* [priority]: [name]
* [priority]: [name]
*/
*/
export
default
{
name
:
'
SastIssueBody
'
,
props
:
{
issue
:
{
type
:
Object
,
required
:
true
,
},
issueIndex
:
{
export
default
{
type
:
Number
,
name
:
'
SastIssueBody
'
,
required
:
true
,
props
:
{
},
issue
:
{
type
:
Object
,
required
:
true
,
},
issueIndex
:
{
type
:
Number
,
required
:
true
,
},
modalTargetId
:
{
modalTargetId
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
},
},
methods
:
{
methods
:
{
openDastModal
()
{
openDastModal
()
{
this
.
$emit
(
'
openDastModal
'
,
this
.
issue
,
this
.
issueIndex
);
this
.
$emit
(
'
openDastModal
'
,
this
.
issue
,
this
.
issueIndex
);
},
},
},
};
},
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"report-block-list-issue-description prepend-top-5 append-bottom-5"
>
<div
class=
"report-block-list-issue-description prepend-top-5 append-bottom-5"
>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/components/error_row.vue
deleted
100644 → 0
View file @
a990e800
<
script
>
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
/**
* Renders the error row for each security report
*/
export
default
{
name
:
'
SecurityErrorRow
'
,
components
:
{
CiIcon
,
},
computed
:
{
iconStatus
()
{
return
{
group
:
'
warning
'
,
icon
:
'
status_warning
'
,
};
},
},
};
</
script
>
<
template
>
<div
class=
"report-block-list-issue prepend-left-default append-right-default"
>
<div
class=
"report-block-list-icon append-right-10 prepend-left-5"
>
<ci-icon
:status=
"iconStatus"
/>
</div>
<div
class=
"report-block-list-issue-description"
>
{{
__
(
"
There was an error loading results
"
)
}}
</div>
</div>
</
template
>
ee/app/assets/javascripts/vue_shared/security_reports/components/help_popover.vue
View file @
edeab723
<
script
>
<
script
>
import
$
from
'
jquery
'
;
import
_
from
'
underscore
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
popover
from
'
~/vue_shared/directives/popover
'
;
import
{
togglePopover
,
inserted
,
mouseenter
,
mouseleave
,
}
from
'
~/feature_highlight/feature_highlight_helper
'
;
export
default
{
export
default
{
name
:
'
SecurityReportsHelpPopover
'
,
name
:
'
SecurityReportsHelpPopover
'
,
components
:
{
components
:
{
Icon
,
Icon
,
},
},
directives
:
{
popover
,
},
props
:
{
props
:
{
options
:
{
options
:
{
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
},
},
computed
:
{
mounted
()
{
popoverOptions
()
{
$
(
this
.
$el
)
return
{
.
popover
(
{
html
:
true
,
html
:
true
,
trigger
:
'
focus
'
,
trigger
:
'
focus
'
,
container
:
'
body
'
,
placement
:
'
top
'
,
placement
:
'
top
'
,
template
:
template
:
'
<div class="popover" role="tooltip"><div class="arrow"></div><p class="popover-title"></p><div class="popover-content"></div></div>
'
,
'
<div class="popover" role="tooltip"><div class="arrow"></div><p class="popover-title"></p><div class="popover-content"></div></div>
'
,
...
this
.
options
,
...
this
.
options
,
};
})
},
.
on
(
'
mouseenter
'
,
mouseenter
)
.
on
(
'
mouseleave
'
,
_
.
debounce
(
mouseleave
,
300
))
.
on
(
'
inserted.bs.popover
'
,
inserted
)
.
on
(
'
show.bs.popover
'
,
()
=>
{
window
.
addEventListener
(
'
scroll
'
,
togglePopover
.
bind
(
this
.
$el
,
false
),
{
once
:
true
});
});
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<button
<button
type=
"button"
type=
"button"
class=
"btn btn-transparent"
class=
"btn btn-blank btn-transparent btn-help"
v-popover=
"popoverOptions"
tabindex=
"0"
tabindex=
"0"
>
>
<icon
name=
"question"
/>
<icon
name=
"question"
/>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/components/issues_list.vue
0 → 100644
View file @
edeab723
<
script
>
import
IssuesBlock
from
'
./report_issues.vue
'
;
import
SastContainerInfo
from
'
./sast_container_info.vue
'
;
import
{
SAST_CONTAINER
}
from
'
../store/constants
'
;
/**
* Renders block of issues
*/
export
default
{
components
:
{
IssuesBlock
,
SastContainerInfo
,
},
sastContainer
:
SAST_CONTAINER
,
props
:
{
unresolvedIssues
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
resolvedIssues
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
neutralIssues
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
allIssues
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
isFullReportVisible
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
type
:
{
type
:
String
,
required
:
true
,
},
},
};
</
script
>
<
template
>
<div
class=
"report-block-container"
>
<sast-container-info
v-if=
"type === $options.sastContainer"
/>
<issues-block
class=
"js-mr-code-new-issues"
v-if=
"unresolvedIssues.length"
:type=
"type"
status=
"failed"
:issues=
"unresolvedIssues"
/>
<issues-block
class=
"js-mr-code-all-issues"
v-if=
"isFullReportVisible"
:type=
"type"
status=
"failed"
:issues=
"allIssues"
/>
<issues-block
class=
"js-mr-code-non-issues"
v-if=
"neutralIssues.length"
:type=
"type"
status=
"neutral"
:issues=
"neutralIssues"
/>
<issues-block
class=
"js-mr-code-resolved-issues"
v-if=
"resolvedIssues.length"
:type=
"type"
status=
"success"
:issues=
"resolvedIssues"
/>
</div>
</
template
>
ee/app/assets/javascripts/vue_shared/security_reports/components/loading_row.vue
deleted
100644 → 0
View file @
a990e800
<
script
>
import
LoadingIcon
from
'
~/vue_shared/components/loading_icon.vue
'
;
/**
* Renders the loading row for each security report
*/
export
default
{
name
:
'
SecurityLoadingRow
'
,
components
:
{
LoadingIcon
,
},
};
</
script
>
<
template
>
<div
class=
"report-block-list-issue prepend-left-default append-right-default"
>
<div
class=
"report-block-list-icon append-right-10 prepend-left-5"
>
<loading-icon
/>
</div>
<div
class=
"report-block-list-issue-description"
>
{{
__
(
"
in progress
"
)
}}
</div>
</div>
</
template
>
ee/app/assets/javascripts/vue_shared/security_reports/components/report_issues.vue
View file @
edeab723
<
script
>
<
script
>
import
$
from
'
jquery
'
;
import
$
from
'
jquery
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Modal
from
'
~/vue_shared/components/gl_modal.vue
'
;
import
Modal
from
'
~/vue_shared/components/gl_modal.vue
'
;
import
ExpandButton
from
'
~/vue_shared/components/expand_button.vue
'
;
import
ExpandButton
from
'
~/vue_shared/components/expand_button.vue
'
;
import
PerformanceIssue
from
'
ee/vue_merge_request_widget/components/performance_issue_body.vue
'
;
import
PerformanceIssue
from
'
ee/vue_merge_request_widget/components/performance_issue_body.vue
'
;
import
CodequalityIssue
from
'
ee/vue_merge_request_widget/components/codequality_issue_body.vue
'
;
import
CodequalityIssue
from
'
ee/vue_merge_request_widget/components/codequality_issue_body.vue
'
;
import
SastIssue
from
'
./sast_issue_body.vue
'
;
import
SastIssue
from
'
./sast_issue_body.vue
'
;
import
SastContainerIssue
from
'
./sast_container_issue_body.vue
'
;
import
SastContainerIssue
from
'
./sast_container_issue_body.vue
'
;
import
DastIssue
from
'
./dast_issue_body.vue
'
;
import
DastIssue
from
'
./dast_issue_body.vue
'
;
import
{
SAST
,
DAST
,
SAST_CONTAINER
}
from
'
../helpers
/constants
'
;
import
{
SAST
,
DAST
,
SAST_CONTAINER
}
from
'
../store
/constants
'
;
const
modalDefaultData
=
{
const
modalDefaultData
=
{
modalId
:
'
modal-mrwidget-issue
'
,
modalId
:
'
modal-mrwidget-issue
'
,
modalDesc
:
''
,
modalDesc
:
''
,
modalTitle
:
''
,
modalTitle
:
''
,
modalInstances
:
[],
modalInstances
:
[],
modalTargetId
:
'
#modal-mrwidget-issue
'
,
modalTargetId
:
'
#modal-mrwidget-issue
'
,
};
};
export
default
{
export
default
{
name
:
'
ReportIssues
'
,
name
:
'
ReportIssues
'
,
components
:
{
components
:
{
Modal
,
Modal
,
Icon
,
Icon
,
ExpandButton
,
ExpandButton
,
SastIssue
,
SastIssue
,
SastContainerIssue
,
SastContainerIssue
,
DastIssue
,
DastIssue
,
PerformanceIssue
,
PerformanceIssue
,
CodequalityIssue
,
CodequalityIssue
,
},
},
props
:
{
props
:
{
issues
:
{
issues
:
{
type
:
Array
,
type
:
Array
,
required
:
true
,
required
:
true
,
},
},
// security || codequality || performance || docker || dast
// security || codequality || performance || docker || dast
type
:
{
type
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
// failed || success
// failed || success
status
:
{
status
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
},
},
data
()
{
data
()
{
return
modalDefaultData
;
return
modalDefaultData
;
},
},
computed
:
{
computed
:
{
iconName
()
{
iconName
()
{
if
(
this
.
isStatusFailed
)
{
if
(
this
.
isStatusFailed
)
{
return
'
status_failed_borderless
'
;
return
'
status_failed_borderless
'
;
}
else
if
(
this
.
isStatusSuccess
)
{
}
else
if
(
this
.
isStatusSuccess
)
{
return
'
status_success_borderless
'
;
return
'
status_success_borderless
'
;
}
}
return
'
status_created_borderless
'
;
return
'
status_created_borderless
'
;
},
},
isStatusFailed
()
{
isStatusFailed
()
{
return
this
.
status
===
'
failed
'
;
return
this
.
status
===
'
failed
'
;
},
},
isStatusSuccess
()
{
isStatusSuccess
()
{
return
this
.
status
===
'
success
'
;
return
this
.
status
===
'
success
'
;
},
},
isStatusNeutral
()
{
isStatusNeutral
()
{
return
this
.
status
===
'
neutral
'
;
return
this
.
status
===
'
neutral
'
;
},
},
isTypeCodequality
()
{
isTypeCodequality
()
{
return
this
.
type
===
'
codequality
'
;
return
this
.
type
===
'
codequality
'
;
},
},
isTypePerformance
()
{
isTypePerformance
()
{
return
this
.
type
===
'
performance
'
;
return
this
.
type
===
'
performance
'
;
},
},
isTypeSast
()
{
isTypeSast
()
{
return
this
.
type
===
SAST
;
return
this
.
type
===
SAST
;
},
},
isTypeSastContainer
()
{
isTypeSastContainer
()
{
return
this
.
type
===
SAST_CONTAINER
;
return
this
.
type
===
SAST_CONTAINER
;
},
},
isTypeDast
()
{
isTypeDast
()
{
return
this
.
type
===
DAST
;
return
this
.
type
===
DAST
;
},
},
},
},
mounted
()
{
mounted
()
{
$
(
this
.
$refs
.
modal
).
on
(
'
hidden.bs.modal
'
,
()
=>
{
$
(
this
.
$refs
.
modal
).
on
(
'
hidden.bs.modal
'
,
()
=>
{
this
.
clearModalData
();
this
.
clearModalData
();
});
});
},
},
methods
:
{
methods
:
{
getmodalId
(
index
)
{
getmodalId
(
index
)
{
return
`modal-mrwidget-issue-
${
index
}
`
;
return
`modal-mrwidget-issue-
${
index
}
`
;
},
},
modalIdTarget
(
index
)
{
modalIdTarget
(
index
)
{
return
`#
${
this
.
getmodalId
(
index
)}
`
;
return
`#
${
this
.
getmodalId
(
index
)}
`
;
},
},
openDastModal
(
issue
,
index
)
{
openDastModal
(
issue
,
index
)
{
this
.
modalId
=
this
.
getmodalId
(
index
);
this
.
modalId
=
this
.
getmodalId
(
index
);
this
.
modalTitle
=
`
${
issue
.
priority
}
:
${
issue
.
name
}
`
;
this
.
modalTitle
=
`
${
issue
.
priority
}
:
${
issue
.
name
}
`
;
this
.
modalTargetId
=
`#
${
this
.
getmodalId
(
index
)}
`
;
this
.
modalTargetId
=
`#
${
this
.
getmodalId
(
index
)}
`
;
this
.
modalInstances
=
issue
.
instances
;
this
.
modalInstances
=
issue
.
instances
;
this
.
modalDesc
=
issue
.
parsedDescription
;
this
.
modalDesc
=
issue
.
parsedDescription
;
},
},
/**
/**
* Because of https://vuejs.org/v2/guide/list.html#Caveats
* Because of https://vuejs.org/v2/guide/list.html#Caveats
* we need to clear the instances to make sure everything is properly reset.
* we need to clear the instances to make sure everything is properly reset.
*/
*/
clearModalData
()
{
clearModalData
()
{
this
.
modalId
=
modalDefaultData
.
modalId
;
this
.
modalId
=
modalDefaultData
.
modalId
;
this
.
modalDesc
=
modalDefaultData
.
modalDesc
;
this
.
modalDesc
=
modalDefaultData
.
modalDesc
;
this
.
modalTitle
=
modalDefaultData
.
modalTitle
;
this
.
modalTitle
=
modalDefaultData
.
modalTitle
;
this
.
modalInstances
=
modalDefaultData
.
modalInstances
;
this
.
modalInstances
=
modalDefaultData
.
modalInstances
;
this
.
modalTargetId
=
modalDefaultData
.
modalTargetId
;
this
.
modalTargetId
=
modalDefaultData
.
modalTargetId
;
},
},
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<div>
<div>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/components/report_link.vue
View file @
edeab723
<
script
>
<
script
>
export
default
{
export
default
{
name
:
'
ReportIssueLink
'
,
name
:
'
ReportIssueLink
'
,
props
:
{
props
:
{
issue
:
{
issue
:
{
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
},
};
},
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"report-block-list-issue-description-link"
>
<div
class=
"report-block-list-issue-description-link"
>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/components/report_section.vue
View file @
edeab723
<
script
>
<
script
>
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
StatusIcon
from
'
~/vue_merge_request_widget/components/mr_widget_status_icon.vue
'
;
import
StatusIcon
from
'
~/vue_merge_request_widget/components/mr_widget_status_icon.vue
'
;
import
LoadingIcon
from
'
~/vue_shared/components/loading_icon.vue
'
;
import
LoadingIcon
from
'
~/vue_shared/components/loading_icon.vue
'
;
import
IssuesBlock
from
'
./report_issues.vue
'
;
import
IssuesList
from
'
./issues_list.vue
'
;
import
Popover
from
'
./help_popover.vue
'
;
export
default
{
import
{
LOADING
,
ERROR
,
SUCCESS
}
from
'
../store/constants
'
;
name
:
'
ReportSection
'
,
components
:
{
export
default
{
IssuesBlock
,
name
:
'
ReportSection
'
,
LoadingIcon
,
components
:
{
StatusIcon
,
IssuesList
,
},
LoadingIcon
,
props
:
{
StatusIcon
,
isCollapsible
:
{
Popover
,
type
:
Boolean
,
},
required
:
false
,
props
:
{
default
:
true
,
type
:
{
},
type
:
String
,
// security | codequality | performance | docker
required
:
false
,
type
:
{
default
:
''
,
type
:
String
,
},
required
:
true
,
status
:
{
},
type
:
String
,
// loading | success | error
required
:
true
,
status
:
{
},
type
:
String
,
loadingText
:
{
required
:
true
,
type
:
String
,
},
required
:
false
,
loadingText
:
{
default
:
''
,
type
:
String
,
},
required
:
true
,
errorText
:
{
},
type
:
String
,
errorText
:
{
required
:
false
,
type
:
String
,
default
:
''
,
required
:
true
,
},
},
successText
:
{
successText
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
unresolvedIssues
:
{
unresolvedIssues
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
([]),
},
},
resolvedIssues
:
{
resolvedIssues
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
([]),
},
},
neutralIssues
:
{
neutralIssues
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
([]),
},
},
allIssues
:
{
allIssues
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
([]),
},
},
infoText
:
{
infoText
:
{
type
:
[
String
,
Boolean
],
type
:
[
String
,
Boolean
],
required
:
false
,
required
:
false
,
default
:
false
,
default
:
false
,
},
},
hasIssues
:
{
hasPriority
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
true
,
required
:
false
,
},
default
:
false
,
popoverOptions
:
{
},
type
:
Object
,
default
:
()
=>
({}),
required
:
false
,
},
},
data
()
{
return
{
collapseText
:
__
(
'
Expand
'
),
isCollapsed
:
true
,
isFullReportVisible
:
false
,
};
},
computed
:
{
isLoading
()
{
return
this
.
status
===
LOADING
;
},
loadingFailed
()
{
return
this
.
status
===
ERROR
;
},
},
isSuccess
()
{
return
this
.
status
===
SUCCESS
;
},
statusIconName
()
{
if
(
this
.
loadingFailed
||
this
.
unresolvedIssues
.
length
||
this
.
neutralIssues
.
length
)
{
return
'
warning
'
;
}
return
'
success
'
;
},
headerText
()
{
if
(
this
.
isLoading
)
{
return
this
.
loadingText
;
}
data
()
{
if
(
this
.
isSuccess
)
{
if
(
this
.
isCollapsible
)
{
return
this
.
successText
;
return
{
collapseText
:
__
(
'
Expand
'
),
isCollapsed
:
true
,
isFullReportVisible
:
false
,
};
}
}
return
{
if
(
this
.
loadingFailed
)
{
isFullReportVisible
:
true
,
return
this
.
errorText
;
};
}
},
computed
:
{
return
''
;
isLoading
()
{
},
return
this
.
status
===
'
loading
'
;
hasPopover
()
{
},
return
Object
.
keys
(
this
.
popoverOptions
).
length
>
0
;
loadingFailed
()
{
return
this
.
status
===
'
error
'
;
},
isSuccess
()
{
return
this
.
status
===
'
success
'
;
},
statusIconName
()
{
if
(
this
.
loadingFailed
||
this
.
unresolvedIssues
.
length
||
this
.
neutralIssues
.
length
)
{
return
'
warning
'
;
}
return
'
success
'
;
},
hasIssues
()
{
return
this
.
unresolvedIssues
.
length
||
this
.
resolvedIssues
.
length
||
this
.
allIssues
.
length
||
this
.
neutralIssues
.
length
;
},
},
},
},
methods
:
{
methods
:
{
toggleCollapsed
()
{
toggleCollapsed
()
{
this
.
isCollapsed
=
!
this
.
isCollapsed
;
this
.
isCollapsed
=
!
this
.
isCollapsed
;
const
text
=
this
.
isCollapsed
?
__
(
'
Expand
'
)
:
__
(
'
Collapse
'
);
const
text
=
this
.
isCollapsed
?
__
(
'
Expand
'
)
:
__
(
'
Collapse
'
);
this
.
collapseText
=
text
;
this
.
collapseText
=
text
;
},
},
openFullReport
()
{
openFullReport
()
{
this
.
isFullReportVisible
=
true
;
this
.
isFullReportVisible
=
true
;
},
},
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<section
class=
"report-block mr-widget-section"
>
<section>
<div
<div
v-if=
"isLoading"
class=
"media prepend-top-default prepend-left-default
class=
"media
"
append-right-default append-bottom-default
"
>
>
<
div
<
loading-icon
class=
"mr-widget-icon"
class=
"mr-widget-icon"
>
v-if=
"isLoading"
<loading-icon
/>
/>
</div>
<div
class=
"media-body"
>
{{
loadingText
}}
</div>
</div>
<div
v-else-if=
"isSuccess"
class=
"media"
>
<status-icon
<status-icon
v-else
:status=
"statusIconName"
:status=
"statusIconName"
/>
/>
<div
<div
class=
"media-body space-children"
class=
"media-body space-children"
>
>
<span
<span
class=
"js-code-text code-text"
class=
"js-code-text code-text"
>
>
{{
successText
}}
{{
headerText
}}
<popover
v-if=
"hasPopover"
class=
"prepend-left-5"
:options=
"popoverOptions"
/>
</span>
</span>
<button
<button
type=
"button"
type=
"button"
class=
"js-collapse-btn btn bt-default pull-right btn-sm"
class=
"js-collapse-btn btn bt-default pull-right btn-sm"
v-if=
"
isCollapsible &&
hasIssues"
v-if=
"hasIssues"
@
click=
"toggleCollapsed"
@
click=
"toggleCollapsed"
>
>
{{
collapseText
}}
{{
collapseText
}}
...
@@ -172,71 +172,28 @@
...
@@ -172,71 +172,28 @@
</div>
</div>
<div
<div
class=
"
report-block
-container"
class=
"
js-report-section
-container"
v-if=
"hasIssues"
v-if=
"hasIssues"
v-show=
"!isCollaps
ible || (isCollapsible && !isCollapsed)
"
v-show=
"!isCollaps
ed
"
>
>
<slot
name=
"body"
>
<issues-list
:unresolved-issues=
"unresolvedIssues"
:resolved-issues=
"resolvedIssues"
:all-issues=
"allIssues"
:type=
"type"
:is-full-report-visible=
"isFullReportVisible"
/>
<p
<button
v-if=
"infoText"
v-if=
"allIssues.length && !isFullReportVisible"
v-html=
"infoText"
type=
"button"
class=
"js-mr-code-quality-info prepend-left-10 report-block-info"
class=
"btn-link btn-blank prepend-left-10 js-expand-full-list break-link"
>
@
click=
"openFullReport"
</p>
>
{{
s__
(
"
ciReport|Show complete code vulnerabilities report
"
)
}}
<issues-block
</button>
class=
"js-mr-code-new-issues"
</slot>
v-if=
"unresolvedIssues.length"
:type=
"type"
status=
"failed"
:issues=
"unresolvedIssues"
:has-priority=
"hasPriority"
/>
<issues-block
class=
"js-mr-code-all-issues"
v-if=
"isFullReportVisible"
:type=
"type"
status=
"failed"
:issues=
"allIssues"
:has-priority=
"hasPriority"
/>
<issues-block
class=
"js-mr-code-non-issues"
v-if=
"neutralIssues.length"
:type=
"type"
status=
"neutral"
:issues=
"neutralIssues"
:has-priority=
"hasPriority"
/>
<issues-block
class=
"js-mr-code-resolved-issues"
v-if=
"resolvedIssues.length"
:type=
"type"
status=
"success"
:issues=
"resolvedIssues"
:has-priority=
"hasPriority"
/>
<button
v-if=
"allIssues.length && !isFullReportVisible"
type=
"button"
class=
"btn-link btn-blank prepend-left-10 js-expand-full-list break-link"
@
click=
"openFullReport"
>
{{
s__
(
"
ciReport|Show complete code vulnerabilities report
"
)
}}
</button>
</div>
<div
v-else-if=
"loadingFailed"
class=
"media"
>
<status-icon
status=
"notfound"
/>
<div
class=
"media-body"
>
{{
errorText
}}
</div>
</div>
</div>
</section>
</section>
</
template
>
</
template
>
ee/app/assets/javascripts/vue_shared/security_reports/components/sast_container_info.vue
0 → 100644
View file @
edeab723
<
script
>
export
default
{
name
:
'
SastContainerInfo
'
,
};
</
script
>
<
template
>
<p
class=
"prepend-top-10 prepend-left-10 report-block-info js-mr-code-quality-info"
>
{{
s__
(
'
ciReport|Unapproved vulnerabilities (red) can be marked as approved.
'
)
}}
<a
href=
"https://gitlab.com/gitlab-org/clair-scanner#example-whitelist-yaml-file"
target=
"_blank"
rel=
"noopener noreferrer nofollow"
>
{{
s__
(
'
ciReport|Learn more about whitelisting
'
)
}}
</a>
</p>
</
template
>
ee/app/assets/javascripts/vue_shared/security_reports/components/sast_container_issue_body.vue
View file @
edeab723
<
script
>
<
script
>
/**
/**
* Renders SAST CONTAINER body text
* Renders SAST CONTAINER body text
* [priority]: [name|link] in [link]:[line]
* [priority]: [name|link] in [link]:[line]
*/
*/
import
ReportLink
from
'
./report_link.vue
'
;
import
ReportLink
from
'
./report_link.vue
'
;
export
default
{
export
default
{
name
:
'
SastContainerIssueBody
'
,
name
:
'
SastContainerIssueBody
'
,
components
:
{
components
:
{
ReportLink
,
ReportLink
,
},
},
props
:
{
props
:
{
issue
:
{
issue
:
{
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
},
};
},
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"report-block-list-issue-description prepend-top-5 append-bottom-5"
>
<div
class=
"report-block-list-issue-description prepend-top-5 append-bottom-5"
>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/components/sast_issue_body.vue
View file @
edeab723
<
script
>
<
script
>
/**
/**
* Renders SAST body text
* Renders SAST body text
* [priority]: [name] in [link] : [line]
* [priority]: [name] in [link] : [line]
*/
*/
import
ReportLink
from
'
./report_link.vue
'
;
import
ReportLink
from
'
./report_link.vue
'
;
export
default
{
export
default
{
name
:
'
SastIssueBody
'
,
name
:
'
SastIssueBody
'
,
components
:
{
components
:
{
ReportLink
,
ReportLink
,
},
},
props
:
{
props
:
{
issue
:
{
issue
:
{
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
},
};
},
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"report-block-list-issue-description prepend-top-5 append-bottom-5"
>
<div
class=
"report-block-list-issue-description prepend-top-5 append-bottom-5"
>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/components/summary_row.vue
View file @
edeab723
<
script
>
<
script
>
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
LoadingIcon
from
'
~/vue_shared/components/loading_icon.vue
'
;
import
Popover
from
'
./help_popover.vue
'
;
import
Popover
from
'
./help_popover.vue
'
;
/**
/**
...
@@ -10,6 +11,7 @@ export default {
...
@@ -10,6 +11,7 @@ export default {
name
:
'
SecuritySummaryRow
'
,
name
:
'
SecuritySummaryRow
'
,
components
:
{
components
:
{
CiIcon
,
CiIcon
,
LoadingIcon
,
Popover
,
Popover
,
},
},
props
:
{
props
:
{
...
@@ -37,12 +39,19 @@ export default {
...
@@ -37,12 +39,19 @@ export default {
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"report-block-list-issue
prepend-left-default append-right-defaul
t"
>
<div
class=
"report-block-list-issue
report-block-list-issue-paren
t"
>
<div
class=
"report-block-list-icon append-right-10 prepend-left-5"
>
<div
class=
"report-block-list-icon append-right-10 prepend-left-5"
>
<ci-icon
:status=
"iconStatus"
/>
<loading-icon
v-if=
"statusIcon === 'loading'"
css-class=
"report-block-list-loading-icon"
/>
<ci-icon
v-else
:status=
"iconStatus"
/>
</div>
</div>
<div
class=
"report-block-list-issue-description
prepend-top-5 append-bottom-5
"
>
<div
class=
"report-block-list-issue-description"
>
<div
class=
"report-block-list-issue-description-text append-right-5"
>
<div
class=
"report-block-list-issue-description-text append-right-5"
>
{{
summary
}}
{{
summary
}}
</div>
</div>
...
...
ee/app/assets/javascripts/vue_shared/security_reports/grouped_security_reports_app.vue
0 → 100644
View file @
edeab723
<
script
>
import
{
mapActions
,
mapState
,
mapGetters
}
from
'
vuex
'
;
import
{
SAST
,
DAST
,
SAST_CONTAINER
}
from
'
./store/constants
'
;
import
store
from
'
./store
'
;
import
ReportSection
from
'
./components/report_section.vue
'
;
import
SummaryRow
from
'
./components/summary_row.vue
'
;
import
IssuesList
from
'
./components/issues_list.vue
'
;
import
securityReportsMixin
from
'
./mixins/security_report_mixin
'
;
export
default
{
store
,
components
:
{
ReportSection
,
SummaryRow
,
IssuesList
,
},
mixins
:
[
securityReportsMixin
],
props
:
{
headBlobPath
:
{
type
:
String
,
required
:
true
,
},
baseBlobPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
sastHeadPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
sastBasePath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
dastHeadPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
dastBasePath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
sastContainerHeadPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
sastContainerBasePath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
dependencyScanningHeadPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
dependencyScanningBasePath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
sastHelpPath
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
sastContainerHelpPath
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
dastHelpPath
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
dependencyScanningHelpPath
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
sast
:
SAST
,
dast
:
DAST
,
sastContainer
:
SAST_CONTAINER
,
computed
:
{
...
mapState
([
'
sast
'
,
'
sastContainer
'
,
'
dast
'
,
'
dependencyScanning
'
,
'
summaryCounts
'
]),
...
mapGetters
([
'
groupedSastText
'
,
'
groupedSummaryText
'
,
'
summaryStatus
'
,
'
groupedSastContainerText
'
,
'
groupedDastText
'
,
'
groupedDependencyText
'
,
'
sastStatusIcon
'
,
'
sastContainerStatusIcon
'
,
'
dastStatusIcon
'
,
'
dependencyScanningStatusIcon
'
,
]),
},
created
()
{
this
.
setHeadBlobPath
(
this
.
headBlobPath
);
this
.
setBaseBlobPath
(
this
.
baseBlobPath
);
if
(
this
.
sastHeadPath
)
{
this
.
setSastHeadPath
(
this
.
sastHeadPath
);
if
(
this
.
sastBasePath
)
{
this
.
setSastBasePath
(
this
.
sastBasePath
);
}
this
.
fetchSastReports
();
}
if
(
this
.
sastContainerHeadPath
)
{
this
.
setSastContainerHeadPath
(
this
.
sastContainerHeadPath
);
if
(
this
.
sastContainerBasePath
)
{
this
.
setSastContainerBasePath
(
this
.
sastContainerBasePath
);
}
this
.
fetchSastContainerReports
();
}
if
(
this
.
dastHeadPath
)
{
this
.
setDastHeadPath
(
this
.
dastHeadPath
);
if
(
this
.
dastBasePath
)
{
this
.
setDastBasePath
(
this
.
dastBasePath
);
}
this
.
fetchDastReports
();
}
if
(
this
.
dependencyScanningHeadPath
)
{
this
.
setDependencyScanningHeadPath
(
this
.
dependencyScanningHeadPath
);
if
(
this
.
dependencyScanningBasePath
)
{
this
.
setDependencyScanningBasePath
(
this
.
dependencyScanningBasePath
);
}
this
.
fetchDependencyScanningReports
();
}
},
methods
:
{
...
mapActions
([
'
setAppType
'
,
'
setHeadBlobPath
'
,
'
setBaseBlobPath
'
,
'
setSastHeadPath
'
,
'
setSastBasePath
'
,
'
setSastContainerHeadPath
'
,
'
setSastContainerBasePath
'
,
'
setDastHeadPath
'
,
'
setDastBasePath
'
,
'
setDependencyScanningHeadPath
'
,
'
setDependencyScanningBasePath
'
,
'
fetchSastReports
'
,
'
fetchSastContainerReports
'
,
'
fetchDastReports
'
,
'
fetchDependencyScanningReports
'
,
]),
},
};
</
script
>
<
template
>
<report-section
class=
"mr-widget-border-top"
:status=
"summaryStatus"
:success-text=
"groupedSummaryText"
:loading-text=
"groupedSummaryText"
:error-text=
"groupedSummaryText"
:has-issues=
"true"
>
<div
slot=
"body"
class=
"mr-widget-grouped-section report-block"
>
<template
v-if=
"sastHeadPath"
>
<summary-row
class=
"js-sast-widget"
:summary=
"groupedSastText"
:status-icon=
"sastStatusIcon"
:popover-options=
"sastPopover"
/>
<issues-list
class=
"report-block-group-list"
v-if=
"sast.newIssues.length"
:unresolved-issues=
"sast.newIssues"
:resolved-issues=
"sast.resolvedIssues"
:all-issues=
"sast.allIssues"
:type=
"$options.sast"
/>
</
template
>
<
template
v-if=
"dependencyScanningHeadPath"
>
<summary-row
class=
"js-dependency-scanning-widget"
:summary=
"groupedDependencyText"
:status-icon=
"dependencyScanningStatusIcon"
:popover-options=
"dependencyScanningPopover"
/>
<issues-list
class=
"report-block-group-list"
v-if=
"dependencyScanning.newIssues.length"
:unresolved-issues=
"dependencyScanning.newIssues"
:resolved-issues=
"dependencyScanning.resolvedIssues"
:all-issues=
"dependencyScanning.allIssues"
:type=
"$options.sast"
/>
</
template
>
<
template
v-if=
"sastContainerHeadPath"
>
<summary-row
class=
"js-sast-container"
:summary=
"groupedSastContainerText"
:status-icon=
"sastContainerStatusIcon"
:popover-options=
"sastContainerPopover"
/>
<issues-list
class=
"report-block-group-list"
v-if=
"sastContainer.newIssues.length"
:unresolved-issues=
"sastContainer.newIssues"
:neutral-issues=
"sastContainer.resolvedIssues"
:type=
"$options.sastContainer"
/>
</
template
>
<
template
v-if=
"dastHeadPath"
>
<summary-row
class=
"js-dast-widget"
:summary=
"groupedDastText"
:status-icon=
"dastStatusIcon"
:popover-options=
"dastPopover"
/>
<issues-list
class=
"report-block-group-list"
v-if=
"dast.newIssues.length"
:unresolved-issues=
"dast.newIssues"
:resolved-issues=
"dast.resolvedIssues"
:type=
"$options.dast"
/>
</
template
>
</div>
</report-section>
</template>
ee/app/assets/javascripts/vue_shared/security_reports/helpers/state.js
deleted
100644 → 0
View file @
a990e800
export
default
{
sast
:
{
isLoading
:
false
,
hasError
:
false
,
newIssues
:
[],
resolvedIssues
:
[],
allIssues
:
[],
},
sastContainer
:
{
approved
:
[],
unapproved
:
[],
vulnerabilities
:
[],
},
dast
:
[],
codeclimate
:
{
newIssues
:
[],
resolvedIssues
:
[],
},
dependencyScanning
:
{
isLoading
:
false
,
hasError
:
false
,
newIssues
:
[],
resolvedIssues
:
[],
allIssues
:
[],
},
};
ee/app/assets/javascripts/vue_shared/security_reports/helpers/utils.js
deleted
100644 → 0
View file @
a990e800
import
{
stripHtml
}
from
'
~/lib/utils/text_utility
'
;
export
const
parseCodeclimateMetrics
=
(
issues
=
[],
path
=
''
)
=>
issues
.
map
(
issue
=>
{
const
parsedIssue
=
{
...
issue
,
name
:
issue
.
description
,
};
if
(
issue
.
location
)
{
let
parseCodeQualityUrl
;
if
(
issue
.
location
.
path
)
{
parseCodeQualityUrl
=
`
${
path
}
/
${
issue
.
location
.
path
}
`
;
parsedIssue
.
path
=
issue
.
location
.
path
;
if
(
issue
.
location
.
lines
&&
issue
.
location
.
lines
.
begin
)
{
parsedIssue
.
line
=
issue
.
location
.
lines
.
begin
;
parseCodeQualityUrl
+=
`#L
${
issue
.
location
.
lines
.
begin
}
`
;
}
parsedIssue
.
urlPath
=
parseCodeQualityUrl
;
}
}
return
parsedIssue
;
});
/**
* Maps SAST & Dependency scanning issues:
* { tool: String, message: String, url: String , cve: String ,
* file: String , solution: String, priority: String }
* to contain:
* { name: String, path: String, line: String, urlPath: String, priority: String }
* @param {Array} issues
* @param {String} path
*/
export
const
parseSastIssues
=
(
issues
=
[],
path
=
''
)
=>
issues
.
map
(
issue
=>
Object
.
assign
({},
issue
,
{
name
:
issue
.
message
,
path
:
issue
.
file
,
urlPath
:
issue
.
line
?
`
${
path
}
/
${
issue
.
file
}
#L
${
issue
.
line
}
`
:
`
${
path
}
/
${
issue
.
file
}
`
,
}),
);
/**
* Compares two arrays by the given key and returns the difference
*
* @param {Array} firstArray
* @param {Array} secondArray
* @param {String} key
* @returns {Array}
*/
export
const
filterByKey
=
(
firstArray
=
[],
secondArray
=
[],
key
=
''
)
=>
firstArray
.
filter
(
item
=>
!
secondArray
.
find
(
el
=>
el
[
key
]
===
item
[
key
]));
/**
* Parses DAST results into a common format to allow to use the same Vue component
* And adds an external link
*
* @param {Array} data
* @returns {Array}
*/
export
const
parseSastContainer
=
(
data
=
[])
=>
data
.
map
(
el
=>
({
name
:
el
.
vulnerability
,
priority
:
el
.
severity
,
path
:
el
.
namespace
,
// external link to provide better description
nameLink
:
`https://cve.mitre.org/cgi-bin/cvename.cgi?name=
${
el
.
vulnerability
}
`
,
...
el
,
}));
/**
* Utils functions to set the reports
*/
/**
* Compares sast results and returns the formatted report
*
* Security report has 3 types of issues, newIssues, resolvedIssues and allIssues.
*
* When we have both base and head:
* - newIssues = head - base
* - resolvedIssues = base - head
* - allIssues = head - newIssues - resolvedIssues
*
* When we only have head
* - newIssues = head
* - resolvedIssues = 0
* - allIssues = 0
* @param {*} data
* @returns {Object}
*/
export
const
setSastReport
=
(
data
=
{})
=>
{
const
securityReport
=
{};
if
(
data
.
base
)
{
const
filterKey
=
'
cve
'
;
const
parsedHead
=
parseSastIssues
(
data
.
head
,
data
.
headBlobPath
);
const
parsedBase
=
parseSastIssues
(
data
.
base
,
data
.
baseBlobPath
);
securityReport
.
newIssues
=
filterByKey
(
parsedHead
,
parsedBase
,
filterKey
,
);
securityReport
.
resolvedIssues
=
filterByKey
(
parsedBase
,
parsedHead
,
filterKey
,
);
// Remove the new Issues and the added issues
securityReport
.
allIssues
=
filterByKey
(
parsedHead
,
securityReport
.
newIssues
.
concat
(
securityReport
.
resolvedIssues
),
filterKey
,
);
}
else
{
securityReport
.
newIssues
=
parseSastIssues
(
data
.
head
,
data
.
headBlobPath
);
}
return
securityReport
;
};
export
const
setSastContainerReport
=
(
data
=
{})
=>
{
const
unapproved
=
data
.
unapproved
||
[];
const
parsedVulnerabilities
=
parseSastContainer
(
data
.
vulnerabilities
);
// Approved can be calculated by subtracting unapproved from vulnerabilities.
return
{
vulnerabilities
:
parsedVulnerabilities
||
[],
approved
:
parsedVulnerabilities
.
filter
(
item
=>
!
unapproved
.
find
(
el
=>
el
===
item
.
vulnerability
))
||
[],
unapproved
:
parsedVulnerabilities
.
filter
(
item
=>
unapproved
.
find
(
el
=>
el
===
item
.
vulnerability
))
||
[],
};
};
/**
* Dast Report sends some keys in HTML, we need to strip the `<p>` tags.
* This should be moved to the backend.
*
* @param {Array} data
* @returns {Array}
*/
export
const
setDastReport
=
data
=>
data
.
site
.
alerts
.
map
(
alert
=>
({
name
:
alert
.
name
,
parsedDescription
:
stripHtml
(
alert
.
desc
,
'
'
),
priority
:
alert
.
riskdesc
,
...
alert
,
}));
ee/app/assets/javascripts/vue_shared/security_reports/mixins/reports_mixin.js
0 → 100644
View file @
edeab723
import
{
LOADING
,
ERROR
,
SUCCESS
,
}
from
'
../store/constants
'
;
export
default
{
methods
:
{
checkReportStatus
(
loading
,
error
)
{
if
(
loading
)
{
return
LOADING
;
}
else
if
(
error
)
{
return
ERROR
;
}
return
SUCCESS
;
},
},
};
ee/app/assets/javascripts/vue_shared/security_reports/mixins/security_report_mixin.js
View file @
edeab723
import
{
s
__
,
n__
,
__
,
sprintf
}
from
'
~/locale
'
;
import
{
s
printf
,
s__
}
from
'
~/locale
'
;
export
default
{
export
default
{
methods
:
{
computed
:
{
sastText
(
newIssues
=
[],
resolvedIssues
=
[],
allIssues
=
[])
{
sastPopover
()
{
const
text
=
[];
if
(
!
newIssues
.
length
&&
!
resolvedIssues
.
length
&&
!
allIssues
.
length
)
{
text
.
push
(
s__
(
'
ciReport|SAST detected no security vulnerabilities
'
));
}
else
if
(
!
newIssues
.
length
&&
!
resolvedIssues
.
length
&&
allIssues
.
length
)
{
text
.
push
(
s__
(
'
ciReport|SAST detected no new security vulnerabilities
'
));
}
else
if
(
newIssues
.
length
||
resolvedIssues
.
length
)
{
text
.
push
(
s__
(
'
ciReport|SAST
'
));
}
if
(
resolvedIssues
.
length
)
{
text
.
push
(
n__
(
'
improved on %d security vulnerability
'
,
'
improved on %d security vulnerabilities
'
,
resolvedIssues
.
length
,
));
}
if
(
newIssues
.
length
>
0
&&
resolvedIssues
.
length
>
0
)
{
text
.
push
(
__
(
'
and
'
));
}
if
(
newIssues
.
length
)
{
text
.
push
(
n__
(
'
degraded on %d security vulnerability
'
,
'
degraded on %d security vulnerabilities
'
,
newIssues
.
length
,
));
}
return
text
.
join
(
''
);
},
depedencyScanningText
(
newIssues
=
[],
resolvedIssues
=
[],
allIssues
=
[])
{
const
text
=
[];
if
(
!
newIssues
.
length
&&
!
resolvedIssues
.
length
&&
!
allIssues
.
length
)
{
text
.
push
(
s__
(
'
ciReport|Dependency scanning detected no security vulnerabilities
'
));
}
else
if
(
!
newIssues
.
length
&&
!
resolvedIssues
.
length
&&
allIssues
.
length
)
{
text
.
push
(
s__
(
'
ciReport|Dependency scanning detected no new security vulnerabilities
'
));
}
else
if
(
newIssues
.
length
||
resolvedIssues
.
length
)
{
text
.
push
(
s__
(
'
ciReport|Dependency scanning
'
));
}
if
(
resolvedIssues
.
length
)
{
text
.
push
(
n__
(
'
improved on %d security vulnerability
'
,
'
improved on %d security vulnerabilities
'
,
resolvedIssues
.
length
,
));
}
if
(
newIssues
.
length
>
0
&&
resolvedIssues
.
length
>
0
)
{
text
.
push
(
__
(
'
and
'
));
}
if
(
newIssues
.
length
)
{
text
.
push
(
n__
(
'
degraded on %d security vulnerability
'
,
'
degraded on %d security vulnerabilities
'
,
newIssues
.
length
,
));
}
return
text
.
join
(
''
);
},
translateText
(
type
)
{
return
{
return
{
error
:
sprintf
(
s__
(
'
ciReport|Failed to load %{reportName} report
'
),
{
reportName
:
type
}),
title
:
s__
(
'
ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code.
'
),
loading
:
sprintf
(
s__
(
'
ciReport|Loading %{reportName} report
'
),
{
reportName
:
type
}),
content
:
sprintf
(
s__
(
'
ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}
'
),
{
linkStartTag
:
`<a href="
${
this
.
sastHelpPath
}
">`
,
linkEndTag
:
'
</a>
'
,
},
false
,
),
};
};
},
},
sastContainerPopover
()
{
checkReportStatus
(
loading
,
error
)
{
return
{
if
(
loading
)
{
title
:
s__
(
'
ciReport|Container scanning detects known vulnerabilities in your docker images.
'
),
return
'
loading
'
;
content
:
sprintf
(
}
else
if
(
error
)
{
s__
(
'
ciReport|%{linkStartTag}Learn more about SAST image %{linkEndTag}
'
),
return
'
error
'
;
{
}
linkStartTag
:
`<a href="
${
this
.
sastContainerHelpPath
}
">`
,
linkEndTag
:
'
</a>
'
,
return
'
success
'
;
},
},
false
,
),
sastContainerText
(
vulnerabilities
=
[],
approved
=
[],
unapproved
=
[])
{
};
if
(
!
vulnerabilities
.
length
)
{
return
s__
(
'
ciReport|SAST:container no vulnerabilities were found
'
);
}
if
(
!
unapproved
.
length
&&
approved
.
length
)
{
return
n__
(
'
SAST:container found %d approved vulnerability
'
,
'
SAST:container found %d approved vulnerabilities
'
,
approved
.
length
,
);
}
else
if
(
unapproved
.
length
&&
!
approved
.
length
)
{
return
n__
(
'
SAST:container found %d vulnerability
'
,
'
SAST:container found %d vulnerabilities
'
,
unapproved
.
length
,
);
}
return
`
${
n__
(
'
SAST:container found %d vulnerability,
'
,
'
SAST:container found %d vulnerabilities,
'
,
vulnerabilities
.
length
,
)}
${
n__
(
'
of which %d is approved
'
,
'
of which %d are approved
'
,
approved
.
length
,
)}
`
;
},
},
dastPopover
()
{
dastText
(
dast
=
[])
{
return
{
if
(
dast
.
length
)
{
title
:
s__
(
'
ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application.
'
),
return
n__
(
content
:
sprintf
(
'
DAST detected %d alert by analyzing the review app
'
,
s__
(
'
ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}
'
),
'
DAST detected %d alerts by analyzing the review app
'
,
{
dast
.
length
,
linkStartTag
:
`<a href="
${
this
.
dastHelpPath
}
">`
,
);
linkEndTag
:
'
</a>
'
,
}
},
false
,
return
s__
(
'
ciReport|DAST detected no alerts by analyzing the review app
'
);
),
};
},
},
dependencyScanningPopover
()
{
sastContainerInformationText
()
{
return
{
return
sprintf
(
title
:
s__
(
'
ciReport|Dependency Scanning detects known vulnerabilities in your source code
\'
s dependencies.
'
),
s__
(
'
ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}
'
),
{
content
:
sprintf
(
helpLink
:
`<a href="https://gitlab.com/gitlab-org/clair-scanner#example-whitelist-yaml-file" target="_blank" rel="noopener noreferrer nofollow">
s__
(
'
ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}
'
),
${
s__
(
'
ciReport|Learn more about whitelisting
'
)}
{
</a>`
,
linkStartTag
:
`<a href="
${
this
.
dependencyScanningHelpPath
}
">`
,
},
linkEndTag
:
'
</a>
'
,
false
,
},
);
false
,
),
};
},
},
},
},
};
};
ee/app/assets/javascripts/vue_shared/security_reports/split_security_reports_app.vue
0 → 100644
View file @
edeab723
<
script
>
import
{
mapActions
,
mapState
}
from
'
vuex
'
;
import
{
s__
,
sprintf
,
n__
}
from
'
~/locale
'
;
import
createFlash
from
'
~/flash
'
;
import
{
SAST
}
from
'
./store/constants
'
;
import
store
from
'
./store
'
;
import
ReportSection
from
'
./components/report_section.vue
'
;
import
mixin
from
'
./mixins/security_report_mixin
'
;
import
reportsMixin
from
'
./mixins/reports_mixin
'
;
export
default
{
store
,
components
:
{
ReportSection
,
},
mixins
:
[
mixin
,
reportsMixin
],
props
:
{
headBlobPath
:
{
type
:
String
,
required
:
true
,
},
sastHeadPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
dependencyScanningHeadPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
sastHelpPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
dependencyScanningHelpPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
},
sast
:
SAST
,
computed
:
{
...
mapState
([
'
sast
'
,
'
dependencyScanning
'
]),
sastText
()
{
return
this
.
summaryTextBuilder
(
'
SAST
'
,
this
.
sast
.
newIssues
.
length
);
},
dependencyScanningText
()
{
return
this
.
summaryTextBuilder
(
'
Dependency scanning
'
,
this
.
dependencyScanning
.
newIssues
.
length
,
);
},
},
created
()
{
// update the store with the received props
this
.
setHeadBlobPath
(
this
.
headBlobPath
);
if
(
this
.
sastHeadPath
)
{
this
.
setSastHeadPath
(
this
.
sastHeadPath
);
this
.
fetchSastReports
()
.
then
(()
=>
{
this
.
$emit
(
'
updateBadgeCount
'
,
this
.
sast
.
newIssues
.
length
);
})
.
catch
(()
=>
createFlash
(
s__
(
'
ciReport|There was an error loading SAST report
'
)));
}
if
(
this
.
dependencyScanningHeadPath
)
{
this
.
setDependencyScanningHeadPath
(
this
.
dependencyScanningHeadPath
);
this
.
fetchDependencyScanningReports
()
.
then
(()
=>
{
this
.
$emit
(
'
updateBadgeCount
'
,
this
.
dependencyScanning
.
newIssues
.
length
);
})
.
catch
(()
=>
createFlash
(
s__
(
'
ciReport|There was an error loading dependency scanning report
'
)),
);
}
},
methods
:
{
...
mapActions
([
'
setHeadBlobPath
'
,
'
setSastHeadPath
'
,
'
setDependencyScanningHeadPath
'
,
'
fetchSastReports
'
,
'
fetchDependencyScanningReports
'
,
]),
summaryTextBuilder
(
type
,
issuesCount
=
0
)
{
if
(
issuesCount
===
0
)
{
return
sprintf
(
s__
(
'
ciReport|%{type} detected no vulnerabilities
'
),
{
type
,
});
}
return
sprintf
(
n__
(
'
%{type} detected %d vulnerability
'
,
'
%{type} detected %d vulnerabilities
'
,
issuesCount
),
{
type
},
);
},
translateText
(
type
)
{
return
{
error
:
sprintf
(
s__
(
'
ciReport|%{reportName} resulted in error while loading results
'
),
{
reportName
:
type
,
}),
loading
:
sprintf
(
s__
(
'
ciReport|%{reportName} is loading
'
),
{
reportName
:
type
,
}),
};
},
},
};
</
script
>
<
template
>
<div>
<report-section
v-if=
"sastHeadPath"
class=
"js-sast-widget split-report-section"
:type=
"$options.sast"
:status=
"checkReportStatus(sast.isLoading, sast.hasError)"
:loading-text=
"translateText('SAST').loading"
:error-text=
"translateText('SAST').error"
:success-text=
"sastText"
:unresolved-issues=
"sast.newIssues"
:has-issues=
"sast.newIssues.length > 0"
:popover-options=
"sastPopover"
/>
<report-section
v-if=
"dependencyScanningHeadPath"
class=
"js-dss-widget split-report-section"
:type=
"$options.sast"
:status=
"checkReportStatus(dependencyScanning.isLoading, dependencyScanning.hasError)"
:loading-text=
"translateText('Dependency scanning').loading"
:error-text=
"translateText('Dependency scanning').error"
:success-text=
"dependencyScanningText"
:unresolved-issues=
"dependencyScanning.newIssues"
:has-issues=
"dependencyScanning.newIssues.length > 0"
:popover-options=
"dependencyScanningPopover"
/>
</div>
</
template
>
ee/app/assets/javascripts/vue_shared/security_reports/store/actions.js
View file @
edeab723
...
@@ -26,14 +26,14 @@ export const fetchSastReports = ({ state, dispatch }) => {
...
@@ -26,14 +26,14 @@ export const fetchSastReports = ({ state, dispatch }) => {
dispatch
(
'
requestSastReports
'
);
dispatch
(
'
requestSastReports
'
);
Promise
.
all
([
return
Promise
.
all
([
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
])
])
.
then
(
values
=>
{
.
then
(
values
=>
{
dispatch
(
'
receiveSastReports
'
,
{
dispatch
(
'
receiveSastReports
'
,
{
head
:
values
[
0
]
?
values
[
0
].
data
:
null
,
head
:
values
&&
values
[
0
]
?
values
[
0
].
data
:
null
,
base
:
values
[
1
]
?
values
[
1
].
data
:
null
,
base
:
values
&&
values
[
1
]
?
values
[
1
].
data
:
null
,
});
});
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
...
@@ -65,7 +65,7 @@ export const fetchSastContainerReports = ({ state, dispatch }) => {
...
@@ -65,7 +65,7 @@ export const fetchSastContainerReports = ({ state, dispatch }) => {
dispatch
(
'
requestSastContainerReports
'
);
dispatch
(
'
requestSastContainerReports
'
);
Promise
.
all
([
return
Promise
.
all
([
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
])
])
...
@@ -100,14 +100,14 @@ export const fetchDastReports = ({ state, dispatch }) => {
...
@@ -100,14 +100,14 @@ export const fetchDastReports = ({ state, dispatch }) => {
dispatch
(
'
requestDastReports
'
);
dispatch
(
'
requestDastReports
'
);
Promise
.
all
([
return
Promise
.
all
([
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
])
])
.
then
(
values
=>
{
.
then
(
values
=>
{
dispatch
(
'
receiveDastReports
'
,
{
dispatch
(
'
receiveDastReports
'
,
{
head
:
values
[
0
]
?
values
[
0
].
data
:
null
,
head
:
values
&&
values
[
0
]
?
values
[
0
].
data
:
null
,
base
:
values
[
1
]
?
values
[
1
].
data
:
null
,
base
:
values
&&
values
[
1
]
?
values
[
1
].
data
:
null
,
});
});
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
...
@@ -139,7 +139,7 @@ export const fetchDependencyScanningReports = ({ state, dispatch }) => {
...
@@ -139,7 +139,7 @@ export const fetchDependencyScanningReports = ({ state, dispatch }) => {
dispatch
(
'
requestDependencyScanningReports
'
);
dispatch
(
'
requestDependencyScanningReports
'
);
Promise
.
all
([
return
Promise
.
all
([
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
head
?
axios
.
get
(
head
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
base
?
axios
.
get
(
base
)
:
Promise
.
resolve
(),
])
])
...
...
ee/app/assets/javascripts/vue_shared/security_reports/
helpers
/constants.js
→
ee/app/assets/javascripts/vue_shared/security_reports/
store
/constants.js
View file @
edeab723
export
const
SAST
=
'
SAST
'
;
export
const
SAST
=
'
SAST
'
;
export
const
DAST
=
'
DAST
'
;
export
const
DAST
=
'
DAST
'
;
export
const
SAST_CONTAINER
=
'
SAST_CONTAINER
'
;
export
const
SAST_CONTAINER
=
'
SAST_CONTAINER
'
;
export
const
LOADING
=
'
LOADING
'
;
export
const
ERROR
=
'
ERROR
'
;
export
const
SUCCESS
=
'
SUCCESS
'
;
ee/app/assets/javascripts/vue_shared/security_reports/store/getters.js
View file @
edeab723
import
{
n__
,
s__
}
from
'
~/locale
'
;
import
{
n__
,
s__
}
from
'
~/locale
'
;
import
{
textBuilder
,
statusIcon
}
from
'
./utils
'
;
import
{
textBuilder
,
statusIcon
}
from
'
./utils
'
;
import
{
LOADING
,
ERROR
,
SUCCESS
}
from
'
./constants
'
;
export
const
groupedSastText
=
({
sast
})
=>
export
const
groupedSastText
=
({
sast
})
=>
{
textBuilder
(
if
(
sast
.
hasError
)
{
return
s__
(
'
ciReport|SAST resulted in error while loading results
'
);
}
if
(
sast
.
isLoading
)
{
return
s__
(
'
ciReport|SAST is loading
'
);
}
return
textBuilder
(
'
SAST
'
,
'
SAST
'
,
sast
.
paths
,
sast
.
paths
,
sast
.
newIssues
.
length
,
sast
.
newIssues
.
length
,
sast
.
resolvedIssues
.
length
,
sast
.
resolvedIssues
.
length
,
sast
.
allIssues
.
length
,
sast
.
allIssues
.
length
,
);
);
};
export
const
groupedSastContainerText
=
({
sastContainer
})
=>
{
if
(
sastContainer
.
hasError
)
{
return
s__
(
'
ciReport|Container scanning resulted in error while loading results
'
);
}
if
(
sastContainer
.
isLoading
)
{
return
s__
(
'
ciReport|Container scanning is loading
'
);
}
export
const
groupedSastContainerText
=
({
sastContainer
})
=>
return
textBuilder
(
textBuilder
(
'
Container scanning
'
,
'
Container scanning
'
,
sastContainer
.
paths
,
sastContainer
.
paths
,
sastContainer
.
newIssues
.
length
,
sastContainer
.
newIssues
.
length
,
sastContainer
.
resolvedIssues
.
length
,
sastContainer
.
resolvedIssues
.
length
,
);
);
};
export
const
groupedDastText
=
({
dast
})
=>
{
if
(
dast
.
hasError
)
{
return
s__
(
'
ciReport|DAST resulted in error while loading results
'
);
}
if
(
dast
.
isLoading
)
{
return
s__
(
'
ciReport|DAST is loading
'
);
}
return
textBuilder
(
'
DAST
'
,
dast
.
paths
,
dast
.
newIssues
.
length
,
dast
.
resolvedIssues
.
length
);
};
export
const
groupedDependencyText
=
({
dependencyScanning
})
=>
{
if
(
dependencyScanning
.
hasError
)
{
return
s__
(
'
ciReport|Dependency scanning resulted in error while loading results
'
);
}
export
const
groupedDastText
=
({
dast
})
=>
if
(
dependencyScanning
.
isLoading
)
{
textBuilder
(
'
DAST
'
,
dast
.
paths
,
dast
.
newIssues
.
length
,
dast
.
resolvedIssues
.
length
);
return
s__
(
'
ciReport|Dependency scanning is loading
'
);
}
export
const
groupedDependencyText
=
({
dependencyScanning
})
=>
return
textBuilder
(
textBuilder
(
'
Dependency scanning
'
,
'
Dependency scanning
'
,
dependencyScanning
.
paths
,
dependencyScanning
.
paths
,
dependencyScanning
.
newIssues
.
length
,
dependencyScanning
.
newIssues
.
length
,
dependencyScanning
.
resolvedIssues
.
length
,
dependencyScanning
.
resolvedIssues
.
length
,
dependencyScanning
.
allIssues
.
length
,
);
);
};
export
const
groupedSummaryText
=
(
state
,
getters
)
=>
{
export
const
groupedSummaryText
=
(
state
,
getters
)
=>
{
const
{
added
,
fixed
}
=
state
.
summaryCounts
;
const
{
added
,
fixed
}
=
state
.
summaryCounts
;
// All reports are loading
if
(
getters
.
areAllReportsLoading
)
{
return
s__
(
'
ciReport|Security scanning is loading
'
);
}
// All reports returned error
// All reports returned error
if
(
getters
.
allReportsHaveError
)
{
if
(
getters
.
allReportsHaveError
)
{
return
s__
(
'
ciReport|Security scanning failed loading any results
'
);
return
s__
(
'
ciReport|Security scanning failed loading any results
'
);
...
@@ -40,21 +84,25 @@ export const groupedSummaryText = (state, getters) => {
...
@@ -40,21 +84,25 @@ export const groupedSummaryText = (state, getters) => {
if
(
getters
.
noBaseInAllReports
)
{
if
(
getters
.
noBaseInAllReports
)
{
if
(
added
>
0
)
{
if
(
added
>
0
)
{
return
n__
(
return
n__
(
'
Security scanning
was unable to compare existing and new vulnerabilities. It detected %d vulnerabilit
y
'
,
'
Security scanning
detected %d vulnerability for the source branch onl
y
'
,
'
Security scanning
was unable to compare existing and new vulnerabilities. It detected %d vulnerabilities
'
,
'
Security scanning
detected %d vulnerabilities for the source branch only
'
,
added
,
added
,
);
);
}
}
return
s__
(
return
s__
(
'
Security scanning
was unable to compare existing and new vulnerabilities. It detected no vulnerabilities.
'
,
'
Security scanning
detected no vulnerabilities for the source branch only
'
,
);
);
}
}
const
text
=
[
s__
(
'
ciReport|Security scanning
'
)];
const
text
=
[
s__
(
'
ciReport|Security scanning
'
)];
if
(
getters
.
areReportsLoading
)
{
if
(
getters
.
areReportsLoading
&&
getters
.
anyReportHasError
)
{
text
.
push
(
'
(in progress)
'
);
text
.
push
(
'
(is loading, errors when loading results)
'
);
}
else
if
(
getters
.
areReportsLoading
&&
!
getters
.
anyReportHasError
)
{
text
.
push
(
'
(is loading)
'
);
}
else
if
(
!
getters
.
areReportsLoading
&&
getters
.
anyReportHasError
)
{
text
.
push
(
'
(errors when loading results)
'
);
}
}
if
(
added
>
0
&&
fixed
===
0
)
{
if
(
added
>
0
&&
fixed
===
0
)
{
...
@@ -81,15 +129,33 @@ export const groupedSummaryText = (state, getters) => {
...
@@ -81,15 +129,33 @@ export const groupedSummaryText = (state, getters) => {
return
text
.
join
(
'
'
);
return
text
.
join
(
'
'
);
};
};
export
const
sastStatusIcon
=
({
sast
})
=>
statusIcon
(
sast
.
hasError
,
sast
.
newIssues
.
length
);
export
const
summaryStatus
=
(
state
,
getters
)
=>
{
if
(
getters
.
areReportsLoading
)
{
return
LOADING
;
}
if
(
getters
.
anyReportHasError
||
getters
.
anyReportHasIssues
)
{
return
ERROR
;
}
return
SUCCESS
;
};
export
const
sastStatusIcon
=
({
sast
})
=>
statusIcon
(
sast
.
isLoading
,
sast
.
hasError
,
sast
.
newIssues
.
length
);
export
const
sastContainerStatusIcon
=
({
sastContainer
})
=>
export
const
sastContainerStatusIcon
=
({
sastContainer
})
=>
statusIcon
(
sastContainer
.
hasError
,
sastContainer
.
newIssues
.
length
);
statusIcon
(
sastContainer
.
isLoading
,
sastContainer
.
hasError
,
sastContainer
.
newIssues
.
length
);
export
const
dastStatusIcon
=
({
dast
})
=>
statusIcon
(
dast
.
hasError
,
dast
.
newIssues
.
length
);
export
const
dastStatusIcon
=
({
dast
})
=>
statusIcon
(
dast
.
isLoading
,
dast
.
hasError
,
dast
.
newIssues
.
length
);
export
const
dependencyScanningStatusIcon
=
({
dependencyScanning
})
=>
export
const
dependencyScanningStatusIcon
=
({
dependencyScanning
})
=>
statusIcon
(
dependencyScanning
.
hasError
,
dependencyScanning
.
newIssues
.
length
);
statusIcon
(
dependencyScanning
.
isLoading
,
dependencyScanning
.
hasError
,
dependencyScanning
.
newIssues
.
length
,
);
export
const
areReportsLoading
=
state
=>
export
const
areReportsLoading
=
state
=>
state
.
sast
.
isLoading
||
state
.
sast
.
isLoading
||
...
@@ -97,6 +163,12 @@ export const areReportsLoading = state =>
...
@@ -97,6 +163,12 @@ export const areReportsLoading = state =>
state
.
sastContainer
.
isLoading
||
state
.
sastContainer
.
isLoading
||
state
.
dependencyScanning
.
isLoading
;
state
.
dependencyScanning
.
isLoading
;
export
const
areAllReportsLoading
=
state
=>
state
.
sast
.
isLoading
&&
state
.
dast
.
isLoading
&&
state
.
sastContainer
.
isLoading
&&
state
.
dependencyScanning
.
isLoading
;
export
const
allReportsHaveError
=
state
=>
export
const
allReportsHaveError
=
state
=>
state
.
sast
.
hasError
&&
state
.
sast
.
hasError
&&
state
.
dast
.
hasError
&&
state
.
dast
.
hasError
&&
...
@@ -114,3 +186,9 @@ export const noBaseInAllReports = state =>
...
@@ -114,3 +186,9 @@ export const noBaseInAllReports = state =>
!
state
.
dast
.
paths
.
base
&&
!
state
.
dast
.
paths
.
base
&&
!
state
.
sastContainer
.
paths
.
base
&&
!
state
.
sastContainer
.
paths
.
base
&&
!
state
.
dependencyScanning
.
paths
.
base
;
!
state
.
dependencyScanning
.
paths
.
base
;
export
const
anyReportHasIssues
=
state
=>
state
.
sast
.
newIssues
.
length
>
0
||
state
.
dast
.
newIssues
.
length
>
0
||
state
.
sastContainer
.
newIssues
.
length
>
0
||
state
.
dependencyScanning
.
newIssues
.
length
>
0
;
ee/app/assets/javascripts/vue_shared/security_reports/store/mutations.js
View file @
edeab723
/* eslint-disable no-param-reassign */
import
*
as
types
from
'
./mutation_types
'
;
import
*
as
types
from
'
./mutation_types
'
;
import
{
import
{
parseSastIssues
,
parseSastIssues
,
...
@@ -9,24 +11,24 @@ import {
...
@@ -9,24 +11,24 @@ import {
export
default
{
export
default
{
[
types
.
SET_HEAD_BLOB_PATH
](
state
,
path
)
{
[
types
.
SET_HEAD_BLOB_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
blobPath
,
{
head
:
path
})
;
state
.
blobPath
.
head
=
path
;
},
},
[
types
.
SET_BASE_BLOB_PATH
](
state
,
path
)
{
[
types
.
SET_BASE_BLOB_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
blobPath
,
{
base
:
path
})
;
state
.
blobPath
.
base
=
path
;
},
},
// SAST
// SAST
[
types
.
SET_SAST_HEAD_PATH
](
state
,
path
)
{
[
types
.
SET_SAST_HEAD_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
sast
.
paths
,
{
head
:
path
})
;
state
.
sast
.
paths
.
head
=
path
;
},
},
[
types
.
SET_SAST_BASE_PATH
](
state
,
path
)
{
[
types
.
SET_SAST_BASE_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
sast
.
paths
,
{
base
:
path
})
;
state
.
sast
.
paths
.
base
=
path
;
},
},
[
types
.
REQUEST_SAST_REPORTS
](
state
)
{
[
types
.
REQUEST_SAST_REPORTS
](
state
)
{
Object
.
assign
(
state
.
sast
,
{
isLoading
:
true
})
;
state
.
sast
.
isLoading
=
true
;
},
},
/**
/**
...
@@ -52,50 +54,39 @@ export default {
...
@@ -52,50 +54,39 @@ export default {
const
newIssues
=
filterByKey
(
parsedHead
,
parsedBase
,
filterKey
);
const
newIssues
=
filterByKey
(
parsedHead
,
parsedBase
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
parsedBase
,
parsedHead
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
parsedBase
,
parsedHead
,
filterKey
);
const
allIssues
=
filterByKey
(
parsedHead
,
newIssues
.
concat
(
resolvedIssues
),
filterKey
);
const
allIssues
=
filterByKey
(
parsedHead
,
newIssues
.
concat
(
resolvedIssues
),
filterKey
);
Object
.
assign
(
state
,
{
state
.
sast
.
newIssues
=
newIssues
;
sast
:
{
state
.
sast
.
resolvedIssues
=
resolvedIssues
;
...
state
.
sast
,
state
.
sast
.
allIssues
=
allIssues
;
newIssues
,
state
.
sast
.
isLoading
=
false
;
resolvedIssues
,
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
allIssues
,
state
.
summaryCounts
.
fixed
+=
resolvedIssues
.
length
;
isLoading
:
false
,
},
summaryCounts
:
{
added
:
state
.
summaryCounts
.
added
+
newIssues
.
length
,
fixed
:
state
.
summaryCounts
.
fixed
+
resolvedIssues
.
length
,
},
});
}
else
if
(
reports
.
head
&&
!
reports
.
base
)
{
}
else
if
(
reports
.
head
&&
!
reports
.
base
)
{
const
newIssues
=
parseSastIssues
(
reports
.
head
,
state
.
blobPath
.
head
);
const
newIssues
=
parseSastIssues
(
reports
.
head
,
state
.
blobPath
.
head
);
Object
.
assign
(
state
.
sast
,
{
state
.
sast
.
newIssues
=
newIssues
;
newIssues
,
state
.
sast
.
isLoading
=
false
;
isLoading
:
false
,
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
});
}
}
},
},
[
types
.
RECEIVE_SAST_REPORTS_ERROR
](
state
)
{
[
types
.
RECEIVE_SAST_REPORTS_ERROR
](
state
)
{
Object
.
assign
(
state
.
sast
,
{
state
.
sast
.
isLoading
=
false
;
isLoading
:
false
,
state
.
sast
.
hasError
=
true
;
hasError
:
true
,
});
},
},
// SAST CONTAINER
// SAST CONTAINER
[
types
.
SET_SAST_CONTAINER_HEAD_PATH
](
state
,
path
)
{
[
types
.
SET_SAST_CONTAINER_HEAD_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
sastContainer
.
paths
,
{
head
:
path
})
;
state
.
sastContainer
.
paths
.
head
=
path
;
},
},
[
types
.
SET_SAST_CONTAINER_BASE_PATH
](
state
,
path
)
{
[
types
.
SET_SAST_CONTAINER_BASE_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
sastContainer
.
paths
,
{
base
:
path
})
;
state
.
sastContainer
.
paths
.
base
=
path
;
},
},
[
types
.
REQUEST_SAST_CONTAINER_REPORTS
](
state
)
{
[
types
.
REQUEST_SAST_CONTAINER_REPORTS
](
state
)
{
Object
.
assign
(
state
.
sastContainer
,
{
isLoading
:
true
})
;
state
.
sastContainer
.
isLoading
=
true
;
},
},
/**
/**
...
@@ -116,48 +107,40 @@ export default {
...
@@ -116,48 +107,40 @@ export default {
const
newIssues
=
filterByKey
(
headIssues
,
baseIssues
,
filterKey
);
const
newIssues
=
filterByKey
(
headIssues
,
baseIssues
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
baseIssues
,
headIssues
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
baseIssues
,
headIssues
,
filterKey
);
Object
.
assign
(
state
,
{
state
.
sastContainer
.
newIssues
=
newIssues
;
sastContainer
:
{
state
.
sastContainer
.
resolvedIssues
=
resolvedIssues
;
...
state
.
sastContainer
,
state
.
sastContainer
.
isLoading
=
false
;
isLoading
:
false
,
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
newIssues
,
state
.
summaryCounts
.
fixed
+=
resolvedIssues
.
length
;
resolvedIssues
,
},
summaryCounts
:
{
added
:
state
.
summaryCounts
.
added
+
newIssues
.
length
,
fixed
:
state
.
summaryCounts
.
fixed
+
resolvedIssues
.
length
,
},
});
}
else
if
(
reports
.
head
&&
!
reports
.
base
)
{
}
else
if
(
reports
.
head
&&
!
reports
.
base
)
{
Object
.
assign
(
state
.
sastContainer
,
{
const
newIssues
=
getUnapprovedVulnerabilities
(
isLoading
:
false
,
parseSastContainer
(
reports
.
head
.
vulnerabilities
),
newIssues
:
getUnapprovedVulnerabilities
(
reports
.
head
.
unapproved
,
parseSastContainer
(
reports
.
head
.
vulnerabilities
),
);
reports
.
head
.
unapproved
,
),
state
.
sastContainer
.
newIssues
=
newIssues
;
});
state
.
sastContainer
.
isLoading
=
false
;
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
}
}
},
},
[
types
.
RECEIVE_SAST_CONTAINER_ERROR
](
state
)
{
[
types
.
RECEIVE_SAST_CONTAINER_ERROR
](
state
)
{
Object
.
assign
(
state
.
sastContainer
,
{
state
.
sastContainer
.
isLoading
=
false
;
isLoading
:
false
,
state
.
sastContainer
.
hasError
=
true
;
hasError
:
true
,
});
},
},
// DAST
// DAST
[
types
.
SET_DAST_HEAD_PATH
](
state
,
path
)
{
[
types
.
SET_DAST_HEAD_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
dast
.
paths
,
{
head
:
path
})
;
state
.
dast
.
paths
.
head
=
path
;
},
},
[
types
.
SET_DAST_BASE_PATH
](
state
,
path
)
{
[
types
.
SET_DAST_BASE_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
dast
.
paths
,
{
base
:
path
})
;
state
.
dast
.
paths
.
base
=
path
;
},
},
[
types
.
REQUEST_DAST_REPORTS
](
state
)
{
[
types
.
REQUEST_DAST_REPORTS
](
state
)
{
Object
.
assign
(
state
.
dast
,
{
isLoading
:
true
})
;
state
.
dast
.
isLoading
=
true
;
},
},
[
types
.
RECEIVE_DAST_REPORTS
](
state
,
reports
)
{
[
types
.
RECEIVE_DAST_REPORTS
](
state
,
reports
)
{
...
@@ -168,45 +151,37 @@ export default {
...
@@ -168,45 +151,37 @@ export default {
const
newIssues
=
filterByKey
(
headIssues
,
baseIssues
,
filterKey
);
const
newIssues
=
filterByKey
(
headIssues
,
baseIssues
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
baseIssues
,
headIssues
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
baseIssues
,
headIssues
,
filterKey
);
Object
.
assign
(
state
,
{
state
.
dast
.
newIssues
=
newIssues
;
dast
:
{
state
.
dast
.
resolvedIssues
=
resolvedIssues
;
...
state
.
dast
,
state
.
dast
.
isLoading
=
false
;
isLoading
:
false
,
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
newIssues
,
state
.
summaryCounts
.
fixed
+=
resolvedIssues
.
length
;
resolvedIssues
,
},
summaryCounts
:
{
added
:
state
.
summaryCounts
.
added
+
newIssues
.
length
,
fixed
:
state
.
summaryCounts
.
fixed
+
resolvedIssues
.
length
,
},
});
}
else
if
(
reports
.
head
&&
!
reports
.
base
)
{
}
else
if
(
reports
.
head
&&
!
reports
.
base
)
{
Object
.
assign
(
state
.
dast
,
{
const
newIssues
=
parseDastIssues
(
reports
.
head
.
site
.
alerts
);
isLoading
:
false
,
newIssues
:
parseDastIssues
(
reports
.
head
.
site
.
alerts
),
state
.
dast
.
newIssues
=
newIssues
;
});
state
.
dast
.
isLoading
=
false
;
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
}
}
},
},
[
types
.
RECEIVE_DAST_ERROR
](
state
)
{
[
types
.
RECEIVE_DAST_ERROR
](
state
)
{
Object
.
assign
(
state
.
dast
,
{
state
.
dast
.
isLoading
=
false
;
isLoading
:
false
,
state
.
dast
.
hasError
=
true
;
hasError
:
true
,
});
},
},
// DEPENDECY SCANNING
// DEPENDECY SCANNING
[
types
.
SET_DEPENDENCY_SCANNING_HEAD_PATH
](
state
,
path
)
{
[
types
.
SET_DEPENDENCY_SCANNING_HEAD_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
dependencyScanning
.
paths
,
{
head
:
path
})
;
state
.
dependencyScanning
.
paths
.
head
=
path
;
},
},
[
types
.
SET_DEPENDENCY_SCANNING_BASE_PATH
](
state
,
path
)
{
[
types
.
SET_DEPENDENCY_SCANNING_BASE_PATH
](
state
,
path
)
{
Object
.
assign
(
state
.
dependencyScanning
.
paths
,
{
base
:
path
})
;
state
.
dependencyScanning
.
paths
.
base
=
path
;
},
},
[
types
.
REQUEST_DEPENDENCY_SCANNING_REPORTS
](
state
)
{
[
types
.
REQUEST_DEPENDENCY_SCANNING_REPORTS
](
state
)
{
Object
.
assign
(
state
.
dependencyScanning
,
{
isLoading
:
true
})
;
state
.
dependencyScanning
.
isLoading
=
true
;
},
},
/**
/**
...
@@ -234,31 +209,24 @@ export default {
...
@@ -234,31 +209,24 @@ export default {
const
resolvedIssues
=
filterByKey
(
parsedBase
,
parsedHead
,
filterKey
);
const
resolvedIssues
=
filterByKey
(
parsedBase
,
parsedHead
,
filterKey
);
const
allIssues
=
filterByKey
(
parsedHead
,
newIssues
.
concat
(
resolvedIssues
),
filterKey
);
const
allIssues
=
filterByKey
(
parsedHead
,
newIssues
.
concat
(
resolvedIssues
),
filterKey
);
Object
.
assign
(
state
,
{
state
.
dependencyScanning
.
newIssues
=
newIssues
;
dependencyScanning
:
{
state
.
dependencyScanning
.
resolvedIssues
=
resolvedIssues
;
...
state
.
dependencyScanning
,
state
.
dependencyScanning
.
allIssues
=
allIssues
;
newIssues
,
state
.
dependencyScanning
.
isLoading
=
false
;
resolvedIssues
,
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
allIssues
,
state
.
summaryCounts
.
fixed
+=
resolvedIssues
.
length
;
isLoading
:
false
,
}
},
summaryCounts
:
{
if
(
reports
.
head
&&
!
reports
.
base
)
{
added
:
state
.
summaryCounts
.
added
+
newIssues
.
length
,
const
newIssues
=
parseSastIssues
(
reports
.
head
,
state
.
blobPath
.
head
);
fixed
:
state
.
summaryCounts
.
fixed
+
resolvedIssues
.
length
,
state
.
dependencyScanning
.
newIssues
=
newIssues
;
},
state
.
dependencyScanning
.
isLoading
=
false
;
});
state
.
summaryCounts
.
added
+=
newIssues
.
length
;
}
else
{
Object
.
assign
(
state
.
dependencyScanning
,
{
newIssues
:
parseSastIssues
(
reports
.
head
,
state
.
blobPath
.
head
),
isLoading
:
false
,
});
}
}
},
},
[
types
.
RECEIVE_DEPENDENCY_SCANNING_ERROR
](
state
)
{
[
types
.
RECEIVE_DEPENDENCY_SCANNING_ERROR
](
state
)
{
Object
.
assign
(
state
.
dependencyScanning
,
{
state
.
dependencyScanning
.
isLoading
=
false
;
isLoading
:
false
,
state
.
dependencyScanning
.
hasError
=
true
;
hasError
:
true
,
});
},
},
};
};
ee/app/assets/javascripts/vue_shared/security_reports/store/utils.js
View file @
edeab723
...
@@ -64,30 +64,33 @@ export const textBuilder = (
...
@@ -64,30 +64,33 @@ export const textBuilder = (
resolvedIssues
=
0
,
resolvedIssues
=
0
,
allIssues
=
0
,
allIssues
=
0
,
)
=>
{
)
=>
{
// With no issues
if
(
newIssues
===
0
&&
resolvedIssues
===
0
&&
allIssues
===
0
)
{
return
sprintf
(
s__
(
'
ciReport|%{type} detected no security vulnerabilities
'
),
{
type
});
}
// with no new or fixed but with vulnerabilities
// with no new or fixed but with vulnerabilities
if
(
newIssues
===
0
&&
resolvedIssues
===
0
&&
allIssues
)
{
if
(
newIssues
===
0
&&
resolvedIssues
===
0
&&
allIssues
)
{
return
sprintf
(
s__
(
'
ciReport|%{type} detected no new security vulnerabilities
'
),
{
type
});
return
sprintf
(
s__
(
'
ciReport|%{type} detected no new security vulnerabilities
'
),
{
type
});
}
}
// with new issues and only head
if
(
!
paths
.
base
)
{
if
(
newIssues
>
0
&&
!
paths
.
base
)
{
if
(
newIssues
>
0
)
{
return
sprintf
(
n__
(
'
%{type} detected %d vulnerability for the source branch only
'
,
'
%{type} detected %d vulnerabilities for the source branch only
'
,
newIssues
,
),
{
type
},
);
}
return
sprintf
(
return
sprintf
(
n__
(
'
%{type} detected no vulnerabilities for the source branch only
'
,
'
%{type} was unable to compare existing and new vulnerabilities. It detected %d vulnerability
'
,
'
%{type} was unable to compare existing and new vulnerabilities. It detected %d vulnerabilities
'
,
newIssues
,
),
{
type
},
{
type
},
);
);
}
}
else
if
(
paths
.
base
&&
paths
.
head
)
{
// With no issues
if
(
newIssues
===
0
&&
resolvedIssues
===
0
&&
allIssues
===
0
)
{
return
sprintf
(
s__
(
'
ciReport|%{type} detected no security vulnerabilities
'
),
{
type
});
}
// with head + base
if
(
paths
.
base
&&
paths
.
head
)
{
// with only new issues
// with only new issues
if
(
newIssues
>
0
&&
resolvedIssues
===
0
)
{
if
(
newIssues
>
0
&&
resolvedIssues
===
0
)
{
return
sprintf
(
return
sprintf
(
...
@@ -128,7 +131,11 @@ export const textBuilder = (
...
@@ -128,7 +131,11 @@ export const textBuilder = (
return
''
;
return
''
;
};
};
export
const
statusIcon
=
(
failed
=
false
,
newIssues
=
0
,
neutralIssues
=
0
)
=>
{
export
const
statusIcon
=
(
loading
=
false
,
failed
=
false
,
newIssues
=
0
,
neutralIssues
=
0
)
=>
{
if
(
loading
)
{
return
'
loading
'
;
}
if
(
failed
||
newIssues
>
0
||
neutralIssues
>
0
)
{
if
(
failed
||
newIssues
>
0
||
neutralIssues
>
0
)
{
return
'
warning
'
;
return
'
warning
'
;
}
}
...
...
ee/app/assets/stylesheets/pages/security_reports.scss
View file @
edeab723
.pipeline-tab-content
{
.split-report-section
{
border-bottom
:
1px
solid
$gray-darker
;
.report-block-list
{
max-height
:
500px
;
overflow
:
auto
;
}
.space-children
,
.space-children
,
.space-children
>
span
{
.space-children
>
span
{
display
:
flex
;
display
:
flex
;
...
@@ -14,11 +21,32 @@
...
@@ -14,11 +21,32 @@
}
}
}
}
.mr-widget-grouped-section
{
.report-block-container
{
max-height
:
170px
;
overflow
:
auto
;
}
.report-block-list-issue-parent
{
padding
:
$gl-padding-top
$gl-padding
;
border-top
:
1px
solid
$border-color
;
}
.report-block-list-icon
.loading-container
{
position
:
relative
;
left
:
-2px
;
// needed to make the next element align with the
// elements below that have a svg with 16px width
.fa-spinner
{
width
:
16px
;
}
}
}
.report-block-container
{
.report-block-container
{
border-top
:
1px
solid
$
gray-darke
r
;
border-top
:
1px
solid
$
border-colo
r
;
padding
:
$gl-padding-top
;
padding
:
$gl-padding-top
;
background-color
:
$gray-light
;
background-color
:
$gray-light
;
margin
:
$gl-padding
#{
-
$gl-padding
}
#{
-
$gl-padding
}
;
// Clean MR widget CSS
// Clean MR widget CSS
line-height
:
20px
;
line-height
:
20px
;
...
@@ -44,6 +72,15 @@
...
@@ -44,6 +72,15 @@
&
.neutral
{
&
.neutral
{
color
:
$theme-gray-700
;
color
:
$theme-gray-700
;
}
}
.ci-status-icon
{
svg
{
width
:
16px
;
height
:
16px
;
top
:
3px
;
left
:
-2px
;
}
}
}
}
.report-block-list-issue
{
.report-block-list-issue
{
...
@@ -65,6 +102,10 @@
...
@@ -65,6 +102,10 @@
word-wrap
:
break-word
;
word-wrap
:
break-word
;
word-break
:
break-all
;
word-break
:
break-all
;
}
}
.btn-help
svg
{
top
:
5px
;
}
}
}
.report-block-issue-code
{
.report-block-issue-code
{
...
...
ee/changelogs/unreleased/4310-security-reports-step-2.yml
0 → 100644
View file @
edeab723
---
title
:
Renders grouped security reports in MR widget & split security reports in CI
view
merge_request
:
author
:
type
:
changed
ee/spec/features/projects/pipelines/pipeline_spec.rb
View file @
edeab723
...
@@ -37,8 +37,8 @@ describe 'Pipeline', :js do
...
@@ -37,8 +37,8 @@ describe 'Pipeline', :js do
expect
(
page
).
to
have_css
(
'#js-tab-security'
)
expect
(
page
).
to
have_css
(
'#js-tab-security'
)
end
end
it
'shows security report'
do
it
'shows security report
section
'
do
expect
(
page
).
to
have_content
(
'SAST
detected no security vulnerabilities
'
)
expect
(
page
).
to
have_content
(
'SAST
is loading
'
)
end
end
end
end
...
...
spec/javascripts/pipelines/pipeline_details_mediator_spec.js
View file @
edeab723
import
_
from
'
underscore
'
;
import
_
from
'
underscore
'
;
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
PipelineMediator
from
'
~/pipelines/pipeline_details_mediator
'
;
import
PipelineMediator
from
'
~/pipelines/pipeline_details_mediator
'
;
import
{
sastIssues
,
parsedSastIssuesStore
}
from
'
../vue_shared/security_reports/mock_data
'
;
describe
(
'
PipelineMdediator
'
,
()
=>
{
describe
(
'
PipelineMdediator
'
,
()
=>
{
let
mediator
;
let
mediator
;
...
@@ -40,29 +39,4 @@ describe('PipelineMdediator', () => {
...
@@ -40,29 +39,4 @@ describe('PipelineMdediator', () => {
});
});
});
});
});
});
describe
(
'
security reports
'
,
()
=>
{
const
interceptor
=
(
request
,
next
)
=>
{
next
(
request
.
respondWith
(
JSON
.
stringify
(
sastIssues
),
{
status
:
200
,
}));
};
beforeEach
(()
=>
{
Vue
.
http
.
interceptors
.
push
(
interceptor
);
});
afterEach
(()
=>
{
Vue
.
http
.
interceptors
=
_
.
without
(
Vue
.
http
.
interceptor
,
interceptor
);
});
it
(
'
fetches the requests endpoint and stores the data
'
,
(
done
)
=>
{
mediator
.
fetchSastReport
(
'
sast.json
'
,
'
path
'
);
setTimeout
(()
=>
{
expect
(
mediator
.
store
.
state
.
securityReports
.
sast
.
newIssues
).
toEqual
(
parsedSastIssuesStore
);
done
();
},
0
);
});
});
});
});
spec/javascripts/pipelines/pipeline_store_spec.js
View file @
edeab723
import
PipelineStore
from
'
~/pipelines/stores/pipeline_store
'
;
import
PipelineStore
from
'
~/pipelines/stores/pipeline_store
'
;
import
securityState
from
'
ee/vue_shared/security_reports/helpers/state
'
;
describe
(
'
Pipeline Store
'
,
()
=>
{
describe
(
'
Pipeline Store
'
,
()
=>
{
let
store
;
let
store
;
...
@@ -24,11 +23,4 @@ describe('Pipeline Store', () => {
...
@@ -24,11 +23,4 @@ describe('Pipeline Store', () => {
expect
(
store
.
state
.
pipeline
).
toEqual
({
foo
:
'
bar
'
});
expect
(
store
.
state
.
pipeline
).
toEqual
({
foo
:
'
bar
'
});
});
});
});
});
/**
* EE only
*/
it
(
'
should set default security state
'
,
()
=>
{
expect
(
store
.
state
.
securityReports
).
toEqual
(
securityState
);
});
});
});
spec/javascripts/pipelines/security_reports/report_summary_widget_spec.js
0 → 100644
View file @
edeab723
import
Vue
from
'
vue
'
;
import
store
from
'
ee/vue_shared/security_reports/store
'
;
import
state
from
'
ee/vue_shared/security_reports/store/state
'
;
import
reportSummary
from
'
ee/pipelines/components/security_reports/report_summary_widget.vue
'
;
import
{
createComponentWithStore
}
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
sastIssues
}
from
'
../../vue_shared/security_reports/mock_data
'
;
describe
(
'
Report summary widget
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
reportSummary
);
let
vm
;
beforeEach
(()
=>
{
vm
=
createComponentWithStore
(
Component
,
store
).
$mount
();
});
afterEach
(()
=>
{
vm
.
$destroy
();
// clean up the error state
vm
.
$store
.
replaceState
(
state
());
});
describe
(
'
without paths
'
,
()
=>
{
it
(
'
does not render any summary
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
)).
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
)).
toBeNull
();
});
});
describe
(
'
while loading
'
,
()
=>
{
beforeEach
(()
=>
{
vm
.
$store
.
dispatch
(
'
setSastHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
setDependencyScanningHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
requestSastReports
'
);
vm
.
$store
.
dispatch
(
'
requestDependencyScanningReports
'
);
});
it
(
'
renders loading icon and text for sast
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
SAST is loading
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary .fa-spinner
'
)).
not
.
toBeNull
();
done
();
});
});
it
(
'
renders loading icon and text for dependency scanning
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
Dependency scanning is loading
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary .fa-spinner
'
)).
not
.
toBeNull
();
done
();
});
});
});
describe
(
'
with error
'
,
()
=>
{
beforeEach
(()
=>
{
vm
.
$store
.
dispatch
(
'
setSastHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
setDependencyScanningHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
receiveSastError
'
);
vm
.
$store
.
dispatch
(
'
receiveDependencyScanningError
'
);
});
it
(
'
renders warning icon and error text for sast
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
SAST resulted in error while loading results
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary .js-ci-status-icon-warning
'
)).
not
.
toBeNull
();
done
();
});
});
it
(
'
renders warnin icon and error text for dependency scanning
'
,
done
=>
{
vm
.
$nextTick
()
.
then
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
Dependency scanning resulted in error while loading results
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary .js-ci-status-icon-warning
'
)).
not
.
toBeNull
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
describe
(
'
with vulnerabilities
'
,
()
=>
{
beforeEach
(()
=>
{
vm
.
$store
.
dispatch
(
'
setSastHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
setDependencyScanningHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
receiveSastReports
'
,
{
head
:
sastIssues
,
});
vm
.
$store
.
dispatch
(
'
receiveDependencyScanningReports
'
,
{
head
:
sastIssues
,
});
});
it
(
'
renders warning icon and vulnerabilities text for sast
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
SAST detected 3 vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary .js-ci-status-icon-warning
'
)).
not
.
toBeNull
();
done
();
});
});
it
(
'
renders warning icon and vulnerabilities text for dependency scanning
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
Dependency scanning detected 3 vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary .js-ci-status-icon-warning
'
)).
not
.
toBeNull
();
done
();
});
});
});
describe
(
'
without vulnerabilities
'
,
()
=>
{
beforeEach
(()
=>
{
vm
.
$store
.
dispatch
(
'
setSastHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
setDependencyScanningHeadPath
'
,
'
head.json
'
);
vm
.
$store
.
dispatch
(
'
receiveSastReports
'
,
{
head
:
[],
});
vm
.
$store
.
dispatch
(
'
receiveDependencyScanningReports
'
,
{
head
:
[],
});
});
it
(
'
renders success icon and vulnerabilities text for sast
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
SAST detected no vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary .js-ci-status-icon-success
'
)).
not
.
toBeNull
();
done
();
});
});
it
(
'
renders success icon and vulnerabilities text for dependency scanning
'
,
done
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
)
.
textContent
.
trim
()
.
replace
(
/
\s\s
+/g
,
'
'
),
).
toEqual
(
'
Dependency scanning detected no vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary .js-ci-status-icon-success
'
)).
not
.
toBeNull
();
done
();
});
});
});
});
spec/javascripts/pipelines/security_reports/sast_report_summary_widget_spec.js
deleted
100644 → 0
View file @
a990e800
import
Vue
from
'
vue
'
;
import
reportSummary
from
'
ee/pipelines/components/security_reports/report_summary_widget.vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
describe
(
'
Report summary widget
'
,
()
=>
{
let
vm
;
let
Component
;
beforeEach
(()
=>
{
Component
=
Vue
.
extend
(
reportSummary
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
describe
(
'
with vulnerabilities
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
sastIssues
:
2
,
dependencyScanningIssues
:
4
,
hasSast
:
true
,
hasDependencyScanning
:
true
,
});
});
it
(
'
renders summary text with warning icon for sast
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
).
textContent
.
trim
().
replace
(
/
\s\s
+/g
,
'
'
)).
toEqual
(
'
SAST detected 2 vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary span
'
).
classList
).
toContain
(
'
ci-status-icon-warning
'
);
});
it
(
'
renders summary text with warning icon for dependency scanning
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
).
textContent
.
trim
().
replace
(
/
\s\s
+/g
,
'
'
)).
toEqual
(
'
Dependency scanning detected 4 vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary span
'
).
classList
).
toContain
(
'
ci-status-icon-warning
'
);
});
});
describe
(
'
without vulnerabilities
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
hasSast
:
true
,
hasDependencyScanning
:
true
,
});
});
it
(
'
render summary text with success icon for sast
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary
'
).
textContent
.
trim
().
replace
(
/
\s\s
+/g
,
'
'
)).
toEqual
(
'
SAST detected no vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-summary span
'
).
classList
).
toContain
(
'
ci-status-icon-success
'
);
});
it
(
'
render summary text with success icon for dependecy scanning
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary
'
).
textContent
.
trim
().
replace
(
/
\s\s
+/g
,
'
'
)).
toEqual
(
'
Dependency scanning detected no vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-dss-summary span
'
).
classList
).
toContain
(
'
ci-status-icon-success
'
);
});
});
});
spec/javascripts/pipelines/security_reports/security_report_app_spec.js
deleted
100644 → 0
View file @
a990e800
import
Vue
from
'
vue
'
;
import
securityReportApp
from
'
ee/pipelines/components/security_reports/security_report_app.vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
parsedSastIssuesHead
}
from
'
spec/vue_shared/security_reports/mock_data
'
;
describe
(
'
Security Report App
'
,
()
=>
{
let
vm
;
let
Component
;
beforeEach
(()
=>
{
Component
=
Vue
.
extend
(
securityReportApp
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
describe
(
'
sast report
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
securityReports
:
{
sast
:
{
isLoading
:
false
,
hasError
:
false
,
newIssues
:
parsedSastIssuesHead
,
resolvedIssues
:
[],
allIssues
:
[],
},
dependencyScanning
:
{
isLoading
:
false
,
hasError
:
false
,
newIssues
:
parsedSastIssuesHead
,
resolvedIssues
:
[],
allIssues
:
[],
},
},
hasDependencyScanning
:
true
,
hasSast
:
true
,
});
});
it
(
'
renders the sast report
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget .js-code-text
'
).
textContent
.
trim
()).
toEqual
(
'
SAST degraded on 2 security vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
.js-sast-widget .js-mr-code-new-issues li
'
).
length
).
toEqual
(
parsedSastIssuesHead
.
length
);
const
issue
=
vm
.
$el
.
querySelector
(
'
.js-sast-widget .js-mr-code-new-issues li
'
).
textContent
;
expect
(
issue
).
toContain
(
parsedSastIssuesHead
[
0
].
message
);
expect
(
issue
).
toContain
(
parsedSastIssuesHead
[
0
].
path
);
});
it
(
'
renders the dependency scanning report
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .js-code-text
'
).
textContent
.
trim
()).
toEqual
(
'
Dependency scanning degraded on 2 security vulnerabilities
'
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
.js-dependency-scanning-widget .js-mr-code-new-issues li
'
).
length
).
toEqual
(
parsedSastIssuesHead
.
length
);
const
issue
=
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .js-mr-code-new-issues li
'
).
textContent
;
expect
(
issue
).
toContain
(
parsedSastIssuesHead
[
0
].
message
);
expect
(
issue
).
toContain
(
parsedSastIssuesHead
[
0
].
path
);
});
});
});
spec/javascripts/vue_mr_widget/ee_mr_widget_options_spec.js
View file @
edeab723
...
@@ -5,35 +5,48 @@ import mrWidgetOptions from 'ee/vue_merge_request_widget/mr_widget_options';
...
@@ -5,35 +5,48 @@ import mrWidgetOptions from 'ee/vue_merge_request_widget/mr_widget_options';
import
MRWidgetService
from
'
ee/vue_merge_request_widget/services/mr_widget_service
'
;
import
MRWidgetService
from
'
ee/vue_merge_request_widget/services/mr_widget_service
'
;
import
MRWidgetStore
from
'
ee/vue_merge_request_widget/stores/mr_widget_store
'
;
import
MRWidgetStore
from
'
ee/vue_merge_request_widget/stores/mr_widget_store
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
mockData
,
{
import
state
from
'
ee/vue_shared/security_reports/store/state
'
;
baseIssues
,
import
mockData
,
{
baseIssues
,
headIssues
,
basePerformance
,
headPerformance
}
from
'
./mock_data
'
;
headIssues
,
basePerformance
,
headPerformance
,
}
from
'
./mock_data
'
;
import
{
import
{
sastIssues
,
sastIssues
,
sastIssuesBase
,
sastIssuesBase
,
dockerReport
,
dockerReport
,
docker
ReportParsed
,
docker
BaseReport
,
dast
,
dast
,
parsedDast
,
dastBase
,
sastBaseAllIssues
,
sastBaseAllIssues
,
sastHeadAllIssues
,
sastHeadAllIssues
,
}
from
'
../vue_shared/security_reports/mock_data
'
;
}
from
'
../vue_shared/security_reports/mock_data
'
;
describe
(
'
ee merge request widget options
'
,
()
=>
{
describe
(
'
ee merge request widget options
'
,
()
=>
{
let
vm
;
let
vm
;
let
mock
;
let
Component
;
let
Component
;
function
removeBreakLine
(
data
)
{
return
data
.
replace
(
/
\r?\n
|
\r
/g
,
''
)
.
replace
(
/
\s\s
+/g
,
'
'
)
.
trim
();
}
beforeEach
(()
=>
{
beforeEach
(()
=>
{
delete
mrWidgetOptions
.
extends
.
el
;
// Prevent component mounting
delete
mrWidgetOptions
.
extends
.
el
;
// Prevent component mounting
Component
=
Vue
.
extend
(
mrWidgetOptions
);
Component
=
Vue
.
extend
(
mrWidgetOptions
);
mock
=
new
MockAdapter
(
axios
);
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
vm
.
$destroy
();
vm
.
$destroy
();
mock
.
restore
();
// Clean security reports state
Component
.
mr
.
sast
=
state
().
sast
;
Component
.
mr
.
sastContainer
=
state
().
sastContainer
;
Component
.
mr
.
dast
=
state
().
dast
;
Component
.
mr
.
dependencyScanning
=
state
().
dependencyScanning
;
});
});
describe
(
'
security widget
'
,
()
=>
{
describe
(
'
security widget
'
,
()
=>
{
...
@@ -52,56 +65,51 @@ describe('ee merge request widget options', () => {
...
@@ -52,56 +65,51 @@ describe('ee merge request widget options', () => {
describe
(
'
when it is loading
'
,
()
=>
{
describe
(
'
when it is loading
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastBaseAllIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastHeadAllIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget
'
).
textContent
.
trim
()).
toContain
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget
'
).
textContent
.
trim
()
,
'
SAST is loading
'
,
)
.
toContain
(
'
Loading security report
'
)
;
);
});
});
});
});
describe
(
'
with successful request
'
,
()
=>
{
describe
(
'
with successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
).
toEqual
(
'
SAST improved on 1 security vulnerability and degraded on 2 security vulnerabilities
'
);
vm
.
$el
.
querySelector
(
'
.js-sast-widget .report-block-list-issue-description
'
)
.
textContent
,
),
).
toEqual
(
'
SAST detected 2 new vulnerabilities and 1 fixed vulnerability
'
);
done
();
done
();
},
0
);
},
0
);
});
});
});
});
describe
(
'
with full report and no added or fixed issues
'
,
()
=>
{
describe
(
'
with full report and no added or fixed issues
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastBaseAllIssues
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastBaseAllIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastHeadAllIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastHeadAllIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
renders no new vulnerabilities message
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
renders no new vulnerabilities message
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget .report-block-list-issue-description
'
)
.
textContent
,
),
).
toEqual
(
'
SAST detected no new security vulnerabilities
'
);
).
toEqual
(
'
SAST detected no new security vulnerabilities
'
);
done
();
done
();
},
0
);
},
0
);
...
@@ -109,24 +117,20 @@ describe('ee merge request widget options', () => {
...
@@ -109,24 +117,20 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with empty successful request
'
,
()
=>
{
describe
(
'
with empty successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget .report-block-list-issue-description
'
)
.
textContent
,
).
trim
(),
).
toEqual
(
'
SAST detected no security vulnerabilities
'
);
).
toEqual
(
'
SAST detected no security vulnerabilities
'
);
done
();
done
();
},
0
);
},
0
);
...
@@ -134,24 +138,17 @@ describe('ee merge request widget options', () => {
...
@@ -134,24 +138,17 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with failed request
'
,
()
=>
{
describe
(
'
with failed request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
path.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
500
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render error indicator
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render error indicator
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget
'
).
textContent
)).
toContain
(
vm
.
$el
.
querySelector
(
'
.js-sast-widget
'
).
textContent
.
trim
()
,
'
SAST resulted in error while loading results
'
,
)
.
toContain
(
'
Failed to load security report
'
)
;
);
done
();
done
();
},
0
);
},
0
);
});
});
...
@@ -174,56 +171,55 @@ describe('ee merge request widget options', () => {
...
@@ -174,56 +171,55 @@ describe('ee merge request widget options', () => {
describe
(
'
when it is loading
'
,
()
=>
{
describe
(
'
when it is loading
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget
'
).
textContent
.
trim
(
),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget
'
).
textContent
),
).
toContain
(
'
Loading dependency scanning report
'
);
).
toContain
(
'
Dependency scanning is loading
'
);
});
});
});
});
describe
(
'
with successful request
'
,
()
=>
{
describe
(
'
with successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
).
toEqual
(
'
Dependency scanning improved on 1 security vulnerability and degraded on 2 security vulnerabilities
'
);
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .report-block-list-issue-description
'
,
).
textContent
,
),
).
toEqual
(
'
Dependency scanning detected 2 new vulnerabilities and 1 fixed vulnerability
'
,
);
done
();
done
();
},
0
);
},
0
);
});
});
});
});
describe
(
'
with full report and no added or fixed issues
'
,
()
=>
{
describe
(
'
with full report and no added or fixed issues
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastBaseAllIssues
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
sastBaseAllIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastHeadAllIssues
);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
sastHeadAllIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
renders no new vulnerabilities message
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
renders no new vulnerabilities message
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .report-block-list-issue-description
'
,
).
textContent
,
),
).
toEqual
(
'
Dependency scanning detected no new security vulnerabilities
'
);
).
toEqual
(
'
Dependency scanning detected no new security vulnerabilities
'
);
done
();
done
();
},
0
);
},
0
);
...
@@ -231,24 +227,21 @@ describe('ee merge request widget options', () => {
...
@@ -231,24 +227,21 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with empty successful request
'
,
()
=>
{
describe
(
'
with empty successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
path.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
200
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget .report-block-list-issue-description
'
,
).
textContent
,
),
).
toEqual
(
'
Dependency scanning detected no security vulnerabilities
'
);
).
toEqual
(
'
Dependency scanning detected no security vulnerabilities
'
);
done
();
done
();
},
0
);
},
0
);
...
@@ -256,24 +249,17 @@ describe('ee merge request widget options', () => {
...
@@ -256,24 +249,17 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with failed request
'
,
()
=>
{
describe
(
'
with failed request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
path.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
path.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
head_path.json
'
).
reply
(
500
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render error indicator
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render error indicator
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget
'
).
textContent
.
trim
(
),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-dependency-scanning-widget
'
).
textContent
),
).
toContain
(
'
Failed to load dependency scanning report
'
);
).
toContain
(
'
Dependency scanning resulted in error while loading results
'
);
done
();
done
();
},
0
);
},
0
);
});
});
...
@@ -296,56 +282,57 @@ describe('ee merge request widget options', () => {
...
@@ -296,56 +282,57 @@ describe('ee merge request widget options', () => {
describe
(
'
when it is loading
'
,
()
=>
{
describe
(
'
when it is loading
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
headIssues
);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
baseIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget
'
).
textContent
.
trim
(
),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget
'
).
textContent
),
).
toContain
(
'
Loading codeclimate report
'
);
).
toContain
(
'
Loading codeclimate report
'
);
});
});
});
});
describe
(
'
with successful request
'
,
()
=>
{
describe
(
'
with successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
headIssues
);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
headIssues
);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
baseIssues
);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
baseIssues
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget .js-code-text
'
).
textContent
,
),
).
toEqual
(
'
Code quality improved on 1 point and degraded on 1 point
'
);
).
toEqual
(
'
Code quality improved on 1 point and degraded on 1 point
'
);
done
();
done
();
},
0
);
},
0
);
});
});
describe
(
'
text connector
'
,
()
=>
{
describe
(
'
text connector
'
,
()
=>
{
it
(
'
should only render information about fixed issues
'
,
(
done
)
=>
{
it
(
'
should only render information about fixed issues
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
vm
.
mr
.
codeclimateMetrics
.
newIssues
=
[];
vm
.
mr
.
codeclimateMetrics
.
newIssues
=
[];
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget .js-code-text
'
).
textContent
,
),
).
toEqual
(
'
Code quality improved on 1 point
'
);
).
toEqual
(
'
Code quality improved on 1 point
'
);
done
();
done
();
});
});
},
0
);
},
0
);
});
});
it
(
'
should only render information about added issues
'
,
(
done
)
=>
{
it
(
'
should only render information about added issues
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
vm
.
mr
.
codeclimateMetrics
.
resolvedIssues
=
[];
vm
.
mr
.
codeclimateMetrics
.
resolvedIssues
=
[];
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget .js-code-text
'
).
textContent
,
),
).
toEqual
(
'
Code quality degraded on 1 point
'
);
).
toEqual
(
'
Code quality degraded on 1 point
'
);
done
();
done
();
});
});
...
@@ -355,10 +342,7 @@ describe('ee merge request widget options', () => {
...
@@ -355,10 +342,7 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with empty successful request
'
,
()
=>
{
describe
(
'
with empty successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
...
@@ -368,10 +352,12 @@ describe('ee merge request widget options', () => {
...
@@ -368,10 +352,12 @@ describe('ee merge request widget options', () => {
mock
.
restore
();
mock
.
restore
();
});
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
it
(
'
should render provided data
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget .js-code-text
'
).
textContent
,
),
).
toEqual
(
'
No changes to code quality
'
);
).
toEqual
(
'
No changes to code quality
'
);
done
();
done
();
},
0
);
},
0
);
...
@@ -379,22 +365,19 @@ describe('ee merge request widget options', () => {
...
@@ -379,22 +365,19 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with failed request
'
,
()
=>
{
describe
(
'
with failed request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
head.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
head.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
500
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render error indicator
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render error indicator
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget
'
).
textContent
.
trim
()).
toContain
(
'
Failed to load codeclimate report
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-codequality-widget .js-code-text
'
).
textContent
,
),
).
toContain
(
'
Failed to load codeclimate report
'
);
done
();
done
();
},
0
);
},
0
);
});
});
...
@@ -417,57 +400,63 @@ describe('ee merge request widget options', () => {
...
@@ -417,57 +400,63 @@ describe('ee merge request widget options', () => {
describe
(
'
when it is loading
'
,
()
=>
{
describe
(
'
when it is loading
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
headPerformance
);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
basePerformance
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget
'
).
textContent
.
trim
(
),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget
'
).
textContent
),
).
toContain
(
'
Loading performance report
'
);
).
toContain
(
'
Loading performance report
'
);
});
});
});
});
describe
(
'
with successful request
'
,
()
=>
{
describe
(
'
with successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
headPerformance
);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
headPerformance
);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
basePerformance
);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
basePerformance
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
)
.
textContent
,
),
).
toEqual
(
'
Performance metrics improved on 2 points and degraded on 1 point
'
);
).
toEqual
(
'
Performance metrics improved on 2 points and degraded on 1 point
'
);
done
();
done
();
},
0
);
},
0
);
});
});
describe
(
'
text connector
'
,
()
=>
{
describe
(
'
text connector
'
,
()
=>
{
it
(
'
should only render information about fixed issues
'
,
(
done
)
=>
{
it
(
'
should only render information about fixed issues
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
vm
.
mr
.
performanceMetrics
.
degraded
=
[];
vm
.
mr
.
performanceMetrics
.
degraded
=
[];
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
,
).
textContent
,
),
).
toEqual
(
'
Performance metrics improved on 2 points
'
);
).
toEqual
(
'
Performance metrics improved on 2 points
'
);
done
();
done
();
});
});
},
0
);
},
0
);
});
});
it
(
'
should only render information about added issues
'
,
(
done
)
=>
{
it
(
'
should only render information about added issues
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
vm
.
mr
.
performanceMetrics
.
improved
=
[];
vm
.
mr
.
performanceMetrics
.
improved
=
[];
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
,
).
textContent
,
),
).
toEqual
(
'
Performance metrics degraded on 1 point
'
);
).
toEqual
(
'
Performance metrics degraded on 1 point
'
);
done
();
done
();
});
});
...
@@ -477,23 +466,19 @@ describe('ee merge request widget options', () => {
...
@@ -477,23 +466,19 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with empty successful request
'
,
()
=>
{
describe
(
'
with empty successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
head.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
200
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render provided data
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
)
.
textContent
,
),
).
toEqual
(
'
No changes to performance metrics
'
);
).
toEqual
(
'
No changes to performance metrics
'
);
done
();
done
();
},
0
);
},
0
);
...
@@ -501,34 +486,30 @@ describe('ee merge request widget options', () => {
...
@@ -501,34 +486,30 @@ describe('ee merge request widget options', () => {
});
});
describe
(
'
with failed request
'
,
()
=>
{
describe
(
'
with failed request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
head.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
head.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
500
,
[]);
mock
.
onGet
(
'
base.json
'
).
reply
(
500
,
[]);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
});
});
afterEach
(()
=>
{
it
(
'
should render error indicator
'
,
done
=>
{
mock
.
restore
();
});
it
(
'
should render error indicator
'
,
(
done
)
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget
'
).
textContent
.
trim
()).
toContain
(
'
Failed to load performance report
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-performance-widget .js-code-text
'
).
textContent
),
).
toContain
(
'
Failed to load performance report
'
);
done
();
done
();
},
0
);
},
0
);
});
});
});
});
});
});
describe
(
'
dock
er report
'
,
()
=>
{
describe
(
'
sast contain
er report
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
gl
.
mrWidgetData
=
{
gl
.
mrWidgetData
=
{
...
mockData
,
...
mockData
,
sast_container
:
{
sast_container
:
{
head_path
:
'
gl-sast-container.json
'
,
head_path
:
'
gl-sast-container.json
'
,
base_path
:
'
sast-container-base.json
'
,
},
},
};
};
...
@@ -538,71 +519,50 @@ describe('ee merge request widget options', () => {
...
@@ -538,71 +519,50 @@ describe('ee merge request widget options', () => {
describe
(
'
when it is loading
'
,
()
=>
{
describe
(
'
when it is loading
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
mock
.
onGet
(
'
gl-sast-container.json
'
).
reply
(
200
,
dockerReport
);
mock
.
onGet
(
'
sast-container-base.json
'
).
reply
(
200
,
dockerBaseReport
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
expect
(
expect
(
removeBreakLine
(
vm
.
$el
.
querySelector
(
'
.js-sast-container
'
).
textContent
)).
toContain
(
vm
.
$el
.
querySelector
(
'
.js-docker-widget
'
).
textContent
.
trim
()
,
'
Container scanning is loading
'
,
)
.
toContain
(
'
Loading sast:container report
'
)
;
);
});
});
});
});
describe
(
'
with successful request
'
,
()
=>
{
describe
(
'
with successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
gl-sast-container.json
'
).
reply
(
200
,
dockerReport
);
mock
.
onGet
(
'
gl-sast-container.json
'
).
reply
(
200
,
dockerReport
);
vm
=
mountComponent
(
Component
);
mock
.
onGet
(
'
sast-container-base.json
'
).
reply
(
200
,
dockerBaseReport
);
});
afterEach
(()
=>
{
vm
=
mountComponent
(
Component
);
mock
.
restore
();
});
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
it
(
'
should render provided data
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-docker-widget .js-code-text
'
).
textContent
.
trim
(),
removeBreakLine
(
).
toEqual
(
'
SAST:container found 3 vulnerabilities, of which 1 is approved
'
);
vm
.
$el
.
querySelector
(
'
.js-sast-container .report-block-list-issue-description
'
)
.
textContent
,
vm
.
$el
.
querySelector
(
'
.js-docker-widget .js-collapse-btn
'
).
click
();
),
).
toEqual
(
'
Container scanning detected 1 new vulnerability
'
);
Vue
.
nextTick
(()
=>
{
done
();
expect
(
vm
.
$el
.
querySelector
(
'
.js-docker-widget .report-block-info
'
).
textContent
.
trim
(),
).
toContain
(
'
Unapproved vulnerabilities (red) can be marked as approved.
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-docker-widget .report-block-info a
'
).
textContent
.
trim
(),
).
toContain
(
'
Learn more about whitelisting
'
);
const
firstVulnerability
=
vm
.
$el
.
querySelector
(
'
.js-docker-widget .report-block-list
'
).
textContent
.
trim
();
expect
(
firstVulnerability
).
toContain
(
dockerReportParsed
.
unapproved
[
0
].
name
);
expect
(
firstVulnerability
).
toContain
(
dockerReportParsed
.
unapproved
[
0
].
path
);
done
();
});
},
0
);
},
0
);
});
});
});
});
describe
(
'
with failed request
'
,
()
=>
{
describe
(
'
with failed request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
gl-sast-container.json
'
).
reply
(
500
,
{});
mock
.
onGet
(
'
gl-sast-container.json
'
).
reply
(
500
,
{});
vm
=
mountComponent
(
Component
);
mock
.
onGet
(
'
sast-container-base.json
'
).
reply
(
500
,
{});
});
afterEach
(()
=>
{
vm
=
mountComponent
(
Component
);
mock
.
restore
();
});
});
it
(
'
should render error indicator
'
,
(
done
)
=>
{
it
(
'
should render error indicator
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-sast-container
'
).
textContent
.
trim
()).
toContain
(
vm
.
$el
.
querySelector
(
'
.js-docker-widget
'
).
textContent
.
trim
()
,
'
Container scanning resulted in error while loading results
'
,
)
.
toContain
(
'
Failed to load sast:container report
'
)
;
);
done
();
done
();
},
0
);
},
0
);
});
});
...
@@ -615,6 +575,7 @@ describe('ee merge request widget options', () => {
...
@@ -615,6 +575,7 @@ describe('ee merge request widget options', () => {
...
mockData
,
...
mockData
,
dast
:
{
dast
:
{
head_path
:
'
dast.json
'
,
head_path
:
'
dast.json
'
,
base_path
:
'
dast_base.json
'
,
},
},
};
};
...
@@ -624,63 +585,49 @@ describe('ee merge request widget options', () => {
...
@@ -624,63 +585,49 @@ describe('ee merge request widget options', () => {
describe
(
'
when it is loading
'
,
()
=>
{
describe
(
'
when it is loading
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
mock
.
onGet
(
'
dast.json
'
).
reply
(
200
,
dast
);
mock
.
onGet
(
'
dast_base.json
'
).
reply
(
200
,
dastBase
);
vm
=
mountComponent
(
Component
);
vm
=
mountComponent
(
Component
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dast-widget
'
).
textContent
.
trim
()).
toContain
(
vm
.
$el
.
querySelector
(
'
.js-dast-widget
'
).
textContent
.
trim
()
,
'
DAST is loading
'
,
)
.
toContain
(
'
Loading DAST report
'
)
;
);
});
});
});
});
describe
(
'
with successful request
'
,
()
=>
{
describe
(
'
with successful request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
dast.json
'
).
reply
(
200
,
dast
);
mock
.
onGet
(
'
dast.json
'
).
reply
(
200
,
dast
);
vm
=
mountComponent
(
Component
);
mock
.
onGet
(
'
dast_base.json
'
).
reply
(
200
,
dastBase
);
});
afterEach
(()
=>
{
vm
=
mountComponent
(
Component
);
mock
.
restore
();
});
});
it
(
'
should render provided data
'
,
(
done
)
=>
{
it
(
'
should render provided data
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dast-widget .js-code-text
'
).
textContent
.
trim
(),
vm
.
$el
).
toEqual
(
'
DAST detected 2 alerts by analyzing the review app
'
);
.
querySelector
(
'
.js-dast-widget .report-block-list-issue-description
'
)
.
textContent
.
trim
(),
vm
.
$el
.
querySelector
(
'
.js-dast-widget button
'
).
click
();
).
toEqual
(
'
DAST detected 1 new vulnerability
'
);
done
();
Vue
.
nextTick
(()
=>
{
const
firstVulnerability
=
vm
.
$el
.
querySelector
(
'
.js-dast-widget .report-block-list
'
).
textContent
.
trim
();
expect
(
firstVulnerability
).
toContain
(
parsedDast
[
0
].
name
);
expect
(
firstVulnerability
).
toContain
(
parsedDast
[
0
].
priority
);
done
();
});
},
0
);
},
0
);
});
});
});
});
describe
(
'
with failed request
'
,
()
=>
{
describe
(
'
with failed request
'
,
()
=>
{
let
mock
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mock
=
mock
=
new
MockAdapter
(
axios
);
mock
.
onGet
(
'
dast.json
'
).
reply
(
500
,
{});
mock
.
onGet
(
'
dast.json
'
).
reply
(
500
,
{});
vm
=
mountComponent
(
Component
);
mock
.
onGet
(
'
dast_base.json
'
).
reply
(
500
,
{});
});
afterEach
(()
=>
{
vm
=
mountComponent
(
Component
);
mock
.
restore
();
});
});
it
(
'
should render error indicator
'
,
(
done
)
=>
{
it
(
'
should render error indicator
'
,
done
=>
{
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.js-dast-widget
'
).
textContent
.
trim
()).
toContain
(
vm
.
$el
.
querySelector
(
'
.js-dast-widget
'
).
textContent
.
trim
()
,
'
DAST resulted in error while loading results
'
,
)
.
toContain
(
'
Failed to load DAST report
'
)
;
);
done
();
done
();
},
0
);
},
0
);
});
});
...
@@ -725,183 +672,6 @@ describe('ee merge request widget options', () => {
...
@@ -725,183 +672,6 @@ describe('ee merge request widget options', () => {
expect
(
vm
.
shouldRenderApprovals
).
toBeTruthy
();
expect
(
vm
.
shouldRenderApprovals
).
toBeTruthy
();
});
});
});
});
describe
(
'
dockerText
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
mrData
:
{
...
mockData
,
sast_container
:
{
path
:
'
foo
'
,
},
},
});
});
describe
(
'
with no vulnerabilities
'
,
()
=>
{
it
(
'
returns No vulnerabilities found
'
,
()
=>
{
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container no vulnerabilities were found
'
);
});
});
describe
(
'
without unapproved vulnerabilities
'
,
()
=>
{
it
(
'
returns approved information - single
'
,
()
=>
{
vm
.
mr
.
dockerReport
=
{
vulnerabilities
:
[{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
}],
approved
:
[{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
}],
unapproved
:
[],
};
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container found 1 approved vulnerability
'
);
});
it
(
'
returns approved information - plural
'
,
()
=>
{
vm
.
mr
.
dockerReport
=
{
vulnerabilities
:
[{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
}],
approved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
{
vulnerability
:
'
CVE-2017-13726
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
unapproved
:
[],
};
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container found 2 approved vulnerabilities
'
);
});
});
describe
(
'
with only unapproved vulnerabilities
'
,
()
=>
{
it
(
'
returns number of vulnerabilities - single
'
,
()
=>
{
vm
.
mr
.
dockerReport
=
{
vulnerabilities
:
[{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
}],
unapproved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
approved
:
[],
};
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container found 1 vulnerability
'
);
});
it
(
'
returns number of vulnerabilities - plural
'
,
()
=>
{
vm
.
mr
.
dockerReport
=
{
vulnerabilities
:
[{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
}],
unapproved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
approved
:
[],
};
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container found 2 vulnerabilities
'
);
});
});
describe
(
'
with approved and unapproved vulnerabilities
'
,
()
=>
{
it
(
'
returns message with information about both - single
'
,
()
=>
{
vm
.
mr
.
dockerReport
=
{
vulnerabilities
:
[{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
}],
unapproved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
approved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
};
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container found 1 vulnerability, of which 1 is approved
'
);
});
it
(
'
returns message with information about both - plural
'
,
()
=>
{
vm
.
mr
.
dockerReport
=
{
vulnerabilities
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
unapproved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
{
vulnerability
:
'
CVE-2017-12923
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
approved
:
[
{
vulnerability
:
'
CVE-2017-12944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
{
vulnerability
:
'
CVE-2017-13944
'
,
namespace
:
'
debian:8
'
,
severity
:
'
Medium
'
,
},
],
};
expect
(
vm
.
dockerText
).
toEqual
(
'
SAST:container found 2 vulnerabilities, of which 2 are approved
'
);
});
});
});
});
});
describe
(
'
rendering source branch removal status
'
,
()
=>
{
describe
(
'
rendering source branch removal status
'
,
()
=>
{
...
@@ -913,7 +683,7 @@ describe('ee merge request widget options', () => {
...
@@ -913,7 +683,7 @@ describe('ee merge request widget options', () => {
});
});
});
});
it
(
'
renders when user cannot remove branch and branch should be removed
'
,
(
done
)
=>
{
it
(
'
renders when user cannot remove branch and branch should be removed
'
,
done
=>
{
vm
.
mr
.
canRemoveSourceBranch
=
false
;
vm
.
mr
.
canRemoveSourceBranch
=
false
;
vm
.
mr
.
shouldRemoveSourceBranch
=
true
;
vm
.
mr
.
shouldRemoveSourceBranch
=
true
;
vm
.
mr
.
state
=
'
readyToMerge
'
;
vm
.
mr
.
state
=
'
readyToMerge
'
;
...
@@ -930,7 +700,7 @@ describe('ee merge request widget options', () => {
...
@@ -930,7 +700,7 @@ describe('ee merge request widget options', () => {
});
});
});
});
it
(
'
does not render in merged state
'
,
(
done
)
=>
{
it
(
'
does not render in merged state
'
,
done
=>
{
vm
.
mr
.
canRemoveSourceBranch
=
false
;
vm
.
mr
.
canRemoveSourceBranch
=
false
;
vm
.
mr
.
shouldRemoveSourceBranch
=
true
;
vm
.
mr
.
shouldRemoveSourceBranch
=
true
;
vm
.
mr
.
state
=
'
merged
'
;
vm
.
mr
.
state
=
'
merged
'
;
...
@@ -958,19 +728,22 @@ describe('ee merge request widget options', () => {
...
@@ -958,19 +728,22 @@ describe('ee merge request widget options', () => {
deployed_at_formatted
:
'
Mar 22, 2017 10:44pm
'
,
deployed_at_formatted
:
'
Mar 22, 2017 10:44pm
'
,
};
};
beforeEach
(
(
done
)
=>
{
beforeEach
(
done
=>
{
vm
=
mountComponent
(
Component
,
{
vm
=
mountComponent
(
Component
,
{
mrData
:
{
mrData
:
{
...
mockData
,
...
mockData
,
},
},
});
});
vm
.
mr
.
deployments
.
push
({
vm
.
mr
.
deployments
.
push
(
...
deploymentMockData
,
{
},
{
...
deploymentMockData
,
...
deploymentMockData
,
},
id
:
deploymentMockData
.
id
+
1
,
{
});
...
deploymentMockData
,
id
:
deploymentMockData
.
id
+
1
,
},
);
vm
.
$nextTick
(
done
);
vm
.
$nextTick
(
done
);
});
});
...
...
spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
View file @
edeab723
...
@@ -6,18 +6,6 @@ import mockData, {
...
@@ -6,18 +6,6 @@ import mockData, {
parsedBaseIssues
,
parsedBaseIssues
,
parsedHeadIssues
,
parsedHeadIssues
,
}
from
'
../mock_data
'
;
}
from
'
../mock_data
'
;
import
{
sastIssues
,
sastIssuesBase
,
parsedSastBaseStore
,
parsedSastIssuesHead
,
parsedSastIssuesStore
,
allIssuesParsed
,
dockerReport
,
dockerReportParsed
,
dast
,
parsedDast
,
}
from
'
../../vue_shared/security_reports/mock_data
'
;
describe
(
'
MergeRequestStore
'
,
()
=>
{
describe
(
'
MergeRequestStore
'
,
()
=>
{
let
store
;
let
store
;
...
@@ -98,43 +86,12 @@ describe('MergeRequestStore', () => {
...
@@ -98,43 +86,12 @@ describe('MergeRequestStore', () => {
});
});
});
});
describe
(
'
setSecurityReport
'
,
()
=>
{
describe
(
'
parseCodeclimateMetrics
'
,
()
=>
{
it
(
'
should set security issues with head
'
,
()
=>
{
it
(
'
should parse the received issues
'
,
()
=>
{
store
.
setSecurityReport
({
head
:
sastIssues
,
headBlobPath
:
'
path
'
});
const
codequality
=
MergeRequestStore
.
parseCodeclimateMetrics
(
baseIssues
,
'
path
'
)[
0
];
expect
(
store
.
securityReport
.
newIssues
).
toEqual
(
parsedSastIssuesStore
);
expect
(
codequality
.
name
).
toEqual
(
baseIssues
[
0
].
check_name
);
});
expect
(
codequality
.
path
).
toEqual
(
baseIssues
[
0
].
location
.
path
);
expect
(
codequality
.
line
).
toEqual
(
baseIssues
[
0
].
location
.
lines
.
begin
);
it
(
'
should set security issues with head and base
'
,
()
=>
{
store
.
setSecurityReport
({
head
:
sastIssues
,
headBlobPath
:
'
path
'
,
base
:
sastIssuesBase
,
baseBlobPath
:
'
path
'
,
});
expect
(
store
.
securityReport
.
newIssues
).
toEqual
(
parsedSastIssuesHead
);
expect
(
store
.
securityReport
.
resolvedIssues
).
toEqual
(
parsedSastBaseStore
);
expect
(
store
.
securityReport
.
allIssues
).
toEqual
(
allIssuesParsed
);
});
});
describe
(
'
setDependencyScanningReport
'
,
()
=>
{
it
(
'
should set security issues with head
'
,
()
=>
{
store
.
setDependencyScanningReport
({
head
:
sastIssues
,
headBlobPath
:
'
path
'
});
expect
(
store
.
dependencyScanningReport
.
newIssues
).
toEqual
(
parsedSastIssuesStore
);
});
it
(
'
should set security issues with head and base
'
,
()
=>
{
store
.
setDependencyScanningReport
({
head
:
sastIssues
,
headBlobPath
:
'
path
'
,
base
:
sastIssuesBase
,
baseBlobPath
:
'
path
'
,
});
expect
(
store
.
dependencyScanningReport
.
newIssues
).
toEqual
(
parsedSastIssuesHead
);
expect
(
store
.
dependencyScanningReport
.
resolvedIssues
).
toEqual
(
parsedSastBaseStore
);
expect
(
store
.
dependencyScanningReport
.
allIssues
).
toEqual
(
allIssuesParsed
);
});
});
});
});
...
@@ -149,43 +106,4 @@ describe('MergeRequestStore', () => {
...
@@ -149,43 +106,4 @@ describe('MergeRequestStore', () => {
expect
(
store
.
isNothingToMergeState
).
toEqual
(
false
);
expect
(
store
.
isNothingToMergeState
).
toEqual
(
false
);
});
});
});
});
describe
(
'
initDockerReport
'
,
()
=>
{
it
(
'
sets the defaults
'
,
()
=>
{
store
.
initDockerReport
({
sast_container
:
{
path
:
'
gl-sast-container.json
'
}
});
expect
(
store
.
sastContainer
).
toEqual
({
path
:
'
gl-sast-container.json
'
});
expect
(
store
.
dockerReport
).
toEqual
({
approved
:
[],
unapproved
:
[],
vulnerabilities
:
[],
});
});
});
describe
(
'
setDockerReport
'
,
()
=>
{
it
(
'
sets docker report with approved and unapproved vulnerabilities parsed
'
,
()
=>
{
store
.
setDockerReport
(
dockerReport
);
expect
(
store
.
dockerReport
.
vulnerabilities
).
toEqual
(
dockerReportParsed
.
vulnerabilities
);
expect
(
store
.
dockerReport
.
approved
).
toEqual
(
dockerReportParsed
.
approved
);
expect
(
store
.
dockerReport
.
unapproved
).
toEqual
(
dockerReportParsed
.
unapproved
);
});
});
describe
(
'
initDastReport
'
,
()
=>
{
it
(
'
sets the defaults
'
,
()
=>
{
store
.
initDastReport
({
dast
:
{
path
:
'
dast.json
'
}
});
expect
(
store
.
dast
).
toEqual
({
path
:
'
dast.json
'
});
expect
(
store
.
dastReport
).
toEqual
([]);
});
});
describe
(
'
setDastReport
'
,
()
=>
{
it
(
'
parsed data and sets the report
'
,
()
=>
{
store
.
setDastReport
(
dast
);
expect
(
store
.
dastReport
).
toEqual
(
parsedDast
);
});
});
});
});
spec/javascripts/vue_shared/security_reports/components/error_row_spec.js
deleted
100644 → 0
View file @
a990e800
import
Vue
from
'
vue
'
;
import
component
from
'
ee/vue_shared/security_reports/components/error_row.vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
describe
(
'
loading row
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
component
);
let
vm
;
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
it
(
'
renders warning icon with error message
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.report-block-list-icon span
'
).
classList
).
toContain
(
'
js-ci-status-icon-warning
'
,
);
expect
(
vm
.
$el
.
querySelector
(
'
.report-block-list-issue-description
'
).
textContent
.
trim
()).
toEqual
(
'
There was an error loading results
'
,
);
});
});
spec/javascripts/vue_shared/security_reports/components/loading_row_spec.js
deleted
100644 → 0
View file @
a990e800
import
Vue
from
'
vue
'
;
import
component
from
'
ee/vue_shared/security_reports/components/loading_row.vue
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
describe
(
'
loading row
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
component
);
let
vm
;
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
});
it
(
'
renders loading icon with message
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.report-block-list-icon i
'
).
classList
).
toContain
(
'
fa-spin
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.report-block-list-issue-description
'
).
textContent
.
trim
()).
toEqual
(
'
in progress
'
,
);
});
});
spec/javascripts/vue_shared/security_reports/components/report_section_spec.js
View file @
edeab723
...
@@ -19,10 +19,11 @@ describe('Report section', () => {
...
@@ -19,10 +19,11 @@ describe('Report section', () => {
it
(
'
should render loading indicator
'
,
()
=>
{
it
(
'
should render loading indicator
'
,
()
=>
{
vm
=
mountComponent
(
ReportSection
,
{
vm
=
mountComponent
(
ReportSection
,
{
type
:
'
codequality
'
,
type
:
'
codequality
'
,
status
:
'
loading
'
,
status
:
'
LOADING
'
,
loadingText
:
'
Loading codeclimate report
'
,
loadingText
:
'
Loading codeclimate report
'
,
errorText
:
'
foo
'
,
errorText
:
'
foo
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
hasIssues
:
false
,
});
});
expect
(
vm
.
$el
.
textContent
.
trim
()).
toEqual
(
'
Loading codeclimate report
'
);
expect
(
vm
.
$el
.
textContent
.
trim
()).
toEqual
(
'
Loading codeclimate report
'
);
});
});
...
@@ -32,11 +33,12 @@ describe('Report section', () => {
...
@@ -32,11 +33,12 @@ describe('Report section', () => {
it
(
'
should render provided data
'
,
()
=>
{
it
(
'
should render provided data
'
,
()
=>
{
vm
=
mountComponent
(
ReportSection
,
{
vm
=
mountComponent
(
ReportSection
,
{
type
:
'
codequality
'
,
type
:
'
codequality
'
,
status
:
'
success
'
,
status
:
'
SUCCESS
'
,
loadingText
:
'
Loading codeclimate report
'
,
loadingText
:
'
Loading codeclimate report
'
,
errorText
:
'
foo
'
,
errorText
:
'
foo
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
resolvedIssues
:
codequalityParsedIssues
,
resolvedIssues
:
codequalityParsedIssues
,
hasIssues
:
true
,
});
});
expect
(
expect
(
...
@@ -52,18 +54,19 @@ describe('Report section', () => {
...
@@ -52,18 +54,19 @@ describe('Report section', () => {
it
(
'
toggles issues
'
,
(
done
)
=>
{
it
(
'
toggles issues
'
,
(
done
)
=>
{
vm
=
mountComponent
(
ReportSection
,
{
vm
=
mountComponent
(
ReportSection
,
{
type
:
'
codequality
'
,
type
:
'
codequality
'
,
status
:
'
success
'
,
status
:
'
SUCCESS
'
,
loadingText
:
'
Loading codeclimate report
'
,
loadingText
:
'
Loading codeclimate report
'
,
errorText
:
'
foo
'
,
errorText
:
'
foo
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
resolvedIssues
:
codequalityParsedIssues
,
resolvedIssues
:
codequalityParsedIssues
,
hasIssues
:
true
,
});
});
vm
.
$el
.
querySelector
(
'
button
'
).
click
();
vm
.
$el
.
querySelector
(
'
button
'
).
click
();
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.
report-block
-container
'
).
getAttribute
(
'
style
'
),
vm
.
$el
.
querySelector
(
'
.
js-report-section
-container
'
).
getAttribute
(
'
style
'
),
).
toEqual
(
''
);
).
toEqual
(
''
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
button
'
).
textContent
.
trim
(),
vm
.
$el
.
querySelector
(
'
button
'
).
textContent
.
trim
(),
...
@@ -73,7 +76,7 @@ describe('Report section', () => {
...
@@ -73,7 +76,7 @@ describe('Report section', () => {
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
.
report-block
-container
'
).
getAttribute
(
'
style
'
),
vm
.
$el
.
querySelector
(
'
.
js-report-section
-container
'
).
getAttribute
(
'
style
'
),
).
toEqual
(
'
display: none;
'
);
).
toEqual
(
'
display: none;
'
);
expect
(
expect
(
vm
.
$el
.
querySelector
(
'
button
'
).
textContent
.
trim
(),
vm
.
$el
.
querySelector
(
'
button
'
).
textContent
.
trim
(),
...
@@ -90,10 +93,11 @@ describe('Report section', () => {
...
@@ -90,10 +93,11 @@ describe('Report section', () => {
it
(
'
should render error indicator
'
,
()
=>
{
it
(
'
should render error indicator
'
,
()
=>
{
vm
=
mountComponent
(
ReportSection
,
{
vm
=
mountComponent
(
ReportSection
,
{
type
:
'
codequality
'
,
type
:
'
codequality
'
,
status
:
'
error
'
,
status
:
'
ERROR
'
,
loadingText
:
'
Loading codeclimate report
'
,
loadingText
:
'
Loading codeclimate report
'
,
errorText
:
'
Failed to load codeclimate report
'
,
errorText
:
'
Failed to load codeclimate report
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
hasIssues
:
false
,
});
});
expect
(
vm
.
$el
.
textContent
.
trim
()).
toEqual
(
'
Failed to load codeclimate report
'
);
expect
(
vm
.
$el
.
textContent
.
trim
()).
toEqual
(
'
Failed to load codeclimate report
'
);
});
});
...
@@ -102,11 +106,11 @@ describe('Report section', () => {
...
@@ -102,11 +106,11 @@ describe('Report section', () => {
describe
(
'
With full report
'
,
()
=>
{
describe
(
'
With full report
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
ReportSection
,
{
vm
=
mountComponent
(
ReportSection
,
{
status
:
'
success
'
,
status
:
'
SUCCESS
'
,
successText
:
'
SAST improved on 1 security vulnerability and degraded on 1 security vulnerability
'
,
successText
:
'
SAST improved on 1 security vulnerability and degraded on 1 security vulnerability
'
,
type
:
'
SAST
'
,
type
:
'
SAST
'
,
errorText
:
'
Failed to load security report
'
,
errorText
:
'
Failed to load security report
'
,
has
Priority
:
true
,
has
Issues
:
true
,
loadingText
:
'
Loading security report
'
,
loadingText
:
'
Loading security report
'
,
resolvedIssues
:
[{
resolvedIssues
:
[{
cve
:
'
CVE-2016-9999
'
,
cve
:
'
CVE-2016-9999
'
,
...
@@ -172,28 +176,4 @@ describe('Report section', () => {
...
@@ -172,28 +176,4 @@ describe('Report section', () => {
});
});
});
});
});
});
describe
(
'
When it is not collapsible
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
ReportSection
,
{
type
:
'
codequality
'
,
status
:
'
success
'
,
loadingText
:
'
Loading codeclimate report
'
,
errorText
:
'
foo
'
,
successText
:
'
Code quality improved on 1 point and degraded on 1 point
'
,
resolvedIssues
:
codequalityParsedIssues
,
isCollapsible
:
false
,
});
});
it
(
'
should not render collapse button
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-collapse-btn
'
)).
toBe
(
null
);
});
it
(
'
should show the report by default
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.report-block-list .report-block-list-issue
'
).
length
,
).
toEqual
(
codequalityParsedIssues
.
length
);
});
});
});
});
spec/javascripts/vue_shared/security_reports/grouped_security_reports_app_spec.js
0 → 100644
View file @
edeab723
import
Vue
from
'
vue
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
component
from
'
ee/vue_shared/security_reports/grouped_security_reports_app.vue
'
;
import
state
from
'
ee/vue_shared/security_reports/store/state
'
;
import
mountComponent
from
'
../../helpers/vue_mount_component_helper
'
;
import
{
sastIssues
,
sastIssuesBase
,
dockerReport
,
dockerBaseReport
,
dast
,
dastBase
,
}
from
'
./mock_data
'
;
describe
(
'
Grouped security reports app
'
,
()
=>
{
let
vm
;
let
mock
;
const
Component
=
Vue
.
extend
(
component
);
function
removeBreakLine
(
data
)
{
return
data
.
replace
(
/
\r?\n
|
\r
/g
,
''
)
.
replace
(
/
\s\s
+/g
,
'
'
)
.
trim
();
}
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
mock
.
restore
();
vm
.
$store
.
replaceState
(
state
());
});
describe
(
'
with error
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
sast_head.json
'
).
reply
(
500
);
mock
.
onGet
(
'
sast_base.json
'
).
reply
(
500
);
mock
.
onGet
(
'
dast_head.json
'
).
reply
(
500
);
mock
.
onGet
(
'
dast_base.json
'
).
reply
(
500
);
mock
.
onGet
(
'
sast_container_head.json
'
).
reply
(
500
);
mock
.
onGet
(
'
sast_container_base.json
'
).
reply
(
500
);
mock
.
onGet
(
'
dss_head.json
'
).
reply
(
500
);
mock
.
onGet
(
'
dss_base.json
'
).
reply
(
500
);
vm
=
mountComponent
(
Component
,
{
headBlobPath
:
'
path
'
,
baseBlobPath
:
'
path
'
,
sastHeadPath
:
'
sast_head.json
'
,
sastBasePath
:
'
sast_base.json
'
,
dastHeadPath
:
'
dast_head.json
'
,
dastBasePath
:
'
dast_base.json
'
,
sastContainerHeadPath
:
'
sast_container_head.json
'
,
sastContainerBasePath
:
'
sast_container_base.json
'
,
dependencyScanningHeadPath
:
'
dss_head.json
'
,
dependencyScanningBasePath
:
'
dss_base.json
'
,
sastHelpPath
:
'
path
'
,
sastContainerHelpPath
:
'
path
'
,
dastHelpPath
:
'
path
'
,
dependencyScanningHelpPath
:
'
path
'
,
});
});
it
(
'
renders loading state
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
)).
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
()).
toEqual
(
'
Security scanning failed loading any results
'
,
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-collapse-btn
'
).
textContent
.
trim
()).
toEqual
(
'
Expand
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
SAST resulted in error while loading results
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
Dependency scanning resulted in error while loading results
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
Container scanning resulted in error while loading results
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
DAST resulted in error while loading results
'
);
done
();
},
0
);
});
});
describe
(
'
while loading
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
sast_head.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
sast_base.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
dast_head.json
'
).
reply
(
200
,
dast
);
mock
.
onGet
(
'
dast_base.json
'
).
reply
(
200
,
dastBase
);
mock
.
onGet
(
'
sast_container_head.json
'
).
reply
(
200
,
dockerReport
);
mock
.
onGet
(
'
sast_container_base.json
'
).
reply
(
200
,
dockerBaseReport
);
mock
.
onGet
(
'
dss_head.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
dss_base.json
'
).
reply
(
200
,
sastIssuesBase
);
vm
=
mountComponent
(
Component
,
{
headBlobPath
:
'
path
'
,
baseBlobPath
:
'
path
'
,
sastHeadPath
:
'
sast_head.json
'
,
sastBasePath
:
'
sast_base.json
'
,
dastHeadPath
:
'
dast_head.json
'
,
dastBasePath
:
'
dast_base.json
'
,
sastContainerHeadPath
:
'
sast_container_head.json
'
,
sastContainerBasePath
:
'
sast_container_base.json
'
,
dependencyScanningHeadPath
:
'
dss_head.json
'
,
dependencyScanningBasePath
:
'
dss_base.json
'
,
sastHelpPath
:
'
path
'
,
sastContainerHelpPath
:
'
path
'
,
dastHelpPath
:
'
path
'
,
dependencyScanningHelpPath
:
'
path
'
,
});
});
it
(
'
renders loading summary text + spinner
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
)).
not
.
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
()).
toEqual
(
'
Security scanning is loading
'
,
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-collapse-btn
'
).
textContent
.
trim
()).
toEqual
(
'
Expand
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
SAST is loading
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
Dependency scanning is loading
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
Container scanning is loading
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
DAST is loading
'
);
});
});
describe
(
'
with all reports
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
sast_head.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
sast_base.json
'
).
reply
(
200
,
sastIssuesBase
);
mock
.
onGet
(
'
dast_head.json
'
).
reply
(
200
,
dast
);
mock
.
onGet
(
'
dast_base.json
'
).
reply
(
200
,
dastBase
);
mock
.
onGet
(
'
sast_container_head.json
'
).
reply
(
200
,
dockerReport
);
mock
.
onGet
(
'
sast_container_base.json
'
).
reply
(
200
,
dockerBaseReport
);
mock
.
onGet
(
'
dss_head.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
dss_base.json
'
).
reply
(
200
,
sastIssuesBase
);
vm
=
mountComponent
(
Component
,
{
headBlobPath
:
'
path
'
,
baseBlobPath
:
'
path
'
,
sastHeadPath
:
'
sast_head.json
'
,
sastBasePath
:
'
sast_base.json
'
,
dastHeadPath
:
'
dast_head.json
'
,
dastBasePath
:
'
dast_base.json
'
,
sastContainerHeadPath
:
'
sast_container_head.json
'
,
sastContainerBasePath
:
'
sast_container_base.json
'
,
dependencyScanningHeadPath
:
'
dss_head.json
'
,
dependencyScanningBasePath
:
'
dss_base.json
'
,
sastHelpPath
:
'
path
'
,
sastContainerHelpPath
:
'
path
'
,
dastHelpPath
:
'
path
'
,
dependencyScanningHelpPath
:
'
path
'
,
});
});
it
(
'
renders reports
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
)).
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.js-code-text
'
).
textContent
.
trim
()).
toEqual
(
'
Security scanning detected 12 new vulnerabilities and 4 fixed vulnerabilities
'
,
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-collapse-btn
'
).
textContent
.
trim
()).
toEqual
(
'
Expand
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
SAST detected 2 new vulnerabilities and 1 fixed vulnerability
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
Dependency scanning detected 2 new vulnerabilities and 1 fixed vulnerability
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
Container scanning detected 1 new vulnerability
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
DAST detected 1 new vulnerability
'
);
done
();
},
0
);
});
});
});
spec/javascripts/vue_shared/security_reports/helpers/mixins_spec.js
deleted
100644 → 0
View file @
a990e800
import
mixin
from
'
ee/vue_shared/security_reports/mixins/security_report_mixin
'
;
import
{
parsedSastBaseStore
,
parsedSastIssuesHead
,
dockerReportParsed
,
parsedDast
,
}
from
'
../mock_data
'
;
describe
(
'
security report mixin
'
,
()
=>
{
describe
(
'
sastText
'
,
()
=>
{
it
(
'
returns text for new and fixed issues
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastText
(
parsedSastIssuesHead
,
parsedSastBaseStore
,
)).
toEqual
(
'
SAST improved on 1 security vulnerability and degraded on 2 security vulnerabilities
'
,
);
});
it
(
'
returns text for added issues
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastText
(
parsedSastIssuesHead
,
[])).
toEqual
(
'
SAST degraded on 2 security vulnerabilities
'
,
);
});
it
(
'
returns text for fixed issues
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastText
([],
parsedSastIssuesHead
)).
toEqual
(
'
SAST improved on 2 security vulnerabilities
'
,
);
});
it
(
'
returns text for full report and no added or fixed issues
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastText
([],
[],
parsedSastIssuesHead
)).
toEqual
(
'
SAST detected no new security vulnerabilities
'
,
);
});
});
describe
(
'
translateText
'
,
()
=>
{
it
(
'
returns loading and error text for the given value
'
,
()
=>
{
expect
(
mixin
.
methods
.
translateText
(
'
sast
'
)).
toEqual
({
error
:
'
Failed to load sast report
'
,
loading
:
'
Loading sast report
'
,
});
});
});
describe
(
'
checkReportStatus
'
,
()
=>
{
it
(
'
returns loading when loading is true
'
,
()
=>
{
expect
(
mixin
.
methods
.
checkReportStatus
(
true
,
false
)).
toEqual
(
'
loading
'
);
});
it
(
'
returns error when error is true
'
,
()
=>
{
expect
(
mixin
.
methods
.
checkReportStatus
(
false
,
true
)).
toEqual
(
'
error
'
);
});
it
(
'
returns success when loading and error are false
'
,
()
=>
{
expect
(
mixin
.
methods
.
checkReportStatus
(
false
,
false
)).
toEqual
(
'
success
'
);
});
});
describe
(
'
sastContainerText
'
,
()
=>
{
it
(
'
returns no vulnerabitilties text
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastContainerText
()).
toEqual
(
'
SAST:container no vulnerabilities were found
'
,
);
});
it
(
'
returns approved vulnerabilities text
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastContainerText
(
dockerReportParsed
.
vulnerabilities
,
dockerReportParsed
.
approved
,
),
).
toEqual
(
'
SAST:container found 1 approved vulnerability
'
,
);
});
it
(
'
returns unnapproved vulnerabilities text
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastContainerText
(
dockerReportParsed
.
vulnerabilities
,
[],
dockerReportParsed
.
unapproved
,
),
).
toEqual
(
'
SAST:container found 2 vulnerabilities
'
,
);
});
it
(
'
returns approved & unapproved text
'
,
()
=>
{
expect
(
mixin
.
methods
.
sastContainerText
(
dockerReportParsed
.
vulnerabilities
,
dockerReportParsed
.
approved
,
dockerReportParsed
.
unapproved
,
)).
toEqual
(
'
SAST:container found 3 vulnerabilities, of which 1 is approved
'
,
);
});
});
describe
(
'
dastText
'
,
()
=>
{
it
(
'
returns dast text
'
,
()
=>
{
expect
(
mixin
.
methods
.
dastText
(
parsedDast
)).
toEqual
(
'
DAST detected 2 alerts by analyzing the review app
'
,
);
});
it
(
'
returns no alert text
'
,
()
=>
{
expect
(
mixin
.
methods
.
dastText
()).
toEqual
(
'
DAST detected no alerts by analyzing the review app
'
);
});
});
});
spec/javascripts/vue_shared/security_reports/helpers/utils_spec.js
deleted
100644 → 0
View file @
a990e800
import
{
parseSastIssues
,
parseCodeclimateMetrics
,
parseSastContainer
,
setSastReport
,
setDastReport
,
}
from
'
ee/vue_shared/security_reports/helpers/utils
'
;
import
{
baseIssues
,
sastIssues
,
sastIssuesBase
,
parsedSastIssuesStore
,
parsedSastBaseStore
,
allIssuesParsed
,
parsedSastIssuesHead
,
dockerReport
,
dockerReportParsed
,
dast
,
parsedDast
,
}
from
'
../mock_data
'
;
describe
(
'
security reports utils
'
,
()
=>
{
describe
(
'
parseSastIssues
'
,
()
=>
{
it
(
'
should parse the received issues
'
,
()
=>
{
const
security
=
parseSastIssues
(
sastIssues
,
'
path
'
)[
0
];
expect
(
security
.
name
).
toEqual
(
sastIssues
[
0
].
message
);
expect
(
security
.
path
).
toEqual
(
sastIssues
[
0
].
file
);
});
});
describe
(
'
parseCodeclimateMetrics
'
,
()
=>
{
it
(
'
should parse the received issues
'
,
()
=>
{
const
codequality
=
parseCodeclimateMetrics
(
baseIssues
,
'
path
'
)[
0
];
expect
(
codequality
.
name
).
toEqual
(
baseIssues
[
0
].
check_name
);
expect
(
codequality
.
path
).
toEqual
(
baseIssues
[
0
].
location
.
path
);
expect
(
codequality
.
line
).
toEqual
(
baseIssues
[
0
].
location
.
lines
.
begin
);
});
});
describe
(
'
setSastReport
'
,
()
=>
{
it
(
'
should set security issues with head
'
,
()
=>
{
const
securityReport
=
setSastReport
({
head
:
sastIssues
,
headBlobPath
:
'
path
'
});
expect
(
securityReport
.
newIssues
).
toEqual
(
parsedSastIssuesStore
);
});
it
(
'
should set security issues with head and base
'
,
()
=>
{
const
securityReport
=
setSastReport
({
head
:
sastIssues
,
headBlobPath
:
'
path
'
,
base
:
sastIssuesBase
,
baseBlobPath
:
'
path
'
,
});
expect
(
securityReport
.
newIssues
).
toEqual
(
parsedSastIssuesHead
);
expect
(
securityReport
.
resolvedIssues
).
toEqual
(
parsedSastBaseStore
);
expect
(
securityReport
.
allIssues
).
toEqual
(
allIssuesParsed
);
});
});
describe
(
'
parseSastContainer
'
,
()
=>
{
it
(
'
parses sast container report
'
,
()
=>
{
expect
(
parseSastContainer
(
dockerReport
.
vulnerabilities
)).
toEqual
(
dockerReportParsed
.
vulnerabilities
,
);
});
});
describe
(
'
dastReport
'
,
()
=>
{
it
(
'
parsed dast report
'
,
()
=>
{
expect
(
setDastReport
(
dast
)).
toEqual
(
parsedDast
);
});
});
});
spec/javascripts/vue_shared/security_reports/split_security_reports_app_spec.js
0 → 100644
View file @
edeab723
import
Vue
from
'
vue
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
component
from
'
ee/vue_shared/security_reports/split_security_reports_app.vue
'
;
import
state
from
'
ee/vue_shared/security_reports/store/state
'
;
import
mountComponent
from
'
../../helpers/vue_mount_component_helper
'
;
import
{
sastIssues
}
from
'
./mock_data
'
;
describe
(
'
Slipt security reports app
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
component
);
let
vm
;
let
mock
;
function
removeBreakLine
(
data
)
{
return
data
.
replace
(
/
\r?\n
|
\r
/g
,
''
)
.
replace
(
/
\s\s
+/g
,
'
'
)
.
trim
();
}
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
mock
.
restore
();
vm
.
$store
.
replaceState
(
state
());
});
describe
(
'
while loading
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
sast_head.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
dss_head.json
'
).
reply
(
200
,
sastIssues
);
vm
=
mountComponent
(
Component
,
{
headBlobPath
:
'
path
'
,
baseBlobPath
:
'
path
'
,
sastHeadPath
:
'
sast_head.json
'
,
dependencyScanningHeadPath
:
'
dss_head.json
'
,
sastHelpPath
:
'
path
'
,
dependencyScanningHelpPath
:
'
path
'
,
});
});
it
(
'
renders loading summary text + spinner
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
)).
not
.
toBeNull
();
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
SAST is loading
'
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
Dependency scanning is loading
'
);
});
});
describe
(
'
with all reports
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
sast_head.json
'
).
reply
(
200
,
sastIssues
);
mock
.
onGet
(
'
dss_head.json
'
).
reply
(
200
,
sastIssues
);
vm
=
mountComponent
(
Component
,
{
headBlobPath
:
'
path
'
,
baseBlobPath
:
'
path
'
,
sastHeadPath
:
'
sast_head.json
'
,
dependencyScanningHeadPath
:
'
dss_head.json
'
,
sastHelpPath
:
'
path
'
,
dependencyScanningHelpPath
:
'
path
'
,
});
});
it
(
'
renders reports
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
)).
toBeNull
();
expect
(
vm
.
$el
.
querySelector
(
'
.js-collapse-btn
'
).
textContent
.
trim
()).
toEqual
(
'
Expand
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
SAST detected 3 vulnerabilities
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
Dependency scanning detected 3 vulnerabilities
'
,
);
done
();
},
0
);
});
});
describe
(
'
with error
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
sast_head.json
'
).
reply
(
500
);
mock
.
onGet
(
'
dss_head.json
'
).
reply
(
500
);
vm
=
mountComponent
(
Component
,
{
headBlobPath
:
'
path
'
,
baseBlobPath
:
'
path
'
,
sastHeadPath
:
'
sast_head.json
'
,
dependencyScanningHeadPath
:
'
dss_head.json
'
,
sastHelpPath
:
'
path
'
,
dependencyScanningHelpPath
:
'
path
'
,
});
});
it
(
'
renders error state
'
,
done
=>
{
setTimeout
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.fa-spinner
'
)).
toBeNull
();
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
SAST resulted in error while loading results
'
);
expect
(
removeBreakLine
(
vm
.
$el
.
textContent
)).
toContain
(
'
Dependency scanning resulted in error while loading results
'
,
);
done
();
},
0
);
});
});
});
spec/javascripts/vue_shared/security_reports/store/getters_spec.js
View file @
edeab723
...
@@ -23,7 +23,11 @@ describe('Security reports getters', () => {
...
@@ -23,7 +23,11 @@ describe('Security reports getters', () => {
describe
(
'
groupedSastText
'
,
()
=>
{
describe
(
'
groupedSastText
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
expect
(
groupedSastText
(
state
())).
toEqual
(
'
SAST detected no security vulnerabilities
'
);
const
newState
=
state
();
newState
.
sast
.
paths
.
head
=
'
foo
'
;
newState
.
sast
.
paths
.
base
=
'
bar
'
;
expect
(
groupedSastText
(
newState
)).
toEqual
(
'
SAST detected no security vulnerabilities
'
);
});
});
});
});
...
@@ -43,7 +47,7 @@ describe('Security reports getters', () => {
...
@@ -43,7 +47,7 @@ describe('Security reports getters', () => {
newState
.
sast
.
newIssues
=
[{}];
newState
.
sast
.
newIssues
=
[{}];
expect
(
groupedSastText
(
newState
)).
toEqual
(
expect
(
groupedSastText
(
newState
)).
toEqual
(
'
SAST
was unable to compare existing and new vulnerabilities. It detected 1 vulnerabilit
y
'
,
'
SAST
detected 1 vulnerability for the source branch onl
y
'
,
);
);
});
});
});
});
...
@@ -84,13 +88,35 @@ describe('Security reports getters', () => {
...
@@ -84,13 +88,35 @@ describe('Security reports getters', () => {
expect
(
groupedSastText
(
newState
)).
toEqual
(
'
SAST detected 1 fixed vulnerability
'
);
expect
(
groupedSastText
(
newState
)).
toEqual
(
'
SAST detected 1 fixed vulnerability
'
);
});
});
});
});
describe
(
'
with error
'
,
()
=>
{
it
(
'
returns error text
'
,
()
=>
{
const
newState
=
state
();
newState
.
sast
.
hasError
=
true
;
expect
(
groupedSastText
(
newState
)).
toEqual
(
'
SAST resulted in error while loading results
'
);
});
});
describe
(
'
while loading
'
,
()
=>
{
it
(
'
returns loading text
'
,
()
=>
{
const
newState
=
state
();
newState
.
sast
.
isLoading
=
true
;
expect
(
groupedSastText
(
newState
)).
toEqual
(
'
SAST is loading
'
);
});
});
});
});
});
});
describe
(
'
groupedSastContainerText
'
,
()
=>
{
describe
(
'
groupedSastContainerText
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
expect
(
groupedSastContainerText
(
state
())).
toEqual
(
const
newState
=
state
();
newState
.
sastContainer
.
paths
.
head
=
'
foo
'
;
newState
.
sastContainer
.
paths
.
base
=
'
foo
'
;
expect
(
groupedSastContainerText
(
newState
)).
toEqual
(
'
Container scanning detected no security vulnerabilities
'
,
'
Container scanning detected no security vulnerabilities
'
,
);
);
});
});
...
@@ -103,7 +129,7 @@ describe('Security reports getters', () => {
...
@@ -103,7 +129,7 @@ describe('Security reports getters', () => {
newState
.
sastContainer
.
newIssues
=
[{}];
newState
.
sastContainer
.
newIssues
=
[{}];
expect
(
groupedSastContainerText
(
newState
)).
toEqual
(
expect
(
groupedSastContainerText
(
newState
)).
toEqual
(
'
Container scanning
was unable to compare existing and new vulnerabilities. It detected 1 vulnerabilit
y
'
,
'
Container scanning
detected 1 vulnerability for the source branch onl
y
'
,
);
);
});
});
});
});
...
@@ -154,7 +180,11 @@ describe('Security reports getters', () => {
...
@@ -154,7 +180,11 @@ describe('Security reports getters', () => {
describe
(
'
groupedDastText
'
,
()
=>
{
describe
(
'
groupedDastText
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
expect
(
groupedDastText
(
state
())).
toEqual
(
'
DAST detected no security vulnerabilities
'
);
const
newState
=
state
();
newState
.
dast
.
paths
.
head
=
'
foo
'
;
newState
.
dast
.
paths
.
base
=
'
foo
'
;
expect
(
groupedDastText
(
newState
)).
toEqual
(
'
DAST detected no security vulnerabilities
'
);
});
});
});
});
...
@@ -165,7 +195,7 @@ describe('Security reports getters', () => {
...
@@ -165,7 +195,7 @@ describe('Security reports getters', () => {
newState
.
dast
.
newIssues
=
[{}];
newState
.
dast
.
newIssues
=
[{}];
expect
(
groupedDastText
(
newState
)).
toEqual
(
expect
(
groupedDastText
(
newState
)).
toEqual
(
'
DAST
was unable to compare existing and new vulnerabilities. It detected 1 vulnerabilit
y
'
,
'
DAST
detected 1 vulnerability for the source branch onl
y
'
,
);
);
});
});
});
});
...
@@ -211,7 +241,11 @@ describe('Security reports getters', () => {
...
@@ -211,7 +241,11 @@ describe('Security reports getters', () => {
describe
(
'
groupedDependencyText
'
,
()
=>
{
describe
(
'
groupedDependencyText
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
it
(
'
returns no issues text
'
,
()
=>
{
expect
(
groupedDependencyText
(
state
())).
toEqual
(
const
newState
=
state
();
newState
.
dependencyScanning
.
paths
.
head
=
'
foo
'
;
newState
.
dependencyScanning
.
paths
.
base
=
'
foo
'
;
expect
(
groupedDependencyText
(
newState
)).
toEqual
(
'
Dependency scanning detected no security vulnerabilities
'
,
'
Dependency scanning detected no security vulnerabilities
'
,
);
);
});
});
...
@@ -224,7 +258,7 @@ describe('Security reports getters', () => {
...
@@ -224,7 +258,7 @@ describe('Security reports getters', () => {
newState
.
dependencyScanning
.
newIssues
=
[{}];
newState
.
dependencyScanning
.
newIssues
=
[{}];
expect
(
groupedDependencyText
(
newState
)).
toEqual
(
expect
(
groupedDependencyText
(
newState
)).
toEqual
(
'
Dependency scanning
was unable to compare existing and new vulnerabilities. It detected 1 vulnerabilit
y
'
,
'
Dependency scanning
detected 1 vulnerability for the source branch onl
y
'
,
);
);
});
});
});
});
...
@@ -290,18 +324,18 @@ describe('Security reports getters', () => {
...
@@ -290,18 +324,18 @@ describe('Security reports getters', () => {
areReportsLoading
:
false
,
areReportsLoading
:
false
,
}),
}),
).
toEqual
(
).
toEqual
(
'
Security scanning
was unable to compare existing and new vulnerabilities. It detected no vulnerabilities.
'
,
'
Security scanning
detected no vulnerabilities for the source branch only
'
,
);
);
});
});
it
(
'
returns i
n progress
text
'
,
()
=>
{
it
(
'
returns i
s loading
text
'
,
()
=>
{
expect
(
expect
(
groupedSummaryText
(
state
(),
{
groupedSummaryText
(
state
(),
{
allReportsHaveError
:
false
,
allReportsHaveError
:
false
,
noBaseInAllReports
:
false
,
noBaseInAllReports
:
false
,
areReportsLoading
:
true
,
areReportsLoading
:
true
,
}),
}),
).
toContain
(
'
(i
n progress
)
'
);
).
toContain
(
'
(i
s loading
)
'
);
});
});
it
(
'
returns added and fixed text
'
,
()
=>
{
it
(
'
returns added and fixed text
'
,
()
=>
{
...
@@ -337,7 +371,7 @@ describe('Security reports getters', () => {
...
@@ -337,7 +371,7 @@ describe('Security reports getters', () => {
});
});
it
(
'
returns fixed text
'
,
()
=>
{
it
(
'
returns fixed text
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
summaryCounts
=
{
newState
.
summaryCounts
=
{
added
:
0
,
added
:
0
,
fixed
:
4
,
fixed
:
4
,
...
@@ -353,7 +387,7 @@ describe('Security reports getters', () => {
...
@@ -353,7 +387,7 @@ describe('Security reports getters', () => {
});
});
it
(
'
returns added and fixed while loading text
'
,
()
=>
{
it
(
'
returns added and fixed while loading text
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
summaryCounts
=
{
newState
.
summaryCounts
=
{
added
:
2
,
added
:
2
,
fixed
:
4
,
fixed
:
4
,
...
@@ -366,20 +400,20 @@ describe('Security reports getters', () => {
...
@@ -366,20 +400,20 @@ describe('Security reports getters', () => {
areReportsLoading
:
true
,
areReportsLoading
:
true
,
}),
}),
).
toContain
(
).
toContain
(
'
Security scanning (i
n progress
) detected 2 new vulnerabilities and 4 fixed vulnerabilities
'
,
'
Security scanning (i
s loading
) detected 2 new vulnerabilities and 4 fixed vulnerabilities
'
,
);
);
});
});
});
});
describe
(
'
sastStatusIcon
'
,
()
=>
{
describe
(
'
sastStatusIcon
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sast
.
newIssues
=
[{}];
newState
.
sast
.
newIssues
=
[{}];
expect
(
sastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
sastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
it
(
'
returns warning with failed report
'
,
()
=>
{
it
(
'
returns warning with failed report
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sast
.
hasError
=
true
;
newState
.
sast
.
hasError
=
true
;
expect
(
sastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
sastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
...
@@ -391,13 +425,13 @@ describe('Security reports getters', () => {
...
@@ -391,13 +425,13 @@ describe('Security reports getters', () => {
describe
(
'
dastStatusIcon
'
,
()
=>
{
describe
(
'
dastStatusIcon
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
dast
.
newIssues
=
[{}];
newState
.
dast
.
newIssues
=
[{}];
expect
(
dastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
dastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
it
(
'
returns warning with failed report
'
,
()
=>
{
it
(
'
returns warning with failed report
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
dast
.
hasError
=
true
;
newState
.
dast
.
hasError
=
true
;
expect
(
dastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
dastStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
...
@@ -409,13 +443,13 @@ describe('Security reports getters', () => {
...
@@ -409,13 +443,13 @@ describe('Security reports getters', () => {
describe
(
'
sastContainerStatusIcon
'
,
()
=>
{
describe
(
'
sastContainerStatusIcon
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sastContainer
.
newIssues
=
[{}];
newState
.
sastContainer
.
newIssues
=
[{}];
expect
(
sastContainerStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
sastContainerStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
it
(
'
returns warning with failed report
'
,
()
=>
{
it
(
'
returns warning with failed report
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sastContainer
.
hasError
=
true
;
newState
.
sastContainer
.
hasError
=
true
;
expect
(
sastContainerStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
sastContainerStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
...
@@ -427,13 +461,13 @@ describe('Security reports getters', () => {
...
@@ -427,13 +461,13 @@ describe('Security reports getters', () => {
describe
(
'
dependencyScanningStatusIcon
'
,
()
=>
{
describe
(
'
dependencyScanningStatusIcon
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
it
(
'
returns warning with new issues
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
dependencyScanning
.
newIssues
=
[{}];
newState
.
dependencyScanning
.
newIssues
=
[{}];
expect
(
dependencyScanningStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
dependencyScanningStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
it
(
'
returns warning with failed report
'
,
()
=>
{
it
(
'
returns warning with failed report
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
dependencyScanning
.
hasError
=
true
;
newState
.
dependencyScanning
.
hasError
=
true
;
expect
(
dependencyScanningStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
expect
(
dependencyScanningStatusIcon
(
newState
)).
toEqual
(
'
warning
'
);
});
});
...
@@ -445,7 +479,7 @@ describe('Security reports getters', () => {
...
@@ -445,7 +479,7 @@ describe('Security reports getters', () => {
describe
(
'
areReportsLoading
'
,
()
=>
{
describe
(
'
areReportsLoading
'
,
()
=>
{
it
(
'
returns true when any report is loading
'
,
()
=>
{
it
(
'
returns true when any report is loading
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sast
.
isLoading
=
true
;
newState
.
sast
.
isLoading
=
true
;
expect
(
areReportsLoading
(
newState
)).
toEqual
(
true
);
expect
(
areReportsLoading
(
newState
)).
toEqual
(
true
);
});
});
...
@@ -457,7 +491,7 @@ describe('Security reports getters', () => {
...
@@ -457,7 +491,7 @@ describe('Security reports getters', () => {
describe
(
'
allReportsHaveError
'
,
()
=>
{
describe
(
'
allReportsHaveError
'
,
()
=>
{
it
(
'
returns true when all reports have error
'
,
()
=>
{
it
(
'
returns true when all reports have error
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sast
.
hasError
=
true
;
newState
.
sast
.
hasError
=
true
;
newState
.
dast
.
hasError
=
true
;
newState
.
dast
.
hasError
=
true
;
newState
.
sastContainer
.
hasError
=
true
;
newState
.
sastContainer
.
hasError
=
true
;
...
@@ -466,14 +500,24 @@ describe('Security reports getters', () => {
...
@@ -466,14 +500,24 @@ describe('Security reports getters', () => {
expect
(
allReportsHaveError
(
newState
)).
toEqual
(
true
);
expect
(
allReportsHaveError
(
newState
)).
toEqual
(
true
);
});
});
it
(
'
returns false when none of the reports ha
s
error
'
,
()
=>
{
it
(
'
returns false when none of the reports ha
ve
error
'
,
()
=>
{
expect
(
allReportsHaveError
(
state
())).
toEqual
(
false
);
expect
(
allReportsHaveError
(
state
())).
toEqual
(
false
);
});
});
it
(
'
returns false when one of the reports does not have error
'
,
()
=>
{
const
newState
=
state
();
newState
.
sast
.
hasError
=
false
;
newState
.
dast
.
hasError
=
true
;
newState
.
sastContainer
.
hasError
=
true
;
newState
.
dependencyScanning
.
hasError
=
true
;
expect
(
allReportsHaveError
(
newState
)).
toEqual
(
false
);
});
});
});
describe
(
'
anyReportHasError
'
,
()
=>
{
describe
(
'
anyReportHasError
'
,
()
=>
{
it
(
'
returns true when any of the reports has error
'
,
()
=>
{
it
(
'
returns true when any of the reports has error
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sast
.
hasError
=
true
;
newState
.
sast
.
hasError
=
true
;
expect
(
anyReportHasError
(
newState
)).
toEqual
(
true
);
expect
(
anyReportHasError
(
newState
)).
toEqual
(
true
);
...
@@ -490,7 +534,7 @@ describe('Security reports getters', () => {
...
@@ -490,7 +534,7 @@ describe('Security reports getters', () => {
});
});
it
(
'
returns false when any of the reports has base
'
,
()
=>
{
it
(
'
returns false when any of the reports has base
'
,
()
=>
{
const
newState
=
Object
.
assign
({},
state
()
);
const
newState
=
state
(
);
newState
.
sast
.
paths
.
base
=
'
foo
'
;
newState
.
sast
.
paths
.
base
=
'
foo
'
;
expect
(
noBaseInAllReports
(
newState
)).
toEqual
(
false
);
expect
(
noBaseInAllReports
(
newState
)).
toEqual
(
false
);
});
});
...
...
spec/javascripts/vue_shared/security_reports/store/utils_spec.js
View file @
edeab723
...
@@ -64,7 +64,7 @@ describe('security reports utils', () => {
...
@@ -64,7 +64,7 @@ describe('security reports utils', () => {
describe
(
'
textBuilder
'
,
()
=>
{
describe
(
'
textBuilder
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
describe
(
'
with no issues
'
,
()
=>
{
it
(
'
should return no vulnerabiltities text
'
,
()
=>
{
it
(
'
should return no vulnerabiltities text
'
,
()
=>
{
expect
(
textBuilder
()).
toEqual
(
'
detected no security vulnerabilities
'
);
expect
(
textBuilder
(
''
,
{
head
:
'
foo
'
,
base
:
'
bar
'
},
0
,
0
,
0
)).
toEqual
(
'
detected no security vulnerabilities
'
);
});
});
});
});
...
@@ -77,7 +77,13 @@ describe('security reports utils', () => {
...
@@ -77,7 +77,13 @@ describe('security reports utils', () => {
describe
(
'
with new issues and without base
'
,
()
=>
{
describe
(
'
with new issues and without base
'
,
()
=>
{
it
(
'
should return unable to compare text
'
,
()
=>
{
it
(
'
should return unable to compare text
'
,
()
=>
{
expect
(
textBuilder
(
''
,
{
head
:
'
foo
'
},
1
,
0
,
0
)).
toEqual
(
expect
(
textBuilder
(
''
,
{
head
:
'
foo
'
},
1
,
0
,
0
)).
toEqual
(
'
was unable to compare existing and new vulnerabilities. It detected 1 vulnerability
'
,
'
detected 1 vulnerability for the source branch only
'
,
);
});
it
(
'
should return unable to compare text with no vulnerability
'
,
()
=>
{
expect
(
textBuilder
(
''
,
{
head
:
'
foo
'
},
0
,
0
,
0
)).
toEqual
(
'
detected no vulnerabilities for the source branch only
'
,
);
);
});
});
});
});
...
@@ -112,19 +118,19 @@ describe('security reports utils', () => {
...
@@ -112,19 +118,19 @@ describe('security reports utils', () => {
describe
(
'
statusIcon
'
,
()
=>
{
describe
(
'
statusIcon
'
,
()
=>
{
describe
(
'
with failed report
'
,
()
=>
{
describe
(
'
with failed report
'
,
()
=>
{
it
(
'
returns warning
'
,
()
=>
{
it
(
'
returns warning
'
,
()
=>
{
expect
(
statusIcon
(
true
)).
toEqual
(
'
warning
'
);
expect
(
statusIcon
(
false
,
true
)).
toEqual
(
'
warning
'
);
});
});
});
});
describe
(
'
with new issues
'
,
()
=>
{
describe
(
'
with new issues
'
,
()
=>
{
it
(
'
returns warning
'
,
()
=>
{
it
(
'
returns warning
'
,
()
=>
{
expect
(
statusIcon
(
false
,
1
)).
toEqual
(
'
warning
'
);
expect
(
statusIcon
(
false
,
false
,
1
)).
toEqual
(
'
warning
'
);
});
});
});
});
describe
(
'
with neutral issues
'
,
()
=>
{
describe
(
'
with neutral issues
'
,
()
=>
{
it
(
'
returns warning
'
,
()
=>
{
it
(
'
returns warning
'
,
()
=>
{
expect
(
statusIcon
(
false
,
0
,
1
)).
toEqual
(
'
warning
'
);
expect
(
statusIcon
(
false
,
false
,
0
,
1
)).
toEqual
(
'
warning
'
);
});
});
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment