Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancisco Javier López <fjlopez@gitlab.com>2018-06-01 14:43:53 +0300
committerDouwe Maan <douwe@gitlab.com>2018-06-01 14:43:53 +0300
commit840f80d48b7d8363f171f6137cd9f1fbafb52bfc (patch)
tree612c6f9b846f9f2f3b44931db12557024c49ef66 /app/validators
parente206e32881e4fbfcbe647d7b2ee713c99ef1bf99 (diff)
Add validation to webhook and service URLs to ensure they are not blocked because of SSRF
Diffstat (limited to 'app/validators')
-rw-r--r--app/validators/addressable_url_validator.rb45
-rw-r--r--app/validators/importable_url_validator.rb11
-rw-r--r--app/validators/public_url_validator.rb26
-rw-r--r--app/validators/url_placeholder_validator.rb32
-rw-r--r--app/validators/url_validator.rb52
5 files changed, 71 insertions, 95 deletions
diff --git a/app/validators/addressable_url_validator.rb b/app/validators/addressable_url_validator.rb
deleted file mode 100644
index 94542125d43..00000000000
--- a/app/validators/addressable_url_validator.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# 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 http, https, ssh, and git protocols will be considered valid.
-# Provide a `:protocols` option to configure accepted protocols.
-#
-# Example:
-#
-# class User < ActiveRecord::Base
-# validates :personal_url, addressable_url: true
-#
-# validates :ftp_url, addressable_url: { protocols: %w(ftp) }
-#
-# validates :git_url, addressable_url: { protocols: %w(http https ssh git) }
-# end
-#
-class AddressableUrlValidator < ActiveModel::EachValidator
- DEFAULT_OPTIONS = { protocols: %w(http https ssh git) }.freeze
-
- def validate_each(record, attribute, value)
- unless valid_url?(value)
- record.errors.add(attribute, "must be a valid URL")
- end
- end
-
- private
-
- def valid_url?(value)
- return false unless value
-
- valid_protocol?(value) && valid_uri?(value)
- end
-
- def valid_uri?(value)
- Gitlab::UrlSanitizer.valid?(value)
- end
-
- def valid_protocol?(value)
- options = DEFAULT_OPTIONS.merge(self.options)
- value =~ /\A#{URI.regexp(options[:protocols])}\z/
- end
-end
diff --git a/app/validators/importable_url_validator.rb b/app/validators/importable_url_validator.rb
deleted file mode 100644
index 612d3c71913..00000000000
--- a/app/validators/importable_url_validator.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# ImportableUrlValidator
-#
-# This validator blocks projects from using dangerous import_urls to help
-# protect against Server-side Request Forgery (SSRF).
-class ImportableUrlValidator < ActiveModel::EachValidator
- def validate_each(record, attribute, value)
- Gitlab::UrlBlocker.validate!(value, valid_ports: Project::VALID_IMPORT_PORTS)
- rescue Gitlab::UrlBlocker::BlockedUrlError => e
- record.errors.add(attribute, "is blocked: #{e.message}")
- end
-end
diff --git a/app/validators/public_url_validator.rb b/app/validators/public_url_validator.rb
new file mode 100644
index 00000000000..1e8118fccbb
--- /dev/null
+++ b/app/validators/public_url_validator.rb
@@ -0,0 +1,26 @@
+# PublicUrlValidator
+#
+# Custom validator for URLs. This validator works like UrlValidator but
+# it blocks by default urls pointing to localhost or the local network.
+#
+# This validator accepts the same params UrlValidator does.
+#
+# Example:
+#
+# class User < ActiveRecord::Base
+# validates :personal_url, public_url: true
+#
+# validates :ftp_url, public_url: { protocols: %w(ftp) }
+#
+# validates :git_url, public_url: { allow_localhost: true, allow_local_network: true}
+# end
+#
+class PublicUrlValidator < UrlValidator
+ private
+
+ def default_options
+ # By default block all urls pointing to localhost or the local network
+ super.merge(allow_localhost: false,
+ allow_local_network: false)
+ end
+end
diff --git a/app/validators/url_placeholder_validator.rb b/app/validators/url_placeholder_validator.rb
deleted file mode 100644
index dd681218b6b..00000000000
--- a/app/validators/url_placeholder_validator.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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.
-#
-# Also, this validator can help you validate urls with placeholders inside.
-# Usually, if you have a url like 'http://www.example.com/%{project_path}' the
-# URI parser will reject that URL format. Provide a `:placeholder_regex` option
-# to configure accepted placeholders.
-#
-# 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) }
-#
-# validates :placeholder_url, url: { placeholder_regex: /(project_path|project_id|default_branch)/ }
-# end
-#
-class UrlPlaceholderValidator < UrlValidator
- def validate_each(record, attribute, value)
- placeholder_regex = self.options[:placeholder_regex]
- value = value.gsub(/%{#{placeholder_regex}}/, 'foo') if placeholder_regex && value
-
- super(record, attribute, value)
- end
-end
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
index a77beb2683d..8648c4c75e3 100644
--- a/app/validators/url_validator.rb
+++ b/app/validators/url_validator.rb
@@ -15,25 +15,63 @@
# 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.
+#
+# 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)
- unless valid_url?(value)
+ @record = record
+
+ if value.present?
+ value.strip!
+ else
record.errors.add(attribute, "must be a valid URL")
end
+
+ Gitlab::UrlBlocker.validate!(value, blocker_args)
+ rescue Gitlab::UrlBlocker::BlockedUrlError => e
+ record.errors.add(attribute, "is blocked: #{e.message}")
end
private
def default_options
- @default_options ||= { protocols: %w(http https) }
+ # 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
+ }
end
- def valid_url?(value)
- return false if value.nil?
+ 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
- options = default_options.merge(self.options)
+ def blocker_args
+ current_options.slice(:allow_localhost, :allow_local_network, :protocols, :ports).tap do |args|
+ if allow_setting_local_requests?
+ args[:allow_localhost] = args[:allow_local_network] = true
+ end
+ end
+ end
- value.strip!
- value =~ /\A#{URI.regexp(options[:protocols])}\z/
+ def allow_setting_local_requests?
+ ApplicationSetting.current&.allow_local_requests_from_hooks_and_services?
end
end