Commit db9f80fa authored by Stan Hu's avatar Stan Hu

Log large multipart messages from Rack

This will help track down some routes that appear to be storing
large temporary files.

Relates to
https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3553
parent 72c5f09a
---
title: Log large multipart messages from Rack
merge_request: 55933
author:
type: added
# frozen_string_literal: true
module Rack
module Multipart
class << self
module MultipartPatch
def extract_multipart(req, params = Rack::Utils.default_query_parser)
log_multipart_warning(req) if log_large_multipart?
super
end
def log_multipart_warning(req)
content_length = req.content_length.to_i
return unless content_length > 500_000_000
message = {
message: "Large multipart body detected",
path: req.path,
content_length: content_length,
correlation_id: ::Labkit::Context.correlation_id
}
log_warn(message)
end
def log_warn(message)
warn message.to_json
end
def log_large_multipart?
Gitlab::Utils.to_boolean(ENV['ENABLE_RACK_MULTIPART_LOGGING'], default: true) && Gitlab.com?
end
end
prepend MultipartPatch
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Rack::Multipart do # rubocop:disable RSpec/FilePath
def multipart_fixture(name, length, boundary = "AaB03x")
data = <<EOF
--#{boundary}\r
content-disposition: form-data; name="reply"\r
\r
yes\r
--#{boundary}\r
content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
Content-Type: image/jpeg\r
Content-Transfer-Encoding: base64\r
\r
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
--#{boundary}--\r
EOF
type = %(multipart/form-data; boundary=#{boundary})
length ||= data.bytesize
{
"CONTENT_TYPE" => type,
"CONTENT_LENGTH" => length.to_s,
input: StringIO.new(data)
}
end
context 'with Content-Length under the limit' do
it 'extracts multipart message' do
env = Rack::MockRequest.env_for("/", multipart_fixture(:text, nil))
expect(described_class).to receive(:log_large_multipart?).and_call_original
expect(described_class).not_to receive(:log_multipart_warning)
params = described_class.parse_multipart(env)
expect(params.keys).to include(*%w(reply fileupload))
end
end
context 'with Content-Length over the limit' do
it 'extracts multipart message' do
env = Rack::MockRequest.env_for("/", multipart_fixture(:text, 500_000_001))
expect(described_class).to receive(:log_large_multipart?).and_return(true)
expect(described_class).to receive(:log_multipart_warning).and_call_original
expect(described_class).to receive(:log_warn).with({
message: 'Large multipart body detected',
path: '/',
content_length: anything,
correlation_id: anything
})
params = described_class.parse_multipart(env)
expect(params.keys).to include(*%w(reply fileupload))
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment