Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .mike.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ version_selector: true
title_switch: true
versions_file: docs/versions/versions.json
versions:
- 8.0.2
- 8.0.1
- 8.0.0
- 7.8.2
Expand Down Expand Up @@ -61,4 +62,4 @@ versions:
- 0.3.2
- latest
aliases:
latest: 8.0.1
latest: 8.0.2
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ Release Notes

___

Unreleased
----------
v8.0.2 (2026-09-26)
-------------------

Live smoke: exempt_ips scenario (release prep for the guard-core exempt_ips engine release)
-------------------------------------------------------------------------------------------
Community fixes and exempt_ips lockstep: trailing-slash route config, block response Content-Type, live-smoke exempt_ips scenario; guard-core 4.1.0 tracking (v8.0.2)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

- **Tests** - New `tests/live_smoke/scenarios/exempt_ips.py` covering the guard-core `exempt_ips` skip-list end to end through the live stack, required before the next fastapi-guard release tracks the guard-core release that ships the field (PR #118). Three scenarios: an exempt IP (the stack's fixed client IP, resolved from nginx's `X-Real-IP`) exceeding `rate_limit` 2 keeps getting 200 across the crossing while the pipeline writes no rate-limit bucket for it at all (`smoke:rate_limit:rate:<ip>` stays absent in Redis, pinning that the exempt skip sits ahead of the limiter), a penetration-detection payload from the exempt IP still gets 400 (exemption is not immunity); the same limit without `exempt_ips` throttles the identical request sequence at the crossing with 429 and leaves exactly the bucket the exempt run never wrote (the two scenarios form the exempt/non-exempt contrast the single-client harness expresses through paired configs); and an IP that is both exempt and blacklisted gets 403 (blacklist beats exemption). The scenario file is live now but red against PyPI guard-core (< 4.1.0): the config override key is ignored by the older engine's pydantic model, so the exempt scenario's no-throttle assertions fail until the guard-core release ships - matching the documented lockstep hold pattern (the v8.0.0 release was held until guard-core 4.0.0 shipped). Verified live against a locally built guard-core 4.1.0 wheel: `LIVE_SMOKE_ONLY_MODULES=exempt_ips` green (3/3 scenarios), full-module completeness check green.
- **Fixed** - Route decorator config now resolves through Starlette's trailing-slash redirect (#142): when the request path matches no route but toggling the trailing slash does, the middleware reads the config of the route Starlette would 307-redirect to, so a route override registered on `/path` now governs a request to `/path/` (and vice versa) instead of silently falling through to the global defaults. Each router decides with its own `redirect_slashes` (mounted apps for their paths, the including app for an included router's routes), the root path and directly matched requests are untouched.
- **Fixed** - Block responses now declare `Content-Type: text/plain` (#144): the Starlette response factory left Starlette's media type unset, so middleware-generated 403/429/400 pages went out without a content type and clients were left guessing. The factory now passes `media_type="text/plain"`, matching the engine's reference response factory.
- **Tests** - New live-smoke scenario `tests/live_smoke/scenarios/exempt_ips.py` (#145) covering the guard-core `exempt_ips` skip-list end to end through the live stack: an exempt IP exceeding `rate_limit` keeps getting 200 across the crossing while no rate-limit bucket is written for it at all (`smoke:rate_limit:rate:<ip>` stays absent in Redis, pinning that the exempt skip sits ahead of the limiter), a penetration-detection payload from the exempt IP still gets 400 (exemption is not immunity), the identical sequence without `exempt_ips` throttles at the crossing with 429 and leaves exactly the bucket the exempt run never wrote, and an IP that is both exempt and blacklisted gets 403 (blacklist beats exemption). The scenario merged red against PyPI guard-core (< 4.1.0) by design, the documented lockstep hold; with this release it runs green against the published 4.1.0 engine in CI.
- **Compatibility** - The `guard-core` floor rises from `>=4.0.0` to `>=4.1.0` and this release tracks the guard-core 4.1.0 engine (the exempt_ips skip-list, the recon leading-separator gate, the raw-view recon scan, the cross-engine interop harness). Both fixed behaviors are adapter-side; no guard-core API fastapi-guard uses changed shape.

v8.0.1 (2026-09-23)
-------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ You can also download the example app as a Docker container from [GitHub Contain
docker pull ghcr.io/rennf93/fastapi-guard-example:latest

# Or pull a specific version (matches library releases)
docker pull ghcr.io/rennf93/fastapi-guard-example:v8.0.1
docker pull ghcr.io/rennf93/fastapi-guard-example:v8.0.2
```

___
Expand Down
13 changes: 13 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ Release Notes

___

v8.0.2 (2026-09-26)
-------------------

Community fixes and exempt_ips lockstep: trailing-slash route config, block response Content-Type, live-smoke exempt_ips scenario; guard-core 4.1.0 tracking (v8.0.2)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

- **Fixed** - Route decorator config now resolves through Starlette's trailing-slash redirect (#142): when the request path matches no route but toggling the trailing slash does, the middleware reads the config of the route Starlette would 307-redirect to, so a route override registered on `/path` now governs a request to `/path/` (and vice versa) instead of silently falling through to the global defaults. Each router decides with its own `redirect_slashes` (mounted apps for their paths, the including app for an included router's routes), the root path and directly matched requests are untouched.
- **Fixed** - Block responses now declare `Content-Type: text/plain` (#144): the Starlette response factory left Starlette's media type unset, so middleware-generated 403/429/400 pages went out without a content type and clients were left guessing. The factory now passes `media_type="text/plain"`, matching the engine's reference response factory.
- **Tests** - New live-smoke scenario `tests/live_smoke/scenarios/exempt_ips.py` (#145) covering the guard-core `exempt_ips` skip-list end to end through the live stack: an exempt IP exceeding `rate_limit` keeps getting 200 across the crossing while no rate-limit bucket is written for it at all (`smoke:rate_limit:rate:<ip>` stays absent in Redis, pinning that the exempt skip sits ahead of the limiter), a penetration-detection payload from the exempt IP still gets 400 (exemption is not immunity), the identical sequence without `exempt_ips` throttles at the crossing with 429 and leaves exactly the bucket the exempt run never wrote, and an IP that is both exempt and blacklisted gets 403 (blacklist beats exemption). The scenario merged red against PyPI guard-core (< 4.1.0) by design, the documented lockstep hold; with this release it runs green against the published 4.1.0 engine in CI.
- **Compatibility** - The `guard-core` floor rises from `>=4.0.0` to `>=4.1.0` and this release tracks the guard-core 4.1.0 engine (the exempt_ips skip-list, the recon leading-separator gate, the raw-view recon scan, the cross-engine interop harness). Both fixed behaviors are adapter-side; no guard-core API fastapi-guard uses changed shape.

___

v8.0.1 (2026-09-23)
-------------------

Expand Down
3 changes: 2 additions & 1 deletion docs/versions/versions.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"8.0.2": "8.0.2",
"8.0.1": "8.0.1",
"8.0.0": "8.0.0",
"7.8.2": "7.8.2",
Expand Down Expand Up @@ -57,5 +58,5 @@
"0.3.4": "0.3.4",
"0.3.3": "0.3.3",
"0.3.2": "0.3.2",
"latest": "8.0.1"
"latest": "8.0.2"
}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "fastapi_guard"
version = "8.0.1"
version = "8.0.2"
description = "Production-ready security middleware for FastAPI — IP filtering, rate limiting, penetration detection, and 20+ per-route security decorators."
authors = [
{name = "Renzo Franceschini", email = "rennf93@users.noreply.github.com"}
Expand Down Expand Up @@ -37,7 +37,7 @@ keywords = [
]
dependencies = [
"fastapi>=0.109.0",
"guard-core>=4.0.0",
"guard-core>=4.1.0",
"starlette>=0.35.0",
"uvicorn",
]
Expand Down
Loading