Skip to content

Commit

Permalink
Merge pull request #29626 from stuartwdouglas/cors_same_origin
Browse files Browse the repository at this point in the history
Allow CORS same origin requests
  • Loading branch information
sberyozkin authored Dec 16, 2022
2 parents d5f8007 + 2ce7349 commit 8d40e63
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,60 @@ void corsNotMatchingOrigin() {
.header("Access-Control-Allow-Credentials", "false");
}

@Test
void corsSameOriginRequest() {
String origin = "http://localhost:8081";
given().header("Origin", origin)
.get("/test").then()
.statusCode(200)
.header("Access-Control-Allow-Origin", origin);
}

@Test
void corsInvalidSameOriginRequest1() {
String origin = "http";
given().header("Origin", origin)
.get("/test").then()
.statusCode(403)
.header("Access-Control-Allow-Origin", nullValue());
}

@Test
void corsInvalidSameOriginRequest2() {
String origin = "http://local";
given().header("Origin", origin)
.get("/test").then()
.statusCode(403)
.header("Access-Control-Allow-Origin", nullValue());
}

@Test
void corsInvalidSameOriginRequest3() {
String origin = "http://localhost";
given().header("Origin", origin)
.get("/test").then()
.statusCode(403)
.header("Access-Control-Allow-Origin", nullValue());
}

@Test
void corsInvalidSameOriginRequest4() {
String origin = "http://localhost:9999";
given().header("Origin", origin)
.get("/test").then()
.statusCode(403)
.header("Access-Control-Allow-Origin", nullValue());
}

@Test
void corsInvalidSameOriginRequest5() {
String origin = "https://localhost:8483";
given().header("Origin", origin)
.get("/test").then()
.statusCode(403)
.header("Access-Control-Allow-Origin", nullValue());
}

@Test
@DisplayName("Returns false 'Access-Control-Allow-Credentials' header on matching origin '*'")
void corsMatchingOriginWithWildcard() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.vertx.http.runtime.cors;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -80,7 +81,7 @@ public static List<Pattern> parseAllowedOriginsRegex(Optional<List<String>> allo
* If any regular expression origins are configured, try to match on them.
* Regular expressions must begin and end with '/'
*
* @param allowedOrigins the configured regex origins.
* @param allowOriginsRegex the configured regex origins.
* @param origin the specified origin
* @return true if any configured regular expressions match the specified origin, false otherwise
*/
Expand Down Expand Up @@ -179,7 +180,7 @@ public void handle(RoutingContext event) {
}

boolean allowsOrigin = isConfiguredWithWildcard(corsConfig.origins) || corsConfig.origins.get().contains(origin)
|| isOriginAllowedByRegex(allowedOriginsRegex, origin);
|| isOriginAllowedByRegex(allowedOriginsRegex, origin) || isSameOrigin(request, origin);

if (allowsOrigin) {
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
Expand Down Expand Up @@ -213,4 +214,80 @@ public void handle(RoutingContext event) {
}
}
}

static boolean isSameOrigin(HttpServerRequest request, String origin) {
//fast path check, when everything is the same
if (origin.startsWith(request.scheme())) {
if (!substringMatch(origin, request.scheme().length(), "://", false)) {
return false;
}
if (substringMatch(origin, request.scheme().length() + 3, request.host(), true)) {
//they are a simple match
return true;
}
return isSameOriginSlowPath(request, origin);
} else {
return false;
}
}

static boolean isSameOriginSlowPath(HttpServerRequest request, String origin) {
String absUriString = request.absoluteURI();
//we already know the scheme is correct, as the fast path will reject that
URI baseUri = URI.create(absUriString);
URI originUri = URI.create(origin);
if (!originUri.getPath().isEmpty()) {
//origin should not contain a path component
//just reject it in this case
return false;
}
if (!baseUri.getHost().equals(originUri.getHost())) {
return false;
}
if (baseUri.getPort() == originUri.getPort()) {
return true;
}
if (baseUri.getPort() != -1 && originUri.getPort() != -1) {
//ports are explictly set
return false;
}
if (baseUri.getScheme().equals("http")) {
if (baseUri.getPort() == 80 || baseUri.getPort() == -1) {
if (originUri.getPort() == 80 || originUri.getPort() == -1) {
//port is either unset or 80
return true;
}
}
} else if (baseUri.getScheme().equals("https")) {
if (baseUri.getPort() == 443 || baseUri.getPort() == -1) {
if (originUri.getPort() == 443 || originUri.getPort() == -1) {
//port is either unset or 443
return true;
}
}
}
return false;
}

static boolean substringMatch(String str, int pos, String substring, boolean requireFull) {
int length = str.length();
int subLength = substring.length();
int strPos = pos;
int subPos = 0;
if (pos + subLength > length) {
//too long, avoid checking in the loop
return false;
}
for (;;) {
if (subPos == subLength) {
//if we are at the end return the correct value, depending on if we are also at the end of the origin
return !requireFull || strPos == length;
}
if (str.charAt(strPos) != substring.charAt(subPos)) {
return false;
}
strPos++;
subPos++;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import static io.quarkus.vertx.http.runtime.cors.CORSFilter.isConfiguredWithWildcard;
import static io.quarkus.vertx.http.runtime.cors.CORSFilter.isOriginAllowedByRegex;
import static io.quarkus.vertx.http.runtime.cors.CORSFilter.isSameOrigin;
import static io.quarkus.vertx.http.runtime.cors.CORSFilter.parseAllowedOriginsRegex;
import static io.quarkus.vertx.http.runtime.cors.CORSFilter.substringMatch;

import java.util.Arrays;
import java.util.Collections;
Expand All @@ -12,6 +14,9 @@

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import io.vertx.core.http.HttpServerRequest;

public class CORSFilterTest {

Expand All @@ -37,4 +42,46 @@ public void isOriginAllowedByRegexTest() {
Assertions.assertEquals(regexList.size(), 1);
Assertions.assertTrue(isOriginAllowedByRegex(regexList, "https://abc-123.app.mydomain.com"));
}

@Test
public void sameOriginTest() {
var request = Mockito.mock(HttpServerRequest.class);
Mockito.when(request.scheme()).thenReturn("http");
Mockito.when(request.host()).thenReturn("localhost");
Mockito.when(request.absoluteURI()).thenReturn("http://localhost");
Assertions.assertTrue(isSameOrigin(request, "http://localhost"));
Assertions.assertTrue(isSameOrigin(request, "http://localhost:80"));
Assertions.assertFalse(isSameOrigin(request, "http://localhost:8080"));
Assertions.assertFalse(isSameOrigin(request, "https://localhost"));
Mockito.when(request.host()).thenReturn("localhost:8080");
Mockito.when(request.absoluteURI()).thenReturn("http://localhost:8080");
Assertions.assertFalse(isSameOrigin(request, "http://localhost"));
Assertions.assertFalse(isSameOrigin(request, "http://localhost:80"));
Assertions.assertTrue(isSameOrigin(request, "http://localhost:8080"));
Assertions.assertFalse(isSameOrigin(request, "https://localhost:8080"));
Mockito.when(request.scheme()).thenReturn("https");
Mockito.when(request.host()).thenReturn("localhost");
Mockito.when(request.absoluteURI()).thenReturn("http://localhost");
Assertions.assertFalse(isSameOrigin(request, "http://localhost"));
Assertions.assertFalse(isSameOrigin(request, "http://localhost:443"));
Assertions.assertFalse(isSameOrigin(request, "https://localhost:8080"));
Assertions.assertTrue(isSameOrigin(request, "https://localhost"));
Mockito.when(request.host()).thenReturn("localhost:8443");
Mockito.when(request.absoluteURI()).thenReturn("https://localhost:8443");
Assertions.assertFalse(isSameOrigin(request, "http://localhost"));
Assertions.assertFalse(isSameOrigin(request, "http://localhost:80"));
Assertions.assertFalse(isSameOrigin(request, "http://localhost:8443"));
Assertions.assertTrue(isSameOrigin(request, "https://localhost:8443"));

}

@Test
public void testSubstringMatches() {
Assertions.assertTrue(substringMatch("localhost", 0, "local", false));
Assertions.assertFalse(substringMatch("localhost", 0, "local", true));
Assertions.assertFalse(substringMatch("localhost", 1, "local", false));
Assertions.assertTrue(substringMatch("localhost", 5, "host", false));
Assertions.assertTrue(substringMatch("localhost", 5, "host", true));

}
}

0 comments on commit 8d40e63

Please sign in to comment.