From b608eef52b7b1a0726d06a32718966b378737be9 Mon Sep 17 00:00:00 2001 From: "Paul B." Date: Mon, 25 Nov 2024 10:29:43 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20make=20sure=20to=20match=20servers=20eve?= =?UTF-8?q?n=20if=20they=20contain=20=E2=80=9Cregexp=E2=80=9D=20chars=20(#?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a fix in the JWT token verification in both: - the server verification which was using an uneeded regex in the `start_with?` parameter - the path verification which was using the matching_server data inside a regexp without escaping the input data I added a test case showcasing the issue. --- proxy_server.rb | 4 ++-- spec/proxy_server_spec.rb | 43 ++++++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/proxy_server.rb b/proxy_server.rb index 866f7cc..f1b9792 100644 --- a/proxy_server.rb +++ b/proxy_server.rb @@ -70,7 +70,7 @@ class ProxyServer < Sinatra::Base target_url = request.fullpath[1..].gsub(":/", "://") # Verify server is allowed - matching_server = @payload["servers"].find { |server| target_url.to_s.start_with?(/^#{server}/) }&.chomp("/") + matching_server = @payload["servers"].find { |server| target_url.to_s.start_with?(server) }&.chomp("/") unless matching_server halt 403, {error: "Server not allowed"}.to_json @@ -91,7 +91,7 @@ class ProxyServer < Sinatra::Base helpers do def path_from_target_url(target_url, matching_server) - target_url.gsub(/^#{matching_server}/, "") + target_url.gsub(/^#{Regexp.escape(matching_server)}/, "") end def path_matches_pattern?(actual_path, pattern_path) diff --git a/spec/proxy_server_spec.rb b/spec/proxy_server_spec.rb index 63fda61..b3afa68 100644 --- a/spec/proxy_server_spec.rb +++ b/spec/proxy_server_spec.rb @@ -62,17 +62,18 @@ def expect_json_body(k, v) .to_return(status: 200, body: {title: "updated title"}.to_json, headers: {}) stub_request(:post, "https://jsonplaceholder.typicode.com/posts") .to_return(status: 201, body: {title: "foo", body: "bar", userId: 1}.to_json, headers: {}) - stub_request(:get, "https://staging.bump.sh/api/v1/ping") - .with( - headers: { - 'Accept'=>'*/*', - 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', - 'Cookie'=>'', - 'Host'=>'staging.bump.sh', - 'User-Agent'=>'Ruby', - 'X-Foo'=>'bar' - }) - .to_return(status: 200, body: "", headers: {}) + ["https://staging.bump.sh/api/v1/ping", "https://bump.sh/api/Custom+Api/v1/ping"].each do |server| + stub_request(:get, server) + .with(headers: { + 'Accept' => '*/*', + 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', + 'Cookie'=>'', + 'Host'=> URI.parse(server).host, + 'User-Agent'=>'Ruby', + 'X-Foo'=>'bar' + }) + .to_return(status: 200, body: "", headers: {}) + end end context "preflight request" do @@ -99,7 +100,6 @@ def expect_json_body(k, v) end context "when server contains some path like /api/v1" do - let(:payload) do { "servers": [ @@ -118,6 +118,25 @@ def expect_json_body(k, v) it "returns 200" do expect(last_response.status).to eq(200) end + + context "when server contains path with regexp character" do + let(:payload) do + { + "servers": [ + "https://bump.sh/api/Custom+Api/v1" + ], + "verb": "GET", + "path": "/ping", + "exp": Time.now.to_i + 500 + } + end + + let(:target_url) { "https://bump.sh/api/Custom+Api/v1/ping"} + + it "returns 200" do + expect(last_response.status).to eq(200) + end + end end it "returns cors headers" do