Commit 8abf920d authored by George Koltsov's avatar George Koltsov

Refactor SystemHookUrlValidator and specs

Simplify SystemHookUrlValidator to inherit from PublicUrlValidator
Refactor specs to move out shared examples to be used in both
system hooks and public url validators.
parent ac766192
...@@ -14,8 +14,7 @@ class SystemHook < WebHook ...@@ -14,8 +14,7 @@ class SystemHook < WebHook
default_value_for :repository_update_events, true default_value_for :repository_update_events, true
default_value_for :merge_requests_events, false default_value_for :merge_requests_events, false
validates :url, presence: true, public_url: false, system_hook_url: { allow_localhost: lambda(&:allow_local_requests?), validates :url, system_hook_url: true
allow_local_network: lambda(&:allow_local_requests?) }
# Allow urls pointing localhost and the local network # Allow urls pointing localhost and the local network
def allow_local_requests? def allow_local_requests?
......
...@@ -15,8 +15,8 @@ class WebHook < ApplicationRecord ...@@ -15,8 +15,8 @@ class WebHook < ApplicationRecord
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
validates :url, presence: true, public_url: { allow_localhost: lambda(&:allow_local_requests?), validates :url, presence: true
allow_local_network: lambda(&:allow_local_requests?) } validates :url, public_url: true, unless: ->(hook) { hook.is_a?(SystemHook) }
validates :token, format: { without: /\n/ } validates :token, format: { without: /\n/ }
validates :push_events_branch_filter, branch_filter: true validates :push_events_branch_filter, branch_filter: true
......
...@@ -7,23 +7,11 @@ ...@@ -7,23 +7,11 @@
# ApplicationSetting.allow_local_requests_from_system_hooks # ApplicationSetting.allow_local_requests_from_system_hooks
# #
# Example: # Example:
#
# class SystemHook < WebHook # class SystemHook < WebHook
# validates :url, system_hook_url: { allow_localhost: true, allow_local_network: true } # validates :url, system_hook_url: true
# end # end
# #
class SystemHookUrlValidator < AddressableUrlValidator class SystemHookUrlValidator < PublicUrlValidator
DEFAULT_OPTIONS = {
allow_localhost: false,
allow_local_network: false
}.freeze
def initialize(options)
options.reverse_merge!(DEFAULT_OPTIONS)
super(options)
end
def self.allow_setting_local_requests? def self.allow_setting_local_requests?
ApplicationSetting.current&.allow_local_requests_from_system_hooks? ApplicationSetting.current&.allow_local_requests_from_system_hooks?
end end
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
title: Add new outbound network requests application setting for system hooks title: Add new outbound network requests application setting for system hooks
merge_request: 31177 merge_request: 31177
author: author:
type: changed type: added
...@@ -8,10 +8,10 @@ class RenameAllowLocalRequestsFromHooksAndServicesApplicationSetting < ActiveRec ...@@ -8,10 +8,10 @@ class RenameAllowLocalRequestsFromHooksAndServicesApplicationSetting < ActiveRec
disable_ddl_transaction! disable_ddl_transaction!
def up def up
rename_column :application_settings, :allow_local_requests_from_hooks_and_services, :allow_local_requests_from_web_hooks_and_services rename_column_concurrently :application_settings, :allow_local_requests_from_hooks_and_services, :allow_local_requests_from_web_hooks_and_services
end end
def down def down
rename_column :application_settings, :allow_local_requests_from_web_hooks_and_services, :allow_local_requests_from_hooks_and_services cleanup_concurrent_column_rename :application_settings, :allow_local_requests_from_web_hooks_and_services, :allow_local_requests_from_hooks_and_services
end end
end end
# frozen_string_literal: true
class CleanupAllowLocalRequestsFromHooksAndServicesApplicationSettingRename < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
cleanup_concurrent_column_rename :application_settings, :allow_local_requests_from_hooks_and_services, :allow_local_requests_from_web_hooks_and_services
end
def down
rename_column_concurrently :application_settings, :allow_local_requests_from_web_hooks_and_services, :allow_local_requests_from_hooks_and_services
end
end
...@@ -183,7 +183,6 @@ ActiveRecord::Schema.define(version: 2019_07_31_084415) do ...@@ -183,7 +183,6 @@ ActiveRecord::Schema.define(version: 2019_07_31_084415) do
t.string "external_authorization_service_default_label" t.string "external_authorization_service_default_label"
t.boolean "pages_domain_verification_enabled", default: true, null: false t.boolean "pages_domain_verification_enabled", default: true, null: false
t.string "user_default_internal_regex" t.string "user_default_internal_regex"
t.boolean "allow_local_requests_from_web_hooks_and_services", default: false, null: false
t.float "external_authorization_service_timeout", default: 0.5 t.float "external_authorization_service_timeout", default: 0.5
t.text "external_auth_client_cert" t.text "external_auth_client_cert"
t.text "encrypted_external_auth_client_key" t.text "encrypted_external_auth_client_key"
...@@ -230,6 +229,7 @@ ActiveRecord::Schema.define(version: 2019_07_31_084415) do ...@@ -230,6 +229,7 @@ ActiveRecord::Schema.define(version: 2019_07_31_084415) do
t.string "grafana_url", default: "/-/grafana", null: false t.string "grafana_url", default: "/-/grafana", null: false
t.string "outbound_local_requests_whitelist", limit: 255, default: [], null: false, array: true t.string "outbound_local_requests_whitelist", limit: 255, default: [], null: false, array: true
t.integer "raw_blob_request_limit", default: 300, null: false t.integer "raw_blob_request_limit", default: 300, null: false
t.boolean "allow_local_requests_from_web_hooks_and_services", default: false, null: false
t.boolean "allow_local_requests_from_system_hooks", default: true, null: false t.boolean "allow_local_requests_from_system_hooks", default: true, null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id" t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id" t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
......
...@@ -38,11 +38,12 @@ local network from web hooks and services"* in the *"Outbound requests"* section ...@@ -38,11 +38,12 @@ local network from web hooks and services"* in the *"Outbound requests"* section
inside the Admin area under **Settings** inside the Admin area under **Settings**
(`/admin/application_settings/network`): (`/admin/application_settings/network`):
![Outbound requests admin settings](img/outbound_requests_section_v2.png) ![Outbound requests admin settings](img/outbound_requests_section_v12_2.png)
>**Note:** NOTE: **Note:**
*System hooks* are enabled to make requests to local network by default since they are set up by admins. *System hooks* are enabled to make requests to local network by default since they are
However, it can be turned off by disabling *"Allow requests to the local network from system hooks"* option. set up by administrators. However, you can turn this off by disabling the
**Allow requests to the local network from system hooks** option.
<!-- ## Troubleshooting <!-- ## Troubleshooting
......
...@@ -16,7 +16,7 @@ module Gitlab ...@@ -16,7 +16,7 @@ module Gitlab
private private
def allow_local_requests? def allow_local_requests?
Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services? Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
end end
end end
end end
......
...@@ -30,7 +30,7 @@ describe Gitlab::Octokit::Middleware do ...@@ -30,7 +30,7 @@ describe Gitlab::Octokit::Middleware do
context 'when localhost requests are not allowed' do context 'when localhost requests are not allowed' do
before do before do
stub_application_setting(allow_local_requests_from_hooks_and_services: false) stub_application_setting(allow_local_requests_from_web_hooks_and_services: false)
end end
it_behaves_like 'Local URL' it_behaves_like 'Local URL'
...@@ -38,7 +38,7 @@ describe Gitlab::Octokit::Middleware do ...@@ -38,7 +38,7 @@ describe Gitlab::Octokit::Middleware do
context 'when localhost requests are allowed' do context 'when localhost requests are allowed' do
before do before do
stub_application_setting(allow_local_requests_from_hooks_and_services: true) stub_application_setting(allow_local_requests_from_web_hooks_and_services: true)
end end
it_behaves_like 'Public URL' it_behaves_like 'Public URL'
...@@ -50,7 +50,7 @@ describe Gitlab::Octokit::Middleware do ...@@ -50,7 +50,7 @@ describe Gitlab::Octokit::Middleware do
context 'when local network requests are not allowed' do context 'when local network requests are not allowed' do
before do before do
stub_application_setting(allow_local_requests_from_hooks_and_services: false) stub_application_setting(allow_local_requests_from_web_hooks_and_services: false)
end end
it_behaves_like 'Local URL' it_behaves_like 'Local URL'
...@@ -58,7 +58,7 @@ describe Gitlab::Octokit::Middleware do ...@@ -58,7 +58,7 @@ describe Gitlab::Octokit::Middleware do
context 'when local network requests are allowed' do context 'when local network requests are allowed' do
before do before do
stub_application_setting(allow_local_requests_from_hooks_and_services: true) stub_application_setting(allow_local_requests_from_web_hooks_and_services: true)
end end
it_behaves_like 'Public URL' it_behaves_like 'Public URL'
......
...@@ -23,14 +23,14 @@ describe WebHookService do ...@@ -23,14 +23,14 @@ describe WebHookService do
stub_application_setting(setting_name => setting) stub_application_setting(setting_name => setting)
end end
shared_examples_for 'respecting outbound network setting' do shared_examples_for 'respects outbound network setting' do
context 'local requests are allowed' do context 'when local requests are allowed' do
let(:setting) { true } let(:setting) { true }
it { expect(hook.request_options[:allow_local_requests]).to be_truthy } it { expect(hook.request_options[:allow_local_requests]).to be_truthy }
end end
context 'local requests are not allowed' do context 'when local requests are not allowed' do
let(:setting) { false } let(:setting) { false }
it { expect(hook.request_options[:allow_local_requests]).to be_falsey } it { expect(hook.request_options[:allow_local_requests]).to be_falsey }
...@@ -41,14 +41,14 @@ describe WebHookService do ...@@ -41,14 +41,14 @@ describe WebHookService do
let(:setting_name) { :allow_local_requests_from_system_hooks } let(:setting_name) { :allow_local_requests_from_system_hooks }
let(:hook) { described_class.new(build(:system_hook), data, :system_hook) } let(:hook) { described_class.new(build(:system_hook), data, :system_hook) }
include_examples 'respecting outbound network setting' include_examples 'respects outbound network setting'
end end
context 'when ProjectHook' do context 'when ProjectHook' do
let(:setting_name) { :allow_local_requests_from_web_hooks_and_services } let(:setting_name) { :allow_local_requests_from_web_hooks_and_services }
let(:hook) { described_class.new(build(:project_hook), data, :project_hook) } let(:hook) { described_class.new(build(:project_hook), data, :project_hook) }
include_examples 'respecting outbound network setting' include_examples 'respects outbound network setting'
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'url validator examples' do |schemes| RSpec.shared_examples 'url validator examples' do |schemes|
let(:validator) { described_class.new(attributes: [:link_url], **options) } describe '#validate' do
let!(:badge) { build(:badge, link_url: 'http://www.example.com') } let(:validator) { described_class.new(attributes: [:link_url], **options) }
let(:badge) { build(:badge, link_url: 'http://www.example.com') }
subject { validator.validate(badge) } subject { validator.validate(badge) }
describe '#validate' do
context 'with no options' do context 'with no options' do
let(:options) { {} } let(:options) { {} }
...@@ -42,3 +42,52 @@ RSpec.shared_examples 'url validator examples' do |schemes| ...@@ -42,3 +42,52 @@ RSpec.shared_examples 'url validator examples' do |schemes|
end end
end end
end end
RSpec.shared_examples 'public url validator examples' do |setting|
let(:validator) { described_class.new(attributes: [:link_url]) }
let(:badge) { build(:badge, link_url: 'http://www.example.com') }
subject { validator.validate(badge) }
context 'by default' do
it 'blocks urls pointing to localhost' do
badge.link_url = 'https://127.0.0.1'
subject
expect(badge.errors).to be_present
end
it 'blocks urls pointing to the local network' do
badge.link_url = 'https://192.168.1.1'
subject
expect(badge.errors).to be_present
end
end
context 'when local requests are allowed' do
let!(:settings) { create(:application_setting) }
before do
stub_application_setting(setting)
end
it 'does not block urls pointing to localhost' do
badge.link_url = 'https://127.0.0.1'
subject
expect(badge.errors).not_to be_present
end
it 'does not block urls pointing to the local network' do
badge.link_url = 'https://192.168.1.1'
subject
expect(badge.errors).not_to be_present
end
end
end
...@@ -2,27 +2,5 @@ require 'spec_helper' ...@@ -2,27 +2,5 @@ require 'spec_helper'
describe PublicUrlValidator do describe PublicUrlValidator do
include_examples 'url validator examples', AddressableUrlValidator::DEFAULT_OPTIONS[:schemes] include_examples 'url validator examples', AddressableUrlValidator::DEFAULT_OPTIONS[:schemes]
include_examples 'public url validator examples', allow_local_requests_from_web_hooks_and_services: true
context 'by default' do
let(:validator) { described_class.new(attributes: [:link_url]) }
let!(:badge) { build(:badge, link_url: 'http://www.example.com') }
subject { validator.validate(badge) }
it 'blocks urls pointing to localhost' do
badge.link_url = 'https://127.0.0.1'
subject
expect(badge.errors).to be_present
end
it 'blocks urls pointing to the local network' do
badge.link_url = 'https://192.168.1.1'
subject
expect(badge.errors).to be_present
end
end
end end
...@@ -4,55 +4,5 @@ require 'spec_helper' ...@@ -4,55 +4,5 @@ require 'spec_helper'
describe SystemHookUrlValidator do describe SystemHookUrlValidator do
include_examples 'url validator examples', AddressableUrlValidator::DEFAULT_OPTIONS[:schemes] include_examples 'url validator examples', AddressableUrlValidator::DEFAULT_OPTIONS[:schemes]
include_examples 'public url validator examples', allow_local_requests_from_system_hooks: true
context 'by default' do
let(:validator) { described_class.new(attributes: [:link_url]) }
let!(:badge) { build(:badge, link_url: 'http://www.example.com') }
subject { validator.validate(badge) }
it 'blocks urls pointing to localhost' do
badge.link_url = 'https://127.0.0.1'
subject
expect(badge.errors).to be_present
end
it 'blocks urls pointing to the local network' do
badge.link_url = 'https://192.168.1.1'
subject
expect(badge.errors).to be_present
end
end
context 'when local requests are allowed' do
let(:validator) { described_class.new(attributes: [:link_url]) }
let!(:badge) { build(:badge, link_url: 'http://www.example.com') }
let!(:settings) { create(:application_setting) }
subject { validator.validate(badge) }
before do
stub_application_setting(allow_local_requests_from_system_hooks: true)
end
it 'does not block urls pointing to localhost' do
badge.link_url = 'https://127.0.0.1'
subject
expect(badge.errors).not_to be_present
end
it 'does not block urls pointing to the local network' do
badge.link_url = 'https://192.168.1.1'
subject
expect(badge.errors).not_to be_present
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment