fastapi-guard 7.2.1__tar.gz → 7.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/.mike.yml +2 -1
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/CHANGELOG.md +10 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/PKG-INFO +1 -1
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/guard/middleware.py +40 -31
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/pyproject.toml +1 -1
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/.gitattributes +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/.gitignore +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/.python-version +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/.safety-project.ini +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/CODE_OF_CONDUCT.md +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/CONTRIBUTING.md +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/LICENSE +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/MANIFEST.in +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/README.md +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/SECURITY.md +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/context7.json +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/guard/__init__.py +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/guard/_middleware_state.py +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/guard/adapters.py +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/guard/lifespan.py +0 -0
- {fastapi_guard-7.2.1 → fastapi_guard-7.2.2}/guard/py.typed +0 -0
|
@@ -3,6 +3,16 @@ Release Notes
|
|
|
3
3
|
|
|
4
4
|
___
|
|
5
5
|
|
|
6
|
+
v7.2.2 (2026-07-01)
|
|
7
|
+
-------------------
|
|
8
|
+
|
|
9
|
+
include_router route resolution now descends the FastAPI wrapper (v7.2.2)
|
|
10
|
+
------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
- **Fixed** — Per-route decorator config now resolves on `include_router` routes under FastAPI >= 0.115. 7.2.1 resolved routes with `route.matches()` but stopped at FastAPI's `_IncludedRouter` wrapper: its `matches()` returns `Match.FULL` with an empty child-scope and the real routes nested inside. Older FastAPI flattened included routers onto the app (so `matches()` found the leaves directly and CI passed), but newer FastAPI keeps them inside the wrapper — so 7.2.1 resolved nothing there and every per-route decorator (`rate_limit`, `max_request_size`, `content_type_filter`, `custom_validation`, `detection_exclusion`, `suspicious_detection`, `usage_monitor`, `honeypot_detection`, behavioral rules) was still inert. Resolution now descends recursively into matched sub-routers/mounts (`.routes`, `original_router.routes`, `.app.routes`), so per-route config fires on `include_router` and path-parameter routes. Direct routes and single-level `include_router` (prefixed or not) are covered; deeply-nested prefixed routers remain a known limitation of the wrapper's child-scope handling. Supersedes the incomplete 7.2.1 fix.
|
|
13
|
+
|
|
14
|
+
___
|
|
15
|
+
|
|
6
16
|
v7.2.1 (2026-07-01)
|
|
7
17
|
-------------------
|
|
8
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi_guard
|
|
3
|
-
Version: 7.2.
|
|
3
|
+
Version: 7.2.2
|
|
4
4
|
Summary: Production-ready security middleware for FastAPI — IP filtering, rate limiting, penetration detection, and 20+ per-route security decorators.
|
|
5
5
|
Project-URL: Homepage, https://guard-core.com
|
|
6
6
|
Project-URL: Documentation, https://rennf93.github.io/fastapi-guard/latest/
|
|
@@ -373,45 +373,54 @@ class SecurityMiddleware(BaseHTTPMiddleware):
|
|
|
373
373
|
route = request.scope.get("route")
|
|
374
374
|
if route:
|
|
375
375
|
return route
|
|
376
|
-
|
|
377
376
|
app = request.scope.get("app")
|
|
378
|
-
|
|
377
|
+
routes = getattr(app, "routes", None)
|
|
378
|
+
if not routes:
|
|
379
379
|
return None
|
|
380
|
+
return self._match_route(routes, request.scope, 0)
|
|
380
381
|
|
|
382
|
+
def _match_route(self, routes: Any, scope: Any, depth: int) -> Any:
|
|
381
383
|
# This middleware is a BaseHTTPMiddleware, so it runs before the router
|
|
382
|
-
# and scope["route"] is unset
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
#
|
|
386
|
-
#
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if not
|
|
390
|
-
continue
|
|
391
|
-
matches = getattr(r, "matches", None)
|
|
392
|
-
if matches is None:
|
|
384
|
+
# and scope["route"] is unset. Replicate Starlette's matching, descending
|
|
385
|
+
# into sub-routers / mounts (FastAPI's include_router inserts a wrapper
|
|
386
|
+
# route whose real routes are nested inside), so per-route decorator
|
|
387
|
+
# config resolves for path-parameter and include_router routes, not only
|
|
388
|
+
# routes flattened directly onto the app.
|
|
389
|
+
for r in routes:
|
|
390
|
+
matched, child = self._route_full_match(r, scope)
|
|
391
|
+
if not matched:
|
|
393
392
|
continue
|
|
394
|
-
|
|
395
|
-
match, _ = matches(request.scope)
|
|
396
|
-
except Exception:
|
|
397
|
-
continue
|
|
398
|
-
if match == Match.FULL:
|
|
399
|
-
return r
|
|
400
|
-
|
|
401
|
-
# Fallback for route objects that do not implement matches().
|
|
402
|
-
path = request.url.path
|
|
403
|
-
method = request.method
|
|
404
|
-
for r in app.routes:
|
|
405
|
-
if (
|
|
406
|
-
hasattr(r, "path")
|
|
407
|
-
and hasattr(r, "methods")
|
|
408
|
-
and r.path == path
|
|
409
|
-
and method in r.methods
|
|
410
|
-
and hasattr(r, "endpoint")
|
|
411
|
-
):
|
|
393
|
+
if hasattr(r, "endpoint"):
|
|
412
394
|
return r
|
|
395
|
+
if depth >= 8:
|
|
396
|
+
continue
|
|
397
|
+
found = self._match_route(
|
|
398
|
+
self._sub_routes(r), {**scope, **child}, depth + 1
|
|
399
|
+
)
|
|
400
|
+
if found is not None:
|
|
401
|
+
return found
|
|
413
402
|
return None
|
|
414
403
|
|
|
404
|
+
def _route_full_match(self, route: Any, scope: Any) -> tuple[bool, Any]:
|
|
405
|
+
matches = getattr(route, "matches", None)
|
|
406
|
+
if matches is None:
|
|
407
|
+
return False, {}
|
|
408
|
+
try:
|
|
409
|
+
match, child = matches(scope)
|
|
410
|
+
except Exception:
|
|
411
|
+
return False, {}
|
|
412
|
+
return match == Match.FULL, child
|
|
413
|
+
|
|
414
|
+
def _sub_routes(self, route: Any) -> Any:
|
|
415
|
+
included = getattr(route, "original_router", None)
|
|
416
|
+
mounted = getattr(route, "app", None)
|
|
417
|
+
return (
|
|
418
|
+
getattr(route, "routes", None)
|
|
419
|
+
or getattr(included, "routes", None)
|
|
420
|
+
or getattr(mounted, "routes", None)
|
|
421
|
+
or []
|
|
422
|
+
)
|
|
423
|
+
|
|
415
424
|
def _populate_guard_state(
|
|
416
425
|
self, guard_request: StarletteGuardRequest, request: Request
|
|
417
426
|
) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fastapi_guard"
|
|
3
|
-
version = "7.2.
|
|
3
|
+
version = "7.2.2"
|
|
4
4
|
description = "Production-ready security middleware for FastAPI — IP filtering, rate limiting, penetration detection, and 20+ per-route security decorators."
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Renzo Franceschini", email = "rennf93@users.noreply.github.com"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|