summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Lopez <james@gitlab.com>2019-04-11 06:29:08 +0000
committerJames Lopez <james@gitlab.com>2019-04-11 06:29:08 +0000
commit6643b1c92d3468b84289720c069632df7266fa18 (patch)
treeaeaf0d9503326ec7f51968e8d1de48d83ce90503
parent79bf4bdaad438dc0f82771b102f3c07225a428da (diff)
parentd119d3d1b25aac661e6251addf87b280bd37f0c5 (diff)
downloadgitlab-ce-6643b1c92d3468b84289720c069632df7266fa18.tar.gz
Merge branch 'fix/url_validator_' into 'master'
Align UrlValidator to validate_url gem implementation See merge request gitlab-org/gitlab-ce!27194
-rw-r--r--app/models/application_setting.rb8
-rw-r--r--app/models/badge.rb2
-rw-r--r--app/models/ci/build_runner_session.rb2
-rw-r--r--app/models/environment.rb2
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb2
-rw-r--r--app/models/generic_commit_status.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/releases/link.rb2
-rw-r--r--app/models/remote_mirror.rb2
-rw-r--r--app/validators/addressable_url_validator.rb112
-rw-r--r--app/validators/public_url_validator.rb19
-rw-r--r--app/validators/url_validator.rb104
-rw-r--r--changelogs/unreleased/24985-align-urlvalidator-to-validate_url-gem-implementation.yml5
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb2
-rw-r--r--lib/gitlab/url_blocker.rb10
-rw-r--r--spec/controllers/projects/mirrors_controller_spec.rb2
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb6
-rw-r--r--spec/requests/api/commit_statuses_spec.rb17
-rw-r--r--spec/support/shared_examples/url_validator_examples.rb24
-rw-r--r--spec/validators/addressable_url_validator_spec.rb (renamed from spec/validators/url_validator_spec.rb)131
-rw-r--r--spec/validators/public_url_validator_spec.rb8
21 files changed, 292 insertions, 172 deletions
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index d28a12413bf..21a97c8d773 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -48,17 +48,17 @@ class ApplicationSetting < ApplicationRecord
validates :home_page_url,
allow_blank: true,
- url: true,
+ addressable_url: true,
if: :home_page_url_column_exists?
validates :help_page_support_url,
allow_blank: true,
- url: true,
+ addressable_url: true,
if: :help_page_support_url_column_exists?
validates :after_sign_out_path,
allow_blank: true,
- url: true
+ addressable_url: true
validates :admin_notification_email,
devise_email: true,
@@ -218,7 +218,7 @@ class ApplicationSetting < ApplicationRecord
if: :external_authorization_service_enabled
validates :external_authorization_service_url,
- url: true, allow_blank: true,
+ addressable_url: true, allow_blank: true,
if: :external_authorization_service_enabled
validates :external_authorization_service_timeout,
diff --git a/app/models/badge.rb b/app/models/badge.rb
index a244ed473de..50299cd6652 100644
--- a/app/models/badge.rb
+++ b/app/models/badge.rb
@@ -22,7 +22,7 @@ class Badge < ApplicationRecord
scope :order_created_at_asc, -> { reorder(created_at: :asc) }
- validates :link_url, :image_url, url: { protocols: %w(http https) }
+ validates :link_url, :image_url, addressable_url: true
validates :type, presence: true
def rendered_link_url(project = nil)
diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb
index 80dbb150085..997bf298025 100644
--- a/app/models/ci/build_runner_session.rb
+++ b/app/models/ci/build_runner_session.rb
@@ -13,7 +13,7 @@ module Ci
belongs_to :build, class_name: 'Ci::Build', inverse_of: :runner_session
validates :build, presence: true
- validates :url, url: { protocols: %w(https) }
+ validates :url, addressable_url: { schemes: %w(https) }
def terminal_specification
wss_url = Gitlab::UrlHelpers.as_wss(self.url)
diff --git a/app/models/environment.rb b/app/models/environment.rb
index fa29a83e517..69224635e34 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -35,7 +35,7 @@ class Environment < ApplicationRecord
validates :external_url,
length: { maximum: 255 },
allow_nil: true,
- url: true
+ addressable_url: true
delegate :stop_action, :manual_actions, to: :last_deployment, allow_nil: true
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 70954bf8b05..72270ee8b4f 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -22,7 +22,7 @@ module ErrorTracking
belongs_to :project
- validates :api_url, length: { maximum: 255 }, public_url: true, url: { enforce_sanitization: true, ascii_only: true }, allow_nil: true
+ validates :api_url, length: { maximum: 255 }, public_url: { enforce_sanitization: true, ascii_only: true }, allow_nil: true
validates :api_url, presence: { message: 'is a required field' }, if: :enabled
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index 3028bf21301..8a768b3a2c0 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -3,7 +3,7 @@
class GenericCommitStatus < CommitStatus
before_validation :set_default_values
- validates :target_url, url: true,
+ validates :target_url, addressable_url: true,
length: { maximum: 255 },
allow_nil: true
diff --git a/app/models/project.rb b/app/models/project.rb
index 66fc83113ea..89ad90a964e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -327,7 +327,7 @@ class Project < ApplicationRecord
validates :namespace, presence: true
validates :name, uniqueness: { scope: :namespace_id }
- validates :import_url, public_url: { protocols: ->(project) { project.persisted? ? VALID_MIRROR_PROTOCOLS : VALID_IMPORT_PROTOCOLS },
+ validates :import_url, public_url: { schemes: ->(project) { project.persisted? ? VALID_MIRROR_PROTOCOLS : VALID_IMPORT_PROTOCOLS },
ports: ->(project) { project.persisted? ? VALID_MIRROR_PORTS : VALID_IMPORT_PORTS },
enforce_user: true }, if: [:external_import?, :import_url_changed?]
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
diff --git a/app/models/releases/link.rb b/app/models/releases/link.rb
index 36ec33d3e3e..58c2b98e524 100644
--- a/app/models/releases/link.rb
+++ b/app/models/releases/link.rb
@@ -6,7 +6,7 @@ module Releases
belongs_to :release
- validates :url, presence: true, url: { protocols: %w(http https ftp) }, uniqueness: { scope: :release }
+ validates :url, presence: true, addressable_url: { schemes: %w(http https ftp) }, uniqueness: { scope: :release }
validates :name, presence: true, uniqueness: { scope: :release }
scope :sorted, -> { order(created_at: :desc) }
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 5610cfe0f24..b2fd5394a03 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -17,7 +17,7 @@ class RemoteMirror < ApplicationRecord
belongs_to :project, inverse_of: :remote_mirrors
- validates :url, presence: true, public_url: { protocols: %w(ssh git http https), allow_blank: true, enforce_user: true }
+ validates :url, presence: true, public_url: { schemes: %w(ssh git http https), allow_blank: true, enforce_user: true }
before_save :set_new_remote_name, if: :mirror_url_changed?
diff --git a/app/validators/addressable_url_validator.rb b/app/validators/addressable_url_validator.rb
new file mode 100644
index 00000000000..273e15ef925
--- /dev/null
+++ b/app/validators/addressable_url_validator.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+# AddressableUrlValidator
+#
+# Custom validator for URLs. This is a stricter version of UrlValidator - it also checks
+# for using the right protocol, but it actually parses the URL checking for any syntax errors.
+# The regex is also different from `URI` as we use `Addressable::URI` here.
+#
+# By default, only URLs for the HTTP(S) schemes will be considered valid.
+# Provide a `:schemes` option to configure accepted schemes.
+#
+# Example:
+#
+# class User < ActiveRecord::Base
+# validates :personal_url, addressable_url: true
+#
+# validates :ftp_url, addressable_url: { schemes: %w(ftp) }
+#
+# validates :git_url, addressable_url: { schemes: %w(http https ssh git) }
+# end
+#
+# This validator can also block urls pointing to localhost or the local network to
+# protect against Server-side Request Forgery (SSRF), or check for the right port.
+#
+# Configuration options:
+# * <tt>message</tt> - A custom error message (default is: "must be a valid URL").
+# * <tt>schemes</tt> - Array of URI schemes. Default: +['http', 'https']+
+# * <tt>allow_localhost</tt> - Allow urls pointing to +localhost+. Default: +true+
+# * <tt>allow_local_network</tt> - Allow urls pointing to private network addresses. Default: +true+
+# * <tt>allow_blank</tt> - Allow urls to be +blank+. Default: +false+
+# * <tt>allow_nil</tt> - Allow urls to be +nil+. Default: +false+
+# * <tt>ports</tt> - Allowed ports. Default: +all+.
+# * <tt>enforce_user</tt> - Validate user format. Default: +false+
+# * <tt>enforce_sanitization</tt> - Validate that there are no html/css/js tags. Default: +false+
+#
+# Example:
+# class User < ActiveRecord::Base
+# validates :personal_url, addressable_url: { allow_localhost: false, allow_local_network: false}
+#
+# validates :web_url, addressable_url: { ports: [80, 443] }
+# end
+class AddressableUrlValidator < ActiveModel::EachValidator
+ attr_reader :record
+
+ BLOCKER_VALIDATE_OPTIONS = {
+ schemes: %w(http https),
+ ports: [],
+ allow_localhost: true,
+ allow_local_network: true,
+ ascii_only: false,
+ enforce_user: false,
+ enforce_sanitization: false
+ }.freeze
+
+ DEFAULT_OPTIONS = BLOCKER_VALIDATE_OPTIONS.merge({
+ message: 'must be a valid URL'
+ }).freeze
+
+ def initialize(options)
+ options.reverse_merge!(DEFAULT_OPTIONS)
+
+ super(options)
+ end
+
+ def validate_each(record, attribute, value)
+ @record = record
+
+ unless value.present?
+ record.errors.add(attribute, options.fetch(:message))
+ return
+ end
+
+ value = strip_value!(record, attribute, value)
+
+ Gitlab::UrlBlocker.validate!(value, blocker_args)
+ rescue Gitlab::UrlBlocker::BlockedUrlError => e
+ record.errors.add(attribute, "is blocked: #{e.message}")
+ end
+
+ private
+
+ def strip_value!(record, attribute, value)
+ new_value = value.strip
+ return value if new_value == value
+
+ record.public_send("#{attribute}=", new_value) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def current_options
+ options.map do |option, value|
+ [option, value.is_a?(Proc) ? value.call(record) : value]
+ end.to_h
+ end
+
+ def blocker_args
+ current_options.slice(*BLOCKER_VALIDATE_OPTIONS.keys).tap do |args|
+ if self.class.allow_setting_local_requests?
+ args[:allow_localhost] = args[:allow_local_network] = true
+ end
+ end
+ end
+
+ def self.allow_setting_local_requests?
+ # We cannot use Gitlab::CurrentSettings as ApplicationSetting itself
+ # uses UrlValidator to validate urls. This ends up in a cycle
+ # when Gitlab::CurrentSettings creates an ApplicationSetting which then
+ # calls this validator.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab-ee/issues/9833
+ ApplicationSetting.current&.allow_local_requests_from_hooks_and_services?
+ end
+end
diff --git a/app/validators/public_url_validator.rb b/app/validators/public_url_validator.rb
index 3ff880deedd..91847c5d866 100644
--- a/app/validators/public_url_validator.rb
+++ b/app/validators/public_url_validator.rb
@@ -2,7 +2,7 @@
# PublicUrlValidator
#
-# Custom validator for URLs. This validator works like UrlValidator but
+# Custom validator for URLs. This validator works like AddressableUrlValidator but
# it blocks by default urls pointing to localhost or the local network.
#
# This validator accepts the same params UrlValidator does.
@@ -12,17 +12,20 @@
# class User < ActiveRecord::Base
# validates :personal_url, public_url: true
#
-# validates :ftp_url, public_url: { protocols: %w(ftp) }
+# validates :ftp_url, public_url: { schemes: %w(ftp) }
#
# validates :git_url, public_url: { allow_localhost: true, allow_local_network: true}
# end
#
-class PublicUrlValidator < UrlValidator
- private
+class PublicUrlValidator < AddressableUrlValidator
+ DEFAULT_OPTIONS = {
+ allow_localhost: false,
+ allow_local_network: false
+ }.freeze
- def default_options
- # By default block all urls pointing to localhost or the local network
- super.merge(allow_localhost: false,
- allow_local_network: false)
+ def initialize(options)
+ options.reverse_merge!(DEFAULT_OPTIONS)
+
+ super(options)
end
end
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
deleted file mode 100644
index 3fd015c3cf5..00000000000
--- a/app/validators/url_validator.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-# frozen_string_literal: true
-
-# UrlValidator
-#
-# Custom validator for URLs.
-#
-# By default, only URLs for the HTTP(S) protocols will be considered valid.
-# Provide a `:protocols` option to configure accepted protocols.
-#
-# Example:
-#
-# class User < ActiveRecord::Base
-# validates :personal_url, url: true
-#
-# validates :ftp_url, url: { protocols: %w(ftp) }
-#
-# validates :git_url, url: { protocols: %w(http https ssh git) }
-# end
-#
-# This validator can also block urls pointing to localhost or the local network to
-# protect against Server-side Request Forgery (SSRF), or check for the right port.
-#
-# The available options are:
-# - protocols: Allowed protocols. Default: http and https
-# - allow_localhost: Allow urls pointing to localhost. Default: true
-# - allow_local_network: Allow urls pointing to private network addresses. Default: true
-# - ports: Allowed ports. Default: all.
-# - enforce_user: Validate user format. Default: false
-# - enforce_sanitization: Validate that there are no html/css/js tags. Default: false
-#
-# Example:
-# class User < ActiveRecord::Base
-# validates :personal_url, url: { allow_localhost: false, allow_local_network: false}
-#
-# validates :web_url, url: { ports: [80, 443] }
-# end
-class UrlValidator < ActiveModel::EachValidator
- DEFAULT_PROTOCOLS = %w(http https).freeze
-
- attr_reader :record
-
- def validate_each(record, attribute, value)
- @record = record
-
- unless value.present?
- record.errors.add(attribute, 'must be a valid URL')
- return
- end
-
- value = strip_value!(record, attribute, value)
-
- Gitlab::UrlBlocker.validate!(value, blocker_args)
- rescue Gitlab::UrlBlocker::BlockedUrlError => e
- record.errors.add(attribute, "is blocked: #{e.message}")
- end
-
- private
-
- def strip_value!(record, attribute, value)
- new_value = value.strip
- return value if new_value == value
-
- record.public_send("#{attribute}=", new_value) # rubocop:disable GitlabSecurity/PublicSend
- end
-
- def default_options
- # By default the validator doesn't block any url based on the ip address
- {
- protocols: DEFAULT_PROTOCOLS,
- ports: [],
- allow_localhost: true,
- allow_local_network: true,
- ascii_only: false,
- enforce_user: false,
- enforce_sanitization: false
- }
- end
-
- def current_options
- options = self.options.map do |option, value|
- [option, value.is_a?(Proc) ? value.call(record) : value]
- end.to_h
-
- default_options.merge(options)
- end
-
- def blocker_args
- current_options.slice(*default_options.keys).tap do |args|
- if allow_setting_local_requests?
- args[:allow_localhost] = args[:allow_local_network] = true
- end
- end
- end
-
- def allow_setting_local_requests?
- # We cannot use Gitlab::CurrentSettings as ApplicationSetting itself
- # uses UrlValidator to validate urls. This ends up in a cycle
- # when Gitlab::CurrentSettings creates an ApplicationSetting which then
- # calls this validator.
- #
- # See https://gitlab.com/gitlab-org/gitlab-ee/issues/9833
- ApplicationSetting.current&.allow_local_requests_from_hooks_and_services?
- end
-end
diff --git a/changelogs/unreleased/24985-align-urlvalidator-to-validate_url-gem-implementation.yml b/changelogs/unreleased/24985-align-urlvalidator-to-validate_url-gem-implementation.yml
new file mode 100644
index 00000000000..1143e4effea
--- /dev/null
+++ b/changelogs/unreleased/24985-align-urlvalidator-to-validate_url-gem-implementation.yml
@@ -0,0 +1,5 @@
+---
+title: "Align UrlValidator to validate_url gem implementation"
+merge_request: 27194
+author: Horatiu Eugen Vlad
+type: fixed
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index b30900f7c61..fcf6a25ab00 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -8,7 +8,7 @@ module Gitlab
POST_METHOD = 'POST'.freeze
INVALID_HTTP_METHOD = 'invalid. Only PUT and POST methods allowed.'.freeze
- validates :url, url: true
+ validates :url, addressable_url: true
validate do
unless [PUT_METHOD, POST_METHOD].include?(http_method.upcase)
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
index 9b7b0db9525..641ba70ef83 100644
--- a/lib/gitlab/url_blocker.rb
+++ b/lib/gitlab/url_blocker.rb
@@ -8,7 +8,7 @@ module Gitlab
BlockedUrlError = Class.new(StandardError)
class << self
- def validate!(url, ports: [], protocols: [], allow_localhost: false, allow_local_network: true, ascii_only: false, enforce_user: false, enforce_sanitization: false)
+ def validate!(url, ports: [], schemes: [], allow_localhost: false, allow_local_network: true, ascii_only: false, enforce_user: false, enforce_sanitization: false)
return true if url.nil?
# Param url can be a string, URI or Addressable::URI
@@ -20,7 +20,7 @@ module Gitlab
return true if internal?(uri)
port = get_port(uri)
- validate_protocol!(uri.scheme, protocols)
+ validate_scheme!(uri.scheme, schemes)
validate_port!(port, ports) if ports.any?
validate_user!(uri.user) if enforce_user
validate_hostname!(uri.hostname)
@@ -85,9 +85,9 @@ module Gitlab
raise BlockedUrlError, "Only allowed ports are #{ports.join(', ')}, and any over 1024"
end
- def validate_protocol!(protocol, protocols)
- if protocol.blank? || (protocols.any? && !protocols.include?(protocol))
- raise BlockedUrlError, "Only allowed protocols are #{protocols.join(', ')}"
+ def validate_scheme!(scheme, schemes)
+ if scheme.blank? || (schemes.any? && !schemes.include?(scheme))
+ raise BlockedUrlError, "Only allowed schemes are #{schemes.join(', ')}"
end
end
diff --git a/spec/controllers/projects/mirrors_controller_spec.rb b/spec/controllers/projects/mirrors_controller_spec.rb
index f2b73956e8d..3ababe18055 100644
--- a/spec/controllers/projects/mirrors_controller_spec.rb
+++ b/spec/controllers/projects/mirrors_controller_spec.rb
@@ -79,7 +79,7 @@ describe Projects::MirrorsController do
do_put(project, remote_mirrors_attributes: remote_mirror_attributes)
expect(response).to redirect_to(project_settings_repository_path(project, anchor: 'js-push-remote-settings'))
- expect(flash[:alert]).to match(/Only allowed protocols are/)
+ expect(flash[:alert]).to match(/Only allowed schemes are/)
end
it 'does not create a RemoteMirror object' do
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 62970bd8cb6..445a56ab0d8 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -23,10 +23,10 @@ describe Gitlab::UrlBlocker do
expect(described_class.blocked_url?('https://gitlab.com:25/foo/foo.git', ports: ports)).to be true
end
- it 'returns true for bad protocol' do
- expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', protocols: ['https'])).to be false
+ it 'returns true for bad scheme' do
+ expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', schemes: ['https'])).to be false
expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git')).to be false
- expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', protocols: ['http'])).to be true
+ expect(described_class.blocked_url?('https://gitlab.com/foo/foo.git', schemes: ['http'])).to be true
end
it 'returns true for bad protocol on configured web/SSH host and ports' do
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 9388343c392..b5e45f99109 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -306,7 +306,22 @@ describe API::CommitStatuses do
it 'responds with bad request status and validation errors' do
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['target_url'])
- .to include 'is blocked: Only allowed protocols are http, https'
+ .to include 'is blocked: Only allowed schemes are http, https'
+ end
+ end
+
+ context 'when target URL is an unsupported scheme' do
+ before do
+ post api(post_url, developer), params: {
+ state: 'pending',
+ target_url: 'git://example.com'
+ }
+ end
+
+ it 'responds with bad request status and validation errors' do
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']['target_url'])
+ .to include 'is blocked: Only allowed schemes are http, https'
end
end
end
diff --git a/spec/support/shared_examples/url_validator_examples.rb b/spec/support/shared_examples/url_validator_examples.rb
index 1f7e2f7ff79..25277ccd9aa 100644
--- a/spec/support/shared_examples/url_validator_examples.rb
+++ b/spec/support/shared_examples/url_validator_examples.rb
@@ -1,15 +1,15 @@
-RSpec.shared_examples 'url validator examples' do |protocols|
+RSpec.shared_examples 'url validator examples' do |schemes|
let(:validator) { described_class.new(attributes: [:link_url], **options) }
let!(:badge) { build(:badge, link_url: 'http://www.example.com') }
- subject { validator.validate_each(badge, :link_url, badge.link_url) }
+ subject { validator.validate(badge) }
- describe '#validates_each' do
+ describe '#validate' do
context 'with no options' do
let(:options) { {} }
- it "allows #{protocols.join(',')} protocols by default" do
- expect(validator.send(:default_options)[:protocols]).to eq protocols
+ it "allows #{schemes.join(',')} schemes by default" do
+ expect(validator.options[:schemes]).to eq schemes
end
it 'checks that the url structure is valid' do
@@ -17,25 +17,25 @@ RSpec.shared_examples 'url validator examples' do |protocols|
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
end
- context 'with protocols' do
- let(:options) { { protocols: %w[http] } }
+ context 'with schemes' do
+ let(:options) { { schemes: %w(http) } }
- it 'allows urls with the defined protocols' do
+ it 'allows urls with the defined schemes' do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
- it 'add error if the url protocol does not match the selected ones' do
+ it 'add error if the url scheme does not match the selected ones' do
badge.link_url = 'https://www.example.com'
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
end
end
diff --git a/spec/validators/url_validator_spec.rb b/spec/validators/addressable_url_validator_spec.rb
index 1bb42382e8a..387e84b2d04 100644
--- a/spec/validators/url_validator_spec.rb
+++ b/spec/validators/addressable_url_validator_spec.rb
@@ -2,11 +2,11 @@
require 'spec_helper'
-describe UrlValidator do
+describe AddressableUrlValidator do
let!(:badge) { build(:badge, link_url: 'http://www.example.com') }
- subject { validator.validate_each(badge, :link_url, badge.link_url) }
+ subject { validator.validate(badge) }
- include_examples 'url validator examples', described_class::DEFAULT_PROTOCOLS
+ include_examples 'url validator examples', described_class::DEFAULT_OPTIONS[:schemes]
describe 'validations' do
include_context 'invalid urls'
@@ -14,13 +14,13 @@ describe UrlValidator do
let(:validator) { described_class.new(attributes: [:link_url]) }
it 'returns error when url is nil' do
- expect(validator.validate_each(badge, :link_url, nil)).to be_nil
- expect(badge.errors.first[1]).to eq 'must be a valid URL'
+ expect(validator.validate_each(badge, :link_url, nil)).to be_falsey
+ expect(badge.errors.first[1]).to eq validator.options.fetch(:message)
end
it 'returns error when url is empty' do
- expect(validator.validate_each(badge, :link_url, '')).to be_nil
- expect(badge.errors.first[1]).to eq 'must be a valid URL'
+ expect(validator.validate_each(badge, :link_url, '')).to be_falsey
+ expect(badge.errors.first[1]).to eq validator.options.fetch(:message)
end
it 'does not allow urls with CR or LF characters' do
@@ -30,6 +30,17 @@ describe UrlValidator do
end
end
end
+
+ it 'provides all arguments to UrlBlock validate' do
+ expect(Gitlab::UrlBlocker)
+ .to receive(:validate!)
+ .with(badge.link_url, described_class::BLOCKER_VALIDATE_OPTIONS)
+ .and_return(true)
+
+ subject
+
+ expect(badge.errors).to be_empty
+ end
end
context 'by default' do
@@ -40,7 +51,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
it 'does not block urls pointing to the local network' do
@@ -48,7 +59,23 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
+ end
+
+ it 'does block nil urls' do
+ badge.link_url = nil
+
+ subject
+
+ expect(badge.errors).to be_present
+ end
+
+ it 'does block blank urls' do
+ badge.link_url = '\n\r \n'
+
+ subject
+
+ expect(badge.errors).to be_present
end
it 'strips urls' do
@@ -67,6 +94,40 @@ describe UrlValidator do
end
end
+ context 'when message is set' do
+ let(:message) { 'is blocked: test message' }
+ let(:validator) { described_class.new(attributes: [:link_url], allow_nil: false, message: message) }
+
+ it 'does block nil url with provided error message' do
+ expect(validator.validate_each(badge, :link_url, nil)).to be_falsey
+ expect(badge.errors.first[1]).to eq message
+ end
+ end
+
+ context 'when allow_nil is set to true' do
+ let(:validator) { described_class.new(attributes: [:link_url], allow_nil: true) }
+
+ it 'does not block nil urls' do
+ badge.link_url = nil
+
+ subject
+
+ expect(badge.errors).to be_empty
+ end
+ end
+
+ context 'when allow_blank is set to true' do
+ let(:validator) { described_class.new(attributes: [:link_url], allow_blank: true) }
+
+ it 'does not block blank urls' do
+ badge.link_url = "\n\r \n"
+
+ subject
+
+ expect(badge.errors).to be_empty
+ end
+ end
+
context 'when allow_localhost is set to false' do
let(:validator) { described_class.new(attributes: [:link_url], allow_localhost: false) }
@@ -75,7 +136,21 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
+ end
+
+ context 'when allow_setting_local_requests is set to true' do
+ it 'does not block urls pointing to localhost' do
+ expect(described_class)
+ .to receive(:allow_setting_local_requests?)
+ .and_return(true)
+
+ badge.link_url = 'https://127.0.0.1'
+
+ subject
+
+ expect(badge.errors).to be_empty
+ end
end
end
@@ -87,7 +162,21 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
+ end
+
+ context 'when allow_setting_local_requests is set to true' do
+ it 'does not block urls pointing to local network' do
+ expect(described_class)
+ .to receive(:allow_setting_local_requests?)
+ .and_return(true)
+
+ badge.link_url = 'https://192.168.1.1'
+
+ subject
+
+ expect(badge.errors).to be_empty
+ end
end
end
@@ -100,7 +189,7 @@ describe UrlValidator do
it 'does not block any port' do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
end
@@ -110,7 +199,7 @@ describe UrlValidator do
it 'blocks urls with a different port' do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
end
end
@@ -127,7 +216,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
end
@@ -139,7 +228,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
end
end
@@ -156,7 +245,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
end
@@ -168,7 +257,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
end
end
@@ -191,7 +280,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
it 'prevents unsafe internal urls' do
@@ -199,7 +288,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
it 'allows safe urls' do
@@ -207,7 +296,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
end
@@ -219,7 +308,7 @@ describe UrlValidator do
subject
- expect(badge.errors.empty?).to be true
+ expect(badge.errors).to be_empty
end
end
end
diff --git a/spec/validators/public_url_validator_spec.rb b/spec/validators/public_url_validator_spec.rb
index 710dd3dc38e..f6364fb1dd5 100644
--- a/spec/validators/public_url_validator_spec.rb
+++ b/spec/validators/public_url_validator_spec.rb
@@ -1,20 +1,20 @@
require 'spec_helper'
describe PublicUrlValidator do
- include_examples 'url validator examples', described_class::DEFAULT_PROTOCOLS
+ include_examples 'url validator examples', AddressableUrlValidator::DEFAULT_OPTIONS[:schemes]
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_each(badge, :link_url, badge.link_url) }
+ subject { validator.validate(badge) }
it 'blocks urls pointing to localhost' do
badge.link_url = 'https://127.0.0.1'
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
it 'blocks urls pointing to the local network' do
@@ -22,7 +22,7 @@ describe PublicUrlValidator do
subject
- expect(badge.errors.empty?).to be false
+ expect(badge.errors).to be_present
end
end
end