-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Fix preflight failure on unknown router paths #11376
base: 4.7.x
Are you sure you want to change the base?
Fix preflight failure on unknown router paths #11376
Conversation
a3f933d
to
7bb04d7
Compare
what the motivation for this pr? i think server shouldn't response 200 on non existing route |
Hi! In the current implementation, OPTIONS requests are being blocked with I believe (though I might be mistaken) that it’s not the responsibility of the CorsFilter to block such requests at this stage. The original CORS request should be blocked if the server fails to handle it, not the preflight OPTIONS request. I encountered this issue after upgrading from Micronaut 4.6.X to 4.7.X and traced it back to this PR: #11097. Let me know if you disagree. If there’s another approach to account for routes handled by |
I am confused by the tests that you deleted "A preflight request is rejected for a non-existing route" and "A preflight request is rejected for a route that does exist but doesn't handle the requested HTTP Method" because they must pass. if you fix some functionality, you should at least leave all previous tests valid, but it's better to add new ones |
Have you reviewed the PR I mentioned (#11097)? I believe it introduced a regression. Additionally, there were changes to the tests in the PR itself, and I don't think I can make those tests pass since I'm essentially saying they don't represent the correct behavior. I'm specifically referring to parts like this one: Sorry for the screenshot I couldn't figure out how to link to this diff 😅. |
yeah, thanks to the link on pr, i got you. |
@@ -113,7 +112,7 @@ class CorsVersionSpec extends Specification { | |||
|
|||
when: | |||
request = HttpRequest.OPTIONS("/new-not-allowed-from-private") | |||
preflightHeaders(null, true).each { k, v -> request.header(k, v)} | |||
preflightHeaders("x-api-version", true).each { k, v -> request.header(k, v)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what the reason of "x-api-version" here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While debugging, I discovered that without the "Access-Control-Request-Headers" header, the test failed to target the correct TestController
endpoint (annotated with @CrossOrigin(allowPrivateNetwork = false)
and @Version("2")
).
Before the proposed changes, I believe the test passed for the wrong reason — the 403 FORBIDDEN
response occurred because the route was not found, not due to the @CrossOrigin(allowPrivateNetwork = false)
configuration.
I think the issue is related to the @Version("2")
annotation since temporarily removing this annotation resolved the problem, but I then decided not to remove the annotation and add the header instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ive seen to the code. this behaviour happens by the reason that matchesOrigin method at CorsFilter returns false if we defined Version annotation but request will not support this header cause accessControlRequestHeaders is null and then it responce 200 cause all cors requests return 200 by this pr. so i guess it test right, thanks
@@ -494,20 +494,15 @@ private MutableHttpResponse<?> handlePreflightRequest(@NonNull HttpRequest<?> re | |||
|
|||
@Nullable | |||
private boolean validatePreflightRequest(@NonNull HttpRequest<?> request, | |||
@NonNull CorsOriginConfiguration config) { | |||
@NonNull CorsOriginConfiguration config) { | |||
Optional<HttpMethod> methodToMatchOptional = validateMethodToMatch(request, config); | |||
if (methodToMatchOptional.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (validateMethodToMatch(request, config).isEmpty() ||
!CorsUtil.isPreflightRequest(request) ||
!hasAllowedHeaders(request, config)) {
return false;
}
|
||
@Property(name = "micronaut.server.cors.configurations.foo.allowed-origins", value = "http://www.foo.com") | ||
@Property(name = "micronaut.server.cors.configurations.foo.exposed-headers", value = "Foo-Header,Bar-Header") | ||
void "A preflight request is rejected for a route that does exist but doesn't handle the requested HTTP Method"() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we could handle it as a special case: if the route exists, we perform an additional check on the HTTP methods it supports. However, if the route is not found, we make no assumptions. Honestly, I find this approach a bit trickier. For me, it’s simpler to treat it as if the route it is not known / found.
I'm not sure I like this change. It is not against spec from what I can tell, but it does weaken the filtering we have. A simple workaround would be to make your filter pre-routing as well, since you clearly don't need route information. Other options for this PR would be to either add a config option to disable CORS preflight handling for some paths, or a config option to disable the route requirement entirely like you do in this PR. |
I think we should extract the |
@yawkat In a similar scenario to this use case (https://docs.micronaut.io/latest/guide/#proxyClient), if I move the proxy filter before the CORS filter and find a way to bypass it, I believe I would then need to handle CORS in every backend behind the gateway. That doesn’t seem ideal. I hope this makes sense, and that I'm not overlooking something. I still think the responsibility of the CORS filter for preflight requests is to simply validate the CORS configuration, not the routes. Of course, I could be wrong, but I thought we were on the same page based on the comments in the PR I'm trying to address; did I misunderstand, or has something changed your perspective? Just curious! Maybe I'm missing some background. 😛 |
I like the idea of using a configuration to enforce or disable route requirements. I can try to update the code accordingly as soon as possible. |
I would not disable validation; I would add a new check to validate the route's validity. |
Hi!
I've tried to propose a fix for this issue #11375 and to update tests.
Let me know if there are any suggestions or feedback. I'm open to any improvements or discussions.
Thanks!