Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve CORS handler: no CORS headers if no Origin #9344

Merged
merged 4 commits into from Oct 17, 2023

Conversation

bentsku
Copy link
Contributor

@bentsku bentsku commented Oct 13, 2023

Motivation

By default, we always add and send the CORS headers to LocalStack responses.
However, those headers should be sent only when the request has an Origin header set, indicating that this is a CORS request.
We can read more about this here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_request_headers
https://jakearchibald.com/2021/cors/

Note that in any access control request, the Origin header is always sent.

When debugging and checking logs, we always have the CORS headers being sent, and they pollute the logs quite a lot for no benefits.

Changes

Added a check for the presence of the Origin header, and add the headers only if it's there.
Added the Vary header as we conditionally apply CORS headers depending on it.
Added the credentials header.

@bentsku bentsku self-assigned this Oct 13, 2023
@bentsku bentsku added the semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases label Oct 13, 2023
@github-actions
Copy link

github-actions bot commented Oct 13, 2023

LocalStack Community integration with Pro

       2 files  ±0         2 suites  ±0   1h 14m 47s ⏱️ + 3m 12s
2 254 tests ±0  1 753 ✔️ ±0  501 💤 ±0  0 ±0 
2 255 runs  ±0  1 753 ✔️ ±0  502 💤 ±0  0 ±0 

Results for commit 85c3295. ± Comparison against base commit 6665dde.

This pull request removes 8 and adds 8 tests. Note that renamed tests count towards both.
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-False-False]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-False-True]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-True-False]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-True-True]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-False-False]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-False-True]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-True-False]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-True-True]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-False-UrlType.HOST_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-False-UrlType.PATH_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-True-UrlType.HOST_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://allowed-True-UrlType.PATH_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-False-UrlType.HOST_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-False-UrlType.PATH_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-True-UrlType.HOST_BASED]
tests.aws.services.apigateway.test_apigateway_basic.TestAPIGateway ‑ test_invoke_endpoint_cors_headers[http://denied-True-UrlType.PATH_BASED]

♻️ This comment has been updated with latest results.

@coveralls
Copy link

coveralls commented Oct 14, 2023

Coverage Status

coverage: 82.933% (+0.001%) from 82.932% when pulling 85c3295 on improve-cors-handling into 6665dde on master.

@bentsku bentsku marked this pull request as ready for review October 16, 2023 14:05
@bentsku bentsku requested review from dfangl and removed request for calvernaz October 16, 2023 14:06
Copy link
Member

@dfangl dfangl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good!

@@ -258,3 +266,6 @@ def add_cors_headers(request_headers: Headers, response_headers: Headers):
and ACL_ALLOW_PRIVATE_NETWORK not in response_headers
):
response_headers[ACL_ALLOW_PRIVATE_NETWORK] = "true"

# we conditionally apply CORS headers depending on the Origin, so add it to `Vary`
response_headers["Vary"] = "Origin"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

else "*"
)
if "*" not in response_headers.get(ACL_ORIGIN, ""):
response_headers[ACL_CREDENTIALS] = "true"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we have any problems with that or is this just for completeness?

Copy link
Contributor Author

@bentsku bentsku Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've seen the issue pops sometimes in Chrome or Safari where it would say that the request was not allowed because of this header missing. I believe it can be good to add it, as the browser could block some requests in the frontend if we don't include it.

When a request's credentials mode (Request.credentials) is include, browsers will only expose the response to the frontend JavaScript code if the Access-Control-Allow-Credentials value is true.

If the CORS request includes credentials, the response must include the Access-Control-Allow-Credentials: true header, and the value of Access-Control-Allow-Origin must reflect the request's Origin header (* isn't an acceptable value if the request has credentials).

As we control the list of allowed domains, I believe this can be useful, maybe for cognito login page for example?
We also could add a CORS_ALLOW_CREDENTIALS feature flag in the same way as flask_cors, to add this header only if it's set?

Flask-CORS docstring for it:

"""
    :param supports_credentials:
        Allows users to make authenticated requests. If true, injects the
        `Access-Control-Allow-Credentials` header in responses. This allows
        cookies and credentials to be submitted across domains.

        :note: This option cannot be used in conjunction with a '*' origin

        Default : False
    :type supports_credentials: bool
"""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dfangl do you think we should do something like above, or just merge this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just merge it, the impact is probably minimal anyway.

@bentsku bentsku merged commit a486263 into master Oct 17, 2023
29 checks passed
@bentsku bentsku deleted the improve-cors-handling branch October 17, 2023 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants