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 { ...@@ -87,7 +87,11 @@ export default {
feedback_type: 'issue', feedback_type: 'issue',
category: this.vulnerability.report_type, category: this.vulnerability.report_type,
project_fingerprint: this.projectFingerprint, 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 } }) => { .then(({ data: { issue_url } }) => {
......
...@@ -137,6 +137,7 @@ class Projects::VulnerabilityFeedbackController < Projects::ApplicationControlle ...@@ -137,6 +137,7 @@ class Projects::VulnerabilityFeedbackController < Projects::ApplicationControlle
tool tool
tools tools
url url
vulnerability_id
wascid wascid
] + [ ] + [
instances: %i[ instances: %i[
......
...@@ -65,6 +65,14 @@ module VulnerabilityFeedback ...@@ -65,6 +65,14 @@ module VulnerabilityFeedback
end end
issue = result[:issue] 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 vulnerability_feedback.issue = issue
# Ensure created association is rolled back if feedback can't be saved # Ensure created association is rolled back if feedback can't be saved
...@@ -95,5 +103,17 @@ module VulnerabilityFeedback ...@@ -95,5 +103,17 @@ module VulnerabilityFeedback
merge_request&.destroy && merge_request&.destroy &&
::Branches::DeleteService.new(project, current_user).execute(branch_name) ::Branches::DeleteService.new(project, current_user).execute(branch_name)
end 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
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 ...@@ -150,6 +150,30 @@ describe Projects::VulnerabilityFeedbackController do
expect(response).to match_response_schema('vulnerability_feedback', dir: 'ee') expect(response).to match_response_schema('vulnerability_feedback', dir: 'ee')
end 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 end
context 'with invalid params' do context 'with invalid params' do
......
...@@ -113,6 +113,7 @@ describe('Vulnerability management app', () => { ...@@ -113,6 +113,7 @@ describe('Vulnerability management app', () => {
vulnerability_data: { vulnerability_data: {
...defaultVulnerability, ...defaultVulnerability,
category: defaultVulnerability.report_type, category: defaultVulnerability.report_type,
vulnerability_id: defaultVulnerability.id,
}, },
}, },
}); });
......
...@@ -130,6 +130,124 @@ describe VulnerabilityFeedback::CreateService, '#execute' do ...@@ -130,6 +130,124 @@ describe VulnerabilityFeedback::CreateService, '#execute' do
expect(result[:status]).to eq(:success) expect(result[:status]).to eq(:success)
end 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 end
context 'when feedback_type is merge_request' do 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