Commit 0bdbf2a1 authored by Sam Beckham's avatar Sam Beckham Committed by Nick Thomas

Creates a standalone vulnerability page

- Adds in the standalone vulnerabilty page
- Moves the project security dashboard page to the index
- Renders a 404 on the vulnerability page if the
`first_class_vulnerabilities` feature flag is not present.
- Updates all the path helpers and related tests
- Adds tests for the new route
parent ad1f7390
---
title: Creates a standalone vulnerability page
merge_request: 20734
author:
type: other
...@@ -7,10 +7,22 @@ module Projects ...@@ -7,10 +7,22 @@ module Projects
alias_method :vulnerable, :project alias_method :vulnerable, :project
def show before_action only: [:index] do
push_frontend_feature_flag(:hide_dismissed_vulnerabilities)
end
def index
@pipeline = @project.latest_pipeline_with_security_reports @pipeline = @project.latest_pipeline_with_security_reports
&.present(current_user: current_user) &.present(current_user: current_user)
end end
def show
return render_404 unless Feature.enabled?(:first_class_vulnerabilities, project)
@vulnerability = project.vulnerabilities.find(params[:id])
pipeline = @vulnerability.finding.pipelines.first
@pipeline = pipeline if Ability.allowed?(current_user, :read_pipeline, pipeline)
end
end end
end end
end end
...@@ -28,7 +28,7 @@ module EE ...@@ -28,7 +28,7 @@ module EE
def security_dashboard_data def security_dashboard_data
OpenStruct.new(is_link: false, OpenStruct.new(is_link: false,
label: statistic_icon('lock') + _('Security Dashboard'), label: statistic_icon('lock') + _('Security Dashboard'),
link: project_security_dashboard_path(project), link: project_security_dashboard_index_path(project),
class_modifier: 'default') class_modifier: 'default')
end end
end end
......
- return unless any_project_nav_tab?([:security, :dependencies, :licenses]) - return unless any_project_nav_tab?([:security, :dependencies, :licenses])
- top_level_link = project_nav_tab?(:security) ? project_security_dashboard_path(@project) : project_dependencies_path(@project) - top_level_link = project_nav_tab?(:security) ? project_security_dashboard_index_path(@project) : project_dependencies_path(@project)
- top_level_qa_selector = project_nav_tab?(:security) ? 'security_dashboard_link' : 'dependency_list_link' - top_level_qa_selector = project_nav_tab?(:security) ? 'security_dashboard_link' : 'dependency_list_link'
= nav_link(path: sidebar_security_paths) do = nav_link(path: sidebar_security_paths) do
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
- if project_nav_tab?(:security) - if project_nav_tab?(:security)
= nav_link(path: 'projects/security/dashboard#show') do = nav_link(path: 'projects/security/dashboard#show') do
= link_to project_security_dashboard_path(@project), title: _('Security Dashboard') do = link_to project_security_dashboard_index_path(@project), title: _('Security Dashboard') do
%span= _('Security Dashboard') %span= _('Security Dashboard')
- if project_nav_tab?(:dependencies) - if project_nav_tab?(:dependencies)
......
- breadcrumb_title _("Security Dashboard")
- page_title _("Security Dashboard")
#js-security-report-app{ data: project_security_dashboard_config(@project, @pipeline) }
- @content_class = "limit-container-width" unless fluid_layout
-# - add_to_breadcrumbs _("Security Dashboard"), project_security_dashboard_index_path(@project)
- breadcrumb_title "Vulnerability list"
- page_title "Vulnerability list"
.issue-details.issuable-details
.detail-page-description.content-block
%h2.title= Vulnerability List
- breadcrumb_title _("Security Dashboard") - @content_class = "limit-container-width" unless fluid_layout
- page_title _("Security Dashboard") - add_to_breadcrumbs _("Security Dashboard"), project_security_dashboard_index_path(@project)
- breadcrumb_title @vulnerability.id
- page_title @vulnerability.title
- page_description @vulnerability.description
#js-security-report-app{ data: project_security_dashboard_config(@project, @pipeline) } .detail-page-header
.detail-page-header-body
.issuable-status-box.status-box.status-box-open.closed
%span= @vulnerability.state
- if @pipeline
%span#js-pipeline-created
- timeago = time_ago_with_tooltip(@pipeline.created_at)
- pipeline_link = '<a href="%{url}">%{id}</a>'.html_safe % { url: pipeline_url(@pipeline), id: @pipeline.id }
= _('Detected %{timeago} in pipeline %{pipeline_link}').html_safe % { pipeline_link: pipeline_link, timeago: timeago }
- else
%spa#js-vulnerability-created
= time_ago_with_tooltip(@vulnerability.created_at)
.issue-details.issuable-details
.detail-page-description.content-block
%h2.title= @vulnerability.title
.description
.md
%h3= "Description"
%p= @vulnerability.finding.description
%ul
%li= _("Severity: %{severity}") % { severity: @vulnerability.severity }
%li= _("Confidence: %{confidence}") % { confidence: @vulnerability.confidence }
%li= _("Report Type: %{report_type}") % { report_type: @vulnerability.report_type }
- if @vulnerability.finding.location["image"]
%li= _("Image: %{image}") % { image: @vulnerability.finding.location['image'] }
- if @vulnerability.finding.location["operating_system"]
%li= _("Namespace: %{namespace}") % { namespace: @vulnerability.finding.location['operating_system'] }
- if @vulnerability.finding.links.any?
%h3= _("Links")
%ul
- @vulnerability.finding.links.each do |link|
%li
%a{ :href=>link["url"], target: "_blank", rel: 'noopener noreferrer' }= link["url"]
- if @vulnerability.finding.identifiers.any?
%h3= _("Identifiers")
%ul
- @vulnerability.finding.identifiers.each do |identifier|
%li
%a{ :href=>identifier.url, target: "_blank", rel: 'noopener noreferrer' }= identifier.name
...@@ -169,7 +169,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -169,7 +169,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
namespace :security do namespace :security do
resource :dashboard, only: [:show], controller: :dashboard resources :dashboard, only: [:show, :index], controller: :dashboard
resource :configuration, only: [:show], controller: :configuration resource :configuration, only: [:show], controller: :configuration
resources :vulnerability_findings, only: [:index] do resources :vulnerability_findings, only: [:index] do
......
...@@ -11,23 +11,23 @@ describe Projects::Security::DashboardController do ...@@ -11,23 +11,23 @@ describe Projects::Security::DashboardController do
let(:vulnerable) { project } let(:vulnerable) { project }
let(:security_dashboard_action) do let(:security_dashboard_action) do
get :show, params: { namespace_id: project.namespace, project_id: project } get :index, params: { namespace_id: project.namespace, project_id: project }
end end
end end
before do before do
group.add_developer(user) group.add_developer(user)
stub_licensed_features(security_dashboard: true)
end end
describe 'GET #show' do describe 'GET #index' do
let(:pipeline) { create(:ci_pipeline, sha: project.commit.id, project: project, user: user) } let(:pipeline) { create(:ci_pipeline, sha: project.commit.id, project: project, user: user) }
render_views render_views
def show_security_dashboard(current_user = user) def show_security_dashboard(current_user = user)
stub_licensed_features(security_dashboard: true)
sign_in(current_user) sign_in(current_user)
get :show, params: { namespace_id: project.namespace, project_id: project } get :index, params: { namespace_id: project.namespace, project_id: project }
end end
context 'when uses legacy reports syntax' do context 'when uses legacy reports syntax' do
...@@ -39,7 +39,7 @@ describe Projects::Security::DashboardController do ...@@ -39,7 +39,7 @@ describe Projects::Security::DashboardController do
show_security_dashboard show_security_dashboard
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show) expect(response).to render_template(:index)
expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data=true]") expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data=true]")
end end
end end
...@@ -53,7 +53,7 @@ describe Projects::Security::DashboardController do ...@@ -53,7 +53,7 @@ describe Projects::Security::DashboardController do
show_security_dashboard show_security_dashboard
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show) expect(response).to render_template(:index)
expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data=true]") expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data=true]")
end end
end end
...@@ -63,9 +63,61 @@ describe Projects::Security::DashboardController do ...@@ -63,9 +63,61 @@ describe Projects::Security::DashboardController do
show_security_dashboard show_security_dashboard
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show) expect(response).to render_template(:index)
expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data=false]") expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data=false]")
end end
end end
end end
describe 'GET #show' do
let_it_be(:pipeline) { create(:ci_pipeline, sha: project.commit.id, project: project, user: user) }
let_it_be(:vulnerability) { create(:vulnerability, project: project) }
render_views
def show_vulnerability
sign_in(user)
get :show, params: { namespace_id: project.namespace, project_id: project, id: vulnerability.id }
end
context "when there's an attached pipeline" do
let_it_be(:finding) { create(:vulnerabilities_occurrence, vulnerability: vulnerability, pipelines: [pipeline]) }
it 'renders the vulnerability page' do
show_vulnerability
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show)
expect(response.body).to have_text(vulnerability.title)
end
it 'renders the time pipeline ran' do
show_vulnerability
expect(response.body).to have_css("#js-pipeline-created")
end
end
context "when there's no attached pipeline" do
let_it_be(:finding) { create(:vulnerabilities_occurrence, vulnerability: vulnerability) }
it 'renders the time the vulnerability was created' do
show_vulnerability
expect(response.body).to have_css("#js-vulnerability-created")
end
end
context 'when the feature flag is disabled' do
before do
stub_feature_flags(first_class_vulnerabilities: false)
end
it 'renders the 404 page' do
show_vulnerability
expect(response).to have_gitlab_http_status(404)
end
end
end
end end
...@@ -15,7 +15,7 @@ describe ProjectPresenter do ...@@ -15,7 +15,7 @@ describe ProjectPresenter do
let(:security_dashboard_data) do let(:security_dashboard_data) do
OpenStruct.new(is_link: false, OpenStruct.new(is_link: false,
label: a_string_including('Security Dashboard'), label: a_string_including('Security Dashboard'),
link: project_security_dashboard_path(project), link: project_security_dashboard_index_path(project),
class_modifier: 'default') class_modifier: 'default')
end end
...@@ -25,7 +25,7 @@ describe ProjectPresenter do ...@@ -25,7 +25,7 @@ describe ProjectPresenter do
end end
it 'has security dashboard link' do it 'has security dashboard link' do
expect(presenter.extra_statistics_buttons.find { |button| button[:link] == project_security_dashboard_path(project) }).not_to be_nil expect(presenter.extra_statistics_buttons.find { |button| button[:link] == project_security_dashboard_index_path(project) }).not_to be_nil
end end
end end
...@@ -35,7 +35,7 @@ describe ProjectPresenter do ...@@ -35,7 +35,7 @@ describe ProjectPresenter do
end end
it 'has no security dashboard link' do it 'has no security dashboard link' do
expect(presenter.extra_statistics_buttons.find { |button| button[:link] == project_security_dashboard_path(project) }).to be_nil expect(presenter.extra_statistics_buttons.find { |button| button[:link] == project_security_dashboard_index_path(project) }).to be_nil
end end
end end
end end
......
...@@ -141,11 +141,11 @@ describe 'layouts/nav/sidebar/_project' do ...@@ -141,11 +141,11 @@ describe 'layouts/nav/sidebar/_project' do
let(:can_read_dependencies) { true } let(:can_read_dependencies) { true }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_path(project)) expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
end end
it 'security dashboard link is visible' do it 'security dashboard link is visible' do
expect(rendered).to have_link('Security Dashboard', href: project_security_dashboard_path(project)) expect(rendered).to have_link('Security Dashboard', href: project_security_dashboard_index_path(project))
end end
it 'security configuration link is visible' do it 'security configuration link is visible' do
...@@ -162,11 +162,11 @@ describe 'layouts/nav/sidebar/_project' do ...@@ -162,11 +162,11 @@ describe 'layouts/nav/sidebar/_project' do
let(:can_read_dependencies) { false } let(:can_read_dependencies) { false }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_path(project)) expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
end end
it 'security dashboard link is visible' do it 'security dashboard link is visible' do
expect(rendered).to have_link('Security Dashboard', href: project_security_dashboard_path(project)) expect(rendered).to have_link('Security Dashboard', href: project_security_dashboard_index_path(project))
end end
it 'security configuration link is visible' do it 'security configuration link is visible' do
...@@ -187,7 +187,7 @@ describe 'layouts/nav/sidebar/_project' do ...@@ -187,7 +187,7 @@ describe 'layouts/nav/sidebar/_project' do
end end
it 'security dashboard link is not visible' do it 'security dashboard link is not visible' do
expect(rendered).not_to have_link('Security Dashboard', href: project_security_dashboard_path(project)) expect(rendered).not_to have_link('Security Dashboard', href: project_security_dashboard_index_path(project))
end end
it 'security configuration link is not visible' do it 'security configuration link is not visible' do
...@@ -204,11 +204,11 @@ describe 'layouts/nav/sidebar/_project' do ...@@ -204,11 +204,11 @@ describe 'layouts/nav/sidebar/_project' do
let(:can_read_dashboard) { false } let(:can_read_dashboard) { false }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).not_to have_link('Security & Compliance', href: project_security_dashboard_path(project)) expect(rendered).not_to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
end end
it 'security dashboard link is not visible' do it 'security dashboard link is not visible' do
expect(rendered).not_to have_link('Security Dashboard', href: project_security_dashboard_path(project)) expect(rendered).not_to have_link('Security Dashboard', href: project_security_dashboard_index_path(project))
end end
it 'security configuration link is not visible' do it 'security configuration link is not visible' do
......
...@@ -4825,6 +4825,9 @@ msgstr "" ...@@ -4825,6 +4825,9 @@ msgstr ""
msgid "Complete" msgid "Complete"
msgstr "" msgstr ""
msgid "Confidence: %{confidence}"
msgstr ""
msgid "Confidential" msgid "Confidential"
msgstr "" msgstr ""
...@@ -6402,6 +6405,9 @@ msgstr "" ...@@ -6402,6 +6405,9 @@ msgstr ""
msgid "Detect host keys" msgid "Detect host keys"
msgstr "" msgstr ""
msgid "Detected %{timeago} in pipeline %{pipeline_link}"
msgstr ""
msgid "DevOps Score" msgid "DevOps Score"
msgstr "" msgstr ""
...@@ -9857,6 +9863,9 @@ msgstr "" ...@@ -9857,6 +9863,9 @@ msgstr ""
msgid "Identifier" msgid "Identifier"
msgstr "" msgstr ""
msgid "Identifiers"
msgstr ""
msgid "Identities" msgid "Identities"
msgstr "" msgstr ""
...@@ -9914,6 +9923,9 @@ msgstr "" ...@@ -9914,6 +9923,9 @@ msgstr ""
msgid "Image %{imageName} was scheduled for deletion from the registry." msgid "Image %{imageName} was scheduled for deletion from the registry."
msgstr "" msgstr ""
msgid "Image: %{image}"
msgstr ""
msgid "ImageDiffViewer|2-up" msgid "ImageDiffViewer|2-up"
msgstr "" msgstr ""
...@@ -11104,6 +11116,9 @@ msgstr "" ...@@ -11104,6 +11116,9 @@ msgstr ""
msgid "LinkedPipelines|%{counterLabel} more downstream pipelines" msgid "LinkedPipelines|%{counterLabel} more downstream pipelines"
msgstr "" msgstr ""
msgid "Links"
msgstr ""
msgid "List" msgid "List"
msgstr "" msgstr ""
...@@ -12078,6 +12093,9 @@ msgstr "" ...@@ -12078,6 +12093,9 @@ msgstr ""
msgid "Name:" msgid "Name:"
msgstr "" msgstr ""
msgid "Namespace: %{namespace}"
msgstr ""
msgid "Namespaces to index" msgid "Namespaces to index"
msgstr "" msgstr ""
...@@ -15569,6 +15587,9 @@ msgstr "" ...@@ -15569,6 +15587,9 @@ msgstr ""
msgid "Repo by URL" msgid "Repo by URL"
msgstr "" msgstr ""
msgid "Report Type: %{report_type}"
msgstr ""
msgid "Report abuse to admin" msgid "Report abuse to admin"
msgstr "" msgstr ""
...@@ -16944,6 +16965,9 @@ msgstr "" ...@@ -16944,6 +16965,9 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
msgid "Severity: %{severity}"
msgstr ""
msgid "Share" msgid "Share"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment