Commit 168b0e8a authored by Philip Cunningham's avatar Philip Cunningham

Use dast_scanner_profiles in DAST on-demand scans

Extend GraphQL mutation and add service object support.
parent cf057764
......@@ -2863,6 +2863,11 @@ input DastOnDemandScanCreateInput {
"""
clientMutationId: String
"""
ID of the scanner profile to be used for the scan.
"""
dastScannerProfileId: DastScannerProfileID
"""
ID of the site profile to be used for the scan.
"""
......
......@@ -7761,6 +7761,16 @@
},
"defaultValue": null
},
{
"name": "dastScannerProfileId",
"description": "ID of the scanner profile to be used for the scan.",
"type": {
"kind": "SCALAR",
"name": "DastScannerProfileID",
"ofType": null
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
......@@ -21,16 +21,27 @@ module Mutations
required: true,
description: 'ID of the site profile to be used for the scan.'
argument :dast_scanner_profile_id, ::Types::GlobalIDType[::DastScannerProfile],
required: false,
description: 'ID of the scanner profile to be used for the scan.'
authorize :create_on_demand_dast_scan
def resolve(full_path:, dast_site_profile_id:)
def resolve(full_path:, dast_site_profile_id:, **args)
project = authorized_find_project!(full_path: full_path)
dast_site_profile = find_dast_site_profile(project: project, dast_site_profile_id: dast_site_profile_id)
dast_site = dast_site_profile.dast_site
dast_scanner_profile = find_dast_scanner_profile(project: project, dast_scanner_profile_id: args[:dast_scanner_profile_id])
service = ::Ci::RunDastScanService.new(project, current_user)
result = service.execute(branch: project.default_branch, target_url: dast_site.url)
result = ::Ci::RunDastScanService.new(
project, current_user
).execute(
branch: project.default_branch,
target_url: dast_site.url,
spider_timeout: dast_scanner_profile&.spider_timeout,
target_timeout: dast_scanner_profile&.target_timeout
)
if result.success?
success_response(project: project, pipeline: result.payload)
......@@ -41,11 +52,20 @@ module Mutations
private
# rubocop: disable CodeReuse/ActiveRecord
def find_dast_site_profile(project:, dast_site_profile_id:)
DastSiteProfilesFinder.new(project_id: project.id, id: dast_site_profile_id.model_id)
.execute
.first!
end
# rubocop: enable CodeReuse/ActiveRecord
def find_dast_scanner_profile(project:, dast_scanner_profile_id:)
return unless dast_scanner_profile_id
project
.dast_site_profiles
.with_dast_site
.find(dast_site_profile_id.model_id)
.dast_scanner_profiles
.find(dast_scanner_profile_id.model_id)
end
def success_response(project:, pipeline:)
......
......@@ -2,6 +2,12 @@
module Ci
class RunDastScanService < BaseService
ENV_MAPPING = {
spider_timeout: 'DAST_SPIDER_MINS',
target_timeout: 'DAST_TARGET_AVAILABILITY_TIMEOUT',
target_url: 'DAST_WEBSITE'
}.freeze
def self.ci_template_raw
@ci_template_raw ||= Gitlab::Template::GitlabCiYmlTemplate.find('DAST').content
end
......@@ -13,11 +19,11 @@ module Ci
end
end
def execute(branch:, target_url:)
def execute(branch:, **args)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
service = Ci::CreatePipelineService.new(project, current_user, ref: branch)
pipeline = service.execute(:ondemand_dast_scan, content: ci_yaml(target_url))
pipeline = service.execute(:ondemand_dast_scan, content: ci_yaml(args))
if pipeline.created_successfully?
ServiceResponse.success(payload: pipeline)
......@@ -32,9 +38,16 @@ module Ci
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def ci_yaml(target_url)
def ci_yaml(args)
variables = args.each_with_object({}) do |(key, val), hash|
next unless val && ENV_MAPPING[key]
hash[ENV_MAPPING[key]] = val
hash
end
self.class.ci_template.deep_merge(
'variables' => { 'DAST_WEBSITE' => target_url }
'variables' => variables
).to_yaml
end
end
......
---
title: Use dast_scanner_profiles in DAST on-demand scans
merge_request: 41060
author:
type: added
......@@ -102,6 +102,36 @@ RSpec.describe Mutations::DastOnDemandScans::Create do
end
end
context 'when dast_scanner_profile_id is provided' do
let(:dast_scanner_profile) { create(:dast_scanner_profile, project: project, target_timeout: 200, spider_timeout: 5000) }
let(:dast_scanner_profile_id) { dast_scanner_profile.to_global_id }
subject do
mutation.resolve(
full_path: full_path,
dast_site_profile_id: dast_site_profile_id,
dast_scanner_profile_id: dast_scanner_profile_id
)
end
it 'has no errors' do
group.add_owner(user)
expect(subject[:errors]).to be_empty
end
it 'passes additional arguments to the underlying service object' do
args = hash_including(
spider_timeout: dast_scanner_profile.spider_timeout,
target_timeout: dast_scanner_profile.target_timeout
)
expect_any_instance_of(::Ci::RunDastScanService).to receive(:execute).with(args).and_call_original
subject
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
......
......@@ -6,13 +6,16 @@ RSpec.describe 'Running a DAST Scan' do
include GraphqlHelpers
let(:dast_site_profile) { create(:dast_site_profile, project: project) }
let(:dast_site_profile_id) { dast_site_profile.to_global_id.to_s }
let(:dast_scanner_profile_id) { nil }
let(:mutation_name) { :dast_on_demand_scan_create }
let(:mutation) do
graphql_mutation(
mutation_name,
full_path: full_path,
dast_site_profile_id: dast_site_profile.to_global_id.to_s
dast_site_profile_id: dast_site_profile_id,
dast_scanner_profile_id: dast_scanner_profile_id
)
end
......@@ -28,6 +31,17 @@ RSpec.describe 'Running a DAST Scan' do
expect(mutation_response['pipelineUrl']).to eq(expected_url)
end
context 'when dast_scanner_profile_id is provided' do
let(:dast_scanner_profile) { create(:dast_scanner_profile, project: project, target_timeout: 200, spider_timeout: 5000) }
let(:dast_scanner_profile_id) { dast_scanner_profile.to_global_id.to_s }
it 'returns an empty errors array' do
subject
expect(mutation_response["errors"]).to be_empty
end
end
context 'when wrong type of global id is passed' do
let(:mutation) do
graphql_mutation(
......
......@@ -27,7 +27,7 @@ RSpec.describe Ci::RunDastScanService do
end
describe '#execute' do
subject { described_class.new(project, user).execute(branch: branch, target_url: target_url) }
subject { described_class.new(project, user).execute(branch: branch, target_url: target_url, spider_timeout: 42, target_timeout: 21) }
let(:status) { subject.status }
let(:pipeline) { subject.payload }
......@@ -116,6 +116,15 @@ RSpec.describe Ci::RunDastScanService do
'key' => 'DAST_WEBSITE',
'value' => target_url,
'public' => true
},
{
'key' => 'DAST_SPIDER_MINS',
'value' => '42',
'public' => true
}, {
'key' => 'DAST_TARGET_AVAILABILITY_TIMEOUT',
'value' => '21',
'public' => true
}, {
'key' => 'GIT_STRATEGY',
'value' => 'none',
......
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