Commit 5b5e2cc2 authored by Alan (Maciej) Paruszewski's avatar Alan (Maciej) Paruszewski Committed by Robert Speicher

Create vulnerability issue link when creating issue from vulnerability

This adds to First Class Vulnerabilities automatic creation of issue
link when Issue is created from Vulnerability.
parent e872ea0c
......@@ -87,7 +87,11 @@ export default {
feedback_type: 'issue',
category: this.vulnerability.report_type,
project_fingerprint: this.projectFingerprint,
vulnerability_data: { ...this.vulnerability, category: this.vulnerability.report_type },
vulnerability_data: {
...this.vulnerability,
category: this.vulnerability.report_type,
vulnerability_id: this.vulnerability.id,
},
},
})
.then(({ data: { issue_url } }) => {
......
......@@ -137,6 +137,7 @@ class Projects::VulnerabilityFeedbackController < Projects::ApplicationControlle
tool
tools
url
vulnerability_id
wascid
] + [
instances: %i[
......
......@@ -65,6 +65,14 @@ module VulnerabilityFeedback
end
issue = result[:issue]
issue_link_result = create_vulnerability_issue_link(vulnerability_feedback.vulnerability_data[:vulnerability_id], issue)
if issue_link_result&.error?
vulnerability_feedback.errors[:issue_link] << issue_link_result.message
raise ActiveRecord::Rollback
end
vulnerability_feedback.issue = issue
# Ensure created association is rolled back if feedback can't be saved
......@@ -95,5 +103,17 @@ module VulnerabilityFeedback
merge_request&.destroy &&
::Branches::DeleteService.new(project, current_user).execute(branch_name)
end
def create_vulnerability_issue_link(vulnerability_id, issue)
return unless vulnerability_id
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :read_vulnerability, project)
vulnerability = project.vulnerabilities.find_by_id(vulnerability_id)
VulnerabilityIssueLinks::CreateService
.new(current_user, vulnerability, issue, link_type: Vulnerabilities::IssueLink.link_types[:created])
.execute
end
end
end
---
title: Create issue link when creating issue from vulnerability
merge_request: 27899
author:
type: added
......@@ -150,6 +150,30 @@ describe Projects::VulnerabilityFeedbackController do
expect(response).to match_response_schema('vulnerability_feedback', dir: 'ee')
end
context 'when id of finding is not provided' do
subject { create_feedback user: user, project: project, params: create_params.deep_merge(feedback_type: 'issue', vulnerability_data: { vulnerability_id: nil }) }
it 'creates no vulnerability issue link for related vulnerability' do
expect { subject }.not_to change { Vulnerabilities::IssueLink.count }
end
end
context 'when security dashboard feature enabled' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'when id of finding is provided' do
let!(:vulnerability) { create(:vulnerability, :with_findings, project: project) }
subject { create_feedback user: user, project: project, params: create_params.deep_merge(feedback_type: 'issue', vulnerability_data: { vulnerability_id: vulnerability.id }) }
it 'creates vulnerability issue link for related vulnerability' do
expect { subject }.to change { Vulnerabilities::IssueLink.count }.by(1)
end
end
end
end
context 'with invalid params' do
......
......@@ -113,6 +113,7 @@ describe('Vulnerability management app', () => {
vulnerability_data: {
...defaultVulnerability,
category: defaultVulnerability.report_type,
vulnerability_id: defaultVulnerability.id,
},
},
});
......
......@@ -130,6 +130,124 @@ describe VulnerabilityFeedback::CreateService, '#execute' do
expect(result[:status]).to eq(:success)
end
context 'id of vulnerability is provided in vulnerability_data params' do
before do
stub_licensed_features(security_dashboard: true)
end
let(:feedback_params_with_vulnerability_id) do
feedback_params.deep_merge(
feedback_type: 'issue',
vulnerability_data: { vulnerability_id: vulnerability_id }
)
end
subject(:result) { described_class.new(project, user, feedback_params_with_vulnerability_id).execute }
context 'when id is missing' do
let(:vulnerability_id) { nil }
it 'does not create new Vulnerabilities::IssueLink' do
expect { subject }.not_to change { Vulnerabilities::IssueLink.count }
end
it 'creates the feedback' do
expect(result[:status]).to eq(:success)
expect(result[:vulnerability_feedback]).to be_persisted
end
end
context 'when id is invalid' do
let(:vulnerability_id) { 9999 }
it 'raises Gitlab::Access::AccessDeniedError' do
expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
context 'when id belongs to other project' do
let(:vulnerability) { create(:vulnerability, :with_findings) }
let(:vulnerability_id) { vulnerability.id }
it 'raises Gitlab::Access::AccessDeniedError' do
expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
context 'when id is valid' do
let(:vulnerability) { create(:vulnerability, :with_findings, project: project) }
let(:vulnerability_id) { vulnerability.id }
let(:last_issue_link) { Vulnerabilities::IssueLink.last }
let(:last_vulnerability_feedback) { Vulnerabilities::Feedback.last }
it 'delegates issue link creation to VulnerabilityIssueLinks::CreateService' do
expect(VulnerabilityIssueLinks::CreateService).to(receive(:new)
.with(user, vulnerability, anything, link_type: Vulnerabilities::IssueLink.link_types[:created])
.once
.and_call_original)
expect(result[:status]).to eq(:success)
end
it 'delegates work to VulnerabilityIssueLinks::CreateService' do
expect_next_instance_of(VulnerabilityIssueLinks::CreateService) do |instance|
expect(instance).to receive(:execute).with(no_args).once.and_call_original
end
expect(result[:status]).to eq(:success)
end
it 'issue link has correctly set vulnerability and link type' do
subject
expect(last_issue_link).to be_created
expect(last_issue_link.vulnerability).to eq(vulnerability)
expect(last_issue_link.issue).to eq(last_vulnerability_feedback.issue)
end
it 'creates the feedback' do
expect(result[:status]).to eq(:success)
expect(result[:vulnerability_feedback]).to be_persisted
end
context 'when issue link is already created' do
context 'when feedback does not exist' do
let!(:issue_link) { create(:vulnerabilities_issue_link, :created, vulnerability: vulnerability) }
it 'does not create new issue link' do
expect { subject }.not_to change { Vulnerabilities::IssueLink.count }
end
it 'does not create new issue' do
expect { subject }.not_to change { Issue.count }
end
it 'does not create a feedback' do
expect(result[:status]).to eq(:error)
end
end
context 'when feedback already exists' do
let!(:vulnerability_feedback) { create(:vulnerability_feedback, :issue, :comment, project: project, author: user, **feedback_params_with_vulnerability_id) }
let!(:issue_link) { create(:vulnerabilities_issue_link, :created, vulnerability: vulnerability, issue: vulnerability_feedback.issue) }
it 'does not create new issue link' do
expect { subject }.not_to change { Vulnerabilities::IssueLink.count }
end
it 'does not create new issue' do
expect { subject }.not_to change { Issue.count }
end
it 'returns the feedback' do
expect(result[:status]).to eq(:success)
expect(result[:vulnerability_feedback]).to be_persisted
end
end
end
end
end
end
context 'when feedback_type is merge_request' 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