Commit efa57ed9 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'expose-blob-path-for-group-security-dashboard' into 'master'

Expose blob_path for vulnerability occurrences

See merge request gitlab-org/gitlab-ee!10602
parents 336ac78e 4410acbd
...@@ -4,7 +4,7 @@ class Groups::Security::VulnerabilitiesController < Groups::Security::Applicatio ...@@ -4,7 +4,7 @@ class Groups::Security::VulnerabilitiesController < Groups::Security::Applicatio
HISTORY_RANGE = 3.months HISTORY_RANGE = 3.months
def index def index
vulnerabilities = found_vulnerabilities.ordered.page(params[:page]) vulnerabilities = found_vulnerabilities(:with_sha).ordered.page(params[:page])
respond_to do |format| respond_to do |format|
format.json do format.json do
......
...@@ -54,7 +54,13 @@ module Security ...@@ -54,7 +54,13 @@ module Security
end end
def init_collection(scope) def init_collection(scope)
scope == :all ? group.all_vulnerabilities : group.latest_vulnerabilities if scope == :all
group.all_vulnerabilities
elsif scope == :with_sha
group.latest_vulnerabilities_with_sha
else
group.latest_vulnerabilities
end
end end
end end
end end
...@@ -111,6 +111,11 @@ module EE ...@@ -111,6 +111,11 @@ module EE
.for_pipelines(all_pipelines.with_vulnerabilities.latest_successful_ids_per_project) .for_pipelines(all_pipelines.with_vulnerabilities.latest_successful_ids_per_project)
end end
def latest_vulnerabilities_with_sha
Vulnerabilities::Occurrence
.for_pipelines_with_sha(all_pipelines.with_vulnerabilities.latest_successful_ids_per_project)
end
def all_vulnerabilities def all_vulnerabilities
Vulnerabilities::Occurrence Vulnerabilities::Occurrence
.for_pipelines(all_pipelines.with_vulnerabilities.success) .for_pipelines(all_pipelines.with_vulnerabilities.success)
......
...@@ -4,6 +4,7 @@ module Vulnerabilities ...@@ -4,6 +4,7 @@ module Vulnerabilities
class Occurrence < ApplicationRecord class Occurrence < ApplicationRecord
include ShaAttribute include ShaAttribute
include ::Gitlab::Utils::StrongMemoize include ::Gitlab::Utils::StrongMemoize
include Presentable
self.table_name = "vulnerability_occurrences" self.table_name = "vulnerability_occurrences"
...@@ -83,6 +84,12 @@ module Vulnerabilities ...@@ -83,6 +84,12 @@ module Vulnerabilities
preload(:scanner, :identifiers, project: [:namespace, :project_feature]) preload(:scanner, :identifiers, project: [:namespace, :project_feature])
end end
def self.for_pipelines_with_sha(pipelines)
for_pipelines(pipelines)
.joins(:pipelines)
.select("vulnerability_occurrences.*, ci_pipelines.sha")
end
def self.for_pipelines(pipelines) def self.for_pipelines(pipelines)
joins(:occurrence_pipelines) joins(:occurrence_pipelines)
.where(vulnerability_occurrence_pipelines: { pipeline_id: pipelines }) .where(vulnerability_occurrence_pipelines: { pipeline_id: pipelines })
......
# frozen_string_literal: true
module Vulnerabilities
class OccurrencePresenter < Gitlab::View::Presenter::Delegated
presents :occurrence
def blob_path
return '' unless respond_to?(:sha)
add_line_numbers(location['start_line'], location['end_line'])
end
private
def add_line_numbers(start_line, end_line)
return vulnerability_path unless start_line
vulnerability_path.tap do |complete_path|
complete_path << "#L#{start_line}"
complete_path << "-#{end_line}" if end_line
end
end
def vulnerability_path
@vulnerability_path ||= project_blob_path(project, File.join(sha, location['file']))
end
end
end
...@@ -23,6 +23,10 @@ class Vulnerabilities::OccurrenceEntity < Grape::Entity ...@@ -23,6 +23,10 @@ class Vulnerabilities::OccurrenceEntity < Grape::Entity
expose :solution expose :solution
end end
expose :blob_path do |occurrence|
occurrence.present.blob_path
end
alias_method :occurrence, :object alias_method :occurrence, :object
private private
......
...@@ -288,7 +288,7 @@ describe Group do ...@@ -288,7 +288,7 @@ describe Group do
end end
end end
describe '#latest_vulnerabilities' do describe 'Vulnerabilities::Occurrence collection methods' do
let(:project) { create(:project, namespace: group) } let(:project) { create(:project, namespace: group) }
let(:external_project) { create(:project) } let(:external_project) { create(:project) }
let(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) } let(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
...@@ -298,13 +298,14 @@ describe Group do ...@@ -298,13 +298,14 @@ describe Group do
let!(:external_vuln) { create_vulnerability(external_project) } let!(:external_vuln) { create_vulnerability(external_project) }
let!(:failed_vuln) { create_vulnerability(project, failed_pipeline) } let!(:failed_vuln) { create_vulnerability(project, failed_pipeline) }
subject { group.latest_vulnerabilities }
def create_vulnerability(project, pipeline = nil) def create_vulnerability(project, pipeline = nil)
pipeline ||= create(:ci_pipeline, :success, project: project) pipeline ||= create(:ci_pipeline, :success, project: project)
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project) create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project)
end end
describe '#latest_vulnerabilities' do
subject { group.latest_vulnerabilities }
it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do
is_expected.to contain_exactly(new_vuln) is_expected.to contain_exactly(new_vuln)
end end
...@@ -315,29 +316,38 @@ describe Group do ...@@ -315,29 +316,38 @@ describe Group do
# TODO: This should actually fail and we must scope vulns # TODO: This should actually fail and we must scope vulns
# per branch as soon as we store them for other branches # per branch as soon as we store them for other branches
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(branch_vuln) is_expected.to contain_exactly(branch_vuln)
end end
end end
end end
describe '#all_vulnerabilities' do describe '#latest_vulnerabilities_with_sha' do
let(:project) { create(:project, namespace: group) } subject { group.latest_vulnerabilities_with_sha }
let(:external_project) { create(:project) }
let(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
let!(:old_vuln) { create_vulnerability(project) } it 'returns vulns only for the latest successful pipelines of projects belonging to the group' do
let!(:new_vuln) { create_vulnerability(project) } is_expected.to contain_exactly(new_vuln)
let!(:external_vuln) { create_vulnerability(external_project) } end
let!(:failed_vuln) { create_vulnerability(project, failed_pipeline) }
subject { group.all_vulnerabilities } it { is_expected.to all(respond_to(:sha)) }
def create_vulnerability(project, pipeline = nil) context 'with vulnerabilities from other branches' do
pipeline ||= create(:ci_pipeline, :success, project: project) let!(:branch_pipeline) { create(:ci_pipeline, :success, project: project, ref: 'feature-x') }
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project) let!(:branch_vuln) { create(:vulnerabilities_occurrence, pipelines: [branch_pipeline], project: project) }
# TODO: This should actually fail and we must scope vulns
# per branch as soon as we store them for other branches
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(branch_vuln)
end
end
end end
describe '#all_vulnerabilities' do
subject { group.all_vulnerabilities }
it 'returns vulns for all successful pipelines of projects belonging to the group' do it 'returns vulns for all successful pipelines of projects belonging to the group' do
is_expected.to contain_exactly(old_vuln, new_vuln) is_expected.to contain_exactly(old_vuln, new_vuln)
end end
...@@ -348,11 +358,13 @@ describe Group do ...@@ -348,11 +358,13 @@ describe Group do
# TODO: This should actually fail and we must scope vulns # TODO: This should actually fail and we must scope vulns
# per branch as soon as we store them for other branches # per branch as soon as we store them for other branches
# Dependent on https://gitlab.com/gitlab-org/gitlab-ee/issues/9524
it 'includes vulnerabilities from all branches' do it 'includes vulnerabilities from all branches' do
is_expected.to contain_exactly(old_vuln, new_vuln, branch_vuln) is_expected.to contain_exactly(old_vuln, new_vuln, branch_vuln)
end end
end end
end end
end
describe '#saml_discovery_token' do describe '#saml_discovery_token' do
it 'returns existing tokens' do it 'returns existing tokens' do
......
...@@ -82,6 +82,21 @@ describe Vulnerabilities::Occurrence do ...@@ -82,6 +82,21 @@ describe Vulnerabilities::Occurrence do
end end
end end
describe '.for_pipelines_with_sha' do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, :success, project: project) }
before do
create(:vulnerabilities_occurrence, pipelines: [pipeline], project: project)
end
subject(:occurrences) { described_class.for_pipelines_with_sha([pipeline]) }
it 'sets the sha' do
expect(occurrences.first.sha).to eq(pipeline.sha)
end
end
describe '.count_by_day_and_severity' do describe '.count_by_day_and_severity' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:date_1) { Time.zone.parse('2018-11-11') } let(:date_1) { Time.zone.parse('2018-11-11') }
......
# frozen_string_literal: true
require 'spec_helper'
describe Vulnerabilities::OccurrencePresenter do
let(:presenter) { described_class.new(occurrence) }
let(:occurrence) { build_stubbed(:vulnerabilities_occurrence) }
describe '#blob_path' do
subject { presenter.blob_path }
context 'without a sha' do
it { is_expected.to be_blank }
end
context 'with a sha' do
before do
allow_any_instance_of(Vulnerabilities::Occurrence).to receive(:sha)
.and_return('abc')
end
context 'without start_line or end_line' do
before do
allow(presenter).to receive(:location)
.and_return({ 'file' => 'a.txt' })
end
it { is_expected.to end_with('a.txt') }
end
context 'with start_line only' do
before do
allow(presenter).to receive(:location)
.and_return({ 'file' => 'a.txt', 'start_line' => 1 })
end
it { is_expected.to end_with('#L1') }
end
context 'with start_line and end_line' do
before do
allow(presenter).to receive(:location)
.and_return({ 'file' => 'a.txt', 'start_line' => 1, 'end_line' => 2 })
end
it { is_expected.to end_with('#L1-2') }
end
end
end
end
...@@ -53,6 +53,7 @@ describe Vulnerabilities::OccurrenceEntity do ...@@ -53,6 +53,7 @@ describe Vulnerabilities::OccurrenceEntity do
expect(subject).to include(:scanner, :project, :identifiers) expect(subject).to include(:scanner, :project, :identifiers)
expect(subject).to include(:dismissal_feedback, :issue_feedback) expect(subject).to include(:dismissal_feedback, :issue_feedback)
expect(subject).to include(:description, :links, :location, :remediations, :solution) expect(subject).to include(:description, :links, :location, :remediations, :solution)
expect(subject).to include(:blob_path)
end end
context 'when not allowed to admin vulnerability feedback' do context 'when not allowed to admin vulnerability feedback' do
......
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