Commit 40fa9ce8 authored by Marcos Rocha's avatar Marcos Rocha Committed by Luke Duncalfe

Add scan method to dast site profile GraphQL API

This MR adds the scan method to the dast site profile GraphQL API.

Changelog: added
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78745
EE: true
parent 31ab4415
...@@ -1858,6 +1858,7 @@ Input type: `DastSiteProfileCreateInput` ...@@ -1858,6 +1858,7 @@ Input type: `DastSiteProfileCreateInput`
| <a id="mutationdastsiteprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. | | <a id="mutationdastsiteprofilecreatefullpath"></a>`fullPath` | [`ID!`](#id) | Project the site profile belongs to. |
| <a id="mutationdastsiteprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. | | <a id="mutationdastsiteprofilecreateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. |
| <a id="mutationdastsiteprofilecreaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. | | <a id="mutationdastsiteprofilecreaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
| <a id="mutationdastsiteprofilecreatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. Is not saved or updated if `dast_api_scanner` feature flag is disabled. |
| <a id="mutationdastsiteprofilecreatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. | | <a id="mutationdastsiteprofilecreatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="mutationdastsiteprofilecreatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. | | <a id="mutationdastsiteprofilecreatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
...@@ -1903,6 +1904,7 @@ Input type: `DastSiteProfileUpdateInput` ...@@ -1903,6 +1904,7 @@ Input type: `DastSiteProfileUpdateInput`
| <a id="mutationdastsiteprofileupdateid"></a>`id` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be updated. | | <a id="mutationdastsiteprofileupdateid"></a>`id` | [`DastSiteProfileID!`](#dastsiteprofileid) | ID of the site profile to be updated. |
| <a id="mutationdastsiteprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. | | <a id="mutationdastsiteprofileupdateprofilename"></a>`profileName` | [`String!`](#string) | Name of the site profile. |
| <a id="mutationdastsiteprofileupdaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. | | <a id="mutationdastsiteprofileupdaterequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
| <a id="mutationdastsiteprofileupdatescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method by the scanner. Is not saved or updated if `dast_api_scanner` feature flag is disabled. |
| <a id="mutationdastsiteprofileupdatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. | | <a id="mutationdastsiteprofileupdatetargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="mutationdastsiteprofileupdatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. | | <a id="mutationdastsiteprofileupdatetargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
...@@ -9861,6 +9863,7 @@ Represents a DAST Site Profile. ...@@ -9861,6 +9863,7 @@ Represents a DAST Site Profile.
| <a id="dastsiteprofileprofilename"></a>`profileName` | [`String`](#string) | Name of the site profile. | | <a id="dastsiteprofileprofilename"></a>`profileName` | [`String`](#string) | Name of the site profile. |
| <a id="dastsiteprofilereferencedinsecuritypolicies"></a>`referencedInSecurityPolicies` | [`[String!]`](#string) | List of security policy names that are referencing given project. | | <a id="dastsiteprofilereferencedinsecuritypolicies"></a>`referencedInSecurityPolicies` | [`[String!]`](#string) | List of security policy names that are referencing given project. |
| <a id="dastsiteprofilerequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. | | <a id="dastsiteprofilerequestheaders"></a>`requestHeaders` | [`String`](#string) | Comma-separated list of request header names and values to be added to every request made by DAST. |
| <a id="dastsiteprofilescanmethod"></a>`scanMethod` | [`DastScanMethodType`](#dastscanmethodtype) | Scan method used by the scanner. Always returns `null` if `dast_api_scanner` feature flag is disabled. |
| <a id="dastsiteprofiletargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. | | <a id="dastsiteprofiletargettype"></a>`targetType` | [`DastTargetTypeEnum`](#dasttargettypeenum) | Type of target to be scanned. |
| <a id="dastsiteprofiletargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. | | <a id="dastsiteprofiletargeturl"></a>`targetUrl` | [`String`](#string) | URL of the target to be scanned. |
| <a id="dastsiteprofileuserpermissions"></a>`userPermissions` | [`DastSiteProfilePermissions!`](#dastsiteprofilepermissions) | Permissions for the current user on the resource. | | <a id="dastsiteprofileuserpermissions"></a>`userPermissions` | [`DastSiteProfilePermissions!`](#dastsiteprofilepermissions) | Permissions for the current user on the resource. |
...@@ -17241,6 +17244,17 @@ Unit for the duration of Dast Profile Cadence. ...@@ -17241,6 +17244,17 @@ Unit for the duration of Dast Profile Cadence.
| <a id="dastprofilecadenceunitweek"></a>`WEEK` | DAST Profile Cadence duration in weeks. | | <a id="dastprofilecadenceunitweek"></a>`WEEK` | DAST Profile Cadence duration in weeks. |
| <a id="dastprofilecadenceunityear"></a>`YEAR` | DAST Profile Cadence duration in years. | | <a id="dastprofilecadenceunityear"></a>`YEAR` | DAST Profile Cadence duration in years. |
### `DastScanMethodType`
Scan method to be used by the scanner.
| Value | Description |
| ----- | ----------- |
| <a id="dastscanmethodtypehar"></a>`HAR` | HAR scan method. |
| <a id="dastscanmethodtypeopenapi"></a>`OPENAPI` | OpenAPI scan method. |
| <a id="dastscanmethodtypepostman_collection"></a>`POSTMAN_COLLECTION` | Postman scan method. |
| <a id="dastscanmethodtypewebsite"></a>`WEBSITE` | Website scan method. |
### `DastScanTypeEnum` ### `DastScanTypeEnum`
| Value | Description | | Value | Description |
...@@ -22,6 +22,11 @@ module Mutations ...@@ -22,6 +22,11 @@ module Mutations
required: false, required: false,
description: 'Type of target to be scanned.' description: 'Type of target to be scanned.'
argument :scan_method, Types::Dast::ScanMethodTypeEnum,
required: false,
description: 'Scan method by the scanner. Is not saved or updated ' \
'if `dast_api_scanner` feature flag is disabled.'
argument :request_headers, GraphQL::Types::String, argument :request_headers, GraphQL::Types::String,
required: false, required: false,
description: 'Comma-separated list of request header names and values to be ' \ description: 'Comma-separated list of request header names and values to be ' \
......
...@@ -42,6 +42,10 @@ module Mutations ...@@ -42,6 +42,10 @@ module Mutations
auth_password: auth_params[:password] auth_password: auth_params[:password]
}.compact }.compact
if Feature.enabled?(:dast_api_scanner, project, default_enabled: :yaml)
dast_site_profile_params[:scan_method] = params[:scan_method]
end
result = ::AppSec::Dast::SiteProfiles::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 }
......
...@@ -49,6 +49,10 @@ module Mutations ...@@ -49,6 +49,10 @@ module Mutations
auth_password: auth_params[:password] auth_password: auth_params[:password]
}.compact }.compact
if Feature.enabled?(:dast_api_scanner, dast_site_profile.project, default_enabled: :yaml)
dast_site_profile_params[:scan_method] = params[:scan_method]
end
result = ::AppSec::Dast::SiteProfiles::UpdateService.new(dast_site_profile.project, current_user).execute(**dast_site_profile_params) result = ::AppSec::Dast::SiteProfiles::UpdateService.new(dast_site_profile.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 }
......
# frozen_string_literal: true
module Types
module Dast
class ScanMethodTypeEnum < BaseEnum
graphql_name 'DastScanMethodType'
description 'Scan method to be used by the scanner.'
value 'WEBSITE', description: 'Website scan method.', value: 'site'
value 'OPENAPI', description: 'OpenAPI scan method.', value: 'openapi'
value 'HAR', description: 'HAR scan method.', value: 'har'
value 'POSTMAN_COLLECTION', description: 'Postman scan method.', value: 'postman'
end
end
end
...@@ -50,6 +50,10 @@ module Types ...@@ -50,6 +50,10 @@ module Types
calls_gitaly: true, calls_gitaly: true,
description: 'List of security policy names that are referencing given project.' description: 'List of security policy names that are referencing given project.'
field :scan_method, Types::Dast::ScanMethodTypeEnum, null: true,
description: 'Scan method used by the scanner. Always returns `null` ' \
'if `dast_api_scanner` feature flag is disabled.'
def target_url def target_url
object.dast_site.url object.dast_site.url
end end
...@@ -72,5 +76,11 @@ module Types ...@@ -72,5 +76,11 @@ module Types
object object
) )
end end
def scan_method
return unless Feature.enabled?(:dast_api_scanner, object.project, default_enabled: :yaml)
object.scan_method
end
end end
end end
...@@ -14,6 +14,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -14,6 +14,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
let_it_be(:request_headers) { 'Authorization: token' } let_it_be(:request_headers) { 'Authorization: token' }
let_it_be(:target_type) { 'api' } let_it_be(:target_type) { 'api' }
let_it_be(:scan_method) { 'openapi' }
let(:auth) do let(:auth) do
{ {
...@@ -45,7 +46,8 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -45,7 +46,8 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
target_type: target_type, target_type: target_type,
excluded_urls: excluded_urls, excluded_urls: excluded_urls,
request_headers: request_headers, request_headers: request_headers,
auth: auth auth: auth,
scan_method: scan_method
) )
end end
...@@ -63,6 +65,28 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -63,6 +65,28 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
project.add_developer(user) project.add_developer(user)
end end
context 'when the feature flag dast_api_scanner is disabled' do
before do
stub_feature_flags(dast_api_scanner: false)
end
context 'when the target_type is api' do
it 'creates a dast_site_profile with the default value for the scan_method' do
dast_site_profile = subject[:id].find
expect(dast_site_profile.scan_method).to eq('openapi')
end
end
context 'when the target_type is website' do
let_it_be(:target_type) { 'website' }
it 'creates a dast_site_profile with the default value for the scan_method' do
dast_site_profile = subject[:id].find
expect(dast_site_profile.scan_method).to eq('site')
end
end
end
it 'creates a dast_site_profile and dast_site_profile_secret_variables', :aggregate_failures do it 'creates a dast_site_profile and dast_site_profile_secret_variables', :aggregate_failures do
dast_site_profile = subject[:id].find dast_site_profile = subject[:id].find
...@@ -75,7 +99,8 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -75,7 +99,8 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
auth_password_field: auth[:password_field], auth_password_field: auth[:password_field],
auth_username: auth[:username], auth_username: auth[:username],
dast_site: have_attributes(url: target_url), dast_site: have_attributes(url: target_url),
target_type: target_type target_type: target_type,
scan_method: scan_method
) )
password_variable = dast_site_profile.secret_variables.find_by!(key: Dast::SiteProfileSecretVariable::PASSWORD) password_variable = dast_site_profile.secret_variables.find_by!(key: Dast::SiteProfileSecretVariable::PASSWORD)
...@@ -99,6 +124,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do ...@@ -99,6 +124,7 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
target_type: target_type, target_type: target_type,
excluded_urls: excluded_urls, excluded_urls: excluded_urls,
request_headers: request_headers, request_headers: request_headers,
scan_method: scan_method,
auth_enabled: auth[:enabled], auth_enabled: auth[:enabled],
auth_url: auth[:url], auth_url: auth[:url],
auth_username_field: auth[:username_field], auth_username_field: auth[:username_field],
......
...@@ -13,6 +13,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -13,6 +13,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
let(:new_excluded_urls) { ["#{new_target_url}/signout"] } let(:new_excluded_urls) { ["#{new_target_url}/signout"] }
let(:new_request_headers) { "Authorization: Bearer #{SecureRandom.hex}" } let(:new_request_headers) { "Authorization: Bearer #{SecureRandom.hex}" }
let(:new_target_type) { 'api' } let(:new_target_type) { 'api' }
let(:new_scan_method) { 'postman' }
let(:new_auth) do let(:new_auth) do
{ {
...@@ -42,6 +43,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -42,6 +43,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
target_type: new_target_type, target_type: new_target_type,
excluded_urls: new_excluded_urls, excluded_urls: new_excluded_urls,
request_headers: new_request_headers, request_headers: new_request_headers,
scan_method: new_scan_method,
auth: new_auth auth: new_auth
) )
end end
...@@ -63,6 +65,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -63,6 +65,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
target_type: new_target_type, target_type: new_target_type,
excluded_urls: new_excluded_urls, excluded_urls: new_excluded_urls,
request_headers: new_request_headers, request_headers: new_request_headers,
scan_method: new_scan_method,
auth_enabled: new_auth[:enabled], auth_enabled: new_auth[:enabled],
auth_url: new_auth[:url], auth_url: new_auth[:url],
auth_username_field: new_auth[:username_field], auth_username_field: new_auth[:username_field],
...@@ -88,6 +91,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -88,6 +91,7 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
auth_username_field: new_auth[:username_field], auth_username_field: new_auth[:username_field],
auth_password_field: new_auth[:password_field], auth_password_field: new_auth[:password_field],
auth_username: new_auth[:username], auth_username: new_auth[:username],
scan_method: new_scan_method,
dast_site: have_attributes(url: new_target_url) dast_site: have_attributes(url: new_target_url)
) )
...@@ -143,6 +147,18 @@ RSpec.describe Mutations::DastSiteProfiles::Update do ...@@ -143,6 +147,18 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
expect(subject).to include(errors: ['Oops']) expect(subject).to include(errors: ['Oops'])
end end
end end
context 'when the feature flag dast_api_scanner is disabled' do
before do
stub_feature_flags(dast_api_scanner: false)
end
it 'does not update the scan_method and uses the default value according to the target_type' do
dast_site_profile = subject[:id].find
expect(dast_site_profile.scan_method).to eq('openapi')
end
end
end end
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['DastScanMethodType'] do
it 'exposes all alert field names' do
expect(described_class.values.keys).to match_array(%w(WEBSITE OPENAPI HAR POSTMAN_COLLECTION))
end
end
...@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['DastSiteProfile'] do ...@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['DastSiteProfile'] do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, developer_projects: [project]) } let_it_be(:user) { create(:user, developer_projects: [project]) }
let_it_be(:object, reload: true) { create(:dast_site_profile, project: project) } let_it_be(:object, reload: true) { create(:dast_site_profile, project: project) }
let_it_be(:fields) { %i[id profileName targetUrl targetType editPath excludedUrls requestHeaders validationStatus userPermissions normalizedTargetUrl auth referencedInSecurityPolicies] } let_it_be(:fields) { %i[id profileName targetUrl targetType editPath excludedUrls requestHeaders validationStatus userPermissions normalizedTargetUrl auth referencedInSecurityPolicies scanMethod] }
before do before do
stub_licensed_features(security_on_demand_scans: true) stub_licensed_features(security_on_demand_scans: true)
...@@ -105,6 +105,22 @@ RSpec.describe GitlabSchema.types['DastSiteProfile'] do ...@@ -105,6 +105,22 @@ RSpec.describe GitlabSchema.types['DastSiteProfile'] do
end end
end end
describe 'scan_method field' do
context 'when the feature flag is disabled' do
it 'resolves nil' do
stub_feature_flags(dast_api_scanner: false)
expect(resolve_field(:scan_method, object, current_user: user)).to eq(nil)
end
end
context 'when the feature flag is enabled' do
it 'is the scan method' do
expect(resolve_field(:scan_method, object, current_user: user)).to eq('site')
end
end
end
describe 'dast_site_profiles' do describe 'dast_site_profiles' do
subject(:response) do subject(:response) do
GitlabSchema.execute( GitlabSchema.execute(
......
...@@ -20,6 +20,7 @@ RSpec.describe 'Creating a DAST Site Profile' do ...@@ -20,6 +20,7 @@ RSpec.describe 'Creating a DAST Site Profile' do
profile_name: profile_name, profile_name: profile_name,
target_url: target_url, target_url: target_url,
target_type: 'API', target_type: 'API',
scan_method: 'OPENAPI',
excluded_urls: ["#{target_url}/logout"], excluded_urls: ["#{target_url}/logout"],
request_headers: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', request_headers: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',
auth: { auth: {
......
...@@ -22,6 +22,7 @@ RSpec.describe 'Creating a DAST Site Profile' do ...@@ -22,6 +22,7 @@ RSpec.describe 'Creating a DAST Site Profile' do
profile_name: new_profile_name, profile_name: new_profile_name,
target_url: new_target_url, target_url: new_target_url,
target_type: 'API', target_type: 'API',
scan_method: 'OPENAPI',
excluded_urls: ["#{new_target_url}/signout"], excluded_urls: ["#{new_target_url}/signout"],
request_headers: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0', request_headers: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',
auth: { auth: {
......
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