From 378ad688b7e57efb190506b3b36be65eb8ad5e6f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Sun, 30 Nov 2025 11:57:01 +0000 Subject: [PATCH] =?utf8?q?=F0=9F=90=9B=20Fix=20hierarchical=20security=20s?= =?utf8?q?cope=20propagation=20(#5624)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Co-authored-by: svlandeg Co-authored-by: Sofie Van Landeghem --- fastapi/dependencies/utils.py | 4 +- tests/test_security_scopes_dont_propagate.py | 45 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/test_security_scopes_dont_propagate.py diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 1e92c1ba2a..45353835b4 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -278,7 +278,9 @@ def get_dependant( use_security_scopes = security_scopes or [] if isinstance(param_details.depends, params.Security): if param_details.depends.scopes: - use_security_scopes.extend(param_details.depends.scopes) + use_security_scopes = use_security_scopes + list( + param_details.depends.scopes + ) sub_dependant = get_dependant( path=path, call=param_details.depends.dependency, diff --git a/tests/test_security_scopes_dont_propagate.py b/tests/test_security_scopes_dont_propagate.py new file mode 100644 index 0000000000..2bbcc749d3 --- /dev/null +++ b/tests/test_security_scopes_dont_propagate.py @@ -0,0 +1,45 @@ +# Ref: https://github.com/tiangolo/fastapi/issues/5623 + +from typing import Any, Dict, List + +from fastapi import FastAPI, Security +from fastapi.security import SecurityScopes +from fastapi.testclient import TestClient +from typing_extensions import Annotated + + +async def security1(scopes: SecurityScopes): + return scopes.scopes + + +async def security2(scopes: SecurityScopes): + return scopes.scopes + + +async def dep3( + dep1: Annotated[List[str], Security(security1, scopes=["scope1"])], + dep2: Annotated[List[str], Security(security2, scopes=["scope2"])], +): + return {"dep1": dep1, "dep2": dep2} + + +app = FastAPI() + + +@app.get("/scopes") +def get_scopes( + dep3: Annotated[Dict[str, Any], Security(dep3, scopes=["scope3"])], +): + return dep3 + + +client = TestClient(app) + + +def test_security_scopes_dont_propagate(): + response = client.get("/scopes") + assert response.status_code == 200 + assert response.json() == { + "dep1": ["scope3", "scope1"], + "dep2": ["scope3", "scope2"], + } -- 2.47.3