Skip to content

Commit

Permalink
Validate payload
Browse files Browse the repository at this point in the history
  • Loading branch information
hack3rvaillant committed Nov 4, 2024
1 parent df589f8 commit 088b044
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 39 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ gem "jwt", "~> 2.9"
gem "sinatra", "~> 4.0"
gem "rackup", "~> 2.1"
gem "puma"
gem "logger"

group :development, :test do
gem "rspec", "~> 3.13"
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ GEM
base64
language_server-protocol (3.17.0.3)
lint_roller (1.1.0)
logger (1.6.1)
mustermann (3.0.3)
ruby2_keywords (~> 0.0.1)
nio4r (2.7.3)
Expand Down Expand Up @@ -122,6 +123,7 @@ DEPENDENCIES
debug (>= 1.0.0)
dotenv
jwt (~> 2.9)
logger
puma
rack-test (~> 2.1)
rackup (~> 2.1)
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ Run the following command to start the server on port 4567:
rackup config.ru
```

### Run the tests

Run the following command to run the test
```bash
RACK_ENV=test bundle exec rspec --color -fd spec/proxy_server_spec.rb
```

### Making Requests

- Include the `x-bump-jwt-token` header with a valid JWT in your requests.
Expand Down
45 changes: 43 additions & 2 deletions proxy_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,38 @@ class ProxyServer < Sinatra::Base
# Verify JWT token
begin
public_key = OpenSSL::PKey.read(PUBLIC_KEY)
JWT.decode(token, public_key, true, {algorithm: "RS512"})
@payload = JWT.decode(token, public_key, false, {algorithm: "RS512"})[0]


# Verify token hasn't expired
if Time.now.to_i >= @payload["exp"]
halt 401, {error: "Token has expired"}.to_json
end

# Verify HTTP method matches
unless @payload["verb"] == request.request_method
halt 403, {error: "HTTP method not allowed"}.to_json
end

# Get target URL from the request
target_url = request.fullpath[1..].gsub(":/", "://")
uri = URI.parse(target_url)

# Verify server is allowed
# base_url = "#{uri.scheme}://#{uri.host}#{uri.port == uri.default_port ? '' : ":#{uri.port}"}"
matching_server = @payload["servers"].find { |server| target_url.to_s.include?(server) }

unless matching_server
halt 403, {error: "Server not allowed"}.to_json
end

# Verify path matches the pattern
unless path_matches_pattern?(uri.path, @payload["path"])
halt 403, {error: "Path not allowed"}.to_json
end

JWT.decode(token, public_key, true, {algorithm: "RS512"})[0]

rescue JWT::DecodeError
halt 401, {error: "Invalid token"}.to_json
end
Expand All @@ -51,8 +82,18 @@ class ProxyServer < Sinatra::Base
end

helpers do
def path_matches_pattern?(actual_path, pattern_path)
# Convert pattern with {param} to regex
# e.g., "/docs/{doc_id}/branches/{slug}" becomes /^\/docs\/[^\/]+\/branches\/[^\/]+$/
pattern_regex = pattern_path.gsub(/\{[^}]+\}/, '[^/]+')
pattern_regex = "^#{pattern_regex}$"

# Match the actual path against the regex
Regexp.new(pattern_regex).match?(actual_path)
end

def forward_request(method)
target_url = params["splat"][0].gsub(":/", "://")
target_url = request.fullpath[1..].gsub(":/", "://")
uri = URI.parse(target_url)

# Set up the request to the target server
Expand Down
Loading

0 comments on commit 088b044

Please sign in to comment.