Commit 07011623 authored by Mehmet Emin INAC's avatar Mehmet Emin INAC

Render security dashboard data for project if it has vulnerabilities

Previously we were relying on existence of pipeline to render the
security dashboard but with the standalone vulnerabilities dashboard
we will allow users to create their own vulnerabilities which do not
belong to any pipeline. To support this feature, we remove the pipeline
information from the dashboard and show the dashboard if it has any
vulnerabilities associated with itself.
parent 73ca8b87
......@@ -179,7 +179,7 @@ module EE
end
def project_security_dashboard_config(project, pipeline)
if pipeline.nil?
if project.vulnerabilities.none?
{
empty_state_svg_path: image_path('illustrations/security-dashboard_empty.svg'),
security_dashboard_help_path: help_page_path('user/application_security/security_dashboard/index')
......@@ -194,24 +194,36 @@ module EE
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'),
security_dashboard_help_path: help_page_path('user/application_security/security_dashboard/index'),
pipeline_id: pipeline.id,
user_path: user_url(pipeline.user),
user_avatar_path: pipeline.user.avatar_url,
user_name: pipeline.user.name,
commit_id: pipeline.commit.short_id,
commit_path: project_commit_url(project, pipeline.commit),
ref_id: pipeline.ref,
ref_path: project_commits_url(project, pipeline.ref),
pipeline_path: pipeline_url(pipeline),
pipeline_created: pipeline.created_at.to_s(:iso8601),
has_pipeline_data: "true",
user_callouts_path: user_callouts_path,
user_callout_id: UserCalloutsHelper::STANDALONE_VULNERABILITIES_INTRODUCTION_BANNER,
show_introduction_banner: show_standalone_vulnerabilities_introduction_banner?.to_s
}.merge(project_vulnerabilities_config(project))
}.merge!(
project_vulnerabilities_config(project),
security_dashboard_pipeline_data(project, pipeline)
)
end
end
# TODO(@gitlab-org/defend/backend): Remove this method and it's
# references with https://gitlab.com/gitlab-org/gitlab/-/issues/207448.
def security_dashboard_pipeline_data(project, pipeline)
return { has_pipeline_data: 'false' } unless pipeline
{
pipeline_id: pipeline.id,
user_path: pipeline.user && user_url(pipeline.user),
user_avatar_path: pipeline.user&.avatar_url,
user_name: pipeline.user&.name,
commit_id: pipeline.commit.short_id,
commit_path: project_commit_url(project, pipeline.commit),
ref_id: pipeline.ref,
ref_path: project_commits_url(project, pipeline.ref),
pipeline_path: pipeline_url(pipeline),
pipeline_created: pipeline.created_at.to_s(:iso8601),
has_pipeline_data: 'true'
}
end
def project_vulnerabilities_config(project)
return {} unless first_class_vulnerabilities_available?(project)
......
......@@ -30,41 +30,27 @@ RSpec.describe Projects::Security::DashboardController do
get :index, params: { namespace_id: project.namespace, project_id: project }
end
context 'when uses legacy reports syntax' do
before do
create(:ci_build, :artifacts, pipeline: pipeline, name: 'sast')
end
it 'returns the latest pipeline with security reports for project' do
context 'when project has no vulnerabilities' do
it 'renders empty state' do
show_security_dashboard
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data]")
expect(response.body).not_to have_css('div#js-security-report-app[data-vulnerabilities-endpoint]')
end
end
context 'when uses new reports syntax' do
context 'when project has vulnerabilities' do
before do
create(:ee_ci_build, :sast, pipeline: pipeline)
end
it 'returns the latest pipeline with security reports for project' do
show_security_dashboard
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
expect(response.body).to have_css("div#js-security-report-app[data-has-pipeline-data]")
create(:vulnerability, project: project)
end
end
context 'when there is no matching pipeline' do
it 'renders empty state' do
it 'renders dashboard with vulnerability metadata' do
show_security_dashboard
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
expect(response.body).not_to have_css("div#js-security-report-app[data-has-pipeline-data]")
expect(response.body).to have_css('div#js-security-report-app[data-vulnerabilities-endpoint]')
end
end
end
......
......@@ -87,83 +87,111 @@ RSpec.describe ProjectsHelper do
end
end
shared_context 'project with owner and pipeline' do
let(:user) { create(:user) }
let(:group) { create(:group).tap { |g| g.add_owner(user) } }
let(:pipeline) do
create(:ee_ci_pipeline,
:with_sast_report,
user: user,
project: project,
ref: project.default_branch,
sha: project.commit.sha)
end
let(:project) { create(:project, :repository, group: group) }
end
describe '#project_security_dashboard_config' do
include_context 'project with owner and pipeline'
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let(:pipeline) { nil }
subject { helper.project_security_dashboard_config(project, pipeline) }
before do
group.add_owner(user)
allow(helper).to receive(:current_user).and_return(user)
end
let(:project) { create(:project, :repository, group: group) }
context 'project without pipeline' do
subject { helper.project_security_dashboard_config(project, nil) }
it 'returns simple config' do
expect(subject).to match(
context 'project without vulnerabilities' do
let(:expected_value) do
{
empty_state_svg_path: start_with('/assets/illustrations/security-dashboard_empty'),
security_dashboard_help_path: '/help/user/application_security/security_dashboard/index'
)
}
end
end
context 'project with pipeline' do
subject { helper.project_security_dashboard_config(project, pipeline) }
it { is_expected.to match(expected_value) }
end
it 'checks if first vulnerability class is enabled' do
expect(::Feature).to receive(:enabled?).with(:first_class_vulnerabilities, project, default_enabled: true)
context 'project with vulnerabilities' do
before do
create(:vulnerability, project: project)
end
subject
let(:expected_core_values) do
hash_including(
project: { id: project.id, name: project.name },
project_full_path: project.full_path,
vulnerabilities_endpoint: "/#{project.full_path}/-/security/vulnerability_findings",
vulnerabilities_summary_endpoint: "/#{project.full_path}/-/security/vulnerability_findings/summary",
vulnerability_feedback_help_path: '/help/user/application_security/index#interacting-with-the-vulnerabilities',
empty_state_svg_path: start_with('/assets/illustrations/security-dashboard-empty-state'),
dashboard_documentation: '/help/user/application_security/security_dashboard/index',
security_dashboard_help_path: '/help/user/application_security/security_dashboard/index',
user_callouts_path: '/-/user_callouts',
user_callout_id: 'standalone_vulnerabilities_introduction_banner',
show_introduction_banner: 'true'
)
end
context 'when first first class vulnerabilities is enabled for project' do
it { is_expected.to match(expected_core_values) }
context 'when the first_class_vulnerabilities available' do
let(:export_endpoint) { "/api/v4/security/projects/#{project.id}/vulnerability_exports" }
let(:expected_sub_hash) { hash_including(vulnerabilities_export_endpoint: export_endpoint) }
before do
expect(::Feature).to receive(:enabled?).with(:first_class_vulnerabilities, project, default_enabled: true).and_return(true)
allow(::Feature).to receive(:enabled?).with(:first_class_vulnerabilities, project, default_enabled: true).and_return(true)
end
it 'checks if first vulnerability class is enabled' do
expect(subject[:vulnerabilities_export_endpoint]).to(
eq(
api_v4_security_projects_vulnerability_exports_path(id: project.id)
))
end
it { is_expected.to match(expected_sub_hash) }
end
context 'when first first class vulnerabilities is disabled for project' do
context 'when the first_class_vulnerabilities is not available' do
before do
expect(::Feature).to receive(:enabled?).with(:first_class_vulnerabilities, project, default_enabled: true).and_return(false)
allow(::Feature).to receive(:enabled?).with(:first_class_vulnerabilities, project, default_enabled: true).and_return(false)
end
it 'checks if first vulnerability class is enabled' do
expect(subject).not_to have_key(:vulnerabilities_export_endpoint)
end
it { is_expected.not_to have_key(:vulnerabilities_export_endpoint) }
end
it 'returns config containing pipeline details' do
expect(subject[:security_dashboard_help_path]).to eq '/help/user/application_security/security_dashboard/index'
expect(subject[:has_pipeline_data]).to eq 'true'
context 'project without pipeline' do
let(:expected_sub_hash) do
hash_including(
has_pipeline_data: 'false'
)
end
it { is_expected.to match(expected_sub_hash) }
end
it 'returns the "vulnerability findings" endpoint paths' do
expect(subject[:vulnerabilities_endpoint]).to eq project_security_vulnerability_findings_path(project)
expect(subject[:vulnerabilities_summary_endpoint]).to(
eq(
summary_project_security_vulnerability_findings_path(project)
))
context 'project with pipeline' do
let_it_be(:pipeline) do
create(:ee_ci_pipeline,
:with_sast_report,
user: user,
project: project,
ref: project.default_branch,
sha: project.commit.sha)
end
let(:project_path) { "http://test.host/#{project.full_path}" }
let(:expected_sub_hash) do
hash_including(
pipeline_id: pipeline.id,
user_path: "http://test.host/#{pipeline.user.username}",
user_avatar_path: pipeline.user.avatar_url,
user_name: pipeline.user.name,
commit_id: pipeline.commit.short_id,
commit_path: "#{project_path}/-/commit/#{pipeline.commit.sha}",
ref_id: project.default_branch,
ref_path: "#{project_path}/-/commits/#{project.default_branch}",
pipeline_path: "#{project_path}/-/pipelines/#{pipeline.id}",
pipeline_created: pipeline.created_at.to_s(:iso8601),
has_pipeline_data: 'true'
)
end
it { is_expected.to match(expected_sub_hash) }
end
end
end
......
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