Repository: encode/starlette

Issue:
Dangerous example regex for CORS Middleware?
Looking at the docs for CORS Middlware here: https://www.starlette.io/middleware/#corsmiddleware , under the `allow_origin_regex` attribute, the example value is `https://.*\.example\.org`.

However, based on the handler code for this at https://github.com/encode/starlette/blob/ab86530eddfcf56e0f7e5ca56f6ab69c15594a7d/starlette/middleware/cors.py#L90 , it appears `https://www.example.org.hacker.com` would pass as a valid origin, right?

It seems like the example should be `https://.*\.example\.org$`, yes?


Discussion:
I don’t recall if it’s automatically anchored. Might be?
Huh, I don't follow you.

Either the documentation needs to be updated to show a safe example, or (the better solution), would be to update https://github.com/encode/starlette/blob/ab86530eddfcf56e0f7e5ca56f6ab69c15594a7d/starlette/middleware/cors.py#L90 to do `fullmatch` instead of `match`
My question was if it was already a full match. (Which yeah, it really ought to be)

Resolving patch:
diff --git a/starlette/middleware/cors.py b/starlette/middleware/cors.py
--- a/starlette/middleware/cors.py
+++ b/starlette/middleware/cors.py
@@ -87,7 +87,7 @@ def is_allowed_origin(self, origin: str) -> bool:
         if self.allow_all_origins:
             return True
 
-        if self.allow_origin_regex is not None and self.allow_origin_regex.match(
+        if self.allow_origin_regex is not None and self.allow_origin_regex.fullmatch(
             origin
         ):
             return True


Test patch:
diff --git a/tests/middleware/test_cors.py b/tests/middleware/test_cors.py
--- a/tests/middleware/test_cors.py
+++ b/tests/middleware/test_cors.py
@@ -122,7 +122,7 @@ def test_cors_allow_origin_regex():
     app.add_middleware(
         CORSMiddleware,
         allow_headers=["X-Example", "Content-Type"],
-        allow_origin_regex="https://*",
+        allow_origin_regex="https://.*",
     )
 
     @app.route("/")
@@ -171,6 +171,39 @@ def homepage(request):
     assert "access-control-allow-origin" not in response.headers
 
 
+def test_cors_allow_origin_regex_fullmatch():
+    app = Starlette()
+
+    app.add_middleware(
+        CORSMiddleware,
+        allow_headers=["X-Example", "Content-Type"],
+        allow_origin_regex="https://.*\.example.org",
+    )
+
+    @app.route("/")
+    def homepage(request):
+        return PlainTextResponse("Homepage", status_code=200)
+
+    client = TestClient(app)
+
+    # Test standard response
+    headers = {"Origin": "https://subdomain.example.org"}
+    response = client.get("/", headers=headers)
+    assert response.status_code == 200
+    assert response.text == "Homepage"
+    assert (
+        response.headers["access-control-allow-origin"]
+        == "https://subdomain.example.org"
+    )
+
+    # Test diallowed standard response
+    headers = {"Origin": "https://subdomain.example.org.hacker.com"}
+    response = client.get("/", headers=headers)
+    assert response.status_code == 200
+    assert response.text == "Homepage"
+    assert "access-control-allow-origin" not in response.headers
+
+
 def test_cors_credentialed_requests_return_specific_origin():
     app = Starlette()