Commit dbb914db authored by Philip Cunningham's avatar Philip Cunningham Committed by Heinrich Lee Yu

Refactor DAST on-demand CI config generation

parent 2651ee6a
......@@ -32,11 +32,17 @@ module Mutations
def resolve(project_path:, target_url:, branch:, scan_type:)
project = authorized_find!(project_path)
service = ::Ci::RunDastScanService.new(project, current_user)
result = service.execute(branch: branch, target_url: target_url)
result = ::DastOnDemandScans::CreateService.new(
container: project,
current_user: current_user,
params: {
branch: branch,
dast_site_profile: DastSiteProfile.new(dast_site: DastSite.new(url: target_url))
}
).execute
if result.success?
success_response(project: project, pipeline: result.payload)
success_response(project: project, pipeline: result.payload[:pipeline])
else
error_response(result.message)
end
......
# frozen_string_literal: true
module AppSec
module Dast
module ScanConfigs
class BuildService < BaseContainerService
include Gitlab::Utils::StrongMemoize
def execute
return ServiceResponse.error(message: 'Dast site profile was not provided') unless dast_site_profile.present?
return ServiceResponse.error(message: 'Cannot run active scan against unvalidated target') unless active_scan_allowed?
ServiceResponse.success(
payload: {
dast_profile: dast_profile,
dast_site_profile: dast_site_profile,
dast_scanner_profile: dast_scanner_profile,
branch: branch,
ci_configuration: ci_configuration
}
)
end
private
def active_scan_allowed?
return true unless dast_scanner_profile&.full_scan_enabled?
url_base = DastSiteValidation.get_normalized_url_base(dast_site&.url)
DastSiteValidationsFinder.new(
project_id: container.id,
state: :passed,
url_base: url_base
).execute.present?
end
def branch
strong_memoize(:branch) do
dast_profile&.branch_name || params[:branch] || container.default_branch
end
end
def ci_configuration
{
'stages' => ['dast'],
'include' => [{ 'template' => 'DAST-On-Demand-Scan.gitlab-ci.yml' }],
'variables' => ci_variables.to_hash.to_hash # Collection#to_hash returns HashWithIndifferentAccess which does not serialise correctly
}.to_yaml
end
def ci_variables
dast_site_profile.ci_variables.tap do |collection|
collection.concat(dast_scanner_profile.ci_variables) if dast_scanner_profile
end
end
def dast_profile
strong_memoize(:dast_profile) do
params[:dast_profile]
end
end
def dast_site_profile
strong_memoize(:dast_site_profile) do
dast_profile&.dast_site_profile || params[:dast_site_profile]
end
end
def dast_scanner_profile
strong_memoize(:dast_scanner_profile) do
dast_profile&.dast_scanner_profile || params[:dast_scanner_profile]
end
end
def dast_site
strong_memoize(:dast_site) do
dast_site_profile.dast_site
end
end
end
end
end
end
# frozen_string_literal: true
module Ci
module DastScanCiConfigurationService
ENV_MAPPING = {
spider_timeout: 'DAST_SPIDER_MINS',
target_timeout: 'DAST_TARGET_AVAILABILITY_TIMEOUT',
target_url: 'DAST_WEBSITE',
api_specification_url: 'DAST_API_SPECIFICATION',
api_host_override: 'DAST_API_HOST_OVERRIDE',
use_ajax_spider: 'DAST_USE_AJAX_SPIDER',
show_debug_messages: 'DAST_DEBUG',
full_scan_enabled: 'DAST_FULL_SCAN_ENABLED',
excluded_urls: 'DAST_EXCLUDE_URLS',
auth_url: 'DAST_AUTH_URL',
auth_username_field: 'DAST_USERNAME_FIELD',
auth_password_field: 'DAST_PASSWORD_FIELD',
auth_username: 'DAST_USERNAME'
}.freeze
def self.execute(args)
variables = args.slice(*ENV_MAPPING.keys).compact.to_h do |key, val|
[ENV_MAPPING[key], to_env_value(val)]
end
{
'stages' => ['dast'],
'include' => [{ 'template' => 'DAST-On-Demand-Scan.gitlab-ci.yml' }],
'variables' => variables
}.to_yaml
end
def self.bool?(value)
!!value == value
end
private_class_method :bool?
def self.to_env_value(value)
bool?(value) ? value.to_s : value
end
private_class_method :to_env_value
end
end
......@@ -2,12 +2,12 @@
module Ci
class RunDastScanService < BaseService
def execute(branch:, dast_profile: nil, dast_site_profile: nil, **args)
def execute(branch:, ci_configuration:, dast_profile: nil, dast_site_profile: nil, dast_scanner_profile: nil)
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(args)) do |pipeline|
pipeline = service.execute(:ondemand_dast_scan, content: ci_configuration) do |pipeline|
if dast_profile
pipeline.dast_profile = dast_profile
else
......@@ -28,9 +28,5 @@ module Ci
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def ci_yaml(args)
Ci::DastScanCiConfigurationService.execute(args)
end
end
end
......@@ -31,11 +31,11 @@ module DastOnDemandScans
end
def create_pipeline
params_result = DastOnDemandScans::ParamsCreateService.new(container: container, current_user: current_user, params: params).execute
config_result = AppSec::Dast::ScanConfigs::BuildService.new(container: container, current_user: current_user, params: params).execute
return params_result unless params_result.success?
return config_result unless config_result.success?
result = ::Ci::RunDastScanService.new(container, current_user).execute(**params_result.payload)
result = ::Ci::RunDastScanService.new(container, current_user).execute(**config_result.payload)
return success_response(result.payload) if result.success?
......
# frozen_string_literal: true
module DastOnDemandScans
class ParamsCreateService < BaseContainerService
include Gitlab::Utils::StrongMemoize
def execute
return ServiceResponse.error(message: 'Dast site profile was not provided') unless dast_site_profile.present?
return ServiceResponse.error(message: 'Cannot run active scan against unvalidated target') unless active_scan_allowed?
ServiceResponse.success(
payload: default_config.merge(target_config, site_profile_config, scanner_profile_config)
)
end
private
def active_scan_allowed?
return true unless dast_scanner_profile&.full_scan_enabled?
DastSiteValidationsFinder.new(
project_id: container.id,
state: :passed,
url_base: url_base
).execute.present?
end
def dast_profile
strong_memoize(:dast_profile) do
params[:dast_profile]
end
end
def dast_site_profile
strong_memoize(:dast_site_profile) do
dast_profile&.dast_site_profile || params[:dast_site_profile]
end
end
def dast_scanner_profile
strong_memoize(:dast_scanner_profile) do
dast_profile&.dast_scanner_profile || params[:dast_scanner_profile]
end
end
def dast_site
strong_memoize(:dast_site) do
dast_site_profile.dast_site
end
end
def branch
strong_memoize(:branch) do
dast_profile&.branch_name || params[:branch] || container.default_branch
end
end
def url_base
strong_memoize(:url_base) do
DastSiteValidation.get_normalized_url_base(dast_site&.url)
end
end
def default_config
{ dast_profile: dast_profile, dast_site_profile: dast_site_profile, branch: branch }
end
def target_config
url = dast_site.url
if dast_site_profile.target_type == 'website'
{ target_url: url }
else
{ api_specification_url: url, api_host_override: URI(url).host }
end
end
def site_profile_config
return {} unless dast_site_profile
excluded_urls = dast_site_profile.excluded_urls.presence&.join(',')
return { excluded_urls: excluded_urls } unless dast_site_profile.auth_enabled
{
excluded_urls: excluded_urls,
auth_username_field: dast_site_profile.auth_username_field,
auth_password_field: dast_site_profile.auth_password_field,
auth_username: dast_site_profile.auth_username,
auth_url: dast_site_profile.auth_url
}
end
def scanner_profile_config
return {} unless dast_scanner_profile
{
spider_timeout: dast_scanner_profile.spider_timeout,
target_timeout: dast_scanner_profile.target_timeout,
full_scan_enabled: dast_scanner_profile.full_scan_enabled?,
use_ajax_spider: dast_scanner_profile.use_ajax_spider,
show_debug_messages: dast_scanner_profile.show_debug_messages
}
end
end
end
......@@ -28,10 +28,10 @@ module Security
end
def prepare_on_demand_scan_configuration(action)
result = prepare_on_demand_scan_params(action[:site_profile], action[:scanner_profile])
result = prepare_base_configuration(action[:site_profile], action[:scanner_profile])
return error_script(result.message) unless result.success?
ci_configuration = YAML.safe_load(::Ci::DastScanCiConfigurationService.execute(result.payload))
ci_configuration = YAML.safe_load(result.payload[:ci_configuration])
dast_on_demand_template[:dast].deep_merge(
'variables' => dast_on_demand_template[:variables].deep_merge(ci_configuration['variables']),
......@@ -39,11 +39,11 @@ module Security
)
end
def prepare_on_demand_scan_params(site_profile_name, scanner_profile_name)
def prepare_base_configuration(site_profile_name, scanner_profile_name)
site_profile = DastSiteProfilesFinder.new(project_id: project.id, name: site_profile_name).execute.first
scanner_profile = DastScannerProfilesFinder.new(project_ids: [project.id], name: scanner_profile_name).execute.first if scanner_profile_name.present?
DastOnDemandScans::ParamsCreateService
AppSec::Dast::ScanConfigs::BuildService
.new(container: project, params: { dast_site_profile: site_profile, dast_scanner_profile: scanner_profile })
.execute
end
......
......@@ -78,11 +78,11 @@ RSpec.describe Mutations::DastOnDemandScans::Create do
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,
use_ajax_spider: dast_scanner_profile.use_ajax_spider,
show_debug_messages: dast_scanner_profile.show_debug_messages,
full_scan_enabled: dast_scanner_profile.full_scan_enabled?
branch: project.default_branch,
dast_profile: nil,
dast_site_profile: dast_site_profile,
dast_scanner_profile: dast_scanner_profile,
ci_configuration: kind_of(String)
)
expect_any_instance_of(::Ci::RunDastScanService).to receive(:execute).with(args).and_call_original
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) }
let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile, project: project, spider_timeout: 5, target_timeout: 20) }
let_it_be(:dast_profile) { create(:dast_profile, project: project, dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile, branch_name: 'master') }
let(:dast_website) { dast_site_profile.dast_site.url }
let(:dast_exclude_urls) { dast_site_profile.excluded_urls.join(',') }
let(:dast_auth_url) { dast_site_profile.auth_url }
let(:dast_username) { dast_site_profile.auth_username }
let(:dast_username_field) { dast_site_profile.auth_username_field }
let(:dast_password_field) { dast_site_profile.auth_password_field }
let(:dast_spider_mins) { dast_scanner_profile.spider_timeout }
let(:dast_target_availability_timeout) { dast_scanner_profile.target_timeout }
let(:dast_full_scan_enabled) { dast_scanner_profile.full_scan_enabled? }
let(:dast_use_ajax_spider) { dast_scanner_profile.use_ajax_spider? }
let(:dast_debug) { dast_scanner_profile.show_debug_messages? }
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile } }
let(:expected_yaml_configuration) do
<<~YAML
---
stages:
- dast
include:
- template: DAST-On-Demand-Scan.gitlab-ci.yml
variables:
DAST_WEBSITE: #{dast_website}
DAST_EXCLUDE_URLS: #{dast_exclude_urls}
DAST_AUTH_URL: #{dast_auth_url}
DAST_USERNAME: #{dast_username}
DAST_USERNAME_FIELD: #{dast_username_field}
DAST_PASSWORD_FIELD: #{dast_password_field}
DAST_SPIDER_MINS: '#{dast_spider_mins}'
DAST_TARGET_AVAILABILITY_TIMEOUT: '#{dast_target_availability_timeout}'
DAST_FULL_SCAN_ENABLED: '#{dast_full_scan_enabled}'
DAST_USE_AJAX_SPIDER: '#{dast_use_ajax_spider}'
DAST_DEBUG: '#{dast_debug}'
YAML
end
subject { described_class.new(container: project, params: params).execute }
describe 'execute' do
context 'when a dast_profile is provided' do
let(:params) { { dast_profile: dast_profile } }
it 'returns a dast_profile, dast_site_profile, dast_scanner_profile, branch and YAML configuration' do
expected_payload = {
dast_profile: dast_profile,
dast_site_profile: dast_site_profile,
dast_scanner_profile: dast_scanner_profile,
branch: dast_profile.branch_name,
ci_configuration: expected_yaml_configuration
}
expect(subject.payload).to eq(expected_payload)
end
end
context 'when a dast_site_profile is provided' do
context 'when a dast_scanner_profile is provided' do
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile } }
it 'returns a dast_site_profile, dast_scanner_profile, branch and YAML configuration' do
expected_payload = {
dast_profile: nil,
dast_site_profile: dast_site_profile,
dast_scanner_profile: dast_scanner_profile,
branch: project.default_branch,
ci_configuration: expected_yaml_configuration
}
expect(subject.payload).to eq(expected_payload)
end
context 'when the target is not validated and an active scan is requested' do
let_it_be(:active_dast_scanner_profile) { create(:dast_scanner_profile, project: project, scan_type: 'active') }
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: active_dast_scanner_profile } }
it 'responds with an error message', :aggregate_failures do
expect(subject).not_to be_success
expect(subject.message).to eq('Cannot run active scan against unvalidated target')
end
end
end
context 'when a dast_scanner_profile is not provided' do
let(:params) { { dast_site_profile: dast_site_profile } }
let(:expected_yaml_configuration) do
<<~YAML
---
stages:
- dast
include:
- template: DAST-On-Demand-Scan.gitlab-ci.yml
variables:
DAST_WEBSITE: #{dast_website}
DAST_EXCLUDE_URLS: #{dast_exclude_urls}
DAST_AUTH_URL: #{dast_auth_url}
DAST_USERNAME: #{dast_username}
DAST_USERNAME_FIELD: #{dast_username_field}
DAST_PASSWORD_FIELD: #{dast_password_field}
YAML
end
it 'returns a dast_site_profile, branch and YAML configuration' do
expected_payload = {
dast_profile: nil,
dast_site_profile: dast_site_profile,
dast_scanner_profile: nil,
branch: project.default_branch,
ci_configuration: expected_yaml_configuration
}
expect(subject.payload).to eq(expected_payload)
end
end
end
context 'when a dast_site_profile is not provided' do
let(:params) { { dast_site_profile: nil, dast_scanner_profile: dast_scanner_profile } }
it 'responds with an error message', :aggregate_failures do
expect(subject).not_to be_success
expect(subject.message).to eq('Dast site profile was not provided')
end
end
context 'when a branch is provided' do
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile, branch: 'hello-world' } }
it 'returns the branch in the payload' do
expect(subject.payload[:branch]).to match('hello-world')
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::DastScanCiConfigurationService do
describe '.execute' do
subject(:yaml_configuration) { described_class.execute(params) }
context 'when all variables are provided' do
let(:params) do
{
spider_timeout: 1000,
target_timeout: 100,
target_url: 'https://gitlab.local',
api_specification_url: 'https://gitlab.local/api.json',
api_host_override: 'gitlab.local',
use_ajax_spider: true,
show_debug_messages: true,
full_scan_enabled: true,
excluded_urls: 'https://gitlab.local/hello,https://gitlab.local/world',
auth_url: 'https://gitlab.local/login',
auth_username_field: 'session[username]',
auth_password_field: 'session[password]',
auth_username: 'tanuki'
}
end
let(:expected_yaml_configuration) do
<<~YAML
---
stages:
- dast
include:
- template: DAST-On-Demand-Scan.gitlab-ci.yml
variables:
DAST_SPIDER_MINS: 1000
DAST_TARGET_AVAILABILITY_TIMEOUT: 100
DAST_WEBSITE: https://gitlab.local
DAST_API_SPECIFICATION: https://gitlab.local/api.json
DAST_API_HOST_OVERRIDE: gitlab.local
DAST_USE_AJAX_SPIDER: 'true'
DAST_DEBUG: 'true'
DAST_FULL_SCAN_ENABLED: 'true'
DAST_EXCLUDE_URLS: https://gitlab.local/hello,https://gitlab.local/world
DAST_AUTH_URL: https://gitlab.local/login
DAST_USERNAME_FIELD: session[username]
DAST_PASSWORD_FIELD: session[password]
DAST_USERNAME: tanuki
YAML
end
it 'returns the YAML configuration of the On-Demand DAST scan' do
expect(yaml_configuration).to eq(expected_yaml_configuration)
end
end
context 'when unknown variables are provided' do
let(:params) do
{
target_url: 'https://gitlab.local',
use_ajax_spider: false,
show_debug_messages: nil,
full_scan_enabled: nil,
additional_argument: true,
additional_list: ['item a']
}
end
let(:expected_yaml_configuration) do
<<~YAML
---
stages:
- dast
include:
- template: DAST-On-Demand-Scan.gitlab-ci.yml
variables:
DAST_WEBSITE: https://gitlab.local
DAST_USE_AJAX_SPIDER: 'false'
YAML
end
it 'returns the YAML configuration of the On-Demand DAST scan' do
expect(yaml_configuration).to eq(expected_yaml_configuration)
end
end
context 'when a variable is set to nil' do
let(:params) do
{
target_url: 'https://gitlab.local',
api_specification_url: nil
}
end
let(:expected_yaml_configuration) do
<<~YAML
---
stages:
- dast
include:
- template: DAST-On-Demand-Scan.gitlab-ci.yml
variables:
DAST_WEBSITE: https://gitlab.local
YAML
end
it 'returns the YAML configuration of the On-Demand DAST scan' do
expect(yaml_configuration).to eq(expected_yaml_configuration)
end
end
context 'when no variables are provided' do
let(:params) { {} }
let(:expected_yaml_configuration) do
<<~YAML
---
stages:
- dast
include:
- template: DAST-On-Demand-Scan.gitlab-ci.yml
variables: {}
YAML
end
it 'returns the YAML configuration of the On-Demand DAST scan' do
expect(yaml_configuration).to eq(expected_yaml_configuration)
end
end
end
end
......@@ -5,17 +5,9 @@ require 'spec_helper'
RSpec.describe Ci::RunDastScanService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, creator: user) }
let_it_be(:dast_profile) { create(:dast_profile) }
let_it_be(:dast_site_profile) { dast_profile.dast_site_profile }
let_it_be(:branch) { project.default_branch }
let_it_be(:spider_timeout) { 42 }
let_it_be(:target_timeout) { 21 }
let_it_be(:target_url) { generate(:url) }
let_it_be(:use_ajax_spider) { true }
let_it_be(:show_debug_messages) { false }
let_it_be(:full_scan_enabled) { true }
let_it_be(:excluded_urls) { "#{target_url}/hello,#{target_url}/world" }
let_it_be(:auth_url) { "#{target_url}/login" }
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) }
let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile, project: project, spider_timeout: 42, target_timeout: 21) }
let_it_be(:dast_profile) { create(:dast_profile, project: project, dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile) }
before do
stub_licensed_features(security_on_demand_scans: true)
......@@ -23,22 +15,17 @@ RSpec.describe Ci::RunDastScanService do
describe '#execute' do
subject do
described_class.new(project, user).execute(
branch: branch,
target_url: target_url,
spider_timeout: spider_timeout,
target_timeout: target_timeout,
use_ajax_spider: use_ajax_spider,
show_debug_messages: show_debug_messages,
full_scan_enabled: full_scan_enabled,
excluded_urls: excluded_urls,
auth_url: auth_url,
auth_username_field: 'session[username]',
auth_password_field: 'session[password]',
auth_username: 'tanuki',
dast_profile: dast_profile,
dast_site_profile: dast_site_profile
)
config_result = AppSec::Dast::ScanConfigs::BuildService.new(
container: project,
current_user: user,
params: {
branch: project.default_branch,
dast_profile: dast_profile,
dast_site_profile: dast_site_profile
}
).execute
described_class.new(project, user).execute(**config_result.payload)
end
let(:status) { subject.status }
......@@ -73,7 +60,7 @@ RSpec.describe Ci::RunDastScanService do
end
it 'sets the pipeline ref to the branch' do
expect(pipeline.ref).to eq(branch)
expect(pipeline.ref).to eq(project.default_branch)
end
it 'sets the source to indicate an ondemand scan' do
......@@ -117,43 +104,43 @@ RSpec.describe Ci::RunDastScanService do
expected_variables = [
{
'key' => 'DAST_AUTH_URL',
'value' => auth_url,
'value' => dast_site_profile.auth_url,
'public' => true
}, {
'key' => 'DAST_DEBUG',
'value' => 'false',
'value' => String(dast_scanner_profile.show_debug_messages?),
'public' => true
}, {
'key' => 'DAST_EXCLUDE_URLS',
'value' => excluded_urls,
'value' => dast_site_profile.excluded_urls.join(','),
'public' => true
}, {
'key' => 'DAST_FULL_SCAN_ENABLED',
'value' => 'true',
'value' => String(dast_scanner_profile.full_scan_enabled?),
'public' => true
}, {
'key' => 'DAST_PASSWORD_FIELD',
'value' => 'session[password]',
'value' => dast_site_profile.auth_password_field,
'public' => true
}, {
'key' => 'DAST_SPIDER_MINS',
'value' => spider_timeout.to_s,
'value' => String(dast_scanner_profile.spider_timeout),
'public' => true
}, {
'key' => 'DAST_TARGET_AVAILABILITY_TIMEOUT',
'value' => target_timeout.to_s,
'value' => String(dast_scanner_profile.target_timeout),
'public' => true
}, {
'key' => 'DAST_USERNAME',
'value' => 'tanuki',
'value' => dast_site_profile.auth_username,
'public' => true
}, {
'key' => 'DAST_USERNAME_FIELD',
'value' => 'session[username]',
'value' => dast_site_profile.auth_username_field,
'public' => true
}, {
'key' => 'DAST_USE_AJAX_SPIDER',
'value' => 'true',
'value' => String(dast_scanner_profile.use_ajax_spider?),
'public' => true
}, {
'key' => 'DAST_VERSION',
......@@ -161,7 +148,7 @@ RSpec.describe Ci::RunDastScanService do
'public' => true
}, {
'key' => 'DAST_WEBSITE',
'value' => target_url,
'value' => dast_site_profile.dast_site.url,
'public' => true
}, {
'key' => 'GIT_STRATEGY',
......@@ -177,14 +164,6 @@ RSpec.describe Ci::RunDastScanService do
expect(build.yaml_variables).to contain_exactly(*expected_variables)
end
shared_examples 'transactional creation' do
let_it_be(:type_mismatch) { build(:dast_scanner_profile) }
it 'does not create a Ci::Pipeline' do
expect { subject }.to raise_error(ActiveRecord::AssociationTypeMismatch).and change { Ci::Pipeline.count }.by(0)
end
end
context 'when the dast_profile and dast_site_profile are provided' do
it 'associates the dast_profile with the pipeline' do
expect(pipeline.dast_profile).to eq(dast_profile)
......@@ -193,10 +172,6 @@ RSpec.describe Ci::RunDastScanService do
it 'does associate the dast_site_profile with the pipeline' do
expect(pipeline.dast_site_profile).to be_nil
end
it_behaves_like 'transactional creation' do
let_it_be(:dast_profile) { type_mismatch }
end
end
context 'when the dast_site_profile is provided' do
......@@ -205,10 +180,6 @@ RSpec.describe Ci::RunDastScanService do
it 'associates the dast_site_profile with the pipeline' do
expect(pipeline.dast_site_profile).to eq(dast_site_profile)
end
it_behaves_like 'transactional creation' do
let_it_be(:dast_site_profile) { type_mismatch }
end
end
context 'when the pipeline fails to save' do
......
......@@ -63,22 +63,12 @@ RSpec.describe DastOnDemandScans::CreateService do
it_behaves_like 'a service that calls Ci::RunDastScanService' do
let(:expected_params) do
{
auth_password_field: dast_site_profile.auth_password_field,
auth_url: dast_site_profile.auth_url,
auth_username: dast_site_profile.auth_username,
auth_username_field: dast_site_profile.auth_username_field,
hash_including(
branch: project.default_branch,
dast_profile: nil,
dast_site_profile: dast_site_profile,
excluded_urls: dast_site_profile.excluded_urls.join(','),
full_scan_enabled: false,
show_debug_messages: false,
spider_timeout: nil,
target_timeout: nil,
target_url: dast_site_profile.dast_site.url,
use_ajax_spider: false
}
dast_scanner_profile: dast_scanner_profile,
ci_configuration: kind_of(String)
)
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DastOnDemandScans::ParamsCreateService do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) }
let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile, project: project) }
let(:excluded_urls) { dast_site_profile.excluded_urls.join(',') }
let(:target_url) { dast_site_profile.dast_site.url }
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile } }
subject { described_class.new(container: project, params: params).execute }
describe 'execute' do
context 'when the dast_site_profile is not provided' do
let(:params) { { dast_site_profile: nil, dast_scanner_profile: dast_scanner_profile } }
it 'responds with error message', :aggregate_failures do
expect(subject).not_to be_success
expect(subject.message).to eq('Dast site profile was not provided')
end
end
context 'when the dast_site_profile is provided' do
context 'when the branch is provided' do
let(:params) { { dast_site_profile: dast_site_profile, branch: 'other-branch' } }
context 'when the branch exists' do
it 'includes the branch in the prepared params' do
project.repository.create_branch(params[:branch])
expect(subject.payload[:branch]).to eq(params[:branch])
end
end
end
context 'when the dast_scanner_profile is not provided' do
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: nil } }
it 'returns prepared scanner params in the payload' do
expect(subject.payload).to eq(
auth_password_field: dast_site_profile.auth_password_field,
auth_username: dast_site_profile.auth_username,
auth_username_field: dast_site_profile.auth_username_field,
auth_url: dast_site_profile.auth_url,
branch: project.default_branch,
dast_profile: nil,
dast_site_profile: dast_site_profile,
excluded_urls: excluded_urls,
target_url: target_url
)
end
end
context 'when the dast_scanner_profile is provided' do
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile } }
it 'returns prepared scanner params in the payload' do
expect(subject.payload).to eq(
auth_password_field: dast_site_profile.auth_password_field,
auth_username: dast_site_profile.auth_username,
auth_username_field: dast_site_profile.auth_username_field,
auth_url: dast_site_profile.auth_url,
branch: project.default_branch,
dast_profile: nil,
dast_site_profile: dast_site_profile,
excluded_urls: excluded_urls,
full_scan_enabled: false,
show_debug_messages: false,
spider_timeout: nil,
target_timeout: nil,
target_url: target_url,
use_ajax_spider: false
)
end
context 'when dast_site_profile.excluded_urls is empty' do
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project, excluded_urls: []) }
it 'returns nil' do
expect(subject.payload[:excluded_urls]).to be_nil
end
end
context 'when the target is not validated and an active scan is requested' do
let_it_be(:active_dast_scanner_profile) { create(:dast_scanner_profile, project: project, scan_type: 'active') }
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: active_dast_scanner_profile } }
it 'responds with error message', :aggregate_failures do
expect(subject).not_to be_success
expect(subject.message).to eq('Cannot run active scan against unvalidated target')
end
end
end
context 'when authentication is not enabled' do
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project, auth_enabled: false) }
it 'returns prepared scanner params excluding auth params in the payload' do
expect(subject.payload).to eq(
branch: project.default_branch,
dast_profile: nil,
dast_site_profile: dast_site_profile,
excluded_urls: excluded_urls,
full_scan_enabled: false,
show_debug_messages: false,
spider_timeout: nil,
target_timeout: nil,
target_url: target_url,
use_ajax_spider: false
)
end
end
context 'when target_type=api' do
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project, target_type: :api) }
it 'returns params including the api_specification_url and omitting the target_url' do
expected_payload = hash_including(
api_specification_url: target_url,
api_host_override: URI(target_url).host
)
expect(subject.payload).to match(expected_payload).and exclude(:target_url)
end
end
context 'when the dast_profile is provided' do
let_it_be(:dast_profile) { create(:dast_profile, project: project, dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile, branch_name: project.default_branch) }
let(:params) { { dast_profile: dast_profile } }
it 'returns prepared scanner params in the payload' do
expect(subject.payload).to eq(
auth_password_field: dast_site_profile.auth_password_field,
auth_username: dast_site_profile.auth_username,
auth_username_field: dast_site_profile.auth_username_field,
branch: dast_profile.branch_name,
auth_url: dast_site_profile.auth_url,
dast_profile: dast_profile,
dast_site_profile: dast_profile.dast_site_profile,
excluded_urls: excluded_urls,
full_scan_enabled: false,
show_debug_messages: false,
spider_timeout: nil,
target_timeout: nil,
target_url: target_url,
use_ajax_spider: false
)
end
end
end
context 'when the branch is provided' do
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile, branch: 'hello-world' } }
it 'returns the branch in the payload' do
expect(subject.payload[:branch]).to match('hello-world')
end
end
end
end
......@@ -42,31 +42,8 @@ RSpec.describe Security::SecurityOrchestrationPolicies::OnDemandScanPipelineConf
end
it 'delegates params creation to DastOnDemandScans::ParamsCreateService' do
expect(DastOnDemandScans::ParamsCreateService).to receive(:new).with(container: project, params: { dast_site_profile: site_profile, dast_scanner_profile: scanner_profile }).and_call_original
expect(DastOnDemandScans::ParamsCreateService).to receive(:new).with(container: project, params: { dast_site_profile: nil, dast_scanner_profile: nil }).and_call_original
pipeline_configuration
end
it 'delegates variables preparation to ::Ci::DastScanCiConfigurationService' do
expected_params = {
auth_password_field: site_profile.auth_password_field,
auth_url: site_profile.auth_url,
auth_username: site_profile.auth_username,
auth_username_field: site_profile.auth_username_field,
dast_profile: nil,
dast_site_profile: site_profile,
branch: project.default_branch,
excluded_urls: site_profile.excluded_urls.join(','),
full_scan_enabled: false,
show_debug_messages: false,
spider_timeout: nil,
target_timeout: nil,
target_url: site_profile.dast_site.url,
use_ajax_spider: false
}
expect(::Ci::DastScanCiConfigurationService).to receive(:execute).with(expected_params).and_call_original
expect(AppSec::Dast::ScanConfigs::BuildService).to receive(:new).with(container: project, params: { dast_site_profile: site_profile, dast_scanner_profile: scanner_profile }).and_call_original
expect(AppSec::Dast::ScanConfigs::BuildService).to receive(:new).with(container: project, params: { dast_site_profile: nil, dast_scanner_profile: nil }).and_call_original
pipeline_configuration
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