debian-mirror-gitlab/lib/gitlab/middleware/compressed_json.rb
2023-03-04 22:38:38 +05:30

92 lines
2.4 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module Middleware
class CompressedJson
COLLECTOR_PATH = '/api/v4/error_tracking/collector'
PACKAGES_PATH = %r{
\A/api/v4/ (?# prefix)
(?:projects/
(?<project_id>
.+ (?# at least one character)
)/
)? (?# projects segment)
packages/npm/-/npm/v1/security/
(?:(?:advisories/bulk)|(?:audits/quick))\z (?# end)
}xi.freeze
MAXIMUM_BODY_SIZE = 200.kilobytes.to_i
UNSAFE_CHARACTERS = %r{[!"#&'()*+,./:;<>=?@\[\]^`{}|~$]}xi.freeze
def initialize(app)
@app = app
end
def call(env)
if compressed_et_request?(env)
input = extract(env['rack.input'])
if input.length > MAXIMUM_BODY_SIZE
return too_large
end
env.delete('HTTP_CONTENT_ENCODING')
env['CONTENT_LENGTH'] = input.length
env['rack.input'] = StringIO.new(input)
end
@app.call(env)
end
def compressed_et_request?(env)
post_request?(env) &&
gzip_encoding?(env) &&
match_content_type?(env) &&
match_path?(env)
end
def too_large
[413, { 'Content-Type' => 'text/plain' }, ['Payload Too Large']]
end
def relative_url
File.join('', Gitlab.config.gitlab.relative_url_root).chomp('/')
end
def extract(input)
Zlib::GzipReader.new(input).read(MAXIMUM_BODY_SIZE + 1)
end
def post_request?(env)
env['REQUEST_METHOD'] == 'POST'
end
def gzip_encoding?(env)
env['HTTP_CONTENT_ENCODING'] == 'gzip'
end
def match_content_type?(env)
env['CONTENT_TYPE'].nil? ||
env['CONTENT_TYPE'] == 'application/json' ||
env['CONTENT_TYPE'] == 'application/x-sentry-envelope'
end
def match_path?(env)
env['PATH_INFO'].start_with?((File.join(relative_url, COLLECTOR_PATH))) ||
match_packages_path?(env)
end
def match_packages_path?(env)
match_data = env['PATH_INFO'].delete_prefix(relative_url).match(PACKAGES_PATH)
return false unless match_data
return true unless match_data[:project_id] # instance level endpoint was matched
url_encoded?(match_data[:project_id])
end
def url_encoded?(project_id)
project_id !~ UNSAFE_CHARACTERS
end
end
end
end