Skip to content

Commit

Permalink
[CORS] Fixes Cors Headers Match Case Insensitive
Browse files Browse the repository at this point in the history
Resolves #757

This solves two issues with CORS:

Headers currently are not checking case-insensitive values for
`Access-Control-Request-Headers`. Allowed headers should match
against `Access-Control-Request-Headers` values.

There was the belief that `response.flush` writes the contents of the
response halting the request, this is not true, instead the request is
continuing the pipeline.

- Adds case insensitive check to `Access-Control-Request-Headers`
- Validates that `Access-Control-Request-Headers` values match against
ALLOWED_HEADERS
- Adds invalid preflight specs.
- Prevents call next for preflight requests

With these changes in the event of preflight requests should
respond to case insensitive preflight headers and the `call_next`
should not be called.
  • Loading branch information
Elias J. Perez committed Apr 17, 2018
1 parent bea1063 commit 97a50e0
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 18 deletions.
53 changes: 42 additions & 11 deletions spec/amber/pipes/cors_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,51 @@ module Amber::Pipe
end

context "when preflight request" do
it "process valid preflight request" do
context "valid preflight" do
domain = "example.com"
origins = CORS::OriginType.new
origins << domain
context = cors_context(
"OPTIONS",
"Origin": domain,
"Access-Control-Request-Method": "PUT",
"Access-Control-Request-Headers": "Accept"
)
CORS.new(origins: origins).call(context)

context.response.status_code = 200
context.response.headers["Content-Length"].should eq "0"
it "process valid preflight request" do
context = cors_context(
"OPTIONS",
"Origin": domain,
"Access-Control-Request-Method": "PUT",
"Access-Control-Request-Headers": "CoNtEnT-TyPe"
)
CORS.new(origins: origins).call(context)

context.response.status_code = 200
context.response.headers["Content-Length"].should eq "0"
end
end

context "invalid preflight" do
domain = "example.com"
origins = CORS::OriginType.new
origins << domain

it "return status 403 Forbidden for invalid preflight" do
context = cors_context(
"OPTIONS",
"Origin": domain,
"Access-Control-Request-Method": "unsupported method",
"Access-Control-Request-Headers": "CoNtEnT-TyPe"
)
CORS.new(origins: origins).call(context)

context.response.status_code.should eq 403
end

it "return status 403 Forbidden when missing preflight header" do
context = cors_context(
"OPTIONS",
"Origin": domain,
"Access-Control-Request-Method": "PUT",
)
CORS.new(origins: origins).call(context)

context.response.status_code.should eq 403
end
end
end
end
Expand Down
22 changes: 15 additions & 7 deletions src/amber/pipes/cors.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Amber
VARY = "Vary"
ORIGIN = "Origin"
X_ORIGIN = "X-Origin"
OPTIONS = "OPTIONS"
REQUEST_METHOD = "Access-Control-Request-Method"
REQUEST_HEADERS = "Access-Control-Request-Headers"
ALLOW_EXPOSE = "Access-Control-Expose-Headers"
Expand All @@ -19,8 +20,8 @@ module Amber
class CORS < Base
alias OriginType = Array(String | Regex)
FORBIDDEN = "Forbidden for invalid origins, methods or headers"
ALLOW_METHODS = %w(PUT PATCH DELETE)
ALLOW_HEADERS = %w(Accept Content-type)
ALLOW_METHODS = %w(POST PUT PATCH DELETE)
ALLOW_HEADERS = %w(Accept Content-Type)

property origins, headers, methods, credentials, max_age
@origin : Origin
Expand All @@ -42,11 +43,13 @@ module Amber
put_expose_header(context.response)
Preflight.request?(context, self)
put_response_headers(context.response)

if context.request.method != Headers::OPTIONS
call_next(context)
end
else
return forbidden(context)
forbidden(context)
end

call_next(context)
end

def forbidden(context)
Expand Down Expand Up @@ -76,7 +79,7 @@ module Amber
extend self

def request?(context, cors)
if context.request.method == "OPTIONS"
if context.request.method == Headers::OPTIONS
if valid_method?(context.request, cors.methods) &&
valid_headers?(context.request, cors.headers)
put_preflight_headers(context.request, context.response, cors.max_age)
Expand All @@ -99,7 +102,12 @@ module Amber
end

def valid_headers?(request, headers)
!(headers & request.headers[Headers::REQUEST_HEADERS].split(',')).empty?
request_headers = request.headers[Headers::REQUEST_HEADERS]?
return false if request_headers.nil? || request_headers.empty?

headers.any? do |header|
request_headers.downcase.split(',').includes? header.downcase
end
end
end

Expand Down

0 comments on commit 97a50e0

Please sign in to comment.