Skip to content

Commit

Permalink
Prevent CRLF injection due to broken URL normalizer (#765)
Browse files Browse the repository at this point in the history
* Prevent CRLF injection due to broken URL normalizer

* DRY
  • Loading branch information
c960657 committed Oct 4, 2023
1 parent 3b7133c commit 65276d7
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
10 changes: 8 additions & 2 deletions lib/http/request.rb
Expand Up @@ -172,7 +172,9 @@ def headline
uri.omit(:fragment)
else
uri.request_uri
end
end.to_s

raise RequestError, "Invalid request URI: #{request_uri.inspect}" if request_uri.match?(/\s/)

"#{verb.to_s.upcase} #{request_uri} HTTP/#{version}"
end
Expand Down Expand Up @@ -230,7 +232,11 @@ def port

# @return [String] Default host (with port if needed) header value.
def default_host_header_value
PORTS[@scheme] == port ? host : "#{host}:#{port}"
value = PORTS[@scheme] == port ? host : "#{host}:#{port}"

raise RequestError, "Invalid host: #{value.inspect}" if value.match?(/\s/)

value
end

def prepare_body(body)
Expand Down
23 changes: 20 additions & 3 deletions spec/lib/http_spec.rb
Expand Up @@ -460,20 +460,37 @@ def setsockopt(*args)

context "with :normalize_uri" do
it "normalizes URI" do
response = HTTP.get "#{dummy.endpoint}/hello world"
response = HTTP.get "#{dummy.endpoint}/héllö-wörld"
expect(response.to_s).to eq("hello world")
end

it "uses the custom URI Normalizer method" do
client = HTTP.use(:normalize_uri => {:normalizer => :itself.to_proc})
response = client.get("#{dummy.endpoint}/hello world")
response = client.get("#{dummy.endpoint}/héllö-wörld")
expect(response.status).to eq(400)
end

it "raises if custom URI Normalizer returns invalid path" do
client = HTTP.use(:normalize_uri => {:normalizer => :itself.to_proc})
expect { client.get("#{dummy.endpoint}/hello\nworld") }.
to raise_error HTTP::RequestError, 'Invalid request URI: "/hello\nworld"'
end

it "raises if custom URI Normalizer returns invalid host" do
normalizer = lambda do |uri|
uri.port = nil
uri.instance_variable_set(:@host, "example\ncom")
uri
end
client = HTTP.use(:normalize_uri => {:normalizer => normalizer})
expect { client.get(dummy.endpoint) }.
to raise_error HTTP::RequestError, 'Invalid host: "example\ncom"'
end

it "uses the default URI normalizer" do
client = HTTP.use :normalize_uri
expect(HTTP::URI::NORMALIZER).to receive(:call).and_call_original
response = client.get("#{dummy.endpoint}/hello world")
response = client.get("#{dummy.endpoint}/héllö-wörld")
expect(response.to_s).to eq("hello world")
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/support/dummy_server/servlet.rb
Expand Up @@ -149,7 +149,7 @@ def do_#{method.upcase}(req, res)
res.body = req.body
end

get "/hello world" do |_req, res|
get "/héllö-wörld".b do |_req, res|
res.status = 200
res.body = "hello world"
end
Expand Down

0 comments on commit 65276d7

Please sign in to comment.