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

metadata.rb « artifacts « build « ci « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 5748b8e34cf0e4835df0145006d216cf5226639b (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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

require 'zlib'
require 'json'

module Gitlab
  module Ci
    module Build
      module Artifacts
        class Metadata
          ParserError = Class.new(StandardError)
          InvalidStreamError = Class.new(StandardError)

          VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/.freeze
          INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}.freeze

          attr_reader :stream, :path, :full_version

          def initialize(stream, path, **opts)
            @stream = stream

            # Ensure to remove any ./ prefix from the path
            # so that the pattern matching will work as expected
            @path = path.gsub(%r{^\./}, '')

            @opts = opts
            @full_version = read_version
          end

          def version
            @full_version.match(VERSION_PATTERN)[1]
          end

          def errors
            gzip do |gz|
              read_string(gz) # version
              errors = read_string(gz)
              raise ParserError, 'Errors field not found!' unless errors

              begin
                Gitlab::Json.parse(errors)
              rescue JSON::ParserError
                raise ParserError, 'Invalid errors field!'
              end
            end
          end

          def find_entries!
            gzip do |gz|
              2.times { read_string(gz) } # version and errors fields
              match_entries(gz)
            end
          end

          def to_entry
            entries = find_entries!
            Entry.new(@path, entries)
          end

          private

          def match_entries(gz)
            entries = {}

            child_pattern = '[^/]*/?$' unless @opts[:recursive]
            match_pattern = /^#{Regexp.escape(path)}#{child_pattern}/

            until gz.eof?
              begin
                path = read_string(gz)&.force_encoding('UTF-8')
                meta = read_string(gz)&.force_encoding('UTF-8')

                # We might hit an EOF while reading either value, so we should
                # abort if we don't get any data.
                next unless path && meta
                next unless path.valid_encoding? && meta.valid_encoding?
                next unless match_pattern.match?(path)
                next if INVALID_PATH_PATTERN.match?(path)

                entries[path] = Gitlab::Json.parse(meta, symbolize_names: true)
              rescue JSON::ParserError, Encoding::CompatibilityError
                next
              end
            end

            entries
          end

          def read_version
            gzip do |gz|
              version_string = read_string(gz)

              unless version_string
                raise ParserError, 'Artifacts metadata file empty!'
              end

              unless VERSION_PATTERN.match?(version_string)
                raise ParserError, 'Invalid version!'
              end

              version_string.chomp
            end
          end

          def read_uint32(gz)
            binary = gz.read(4)
            binary.unpack1('L>') if binary
          end

          def read_string(gz)
            string_size = read_uint32(gz)
            return unless string_size

            gz.read(string_size)
          end

          def gzip(&block)
            raise InvalidStreamError, "Invalid stream" unless @stream

            # restart gzip reading
            @stream.seek(0)

            gz = Zlib::GzipReader.new(@stream)
            yield(gz)
          rescue Zlib::Error => e
            raise InvalidStreamError, e.message
          ensure
            gz&.finish
          end
        end
      end
    end
  end
end