Commit 3e1bc4f3 authored by Mehmet Emin INAC's avatar Mehmet Emin INAC

Remove VulnerableProjectsController(s) and related logic

As we started using GraphQL for letter grades information, these
controllers became obsolote therefore we are removing them from the
codebase.
parent 66fa2eff
# frozen_string_literal: true
class Groups::Security::VulnerableProjectsController < Groups::ApplicationController
include SecurityDashboardsPermissions
alias_method :vulnerable, :group
def index
vulnerable_projects = ::Security::VulnerableProjectsFinder.new(projects).execute
presented_projects = vulnerable_projects.map do |project|
::Security::VulnerableProjectPresenter.new(project)
end
render json: VulnerableProjectSerializer.new.represent(presented_projects)
end
private
def projects
::Project.for_group_and_its_subgroups(group).non_archived.without_deleted.with_route
end
end
# frozen_string_literal: true
class Security::VulnerableProjectsController < Security::ApplicationController
def index
vulnerable_projects = ::Security::VulnerableProjectsFinder.new(projects).execute
presented_projects = vulnerable_projects.map do |project|
::Security::VulnerableProjectPresenter.new(project)
end
render json: VulnerableProjectSerializer.new.represent(presented_projects)
end
private
def projects
vulnerable.projects.non_archived.without_deleted.with_route
end
end
# frozen_string_literal: true
module Security
class VulnerableProjectsFinder
PROJECTS_LIMIT = 5000
def initialize(projects)
@projects = projects
end
def execute
projects.where("EXISTS(?)", vulnerabilities).limit(PROJECTS_LIMIT) # rubocop:disable CodeReuse/ActiveRecord
end
private
attr_reader :projects
def vulnerabilities
::Vulnerabilities::Finding
.select(1)
.undismissed
.scoped_project
end
end
end
...@@ -38,7 +38,6 @@ module Groups::SecurityFeaturesHelper ...@@ -38,7 +38,6 @@ module Groups::SecurityFeaturesHelper
no_vulnerabilities_svg_path: image_path('illustrations/issues.svg'), no_vulnerabilities_svg_path: image_path('illustrations/issues.svg'),
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'), empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'), dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'),
vulnerable_projects_endpoint: group_security_vulnerable_projects_path(group),
vulnerabilities_export_endpoint: expose_path(api_v4_security_groups_vulnerability_exports_path(id: group.id)) vulnerabilities_export_endpoint: expose_path(api_v4_security_groups_vulnerability_exports_path(id: group.id))
} }
end end
......
...@@ -9,7 +9,6 @@ module SecurityHelper ...@@ -9,7 +9,6 @@ module SecurityHelper
empty_state_svg_path: image_path('illustrations/operations-dashboard_empty.svg'), empty_state_svg_path: image_path('illustrations/operations-dashboard_empty.svg'),
project_add_endpoint: security_projects_path, project_add_endpoint: security_projects_path,
project_list_endpoint: security_projects_path, project_list_endpoint: security_projects_path,
vulnerable_projects_endpoint: security_vulnerable_projects_path,
instance_dashboard_settings_path: security_settings_dashboard_path, instance_dashboard_settings_path: security_settings_dashboard_path,
vulnerability_feedback_help_path: help_page_path('user/application_security/index', anchor: 'interacting-with-the-vulnerabilities'), vulnerability_feedback_help_path: help_page_path('user/application_security/index', anchor: 'interacting-with-the-vulnerabilities'),
vulnerabilities_export_endpoint: expose_path(api_v4_security_vulnerability_exports_path) vulnerabilities_export_endpoint: expose_path(api_v4_security_vulnerability_exports_path)
......
# frozen_string_literal: true
module Security
class VulnerableProjectPresenter < ::Gitlab::View::Presenter::Delegated
SEVERITY_LEVELS = ::Vulnerabilities::Finding::SEVERITY_LEVELS.keys
presents :project
def initialize(project)
super(project, counts_for_project(project))
end
private
def counts_for_project(project)
SEVERITY_LEVELS.each_with_object({}) do |severity, counts|
counts["#{severity}_vulnerability_count".to_sym] = ::Vulnerabilities::Finding.batch_count_by_project_and_severity(project.id, severity)
end
end
end
end
# frozen_string_literal: true
class VulnerableProjectEntity < ProjectEntity
::Vulnerabilities::Finding::SEVERITY_LEVELS.each_key do |severity_level|
expose "#{severity_level}_vulnerability_count"
end
end
# frozen_string_literal: true
class VulnerableProjectSerializer < BaseSerializer
entity VulnerableProjectEntity
end
...@@ -149,7 +149,6 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -149,7 +149,6 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :dashboard, only: [:show], controller: :dashboard resource :dashboard, only: [:show], controller: :dashboard
resources :vulnerabilities, only: [:index] resources :vulnerabilities, only: [:index]
resource :compliance_dashboard, only: [:show] resource :compliance_dashboard, only: [:show]
resources :vulnerable_projects, only: [:index]
resource :discover, only: [:show], controller: :discover resource :discover, only: [:show], controller: :discover
resources :credentials, only: [:index] resources :credentials, only: [:index]
resources :merge_commit_reports, only: [:index], constraints: { format: :csv } resources :merge_commit_reports, only: [:index], constraints: { format: :csv }
......
...@@ -5,5 +5,4 @@ namespace :security do ...@@ -5,5 +5,4 @@ namespace :security do
get 'dasboard/settings', to: 'dashboard#settings', as: :settings_dashboard get 'dasboard/settings', to: 'dashboard#settings', as: :settings_dashboard
resources :projects, only: [:index, :create, :destroy] resources :projects, only: [:index, :create, :destroy]
resources :vulnerable_projects, only: [:index]
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::Security::VulnerableProjectsController do
let(:group) { create(:group) }
let(:user) { create(:user) }
it_behaves_like SecurityDashboardsPermissions do
let(:vulnerable) { group }
let(:security_dashboard_action) { get :index, params: { group_id: group }, format: :json }
end
describe '#index' do
before do
stub_licensed_features(security_dashboard: true)
group.add_developer(user)
sign_in(user)
end
subject { get :index, params: { group_id: group }, format: :json }
it "responds with a list of the group's most vulnerable projects" do
_ungrouped_project = create(:project)
_safe_project = create(:project, namespace: group)
vulnerable_project = create(:project, namespace: group)
pipeline = create(:ci_pipeline, :success, project: vulnerable_project)
create_list(
:vulnerabilities_occurrence,
2,
pipelines: [pipeline],
project: vulnerable_project,
severity: :critical
)
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to be(1)
expect(json_response.first['id']).to eq(vulnerable_project.id)
expect(json_response.first['full_path']).to eq(project_path(vulnerable_project))
expect(json_response.first['critical_vulnerability_count']).to eq(2)
end
it 'includes projects in subgroups' do
subgroup = create(:group, parent: group)
project = create(:project, namespace: subgroup)
create(:vulnerabilities_occurrence, project: project)
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to be(1)
expect(json_response.first['id']).to eq(project.id)
end
it 'does not include archived or deleted projects' do
archived_project = create(:project, :archived, namespace: group)
deleted_project = create(:project, namespace: group, pending_delete: true)
archived_pipeline = create(:ci_pipeline, :success, project: archived_project)
deleted_pipeline = create(:ci_pipeline, :success, project: deleted_project)
create(:vulnerabilities_occurrence, pipelines: [archived_pipeline], project: archived_project)
create(:vulnerabilities_occurrence, pipelines: [deleted_pipeline], project: deleted_project)
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Security::VulnerableProjectsFinder do
describe '#execute' do
let(:projects) { Project.all }
let!(:safe_project) { create(:project) }
let(:vulnerable_project) { create(:project) }
let!(:vulnerability) { create(:vulnerabilities_occurrence, project: vulnerable_project) }
subject { described_class.new(projects).execute }
it 'returns the projects that have vulnerabilities from the collection of projects given to it' do
expect(subject).to contain_exactly(vulnerable_project)
end
it 'does not include projects that only have dismissed vulnerabilities' do
create(:vulnerabilities_occurrence, :dismissed, project: safe_project)
expect(subject).to contain_exactly(vulnerable_project)
end
it 'only uses 1 query' do
another_project = create(:project)
create(:vulnerabilities_occurrence, :dismissed, project: another_project)
expect { subject }.not_to exceed_query_limit(1)
expect(subject).to contain_exactly(vulnerable_project)
end
end
end
...@@ -129,7 +129,6 @@ RSpec.describe Groups::SecurityFeaturesHelper do ...@@ -129,7 +129,6 @@ RSpec.describe Groups::SecurityFeaturesHelper do
vulnerability_feedback_help_path: '/help/user/application_security/index#interacting-with-the-vulnerabilities', vulnerability_feedback_help_path: '/help/user/application_security/index#interacting-with-the-vulnerabilities',
empty_state_svg_path: '/images/illustrations/security-dashboard-empty-state.svg', empty_state_svg_path: '/images/illustrations/security-dashboard-empty-state.svg',
dashboard_documentation: '/help/user/application_security/security_dashboard/index', dashboard_documentation: '/help/user/application_security/security_dashboard/index',
vulnerable_projects_endpoint: "/groups/#{group.full_path}/-/security/vulnerable_projects",
vulnerabilities_export_endpoint: "/api/v4/security/groups/#{group.id}/vulnerability_exports" vulnerabilities_export_endpoint: "/api/v4/security/groups/#{group.id}/vulnerability_exports"
} }
end end
......
...@@ -14,7 +14,6 @@ RSpec.describe SecurityHelper do ...@@ -14,7 +14,6 @@ RSpec.describe SecurityHelper do
empty_state_svg_path: image_path('illustrations/operations-dashboard_empty.svg'), empty_state_svg_path: image_path('illustrations/operations-dashboard_empty.svg'),
project_add_endpoint: security_projects_path, project_add_endpoint: security_projects_path,
project_list_endpoint: security_projects_path, project_list_endpoint: security_projects_path,
vulnerable_projects_endpoint: security_vulnerable_projects_path,
instance_dashboard_settings_path: security_settings_dashboard_path, instance_dashboard_settings_path: security_settings_dashboard_path,
vulnerability_feedback_help_path: help_page_path('user/application_security/index', anchor: 'interacting-with-the-vulnerabilities'), vulnerability_feedback_help_path: help_page_path('user/application_security/index', anchor: 'interacting-with-the-vulnerabilities'),
vulnerabilities_export_endpoint: api_v4_security_vulnerability_exports_path vulnerabilities_export_endpoint: api_v4_security_vulnerability_exports_path
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Security::VulnerableProjectPresenter do
let(:project) { create(:project) }
before do
allow(::Vulnerabilities::Finding).to receive(:batch_count_by_project_and_severity).and_return(1)
end
subject { described_class.new(project) }
it 'presents the given project' do
expect(subject.id).to be(project.id)
end
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
it "exposes a vulnerability count attribute for #{severity_level} vulnerabilities" do
expect(subject.public_send("#{severity_level}_vulnerability_count")).to be(1)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'GET /groups/*group_id/-/security/projects' do
let(:group) { create(:group) }
let(:user) { create(:user) }
before do
stub_licensed_features(security_dashboard: true)
login_as(user)
group.add_developer(user)
end
it 'does not use N+1 queries' do
control_project = create(:project, namespace: group)
create(:vulnerabilities_occurrence, project: control_project)
control_count = ActiveRecord::QueryRecorder.new do
get group_security_vulnerable_projects_path(group, format: :json)
end
projects = create_list(:project, 2, namespace: group)
projects.each do |project|
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity|
create(:vulnerabilities_occurrence, severity: severity, project: project)
end
end
expect do
get group_security_vulnerable_projects_path(group, format: :json)
end.not_to exceed_query_limit(control_count)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to be(3)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'GET /-/security/vulnerable_projects' do
it_behaves_like 'security dashboard JSON endpoint' do
let(:security_dashboard_request) do
get security_vulnerable_projects_path, headers: { 'ACCEPT' => 'application/json' }
end
end
context 'with an authenticated user' do
let(:project) { create(:project) }
let(:user) { create(:user) }
before do
stub_licensed_features(security_dashboard: true)
project.add_developer(user)
user.security_dashboard_projects << project
login_as(user)
end
subject { get security_vulnerable_projects_path, headers: { 'ACCEPT' => 'application/json' } }
it "responds with the projects on the user's dashboard and their vulnerability counts" do
safe_project = create(:project)
safe_project.add_developer(user)
user.security_dashboard_projects << safe_project
pipeline = create(:ci_pipeline, :success, project: project)
create_list(
:vulnerabilities_occurrence,
2,
pipelines: [pipeline],
project: project,
severity: :critical
)
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.count).to be(1)
expect(json_response.first['id']).to eq(project.id)
expect(json_response.first['full_path']).to eq(project_path(project))
expect(json_response.first['critical_vulnerability_count']).to eq(2)
end
it 'does not include archived or deleted projects' do
archived_project = create(:project, :archived)
deleted_project = create(:project, pending_delete: true)
archived_pipeline = create(:ci_pipeline, :success, project: archived_project)
deleted_pipeline = create(:ci_pipeline, :success, project: deleted_project)
create(:vulnerabilities_occurrence, pipelines: [archived_pipeline], project: archived_project)
create(:vulnerabilities_occurrence, pipelines: [deleted_pipeline], project: deleted_project)
archived_project.add_developer(user)
deleted_project.add_developer(user)
user.security_dashboard_projects << [archived_project, deleted_project]
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_empty
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe VulnerableProjectEntity do
let(:project) { create(:project) }
let(:vulnerable_project) { ::Security::VulnerableProjectPresenter.new(project) }
before do
allow(::Vulnerabilities::Finding).to receive(:batch_count_by_project_and_severity).and_return(2)
end
subject { described_class.new(vulnerable_project) }
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
it "exposes a vulnerability count attribute for #{severity_level} vulnerabilities" do
expect(subject.as_json["#{severity_level}_vulnerability_count".to_sym]).to be(2)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe VulnerableProjectSerializer do
let(:project) { create(:project) }
let(:serializer) { described_class.new(project: project, current_user: user) }
let(:user) { create(:user) }
let(:vulnerable_project) { ::Security::VulnerableProjectPresenter.new(project) }
before do
project.add_developer(user)
allow(::Vulnerabilities::Finding).to receive(:batch_count_by_project_and_severity)
end
describe '#represent' do
subject { serializer.represent(vulnerable_project) }
it 'includes counts for each severity of vulnerability' do
::Vulnerabilities::Finding::SEVERITY_LEVELS.keys.each do |severity_level|
expect(subject).to include("#{severity_level}_vulnerability_count".to_sym)
end
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