93 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			93 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
 |