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
path: root/lib
diff options
context:
space:
mode:
authorGitLab Release Tools Bot <robert+release-tools@gitlab.com>2019-06-27 00:41:03 +0300
committerGitLab Release Tools Bot <robert+release-tools@gitlab.com>2019-06-27 00:41:03 +0300
commit55375a154eb4eac249c95d76674b29ba7422159b (patch)
treefeca5df69d1c18433a14044623a74c4264ee08b5 /lib
parentdd6e07eee94fec79c052a7ec0182b4196f8db91b (diff)
parentbb82c34e7eef05cce18adf0304227daeecec6df9 (diff)
Merge branch 'security-fp-prevent-billion-laughs-attack-12-0' into '12-0-stable'
Prevent Billion Laughs attack See merge request gitlab/gitlabhq!3146
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/ci/config.rb3
-rw-r--r--lib/gitlab/config/loader/yaml.rb34
-rw-r--r--lib/gitlab/utils/deep_size.rb79
3 files changed, 112 insertions, 4 deletions
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index f187e456993..a7e4eb47dbb 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -15,6 +15,9 @@ module Gitlab
@global = Entry::Global.new(@config)
@global.compose!
+ rescue Gitlab::Config::Loader::Yaml::DataTooLargeError => e
+ Gitlab::Sentry.track_exception(e, extra: { user: user.inspect, project: project.inspect })
+ raise Config::ConfigError, e.message
rescue Gitlab::Config::Loader::FormatError,
Extendable::ExtensionError,
External::Processor::IncludeError => e
diff --git a/lib/gitlab/config/loader/yaml.rb b/lib/gitlab/config/loader/yaml.rb
index 8159f8b8026..4cedab1545c 100644
--- a/lib/gitlab/config/loader/yaml.rb
+++ b/lib/gitlab/config/loader/yaml.rb
@@ -4,6 +4,13 @@ module Gitlab
module Config
module Loader
class Yaml
+ DataTooLargeError = Class.new(Loader::FormatError)
+
+ include Gitlab::Utils::StrongMemoize
+
+ MAX_YAML_SIZE = 1.megabyte
+ MAX_YAML_DEPTH = 100
+
def initialize(config)
@config = YAML.safe_load(config, [Symbol], [], true)
rescue Psych::Exception => e
@@ -11,16 +18,35 @@ module Gitlab
end
def valid?
- @config.is_a?(Hash)
+ hash? && !too_big?
end
def load!
- unless valid?
- raise Loader::FormatError, 'Invalid configuration format'
- end
+ raise DataTooLargeError, 'The parsed YAML is too big' if too_big?
+ raise Loader::FormatError, 'Invalid configuration format' unless hash?
@config.deep_symbolize_keys
end
+
+ private
+
+ def hash?
+ @config.is_a?(Hash)
+ end
+
+ def too_big?
+ return false unless Feature.enabled?(:ci_yaml_limit_size, default_enabled: true)
+
+ !deep_size.valid?
+ end
+
+ def deep_size
+ strong_memoize(:deep_size) do
+ Gitlab::Utils::DeepSize.new(@config,
+ max_size: MAX_YAML_SIZE,
+ max_depth: MAX_YAML_DEPTH)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/utils/deep_size.rb b/lib/gitlab/utils/deep_size.rb
new file mode 100644
index 00000000000..562cf09e249
--- /dev/null
+++ b/lib/gitlab/utils/deep_size.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'objspace'
+
+module Gitlab
+ module Utils
+ class DeepSize
+ Error = Class.new(StandardError)
+ TooMuchDataError = Class.new(Error)
+
+ DEFAULT_MAX_SIZE = 1.megabyte
+ DEFAULT_MAX_DEPTH = 100
+
+ def initialize(root, max_size: DEFAULT_MAX_SIZE, max_depth: DEFAULT_MAX_DEPTH)
+ @root = root
+ @max_size = max_size
+ @max_depth = max_depth
+ @size = 0
+ @depth = 0
+
+ evaluate
+ end
+
+ def valid?
+ !too_big? && !too_deep?
+ end
+
+ private
+
+ def evaluate
+ add_object(@root)
+ rescue Error
+ # NOOP
+ end
+
+ def too_big?
+ @size > @max_size
+ end
+
+ def too_deep?
+ @depth > @max_depth
+ end
+
+ def add_object(object)
+ @size += ObjectSpace.memsize_of(object)
+ raise TooMuchDataError if @size > @max_size
+
+ add_array(object) if object.is_a?(Array)
+ add_hash(object) if object.is_a?(Hash)
+ end
+
+ def add_array(object)
+ with_nesting do
+ object.each do |n|
+ add_object(n)
+ end
+ end
+ end
+
+ def add_hash(object)
+ with_nesting do
+ object.each do |key, value|
+ add_object(key)
+ add_object(value)
+ end
+ end
+ end
+
+ def with_nesting
+ @depth += 1
+ raise TooMuchDataError if too_deep?
+
+ yield
+
+ @depth -= 1
+ end
+ end
+ end
+end