Commit e50baae5 authored by Craig Smith's avatar Craig Smith

Show vulnerability location URL in the issue

The vulnerability location currently only shows
the path to the offending code, which doesn’t
work from outside GitLab. This MR updates the
vulnerability location to show the full URL.
parent aee1b3f3
...@@ -105,6 +105,7 @@ class Projects::VulnerabilityFeedbackController < Projects::ApplicationControlle ...@@ -105,6 +105,7 @@ class Projects::VulnerabilityFeedbackController < Projects::ApplicationControlle
def vulnerability_data_params_attributes def vulnerability_data_params_attributes
%i[ %i[
blob_path
category category
confidence confidence
count count
......
...@@ -5,15 +5,12 @@ module Issues ...@@ -5,15 +5,12 @@ module Issues
def execute def execute
return error("Can't create issue") unless can?(@current_user, :create_issue, @project) return error("Can't create issue") unless can?(@current_user, :create_issue, @project)
vulnerability = case @params[:category] begin
when 'sast', 'dependency_scanning', 'dast' vulnerability = Gitlab::Vulnerabilities::Parser.fabricate(@params)
Gitlab::Vulnerabilities::StandardVulnerability.new(params) rescue Gitlab::Vulnerabilities::InvalidCategoryError
when 'container_scanning' return error('Invalid vulnerability category')
Gitlab::Vulnerabilities::ContainerScanningVulnerability.new(params)
end end
return error('Invalid vulnerability category') unless vulnerability
issue_params = { issue_params = {
title: "Investigate vulnerability: #{vulnerability.title}", title: "Investigate vulnerability: #{vulnerability.title}",
description: render_description(vulnerability), description: render_description(vulnerability),
...@@ -36,8 +33,10 @@ module Issues ...@@ -36,8 +33,10 @@ module Issues
end end
def render_description(vulnerability) def render_description(vulnerability)
view = ActionView::Base.new(ActionController::Base.view_paths, {}) ApplicationController.render(
view.render(file: 'vulnerabilities/issue_description.md.erb', locals: { vulnerability: vulnerability }) template: 'vulnerabilities/issue_description.md.erb',
locals: { vulnerability: vulnerability }
)
end end
end end
end end
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
module MergeRequests module MergeRequests
class CreateFromVulnerabilityDataService < ::BaseService class CreateFromVulnerabilityDataService < ::BaseService
def execute def execute
vulnerability = create_vulnerability begin
vulnerability = Gitlab::Vulnerabilities::Parser.fabricate(@params)
return error('Invalid vulnerability category') unless vulnerability rescue Gitlab::Vulnerabilities::InvalidCategoryError
return error('Invalid vulnerability category')
end
title_slug = Gitlab::Utils.slugify(vulnerability.title) title_slug = Gitlab::Utils.slugify(vulnerability.title)
source_branch = "remediate/%s-%s" % [ source_branch = "remediate/%s-%s" % [
...@@ -40,15 +42,6 @@ module MergeRequests ...@@ -40,15 +42,6 @@ module MergeRequests
private private
def create_vulnerability
case @params[:category]
when 'sast', 'dependency_scanning', 'dast'
Gitlab::Vulnerabilities::StandardVulnerability.new(@params)
when 'container_scanning'
Gitlab::Vulnerabilities::ContainerScanningVulnerability.new(@params)
end
end
def create_patch(vulnerability, source_branch, target_branch) def create_patch(vulnerability, source_branch, target_branch)
diffs = vulnerability.data[:remediations].map { |remediation| Base64.decode64(remediation[:diff]) } diffs = vulnerability.data[:remediations].map { |remediation| Base64.decode64(remediation[:diff]) }
head_commit = project.repository.find_branch(target_branch).dereferenced_target head_commit = project.repository.find_branch(target_branch).dereferenced_target
......
---
title: Use absolute URLs to ensure links to the source of SAST vulnerabilities resolve
merge_request: 27747
author:
type: fixed
# frozen_string_literal: true
module Gitlab
module Vulnerabilities
InvalidCategoryError = Class.new(StandardError)
module Parser
class << self
def fabricate(params)
raise ::Gitlab::Vulnerabilities::InvalidCategoryError unless valid_categories.key?(params[:category])
category = params[:category]
if standard_vulnerability? category
Gitlab::Vulnerabilities::StandardVulnerability.new(params)
else
Gitlab::Vulnerabilities::ContainerScanningVulnerability.new(params)
end
end
private
def valid_categories
::Vulnerabilities::Feedback.categories
end
def standard_vulnerability?(category)
(valid_categories.keys - ['container_scanning']).include?(category)
end
end
end
end
end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Gitlab module Gitlab
module Vulnerabilities module Vulnerabilities
class StandardVulnerability < BaseVulnerability class StandardVulnerability < BaseVulnerability
include RequestAwareEntity
# Passthrough properties # Passthrough properties
%i[ %i[
severity severity
...@@ -40,10 +41,16 @@ module Gitlab ...@@ -40,10 +41,16 @@ module Gitlab
"#{file}:#{line}" "#{file}:#{line}"
end end
def blob_path
return unless @data[:blob_path]
@data[:blob_path].gsub(/^\//, '')
end
def location_link def location_link
return file unless line return location_text unless blob_path
"#{file}#L#{line}" "#{root_url}#{blob_path}"
end end
end end
end end
......
...@@ -122,6 +122,7 @@ describe Projects::VulnerabilityFeedbackController do ...@@ -122,6 +122,7 @@ describe Projects::VulnerabilityFeedbackController do
title: 'Predictable pseudorandom number generator', title: 'Predictable pseudorandom number generator',
description: 'Description of Predictable pseudorandom number generator', description: 'Description of Predictable pseudorandom number generator',
tool: 'find_sec_bugs', tool: 'find_sec_bugs',
blob_path: '/group_path/project_path/-/blob/commitsha/subdir/src/main/App.java#L15',
location: { location: {
file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java', file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
start_line: '41' start_line: '41'
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Vulnerabilities::Parser do
describe '.fabricate' do
let(:params) do
{
target_branch: 'master'
}
end
subject { described_class.fabricate(params) }
context 'with standard categories' do
let(:categories) do
%w(
sast
dast
dependency_scanning
)
end
it 'returns a Standard Vulnerability' do
categories.each do |category|
params[:category] = category
expect(subject).to be_a(Gitlab::Vulnerabilities::StandardVulnerability)
expect(subject.target_branch).to eq('master')
end
end
end
context 'with container scanning as category' do
it 'returns a Scanning Vulnerability' do
params[:category] = 'container_scanning'
expect(subject).to be_a(Gitlab::Vulnerabilities::ContainerScanningVulnerability)
expect(subject.target_branch).to eq('master')
end
end
context 'with an invalid category' do
it 'raises an exception' do
params[:category] = 'foo'
expect { subject }.to raise_error(Gitlab::Vulnerabilities::InvalidCategoryError)
end
end
end
end
...@@ -259,19 +259,19 @@ describe Gitlab::Vulnerabilities::StandardVulnerability do ...@@ -259,19 +259,19 @@ describe Gitlab::Vulnerabilities::StandardVulnerability do
end end
describe '#location_link' do describe '#location_link' do
context 'when line is nil' do context 'when blob_path is nil' do
it 'returns a string with file' do it 'returns file path' do
vulnerability = described_class.new(file: file) vulnerability = described_class.new(file: "/foo/bar/-/blob/sha/#{file}")
expect(vulnerability.location_link).to eq file expect(vulnerability.location_link).to eq "/foo/bar/-/blob/sha/#{file}"
end end
end end
context 'when line is present' do context 'when line is present' do
it 'returns a string with file and line' do it 'returns a string with file and line' do
vulnerability = described_class.new(file: file, line: line) vulnerability = described_class.new(blob_path: "/foo/bar/-/blob/sha/#{file}#L#{line}")
expect(vulnerability.location_link).to eq "#{file}#L#{line}" expect(vulnerability.location_link).to eq "http://localhost/foo/bar/-/blob/sha/#{file}#L#{line}"
end end
end end
end end
......
...@@ -56,6 +56,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -56,6 +56,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
context 'when a description is present' do context 'when a description is present' do
let(:params) do let(:params) do
{ {
blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
category: 'sast', category: 'sast',
severity: 'Low', confidence: 'High', severity: 'Low', confidence: 'High',
solution: 'Please do something!', solution: 'Please do something!',
...@@ -98,7 +99,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -98,7 +99,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
* Severity: Low * Severity: Low
* Confidence: High * Confidence: High
* Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15) * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)
### Solution: ### Solution:
...@@ -123,6 +124,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -123,6 +124,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
context 'when a description is NOT present' do context 'when a description is NOT present' do
let(:params) do let(:params) do
{ {
blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
category: 'sast', category: 'sast',
severity: 'Low', confidence: 'High', severity: 'Low', confidence: 'High',
solution: 'Please do something!', solution: 'Please do something!',
...@@ -142,7 +144,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -142,7 +144,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
* Severity: Low * Severity: Low
* Confidence: High * Confidence: High
* Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15) * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)
### Solution: ### Solution:
...@@ -158,6 +160,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -158,6 +160,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
context 'when a description is present' do context 'when a description is present' do
let(:params) do let(:params) do
{ {
blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
category: 'dependency_scanning', category: 'dependency_scanning',
severity: 'Low', confidence: 'High', severity: 'Low', confidence: 'High',
solution: 'Please do something!', solution: 'Please do something!',
...@@ -199,7 +202,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -199,7 +202,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
* Severity: Low * Severity: Low
* Confidence: High * Confidence: High
* Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15) * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:15](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)
### Solution: ### Solution:
...@@ -224,6 +227,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -224,6 +227,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
context 'when a description is NOT present' do context 'when a description is NOT present' do
let(:params) do let(:params) do
{ {
blob_path: '/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15',
category: 'dependency_scanning', category: 'dependency_scanning',
priority: 'Low', line: '41', priority: 'Low', line: '41',
severity: 'Low', confidence: 'High', severity: 'Low', confidence: 'High',
...@@ -243,7 +247,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -243,7 +247,7 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
* Severity: Low * Severity: Low
* Confidence: High * Confidence: High
* Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:41](subdir/src/main/java/com/gitlab/security_products/tests/App.java#L41) * Location: [subdir/src/main/java/com/gitlab/security_products/tests/App.java:41](http://localhost/group_path/sub_group_path/project_path/-/blob/commitsha/subdir/src/main/java/com/gitlab/security_products/tests/App.java#L15)
### Solution: ### Solution:
...@@ -402,5 +406,15 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do ...@@ -402,5 +406,15 @@ describe Issues::CreateFromVulnerabilityDataService, '#execute' do
expect(result[:message]).to eq('Invalid vulnerability category') expect(result[:message]).to eq('Invalid vulnerability category')
end end
end end
context 'when category is missing' do
let(:params) { {} }
let(:result) { described_class.new(project, user, params).execute }
it 'return expected error' do
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq('Invalid vulnerability category')
end
end
end end
end end
...@@ -19,6 +19,7 @@ describe VulnerabilityFeedback::CreateService, '#execute' do ...@@ -19,6 +19,7 @@ describe VulnerabilityFeedback::CreateService, '#execute' do
project_fingerprint: '418291a26024a1445b23fe64de9380cdcdfd1fa8', project_fingerprint: '418291a26024a1445b23fe64de9380cdcdfd1fa8',
comment: 'a dismissal comment', comment: 'a dismissal comment',
vulnerability_data: { vulnerability_data: {
blob_path: '/path/to/blob',
category: 'sast', category: 'sast',
priority: 'Low', line: '41', priority: 'Low', line: '41',
file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java', file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
......
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