Commit 9d36e51a authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '215669-service-for-auto-fix' into 'master'

Add Auto Fix service

See merge request gitlab-org/gitlab!48679
parents 0bff6c12 a3c15a14
# frozen_string_literal: true
module Security
class AutoFixService
def initialize(project, pipeline)
@project = project
@pipeline = pipeline
end
def execute
return ServiceResponse.error(message: 'Auto fix is disabled') unless project.security_setting.auto_fix_enabled?
vulnerabilities = pipeline.vulnerability_findings.by_report_types(auto_fix_enabled_types)
processed_vuln_ids = []
vulnerabilities.each do |vulnerability|
next if vulnerability.merge_request_feedback.try(:merge_request_id)
next unless vulnerability.remediations
result = VulnerabilityFeedback::CreateService.new(project, User.security_bot, service_params(vulnerability)).execute
processed_vuln_ids.push vulnerability.id if result[:status] == :success
end
if processed_vuln_ids.any?
ServiceResponse.success
else
ServiceResponse.error(message: 'Impossible to create Merge Requests')
end
end
private
attr_reader :project, :pipeline
def auto_fix_enabled_types
project.security_setting.auto_fix_enabled_types
end
def service_params(vulnerability)
{
feedback_type: :merge_request,
category: vulnerability.report_type,
project_fingerprint: vulnerability.project_fingerprint,
vulnerability_data: {
severity: vulnerability.severity,
confidence: vulnerability.confidence,
description: vulnerability.description,
solution: vulnerability.solution,
remediations: vulnerability.remediations,
category: vulnerability.report_type,
title: vulnerability.name,
name: vulnerability.name,
location: vulnerability.location,
links: vulnerability.links,
identifiers: vulnerability.identifiers.map(&:attributes)
}
}
end
end
end
...@@ -23,6 +23,25 @@ FactoryBot.define do ...@@ -23,6 +23,25 @@ FactoryBot.define do
finding.raw_metadata = raw_metadata.to_json finding.raw_metadata = raw_metadata.to_json
end end
end end
trait :yarn_remediation do
after(:build) do |finding, evaluator|
if evaluator.summary
raw_metadata = Gitlab::Json.parse(finding.raw_metadata)
raw_metadata['remediations'] = [
{
summary: evaluator.summary,
diff: Base64.encode64(
File.read(
File.join(
Rails.root.join('ee/spec/fixtures/security_reports/remediations'), "remediation.patch")
))
}
]
finding.raw_metadata = raw_metadata.to_json
end
end
end
end end
factory :vulnerabilities_finding, class: 'Vulnerabilities::Finding' do factory :vulnerabilities_finding, class: 'Vulnerabilities::Finding' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Security::AutoFixService do
describe '#execute' do
subject(:execute_service) { described_class.new(project, pipeline).execute }
let(:pipeline) { create(:ee_ci_pipeline, :success, project: project) }
let(:project) { create(:project, :custom_repo, files: { 'yarn.lock' => yarn_lock_content }) }
let(:remediations_folder) { Rails.root.join('ee/spec/fixtures/security_reports/remediations') }
let(:yarn_lock_content) do
File.read(
File.join(remediations_folder, "yarn.lock")
)
end
let(:remediation_diff) do
Base64.encode64(
File.read(
File.join(remediations_folder, "remediation.patch")
)
)
end
shared_examples 'disabled auto-fix error' do
it 'returns error' do
result = execute_service
expect(result).to be_error
expect(result.message).to eq('Auto fix is disabled')
end
end
before do
stub_licensed_features(vulnerability_auto_fix: true)
end
context 'when remediations' do
let!(:vulnerability) do
create(:vulnerabilities_finding_with_remediation, :yarn_remediation, :identifier,
project: project,
pipelines: [pipeline],
report_type: :dependency_scanning,
summary: 'Test remediation')
end
it 'creates MR' do
result = execute_service
identifier = vulnerability.identifiers.last
merge_request = MergeRequest.last!
expect(result).to be_success
expect(merge_request.title).to eq("Resolve vulnerability: Cipher with no integrity")
expect(merge_request.description).to include("[#{identifier.external_id}](#{identifier.url})")
end
context 'when merge request exists' do
let(:feedback) { create(:vulnerability_feedback, :merge_request) }
before do
allow_next_found_instance_of(Vulnerabilities::Finding) do |finding|
allow(finding).to receive(:merge_request_feedback).and_return(feedback)
end
end
it 'does not create second merge request' do
execute_service
expect(Vulnerabilities::Feedback.count).to eq(1)
end
end
end
context 'with disabled auto-fix' do
before do
project.security_setting.update!(auto_fix_dependency_scanning: false, auto_fix_container_scanning: false)
end
it_behaves_like 'disabled auto-fix error'
end
context 'with disabled licensed feature' do
before do
stub_licensed_features(vulnerability_auto_fix: false)
end
it_behaves_like 'disabled auto-fix error'
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(security_auto_fix: false)
end
it_behaves_like 'disabled auto-fix error'
end
context 'without remediations' do
before do
create(:vulnerabilities_finding, report_type: :dependency_scanning, pipelines: [pipeline], project: project)
end
it 'does not create merge request' do
result = execute_service
expect(result).to be_error
expect(result.message).to eq('Impossible to create Merge Requests')
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