summaryrefslogtreecommitdiff
path: root/lib/gitlab/config/loader/multi_doc_yaml.rb
blob: 346adc79896c5bdd70000a35ee7d8dcd13db88bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# frozen_string_literal: true

module Gitlab
  module Config
    module Loader
      class MultiDocYaml
        TooManyDocumentsError = Class.new(Loader::FormatError)
        DataTooLargeError = Class.new(Loader::FormatError)
        NotHashError = Class.new(Loader::FormatError)

        MULTI_DOC_DIVIDER = /^---$/.freeze

        def initialize(config, max_documents:, additional_permitted_classes: [])
          @max_documents = max_documents
          @safe_config = load_config(config, additional_permitted_classes)
        end

        def load!
          raise TooManyDocumentsError, 'The parsed YAML has too many documents' if too_many_documents?
          raise DataTooLargeError, 'The parsed YAML is too big' if too_big?
          raise NotHashError, 'Invalid configuration format' unless all_hashes?

          safe_config.map(&:deep_symbolize_keys)
        end

        private

        attr_reader :safe_config, :max_documents

        def load_config(config, additional_permitted_classes)
          config.split(MULTI_DOC_DIVIDER).filter_map do |document|
            YAML.safe_load(document,
              permitted_classes: [Symbol, *additional_permitted_classes],
              permitted_symbols: [],
              aliases: true
            )
          end
        rescue Psych::Exception => e
          raise Loader::FormatError, e.message
        end

        def all_hashes?
          safe_config.all?(Hash)
        end

        def too_many_documents?
          safe_config.count > max_documents
        end

        def too_big?
          !deep_sizes.all?(&:valid?)
        end

        def deep_sizes
          safe_config.map do |config|
            Gitlab::Utils::DeepSize.new(config,
              max_size: Gitlab::CurrentSettings.current_application_settings.max_yaml_size_bytes,
              max_depth: Gitlab::CurrentSettings.current_application_settings.max_yaml_depth)
          end
        end
      end
    end
  end
end