Commit 1785ee84 authored by Markus Koller's avatar Markus Koller

Merge branch '217872-audit-dast-events' into 'master'

Move DAST profile services into AppSec module

See merge request gitlab-org/gitlab!59896
parents 331bf2fa 18c02f23
...@@ -60,7 +60,7 @@ module Mutations ...@@ -60,7 +60,7 @@ module Mutations
dast_site_profile = project.dast_site_profiles.find(site_profile_id.model_id) dast_site_profile = project.dast_site_profiles.find(site_profile_id.model_id)
dast_scanner_profile = project.dast_scanner_profiles.find(scanner_profile_id.model_id) dast_scanner_profile = project.dast_scanner_profiles.find(scanner_profile_id.model_id)
response = ::Dast::Profiles::CreateService.new( response = ::AppSec::Dast::Profiles::CreateService.new(
container: project, container: project,
current_user: current_user, current_user: current_user,
params: { params: {
......
...@@ -17,7 +17,7 @@ module Mutations ...@@ -17,7 +17,7 @@ module Mutations
def resolve(id:) def resolve(id:)
dast_profile = authorized_find!(id) dast_profile = authorized_find!(id)
response = ::Dast::Profiles::DestroyService.new( response = ::AppSec::Dast::Profiles::DestroyService.new(
container: dast_profile.project, container: dast_profile.project,
current_user: current_user, current_user: current_user,
params: { dast_profile: dast_profile } params: { dast_profile: dast_profile }
......
...@@ -75,7 +75,7 @@ module Mutations ...@@ -75,7 +75,7 @@ module Mutations
run_after_update: run_after_update run_after_update: run_after_update
}.compact }.compact
response = ::Dast::Profiles::UpdateService.new( response = ::AppSec::Dast::Profiles::UpdateService.new(
container: project, container: project,
current_user: current_user, current_user: current_user,
params: params params: params
......
...@@ -55,7 +55,7 @@ module Mutations ...@@ -55,7 +55,7 @@ module Mutations
def resolve(full_path:, profile_name:, spider_timeout: nil, target_timeout: nil, scan_type:, use_ajax_spider:, show_debug_messages:) def resolve(full_path:, profile_name:, spider_timeout: nil, target_timeout: nil, scan_type:, use_ajax_spider:, show_debug_messages:)
project = authorized_find!(full_path) project = authorized_find!(full_path)
service = ::DastScannerProfiles::CreateService.new(project, current_user) service = ::AppSec::Dast::ScannerProfiles::CreateService.new(project, current_user)
result = service.execute( result = service.execute(
name: profile_name, name: profile_name,
spider_timeout: spider_timeout, spider_timeout: spider_timeout,
......
...@@ -26,7 +26,7 @@ module Mutations ...@@ -26,7 +26,7 @@ module Mutations
project = authorized_find!(full_path) project = authorized_find!(full_path)
service = ::DastScannerProfiles::DestroyService.new(project, current_user) service = ::AppSec::Dast::ScannerProfiles::DestroyService.new(project, current_user)
result = service.execute(id: id.model_id) result = service.execute(id: id.model_id)
if result.success? if result.success?
......
...@@ -55,7 +55,7 @@ module Mutations ...@@ -55,7 +55,7 @@ module Mutations
project = authorized_find!(full_path) project = authorized_find!(full_path)
service = ::DastScannerProfiles::UpdateService.new(project, current_user) service = ::AppSec::Dast::ScannerProfiles::UpdateService.new(project, current_user)
result = service.execute(**service_args, id: gid.model_id) result = service.execute(**service_args, id: gid.model_id)
if result.success? if result.success?
......
...@@ -66,7 +66,7 @@ module Mutations ...@@ -66,7 +66,7 @@ module Mutations
auth_password: auth_params[:password] auth_password: auth_params[:password]
}.compact }.compact
result = ::DastSiteProfiles::CreateService.new(project, current_user).execute(**dast_site_profile_params) result = ::AppSec::Dast::SiteProfiles::CreateService.new(project, current_user).execute(**dast_site_profile_params)
{ id: result.payload.try(:to_global_id), errors: result.errors } { id: result.payload.try(:to_global_id), errors: result.errors }
end end
......
...@@ -21,7 +21,7 @@ module Mutations ...@@ -21,7 +21,7 @@ module Mutations
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = ::Types::GlobalIDType[::DastSiteProfile].coerce_isolated_input(id) id = ::Types::GlobalIDType[::DastSiteProfile].coerce_isolated_input(id)
service = ::DastSiteProfiles::DestroyService.new(project, current_user) service = ::AppSec::Dast::SiteProfiles::DestroyService.new(project, current_user)
result = service.execute(id: id.model_id) result = service.execute(id: id.model_id)
return { errors: result.errors } unless result.success? return { errors: result.errors } unless result.success?
......
...@@ -74,7 +74,7 @@ module Mutations ...@@ -74,7 +74,7 @@ module Mutations
auth_password: auth_params[:password] auth_password: auth_params[:password]
}.compact }.compact
result = ::DastSiteProfiles::UpdateService.new(project, current_user).execute(**dast_site_profile_params) result = ::AppSec::Dast::SiteProfiles::UpdateService.new(project, current_user).execute(**dast_site_profile_params)
{ id: result.payload.try(:to_global_id), errors: result.errors } { id: result.payload.try(:to_global_id), errors: result.errors }
end end
......
# frozen_string_literal: true
module AppSec
module Dast
module Profiles
class CreateService < BaseContainerService
def execute
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
dast_profile = ::Dast::Profile.create!(
project: container,
name: params.fetch(:name),
description: params.fetch(:description),
branch_name: params[:branch_name],
dast_site_profile: dast_site_profile,
dast_scanner_profile: dast_scanner_profile
)
return ServiceResponse.success(payload: { dast_profile: dast_profile, pipeline_url: nil }) unless params.fetch(:run_after_create)
response = ::DastOnDemandScans::CreateService.new(
container: container,
current_user: current_user,
params: { dast_profile: dast_profile }
).execute
return response if response.error?
ServiceResponse.success(payload: { dast_profile: dast_profile, pipeline_url: response.payload.fetch(:pipeline_url) })
rescue ActiveRecord::RecordInvalid => err
ServiceResponse.error(message: err.record.errors.full_messages)
rescue KeyError => err
ServiceResponse.error(message: err.message.capitalize)
end
private
def allowed?
container.licensed_feature_available?(:security_on_demand_scans)
end
def dast_site_profile
@dast_site_profile ||= params.fetch(:dast_site_profile)
end
def dast_scanner_profile
@dast_scanner_profile ||= params.fetch(:dast_scanner_profile)
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module Profiles
class DestroyService < BaseContainerService
def execute
return unauthorized unless allowed?
return ServiceResponse.error(message: 'Profile parameter missing') unless dast_profile
return ServiceResponse.error(message: 'Profile failed to delete') unless dast_profile.destroy
ServiceResponse.success(payload: dast_profile)
end
private
def allowed?
can?(current_user, :create_on_demand_dast_scan, container)
end
def unauthorized
ServiceResponse.error(
message: 'You are not authorized to update this profile',
http_status: 403
)
end
def dast_profile
params[:dast_profile]
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module Profiles
class UpdateService < BaseContainerService
include Gitlab::Utils::StrongMemoize
def execute
return unauthorized unless allowed?
return error('Profile parameter missing') unless dast_profile
return error(dast_profile.errors.full_messages) unless dast_profile.update(dast_profile_params)
return success(dast_profile: dast_profile, pipeline_url: nil) unless params[:run_after_update]
response = create_scan(dast_profile)
return response if response.error?
success(dast_profile: dast_profile, pipeline_url: response.payload.fetch(:pipeline_url))
end
private
def allowed?
container.licensed_feature_available?(:security_on_demand_scans) &&
can?(current_user, :create_on_demand_dast_scan, container)
end
def error(message, opts = {})
ServiceResponse.error(message: message, **opts)
end
def success(payload)
ServiceResponse.success(payload: payload)
end
def unauthorized
error('You are not authorized to update this profile', http_status: 403)
end
def dast_profile
params[:dast_profile]
end
def dast_profile_params
params.slice(:dast_site_profile_id, :dast_scanner_profile_id, :name, :description, :branch_name)
end
def create_scan(dast_profile)
::DastOnDemandScans::CreateService.new(
container: container,
current_user: current_user,
params: { dast_profile: dast_profile }
).execute
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module ScannerProfiles
class CreateService < BaseService
def execute(name:, target_timeout:, spider_timeout:, scan_type:, use_ajax_spider:, show_debug_messages:)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
dast_scanner_profile = DastScannerProfile.create(
project: project,
name: name,
target_timeout: target_timeout,
spider_timeout: spider_timeout,
scan_type: scan_type,
use_ajax_spider: use_ajax_spider,
show_debug_messages: show_debug_messages
)
return ServiceResponse.success(payload: dast_scanner_profile) if dast_scanner_profile.valid?
ServiceResponse.error(message: dast_scanner_profile.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module ScannerProfiles
class DestroyService < BaseService
include Gitlab::Allowable
def execute(id:)
return unauthorized unless can_delete_scanner_profile?
dast_scanner_profile = find_dast_scanner_profile(id)
return ServiceResponse.error(message: _('Scanner profile not found for given parameters')) unless dast_scanner_profile
return ServiceResponse.error(message: _('Cannot delete %{profile_name} referenced in security policy') % { profile_name: dast_scanner_profile.name }) if referenced_in_security_policy?(dast_scanner_profile)
if dast_scanner_profile.destroy
ServiceResponse.success(payload: dast_scanner_profile)
else
ServiceResponse.error(message: _('Scanner profile failed to delete'))
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to update this scanner profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_delete_scanner_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_scanner_profile(id)
project.dast_scanner_profiles.id_in(id).first
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module ScannerProfiles
class UpdateService < BaseService
include Gitlab::Allowable
def execute(id:, profile_name:, target_timeout:, spider_timeout:, scan_type: nil, use_ajax_spider: nil, show_debug_messages: nil)
return unauthorized unless can_update_scanner_profile?
dast_scanner_profile = find_dast_scanner_profile(id)
return ServiceResponse.error(message: _('Scanner profile not found for given parameters')) unless dast_scanner_profile
return ServiceResponse.error(message: _('Cannot modify %{profile_name} referenced in security policy') % { profile_name: dast_scanner_profile.name }) if referenced_in_security_policy?(dast_scanner_profile)
update_args = {
name: profile_name,
target_timeout: target_timeout,
spider_timeout: spider_timeout
}
update_args[:scan_type] = scan_type if scan_type
update_args[:use_ajax_spider] = use_ajax_spider unless use_ajax_spider.nil?
update_args[:show_debug_messages] = show_debug_messages unless show_debug_messages.nil?
if dast_scanner_profile.update(update_args)
ServiceResponse.success(payload: dast_scanner_profile)
else
ServiceResponse.error(message: dast_scanner_profile.errors.full_messages)
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to update this scanner profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_update_scanner_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_scanner_profile(id)
DastScannerProfilesFinder.new(project_ids: [project.id], ids: [id]).execute.first
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module SiteProfiles
class CreateService < BaseService
class Rollback < StandardError
attr_reader :errors
def initialize(errors)
@errors = errors
end
end
attr_reader :dast_site_profile
def execute(name:, target_url:, **params)
return ServiceResponse.error(message: _('Insufficient permissions')) unless allowed?
ActiveRecord::Base.transaction do
dast_site = ::DastSites::FindOrCreateService.new(project, current_user).execute!(url: target_url)
params.merge!(project: project, dast_site: dast_site, name: name).compact!
@dast_site_profile = DastSiteProfile.create!(params.except(:request_headers, :auth_password))
create_secret_variable!(::Dast::SiteProfileSecretVariable::PASSWORD, params[:auth_password])
create_secret_variable!(::Dast::SiteProfileSecretVariable::REQUEST_HEADERS, params[:request_headers])
ServiceResponse.success(payload: dast_site_profile)
end
rescue Rollback => e
ServiceResponse.error(message: e.errors)
rescue ActiveRecord::RecordInvalid => e
ServiceResponse.error(message: e.record.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def create_secret_variable!(key, value)
return ServiceResponse.success unless value
response = ::Dast::SiteProfileSecretVariables::CreateOrUpdateService.new(
container: project,
current_user: current_user,
params: { dast_site_profile: dast_site_profile, key: key, raw_value: value }
).execute
raise Rollback, response.errors if response.error?
response
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module SiteProfiles
class DestroyService < BaseService
include Gitlab::Allowable
def execute(id:)
return unauthorized unless can_delete_site_profile?
dast_site_profile = find_dast_site_profile(id)
return ServiceResponse.error(message: _('Site profile not found for given parameters')) unless dast_site_profile
return ServiceResponse.error(message: _('Cannot delete %{profile_name} referenced in security policy') % { profile_name: dast_site_profile.name }) if referenced_in_security_policy?(dast_site_profile)
if dast_site_profile.destroy
ServiceResponse.success(payload: dast_site_profile)
else
ServiceResponse.error(message: _('Site profile failed to delete'))
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to delete this site profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_delete_site_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_site_profile(id)
project.dast_site_profiles.id_in(id).first
end
end
end
end
end
# frozen_string_literal: true
module AppSec
module Dast
module SiteProfiles
class UpdateService < BaseService
class Rollback < StandardError
attr_reader :errors
def initialize(errors)
@errors = errors
end
end
attr_reader :dast_site_profile
def execute(id:, **params)
return ServiceResponse.error(message: _('Insufficient permissions')) unless allowed?
find_dast_site_profile!(id)
return ServiceResponse.error(message: _('Cannot modify %{profile_name} referenced in security policy') % { profile_name: dast_site_profile.name }) if referenced_in_security_policy?
ActiveRecord::Base.transaction do
if target_url = params.delete(:target_url)
params[:dast_site] = DastSites::FindOrCreateService.new(project, current_user).execute!(url: target_url)
end
handle_secret_variable!(params, :request_headers, ::Dast::SiteProfileSecretVariable::REQUEST_HEADERS)
handle_secret_variable!(params, :auth_password, ::Dast::SiteProfileSecretVariable::PASSWORD)
params.compact!
dast_site_profile.update!(params)
ServiceResponse.success(payload: dast_site_profile)
end
rescue Rollback => e
ServiceResponse.error(message: e.errors)
rescue ActiveRecord::RecordNotFound => e
ServiceResponse.error(message: _('%{model_name} not found') % { model_name: e.model })
rescue ActiveRecord::RecordInvalid => e
ServiceResponse.error(message: e.record.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def referenced_in_security_policy?
dast_site_profile.referenced_in_security_policies.present?
end
# rubocop: disable CodeReuse/ActiveRecord
def find_dast_site_profile!(id)
@dast_site_profile = DastSiteProfilesFinder.new(project_id: project.id, id: id).execute.first!
end
# rubocop: enable CodeReuse/ActiveRecord
def handle_secret_variable!(params, arg, key)
value = params.delete(arg)
return ServiceResponse.success unless value
return delete_secret_variable!(key) if value == ''
response = ::Dast::SiteProfileSecretVariables::CreateOrUpdateService.new(
container: project,
current_user: current_user,
params: { dast_site_profile: dast_site_profile, key: key, raw_value: value }
).execute
raise Rollback, response.errors if response.error?
response
end
# rubocop: disable CodeReuse/ActiveRecord
def delete_secret_variable!(key)
variable = dast_site_profile.secret_variables.find_by(key: key)
return ServiceResponse.success unless variable
response = ::Dast::SiteProfileSecretVariables::DestroyService.new(
container: project,
current_user: current_user,
params: { dast_site_profile_secret_variable: variable }
).execute
raise Rollback, response.errors if response.error?
response
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
end
# frozen_string_literal: true
module Dast
module Profiles
class CreateService < BaseContainerService
def execute
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
dast_profile = Dast::Profile.create!(
project: container,
name: params.fetch(:name),
description: params.fetch(:description),
branch_name: params[:branch_name],
dast_site_profile: dast_site_profile,
dast_scanner_profile: dast_scanner_profile
)
return ServiceResponse.success(payload: { dast_profile: dast_profile, pipeline_url: nil }) unless params.fetch(:run_after_create)
response = ::DastOnDemandScans::CreateService.new(
container: container,
current_user: current_user,
params: { dast_profile: dast_profile }
).execute
return response if response.error?
ServiceResponse.success(payload: { dast_profile: dast_profile, pipeline_url: response.payload.fetch(:pipeline_url) })
rescue ActiveRecord::RecordInvalid => err
ServiceResponse.error(message: err.record.errors.full_messages)
rescue KeyError => err
ServiceResponse.error(message: err.message.capitalize)
end
private
def allowed?
container.feature_available?(:security_on_demand_scans)
end
def dast_site_profile
@dast_site_profile ||= params.fetch(:dast_site_profile)
end
def dast_scanner_profile
@dast_scanner_profile ||= params.fetch(:dast_scanner_profile)
end
end
end
end
# frozen_string_literal: true
module Dast
module Profiles
class DestroyService < BaseContainerService
def execute
return unauthorized unless allowed?
return ServiceResponse.error(message: 'Profile parameter missing') unless dast_profile
return ServiceResponse.error(message: 'Profile failed to delete') unless dast_profile.destroy
ServiceResponse.success(payload: dast_profile)
end
private
def allowed?
can?(current_user, :create_on_demand_dast_scan, container)
end
def unauthorized
ServiceResponse.error(
message: 'You are not authorized to update this profile',
http_status: 403
)
end
def dast_profile
params[:dast_profile]
end
end
end
end
# frozen_string_literal: true
module Dast
module Profiles
class UpdateService < BaseContainerService
include Gitlab::Utils::StrongMemoize
def execute
return unauthorized unless allowed?
return error('Profile parameter missing') unless dast_profile
return error(dast_profile.errors.full_messages) unless dast_profile.update(dast_profile_params)
return success(dast_profile: dast_profile, pipeline_url: nil) unless params[:run_after_update]
response = create_scan(dast_profile)
return response if response.error?
success(dast_profile: dast_profile, pipeline_url: response.payload.fetch(:pipeline_url))
end
private
def allowed?
container.feature_available?(:security_on_demand_scans) &&
can?(current_user, :create_on_demand_dast_scan, container)
end
def error(message, opts = {})
ServiceResponse.error(message: message, **opts)
end
def success(payload)
ServiceResponse.success(payload: payload)
end
def unauthorized
error('You are not authorized to update this profile', http_status: 403)
end
def dast_profile
params[:dast_profile]
end
def dast_profile_params
params.slice(:dast_site_profile_id, :dast_scanner_profile_id, :name, :description, :branch_name)
end
def create_scan(dast_profile)
::DastOnDemandScans::CreateService.new(
container: container,
current_user: current_user,
params: { dast_profile: dast_profile }
).execute
end
end
end
end
# frozen_string_literal: true
module DastScannerProfiles
class CreateService < BaseService
def execute(name:, target_timeout:, spider_timeout:, scan_type:, use_ajax_spider:, show_debug_messages:)
return ServiceResponse.error(message: 'Insufficient permissions') unless allowed?
dast_scanner_profile = DastScannerProfile.create(
project: project,
name: name,
target_timeout: target_timeout,
spider_timeout: spider_timeout,
scan_type: scan_type,
use_ajax_spider: use_ajax_spider,
show_debug_messages: show_debug_messages
)
return ServiceResponse.success(payload: dast_scanner_profile) if dast_scanner_profile.valid?
ServiceResponse.error(message: dast_scanner_profile.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
end
end
# frozen_string_literal: true
module DastScannerProfiles
class DestroyService < BaseService
include Gitlab::Allowable
def execute(id:)
return unauthorized unless can_delete_scanner_profile?
dast_scanner_profile = find_dast_scanner_profile(id)
return ServiceResponse.error(message: _('Scanner profile not found for given parameters')) unless dast_scanner_profile
return ServiceResponse.error(message: _('Cannot delete %{profile_name} referenced in security policy') % { profile_name: dast_scanner_profile.name }) if referenced_in_security_policy?(dast_scanner_profile)
if dast_scanner_profile.destroy
ServiceResponse.success(payload: dast_scanner_profile)
else
ServiceResponse.error(message: _('Scanner profile failed to delete'))
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to update this scanner profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_delete_scanner_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_scanner_profile(id)
project.dast_scanner_profiles.id_in(id).first
end
end
end
# frozen_string_literal: true
module DastScannerProfiles
class UpdateService < BaseService
include Gitlab::Allowable
def execute(id:, profile_name:, target_timeout:, spider_timeout:, scan_type: nil, use_ajax_spider: nil, show_debug_messages: nil)
return unauthorized unless can_update_scanner_profile?
dast_scanner_profile = find_dast_scanner_profile(id)
return ServiceResponse.error(message: _('Scanner profile not found for given parameters')) unless dast_scanner_profile
return ServiceResponse.error(message: _('Cannot modify %{profile_name} referenced in security policy') % { profile_name: dast_scanner_profile.name }) if referenced_in_security_policy?(dast_scanner_profile)
update_args = {
name: profile_name,
target_timeout: target_timeout,
spider_timeout: spider_timeout
}
update_args[:scan_type] = scan_type if scan_type
update_args[:use_ajax_spider] = use_ajax_spider unless use_ajax_spider.nil?
update_args[:show_debug_messages] = show_debug_messages unless show_debug_messages.nil?
if dast_scanner_profile.update(update_args)
ServiceResponse.success(payload: dast_scanner_profile)
else
ServiceResponse.error(message: dast_scanner_profile.errors.full_messages)
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to update this scanner profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_update_scanner_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_scanner_profile(id)
DastScannerProfilesFinder.new(project_ids: [project.id], ids: [id]).execute.first
end
end
end
# frozen_string_literal: true
module DastSiteProfiles
class CreateService < BaseService
class Rollback < StandardError
attr_reader :errors
def initialize(errors)
@errors = errors
end
end
attr_reader :dast_site_profile
def execute(name:, target_url:, **params)
return ServiceResponse.error(message: _('Insufficient permissions')) unless allowed?
ActiveRecord::Base.transaction do
dast_site = DastSites::FindOrCreateService.new(project, current_user).execute!(url: target_url)
params.merge!(project: project, dast_site: dast_site, name: name).compact!
@dast_site_profile = DastSiteProfile.create!(params.except(:request_headers, :auth_password))
create_secret_variable!(Dast::SiteProfileSecretVariable::PASSWORD, params[:auth_password])
create_secret_variable!(Dast::SiteProfileSecretVariable::REQUEST_HEADERS, params[:request_headers])
ServiceResponse.success(payload: dast_site_profile)
end
rescue Rollback => e
ServiceResponse.error(message: e.errors)
rescue ActiveRecord::RecordInvalid => e
ServiceResponse.error(message: e.record.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def create_secret_variable!(key, value)
return ServiceResponse.success unless value
response = Dast::SiteProfileSecretVariables::CreateOrUpdateService.new(
container: project,
current_user: current_user,
params: { dast_site_profile: dast_site_profile, key: key, raw_value: value }
).execute
raise Rollback, response.errors if response.error?
response
end
end
end
# frozen_string_literal: true
module DastSiteProfiles
class DestroyService < BaseService
include Gitlab::Allowable
def execute(id:)
return unauthorized unless can_delete_site_profile?
dast_site_profile = find_dast_site_profile(id)
return ServiceResponse.error(message: _('Site profile not found for given parameters')) unless dast_site_profile
return ServiceResponse.error(message: _('Cannot delete %{profile_name} referenced in security policy') % { profile_name: dast_site_profile.name }) if referenced_in_security_policy?(dast_site_profile)
if dast_site_profile.destroy
ServiceResponse.success(payload: dast_site_profile)
else
ServiceResponse.error(message: _('Site profile failed to delete'))
end
end
private
def unauthorized
::ServiceResponse.error(message: _('You are not authorized to delete this site profile'), http_status: 403)
end
def referenced_in_security_policy?(profile)
profile.referenced_in_security_policies.present?
end
def can_delete_site_profile?
can?(current_user, :create_on_demand_dast_scan, project)
end
def find_dast_site_profile(id)
project.dast_site_profiles.id_in(id).first
end
end
end
# frozen_string_literal: true
module DastSiteProfiles
class UpdateService < BaseService
class Rollback < StandardError
attr_reader :errors
def initialize(errors)
@errors = errors
end
end
attr_reader :dast_site_profile
def execute(id:, **params)
return ServiceResponse.error(message: _('Insufficient permissions')) unless allowed?
find_dast_site_profile!(id)
return ServiceResponse.error(message: _('Cannot modify %{profile_name} referenced in security policy') % { profile_name: dast_site_profile.name }) if referenced_in_security_policy?
ActiveRecord::Base.transaction do
if target_url = params.delete(:target_url)
params[:dast_site] = DastSites::FindOrCreateService.new(project, current_user).execute!(url: target_url)
end
handle_secret_variable!(params, :request_headers, Dast::SiteProfileSecretVariable::REQUEST_HEADERS)
handle_secret_variable!(params, :auth_password, Dast::SiteProfileSecretVariable::PASSWORD)
params.compact!
dast_site_profile.update!(params)
ServiceResponse.success(payload: dast_site_profile)
end
rescue Rollback => e
ServiceResponse.error(message: e.errors)
rescue ActiveRecord::RecordNotFound => e
ServiceResponse.error(message: _('%{model_name} not found') % { model_name: e.model })
rescue ActiveRecord::RecordInvalid => e
ServiceResponse.error(message: e.record.errors.full_messages)
end
private
def allowed?
Ability.allowed?(current_user, :create_on_demand_dast_scan, project)
end
def referenced_in_security_policy?
dast_site_profile.referenced_in_security_policies.present?
end
# rubocop: disable CodeReuse/ActiveRecord
def find_dast_site_profile!(id)
@dast_site_profile = DastSiteProfilesFinder.new(project_id: project.id, id: id).execute.first!
end
# rubocop: enable CodeReuse/ActiveRecord
def handle_secret_variable!(params, arg, key)
value = params.delete(arg)
return ServiceResponse.success unless value
return delete_secret_variable!(key) if value == ''
response = Dast::SiteProfileSecretVariables::CreateOrUpdateService.new(
container: project,
current_user: current_user,
params: { dast_site_profile: dast_site_profile, key: key, raw_value: value }
).execute
raise Rollback, response.errors if response.error?
response
end
# rubocop: disable CodeReuse/ActiveRecord
def delete_secret_variable!(key)
variable = dast_site_profile.secret_variables.find_by(key: key)
return ServiceResponse.success unless variable
response = Dast::SiteProfileSecretVariables::DestroyService.new(
container: project,
current_user: current_user,
params: { dast_site_profile_secret_variable: variable }
).execute
raise Rollback, response.errors if response.error?
response
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
...@@ -53,7 +53,7 @@ RSpec.describe Mutations::Dast::Profiles::Delete do ...@@ -53,7 +53,7 @@ RSpec.describe Mutations::Dast::Profiles::Delete do
context 'when deletion fails' do context 'when deletion fails' do
it 'returns an error' do it 'returns an error' do
allow_next_instance_of(::Dast::Profiles::DestroyService) do |service| allow_next_instance_of(::AppSec::Dast::Profiles::DestroyService) do |service|
allow(service).to receive(:execute).and_return( allow(service).to receive(:execute).and_return(
ServiceResponse.error(message: 'Profile failed to delete') ServiceResponse.error(message: 'Profile failed to delete')
) )
......
...@@ -97,7 +97,7 @@ RSpec.describe Mutations::Dast::Profiles::Update do ...@@ -97,7 +97,7 @@ RSpec.describe Mutations::Dast::Profiles::Update do
context 'when updating fails' do context 'when updating fails' do
it 'returns an error' do it 'returns an error' do
allow_next_instance_of(::Dast::Profiles::UpdateService) do |service| allow_next_instance_of(::AppSec::Dast::Profiles::UpdateService) do |service|
allow(service).to receive(:execute).and_return( allow(service).to receive(:execute).and_return(
ServiceResponse.error(message: 'Profile failed to update') ServiceResponse.error(message: 'Profile failed to update')
) )
......
...@@ -50,7 +50,7 @@ RSpec.describe Mutations::DastScannerProfiles::Create do ...@@ -50,7 +50,7 @@ RSpec.describe Mutations::DastScannerProfiles::Create do
service = double(described_class) service = double(described_class)
result = double('result', success?: false, errors: []) result = double('result', success?: false, errors: [])
expect(DastScannerProfiles::CreateService).to receive(:new).and_return(service) expect(::AppSec::Dast::ScannerProfiles::CreateService).to receive(:new).and_return(service)
expected_args = { expected_args = {
name: profile_name, name: profile_name,
spider_timeout: nil, spider_timeout: nil,
......
...@@ -58,7 +58,7 @@ RSpec.describe Mutations::DastScannerProfiles::Delete do ...@@ -58,7 +58,7 @@ RSpec.describe Mutations::DastScannerProfiles::Delete do
context 'when deletion fails' do context 'when deletion fails' do
it 'returns an error' do it 'returns an error' do
allow_next_instance_of(::DastScannerProfiles::DestroyService) do |service| allow_next_instance_of(::AppSec::Dast::ScannerProfiles::DestroyService) do |service|
allow(service).to receive(:execute).and_return( allow(service).to receive(:execute).and_return(
ServiceResponse.error(message: 'Scanner profile failed to delete') ServiceResponse.error(message: 'Scanner profile failed to delete')
) )
......
...@@ -90,7 +90,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -90,7 +90,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
end end
it 'calls the dast_site_profile creation service' do it 'calls the dast_site_profile creation service' do
service = double(DastSiteProfiles::CreateService) service = double(::AppSec::Dast::SiteProfiles::CreateService)
result = ServiceResponse.error(message: '') result = ServiceResponse.error(message: '')
service_params = { service_params = {
...@@ -107,7 +107,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -107,7 +107,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
auth_password: auth[:password] auth_password: auth[:password]
} }
expect(DastSiteProfiles::CreateService).to receive(:new).and_return(service) expect(::AppSec::Dast::SiteProfiles::CreateService).to receive(:new).and_return(service)
expect(service).to receive(:execute).with(service_params).and_return(result) expect(service).to receive(:execute).with(service_params).and_return(result)
subject subject
......
...@@ -45,7 +45,7 @@ RSpec.describe Mutations::DastSiteProfiles::Delete do ...@@ -45,7 +45,7 @@ RSpec.describe Mutations::DastSiteProfiles::Delete do
context 'when there is an issue deleting the dast_site_profile' do context 'when there is an issue deleting the dast_site_profile' do
it 'returns an error' do it 'returns an error' do
allow_next_instance_of(::DastSiteProfiles::DestroyService) do |service| allow_next_instance_of(::AppSec::Dast::SiteProfiles::DestroyService) do |service|
allow(service).to receive(:execute).and_return(double(success?: false, errors: ['Name is weird'])) allow(service).to receive(:execute).and_return(double(success?: false, errors: ['Name is weird']))
end end
......
...@@ -63,7 +63,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -63,7 +63,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
end end
it 'calls the dast_site_profile update service' do it 'calls the dast_site_profile update service' do
service = double(DastSiteProfiles::UpdateService) service = double(::AppSec::Dast::SiteProfiles::UpdateService)
result = ServiceResponse.error(message: '') result = ServiceResponse.error(message: '')
service_params = { service_params = {
...@@ -81,7 +81,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -81,7 +81,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
auth_password: new_auth[:password] auth_password: new_auth[:password]
} }
expect(DastSiteProfiles::UpdateService).to receive(:new).and_return(service) expect(::AppSec::Dast::SiteProfiles::UpdateService).to receive(:new).and_return(service)
expect(service).to receive(:execute).with(service_params).and_return(result) expect(service).to receive(:execute).with(service_params).and_return(result)
subject subject
......
...@@ -41,7 +41,7 @@ RSpec.describe 'Updating a DAST Profile' do ...@@ -41,7 +41,7 @@ RSpec.describe 'Updating a DAST Profile' do
context 'when updating fails' do context 'when updating fails' do
it 'returns an error' do it 'returns an error' do
allow_next_instance_of(::Dast::Profiles::UpdateService) do |service| allow_next_instance_of(::AppSec::Dast::Profiles::UpdateService) do |service|
allow(service).to receive(:execute).and_return( allow(service).to receive(:execute).and_return(
ServiceResponse.error(message: 'Profile failed to update') ServiceResponse.error(message: 'Profile failed to update')
) )
......
...@@ -25,7 +25,7 @@ RSpec.describe 'Creating a DAST Site Profile' do ...@@ -25,7 +25,7 @@ RSpec.describe 'Creating a DAST Site Profile' do
context 'when there is an issue deleting the dast_site_profile' do context 'when there is an issue deleting the dast_site_profile' do
before do before do
allow_next_instance_of(::DastSiteProfiles::DestroyService) do |service| allow_next_instance_of(::AppSec::Dast::SiteProfiles::DestroyService) do |service|
allow(service).to receive(:execute).and_return(double(success?: false, errors: ['Name is weird'])) allow(service).to receive(:execute).and_return(double(success?: false, errors: ['Name is weird']))
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Dast::Profiles::CreateService do RSpec.describe AppSec::Dast::Profiles::CreateService do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:developer) { create(:user, developer_projects: [project] ) } let_it_be(:developer) { create(:user, developer_projects: [project] ) }
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) } let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Dast::Profiles::DestroyService do RSpec.describe AppSec::Dast::Profiles::DestroyService do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Dast::Profiles::UpdateService do RSpec.describe AppSec::Dast::Profiles::UpdateService do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:dast_profile, reload: true) { create(:dast_profile, project: project, branch_name: 'orphaned-branch') } let_it_be(:dast_profile, reload: true) { create(:dast_profile, project: project, branch_name: 'orphaned-branch') }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe DastScannerProfiles::CreateService do RSpec.describe AppSec::Dast::ScannerProfiles::CreateService do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :repository, creator: user) } let(:project) { create(:project, :repository, creator: user) }
let(:name) { FFaker::Company.catch_phrase } let(:name) { FFaker::Company.catch_phrase }
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe DastScannerProfiles::DestroyService do RSpec.describe AppSec::Dast::ScannerProfiles::DestroyService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:dast_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) } let_it_be(:dast_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) }
let(:project) { dast_profile.project } let(:project) { dast_profile.project }
before do before do
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe DastScannerProfiles::UpdateService do RSpec.describe AppSec::Dast::ScannerProfiles::UpdateService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:dast_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) } let_it_be(:dast_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000) }
let_it_be(:dast_profile_2, reload: true) { create(:dast_scanner_profile) } let_it_be(:dast_profile_2, reload: true) { create(:dast_scanner_profile) }
...@@ -13,6 +13,7 @@ RSpec.describe DastScannerProfiles::UpdateService do ...@@ -13,6 +13,7 @@ RSpec.describe DastScannerProfiles::UpdateService do
let_it_be(:new_target_timeout) { dast_profile.target_timeout + 1 } let_it_be(:new_target_timeout) { dast_profile.target_timeout + 1 }
let_it_be(:new_spider_timeout) { dast_profile.spider_timeout + 1 } let_it_be(:new_spider_timeout) { dast_profile.spider_timeout + 1 }
let_it_be(:new_scan_type) { (DastScannerProfile.scan_types.keys - [DastScannerProfile.last.scan_type]).first } let_it_be(:new_scan_type) { (DastScannerProfile.scan_types.keys - [DastScannerProfile.last.scan_type]).first }
let(:new_use_ajax_spider) { !dast_profile.use_ajax_spider } let(:new_use_ajax_spider) { !dast_profile.use_ajax_spider }
let(:new_show_debug_messages) { !dast_profile.show_debug_messages } let(:new_show_debug_messages) { !dast_profile.show_debug_messages }
...@@ -120,6 +121,7 @@ RSpec.describe DastScannerProfiles::UpdateService do ...@@ -120,6 +121,7 @@ RSpec.describe DastScannerProfiles::UpdateService do
context 'when setting properties to false' do context 'when setting properties to false' do
let_it_be(:dast_scanner_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000, use_ajax_spider: true, show_debug_messages: true) } let_it_be(:dast_scanner_profile, reload: true) { create(:dast_scanner_profile, target_timeout: 200, spider_timeout: 5000, use_ajax_spider: true, show_debug_messages: true) }
let(:new_use_ajax_spider) { false } let(:new_use_ajax_spider) { false }
let(:new_show_debug_messages) { false } let(:new_show_debug_messages) { false }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe DastSiteProfiles::CreateService do RSpec.describe AppSec::Dast::SiteProfiles::CreateService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, creator: user) } let_it_be(:project) { create(:project, :repository, creator: user) }
let_it_be(:name) { FFaker::Company.catch_phrase } let_it_be(:name) { FFaker::Company.catch_phrase }
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe DastSiteProfiles::DestroyService do RSpec.describe AppSec::Dast::SiteProfiles::DestroyService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:dast_profile, reload: true) { create(:dast_site_profile) } let_it_be(:dast_profile, reload: true) { create(:dast_site_profile) }
let(:project) { dast_profile.project } let(:project) { dast_profile.project }
before do before do
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe DastSiteProfiles::UpdateService do RSpec.describe AppSec::Dast::SiteProfiles::UpdateService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, creator: user) } let_it_be(:project) { create(:project, creator: user) }
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) } let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) }
......
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