feat: add per-IP rate limiting with configurable env vars and 429 handler#80
feat: add per-IP rate limiting with configurable env vars and 429 handler#80harika880 wants to merge 7 commits into
Conversation
📝 WalkthroughWalkthroughAdds optional API-key auth and per-IP rate limiting (slowapi) to API endpoints, swaps permissive CORS for env-driven origins, introduces an HTTP event forwarder and registration guard, adds a CLI module entrypoint, and includes tests plus the slowapi dependency. ChangesAPI Security and Event Forwarding
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@agentwatch/core/http_forwarder.py`:
- Around line 33-38: Forwarder currently posts events without authentication and
will 401 when API-key auth is enabled; update the POST in http_forwarder.py (the
AsyncClient block that posts to f"{self.api_url}/api/v1/events") to include the
same API-key header used by the service's _require_api_key flow. Retrieve the
API key from the same source as authentication in this service (e.g.
self.api_key or the incoming request header) and add it to the request headers
(match the existing scheme: Authorization: Bearer <key> or X-API-Key: <key>)
alongside Content-Type so forwarded events authenticate successfully.
In `@tests/test_rate_limiting.py`:
- Around line 130-153: The test test_different_ips_have_independent_limits
currently relies on limiter.reset() instead of simulating distinct client IPs;
update the two AsyncClient usages to pass different client addresses into
ASGITransport (e.g., client=("1.2.3.4", 123) for c1 and client=("5.6.7.8", 123)
for c2) so slowapi.util.get_remote_address sees different request.client.host
values, and remove the second limiter.reset() call so the assertion that c2's
request returns 200 truly verifies per-IP isolation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 8bf2810e-1675-4e18-a939-f647fab23564
📒 Files selected for processing (4)
agentwatch/__main__.pyagentwatch/api/server.pyagentwatch/core/http_forwarder.pytests/test_rate_limiting.py
| async with httpx.AsyncClient(timeout=2.0) as client: | ||
| resp = await client.post( | ||
| f"{self.api_url}/api/v1/events", | ||
| content=event.model_dump_json(exclude_none=True), | ||
| headers={"Content-Type": "application/json"}, | ||
| ) |
There was a problem hiding this comment.
Forwarded event requests are unauthenticated, so ingestion can break when API-key auth is enabled.
This POST path doesn’t include the API key, but event-ingestion endpoints are now auth-protected in this PR stack. In API-key deployments, forwarding will fail with 401 and silently drop cross-process events. Please propagate the same API-key mechanism/header used by _require_api_key into this forwarder request.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@agentwatch/core/http_forwarder.py` around lines 33 - 38, Forwarder currently
posts events without authentication and will 401 when API-key auth is enabled;
update the POST in http_forwarder.py (the AsyncClient block that posts to
f"{self.api_url}/api/v1/events") to include the same API-key header used by the
service's _require_api_key flow. Retrieve the API key from the same source as
authentication in this service (e.g. self.api_key or the incoming request
header) and add it to the request headers (match the existing scheme:
Authorization: Bearer <key> or X-API-Key: <key>) alongside Content-Type so
forwarded events authenticate successfully.
| async def test_different_ips_have_independent_limits(): | ||
| """After resetting the limiter (simulating a new IP bucket), requests succeed again.""" | ||
| from agentwatch.api.server import app, limiter | ||
|
|
||
| limiter.reset() | ||
|
|
||
| # Exhaust the limit for current IP | ||
| async with AsyncClient( | ||
| transport=ASGITransport(app=app), | ||
| base_url="http://testserver", | ||
| ) as c1: | ||
| await _hit(c1, "/health", n=5) | ||
| r = (await _hit(c1, "/health", n=1))[0] | ||
| assert r.status_code == 429 | ||
|
|
||
| # Reset limiter (simulates a different IP bucket having its own clean slate) | ||
| limiter.reset() | ||
|
|
||
| async with AsyncClient( | ||
| transport=ASGITransport(app=app), | ||
| base_url="http://testserver", | ||
| ) as c2: | ||
| r2 = (await _hit(c2, "/health", n=1))[0] | ||
| assert r2.status_code == 200 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify test currently depends on global reset instead of distinct client IPs.
rg -n "test_different_ips_have_independent_limits|limiter\.reset\(|ASGITransport\(app=app\)" tests/test_rate_limiting.py -n -C 2Repository: sreerevanth/AgentWatch
Length of output: 1000
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect the whole test file around the helper(s) and the limiter setup usage.
echo "==== tests/test_rate_limiting.py (relevant sections) ===="
sed -n '1,220p' tests/test_rate_limiting.py
# 2) Locate limiter configuration and keying semantics.
echo "==== Search limiter/key_func usage in repo ===="
rg -n --hidden --glob '!*node_modules/*' -S "limiter\.reset|key_func|get_remote_address|remote_address|slowapi|Limiter|default_limits|rate limit" .
# 3) Specifically inspect agentwatch/api/server.py (or similarly named) if present.
echo "==== Inspect agentwatch/api/server.* ===="
fd -a -t f 'server\.py$' agentwatch tests || true
fd -a -t f 'api' agentwatch -e || trueRepository: sreerevanth/AgentWatch
Length of output: 5923
🏁 Script executed:
python3 - <<'PY'
import inspect, slowapi.util
print("slowapi.util file:", slowapi.util.__file__)
print(inspect.getsource(slowapi.util.get_remote_address))
PYRepository: sreerevanth/AgentWatch
Length of output: 186
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "==== dependency files (pyproject/requirements/lock) ===="
ls -la
fd -t f -a pyproject.toml requirements*.txt* poetry.lock* setup.cfg* tox.ini* 2>/dev/null || true
echo "==== Search for slowapi version spec ===="
rg -n "slowapi" -S pyproject.toml requirements*.txt poetry.lock setup.cfg tox.ini 2>/dev/null || true
rg -n "slowapi" -S . --glob '!*node_modules/*' --glob '!*__pycache__/*'
echo "==== Inspect agentwatch/api/server.py (limiter + middleware) ===="
sed -n '1,120p' agentwatch/api/server.py
sed -n '120,260p' agentwatch/api/server.py
echo "==== Search tests/test_rate_limiting.py for forwarded headers / overrides ===="
rg -n "forwarded|x-forwarded-for|client|remote_address|get_remote_address" tests/test_rate_limiting.pyRepository: sreerevanth/AgentWatch
Length of output: 13321
🌐 Web query:
slowapi util get_remote_address implementation request.client.host X-Forwarded-For
💡 Result:
SlowAPI provides two built-in helper functions in its slowapi.util module for determining the client's IP address: get_remote_address and get_ipaddr [1][2]. 1. get_remote_address: This function directly returns request.client.host [1]. It represents the IP address of the direct connection to the server [3]. It is considered more secure for applications not running behind a proxy, as it cannot be spoofed by client-provided HTTP headers [3][4]. If request.client or request.client.host is unavailable, it defaults to "127.0.0.1" [1]. 2. get_ipaddr: This function is intended to inspect the X-Forwarded-For header [1][2]. However, it is widely documented as unreliable and potentially insecure for production use [3][5][6]. Specifically: - The implementation has been reported to check for an incorrectly formatted header key (X_FORWARDED_FOR with underscores) [5]. - Even when corrected, it does not reliably parse multi-hop IP lists often found in the X-Forwarded-For header [5]. - Relying on X-Forwarded-For without proper validation allows attackers to easily spoof their IP address, leading to rate-limiting bypasses [3][4]. Best Practices: - Do not use the built-in get_ipaddr in production [6]. - If you are behind a trusted reverse proxy (e.g., Nginx, Cloudflare), use Uvicorn’s ProxyHeadersMiddleware [1][7]. This middleware is designed to parse headers like X-Forwarded-For correctly, validate them against trusted proxy IPs, and update request.client.host automatically [1][7]. Once this middleware is configured, you can simply use the standard get_remote_address in SlowAPI [1][3]. - If you cannot use ProxyHeadersMiddleware, implement a custom key_func in your Limiter that explicitly trusts and extracts the correct header (e.g., CF-Connecting-IP for direct Cloudflare setups) while sanitizing the input [3].
Citations:
- 1: https://github.com/laurentS/slowapi/blob/5e2b9c10/slowapi/util.py
- 2: https://slowapi.readthedocs.io/en/latest/api/
- 3: https://medium.com/@amarharolikar/are-you-rate-limiting-the-wrong-ips-a-slowapi-story-88c2755f5318
- 4: https://dev.to/ratecalc/how-i-secured-my-fastapi-app-6-vulnerabilities-fixed-in-one-session-with-gstack-cso-2i4
- 5: get_ipaddr reads wrong header key ("X_FORWARDED_FOR") and doesn't parse X-Forwarded-For list laurentS/slowapi#255
- 6: XFF Standard
X-Forwarded-Forheader laurentS/slowapi#126 - 7: https://stackoverflow.com/questions/60098005/fastapi-starlette-get-client-real-ip
🌐 Web query:
httpx ASGITransport client parameter set remote address (host, port)
💡 Result:
To set the remote address for requests made through an httpx.ASGITransport, you use the client parameter during the transport's initialization [1][2]. The client parameter expects a two-tuple consisting of the client IP address (as a string) and the port (as an integer), such as ("1.2.3.4", 123) [1][3]. When an ASGITransport is configured this way, it injects these values into the ASGI scope as the client field for every request handled by the transport [3][4]. Example usage: python import httpx # Instantiate the transport with a specific client IP and port transport = httpx.ASGITransport(app=app, client=("1.2.3.4", 123)) # Use the transport with an AsyncClient async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client: response = await client.get("/") Note that this configuration is specific to the ASGITransport class, which is used for testing ASGI applications directly [1][2]. This is distinct from the remote_addr parameter used for httpx.WSGITransport [1][5]. If you are looking to inspect the actual network address of an external server request (rather than setting the client address for an internal application test), you would typically use response extensions like r.extensions["network_stream"] [6].
Citations:
- 1: https://httpx2.pydantic.dev/advanced/transports/
- 2: https://www.python-httpx.org/advanced/transports/
- 3: https://github.com/encode/httpx/blob/master/httpx/_transports/asgi.py
- 4: Can add `state` parameter in httpx.ASGITransport? encode/httpx#3464
- 5: https://github.com/encode/httpx/blob/master/docs/advanced/transports.md
- 6: How to get Remote Address with httpx? encode/httpx#1569
Per-IP isolation test doesn’t exercise different client IPs
Limiter uses slowapi.util.get_remote_address (i.e., request.client.host) as key_func, but the test’s two AsyncClient instances both use ASGITransport(app=app) with no client=... override—so they hit the same (default) client IP. As a result, limiter.reset() alone (e.g., at line 146) can make the second request return 200 even if rate limiting isn’t truly per-IP.
Fix by creating c1/c2 with different ASGITransport(..., client=("1.2.3.4", 123)) values and removing reliance on the second limiter.reset().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_rate_limiting.py` around lines 130 - 153, The test
test_different_ips_have_independent_limits currently relies on limiter.reset()
instead of simulating distinct client IPs; update the two AsyncClient usages to
pass different client addresses into ASGITransport (e.g., client=("1.2.3.4",
123) for c1 and client=("5.6.7.8", 123) for c2) so
slowapi.util.get_remote_address sees different request.client.host values, and
remove the second limiter.reset() call so the assertion that c2's request
returns 200 truly verifies per-IP isolation.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
agentwatch/api/server.py (1)
442-447:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCORS env var rename is a breaking config change.
Line 442 reads
CORS_ALLOWED_ORIGINS, but deployment config/docs still defineALLOWED_ORIGINS. Existing envs will be ignored and silently fall back to["*"](with credentials disabled), which can break browser auth flows and expected origin restrictions.Suggested compatibility patch
-_raw_cors_origins = os.getenv("CORS_ALLOWED_ORIGINS", "") +_raw_cors_origins = os.getenv("CORS_ALLOWED_ORIGINS") or os.getenv("ALLOWED_ORIGINS", "")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@agentwatch/api/server.py` around lines 442 - 447, The code currently reads CORS_ALLOWED_ORIGINS into _raw_cors_origins which breaks existing deployments using ALLOWED_ORIGINS; change the env lookup to prefer CORS_ALLOWED_ORIGINS but fall back to ALLOWED_ORIGINS (e.g. read os.getenv("CORS_ALLOWED_ORIGINS") or if empty/os.getenv("ALLOWED_ORIGINS")), then parse that string into _cors_origins exactly as before and preserve the default ["*"] behavior; update references to _raw_cors_origins and _cors_origins so the logic lives in the same block that builds the list.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@agentwatch/api/server.py`:
- Around line 442-447: The code currently reads CORS_ALLOWED_ORIGINS into
_raw_cors_origins which breaks existing deployments using ALLOWED_ORIGINS;
change the env lookup to prefer CORS_ALLOWED_ORIGINS but fall back to
ALLOWED_ORIGINS (e.g. read os.getenv("CORS_ALLOWED_ORIGINS") or if
empty/os.getenv("ALLOWED_ORIGINS")), then parse that string into _cors_origins
exactly as before and preserve the default ["*"] behavior; update references to
_raw_cors_origins and _cors_origins so the logic lives in the same block that
builds the list.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e4aebe73-c2cc-436e-ac8d-40ab0ff595fb
📒 Files selected for processing (1)
agentwatch/api/server.py
|
❌ Tests failed — please fix the following before this can be merged: Issue: What needs fixing:
Once that's addressed, the PR will be ready to merge. |
|
Hey @harika880 — two things still needed:
Also fix the per-IP test to use different Fix all three and I'll merge immediately 🚀 |
|
All three issues fixed: Added slowapi>=0.1.9 to pyproject.toml dependencies All 8 tests passing ✅ |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pyproject.toml`:
- Line 38: Summary: Ensure the Starlette version in your dependency resolution
is not vulnerable to CVE-2026-48710. Fix: add an explicit constraint for
Starlette (e.g., declare starlette>=1.0.1) in pyproject.toml or pin FastAPI to a
release that depends on Starlette>=1.0.1 so the resolver cannot pull Starlette
<1.0.1; then regenerate your lock file and verify the resolved Starlette version
in the lock (and CI) to confirm it is >=1.0.1. Reference symbols:
pyproject.toml, slowapi, fastapi, starlette, CVE-2026-48710.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0923a54f-e2c8-4e8c-9368-c84c429255bf
📒 Files selected for processing (1)
pyproject.toml
| "typer[all]>=0.12.0", | ||
| "rich>=13.8.0", | ||
| "python-multipart>=0.0.12", | ||
| "slowapi>=0.1.9", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the latest version of slowapi and are there any known security vulnerabilities?
💡 Result:
As of May 29, 2026, the latest version of SlowAPI is 0.1.9, which was released on February 5, 2024 [1][2]. Regarding security, there are no direct vulnerabilities currently reported for the SlowAPI package itself in major vulnerability databases such as Snyk [2]. However, users should be aware of a critical, recently disclosed vulnerability in Starlette (CVE-2026-48710), the underlying framework that powers FastAPI and, consequently, SlowAPI [3][4]. This vulnerability, branded "BadHost," involves an authentication bypass flaw caused by the improper validation of HTTP Host headers [3][4]. Because SlowAPI is frequently used in FastAPI and Starlette-based applications, systems relying on these frameworks are encouraged to update to Starlette version 1.0.1 or later to mitigate the risk of this authentication bypass and related issues like Server-Side Request Forgery (SSRF) [3][4]. Always ensure your dependencies—especially core frameworks like Starlette and FastAPI—are kept up to date to protect against these types of infrastructure-level threats [3][4].
Citations:
- 1: https://pypi.org/project/slowapi/
- 2: https://security.snyk.io/package/pip/slowapi
- 3: https://arstechnica.com/information-technology/2026/05/millions-of-ai-agents-imperiled-by-critical-vulnerability-in-open-source-package/
- 4: https://www.csoonline.com/article/4177711/fastapi-based-ai-tools-exposed-to-authentication-bypass-by-flaw-in-starlette-framework.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the relevant dependency block around the slowapi line
nl -ba pyproject.toml | sed -n '1,120p'Repository: sreerevanth/AgentWatch
Length of output: 109
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
path="pyproject.toml"
start=1
end=120
with open(path,'r',encoding='utf-8') as f:
for i,line in enumerate(f, start=1):
if start <= i <= end:
print(f"{i:4d}: {line.rstrip()}")
if i > end:
break
PYRepository: sreerevanth/AgentWatch
Length of output: 3616
🌐 Web query:
FastAPI 0.115.0 depends on which Starlette version range?
💡 Result:
FastAPI 0.115.0 depends on Starlette in the range: starlette>=0.37.2,<0.39.0 [1].
Citations:
🌐 Web query:
CVE-2026-48710 BadHost Starlette fixed in which version (and does it affect all Starlette <1.0.1)?
💡 Result:
CVE-2026-48710, also known as the "BadHost" vulnerability, is fixed in Starlette version 1.0.1 and later [1][2][3]. All versions of Starlette prior to 1.0.1 are affected by this vulnerability [1][2][4]. The vulnerability occurs because Starlette failed to validate the HTTP Host header before using it to reconstruct the request URL [1][5]. An attacker can exploit this by injecting crafted values into the Host header (or X-Forwarded-Host header), which can cause the application to misinterpret the request path [2][6]. This behavior can lead to security bypasses in middleware or endpoints that make authorization or routing decisions based on the reconstructed request URL's path [1][2][7]. Users are advised to upgrade to Starlette 1.0.1 or newer to remediate this issue [1][2][3]. Version 1.0.1 introduces validation of the Host header against the grammar defined in RFC 9112 and RFC 3986, falling back to secure default values (scope['server']) when malformed headers are detected [1][8][9].
Citations:
- 1: https://cvefeed.io/vuln/detail/CVE-2026-48710
- 2: https://badhost.org/
- 3: https://cryptobriefing.com/starlette-badhost-vulnerability-ai-agents/
- 4: https://www.valuethemarkets.com/cryptocurrency/news/security-flaw-in-pythons-starlette-framework-exposes-millions-to-attacks
- 5: https://ostif.org/disclosing-the-badhost-vulnerability-in-starlette/
- 6: https://cybersecuritynews.com/badhost-ai-agent-vulnerability/amp/
- 7: https://aiweekly.co/alerts/starlette-badhost-flaw-breaks-ai-agent-auth
- 8: https://osv.dev/vulnerability/DEBIAN-CVE-2026-48710
- 9: https://security-tracker.debian.org/tracker/CVE-2026-48710
🌐 Web query:
Snyk slowapi security advisory (any CVEs for slowapi itself)?
💡 Result:
As of May 29, 2026, there are no recorded CVEs (Common Vulnerabilities and Exposures) directly associated with the SlowAPI package in the Snyk vulnerability database [1][2]. Snyk lists no direct vulnerabilities for the package across its versions, including the latest version, 0.1.9 [1][2]. While SlowAPI has been mentioned in broader security advisories—such as when it is used as a dependency in applications that are themselves found to be vulnerable—these instances pertain to the host application's implementation rather than a security flaw in SlowAPI itself [3]. It is common practice to use SlowAPI alongside other security measures, such as edge rate limiting (e.g., via Cloudflare or Nginx) and layered defense strategies, to ensure robust protection in production environments [4][5]. As a rate-limiting tool, SlowAPI acts as an application-layer utility and is not a substitute for comprehensive infrastructure-level security [6][5].
Citations:
- 1: https://security.snyk.io/package/pip/slowapi
- 2: https://security.snyk.io/package/pip/slowapi/0.1.9
- 3: https://securitylab.github.com/advisories/GHSL-2023-225_GHSL-2023-226_Mealie/
- 4: https://johal.in/api-rate-limiting-with-slowapi-protecting-python-endpoints-from-abuse-in-production/
- 5: https://shiladityamajumder.medium.com/using-slowapi-in-fastapi-mastering-rate-limiting-like-a-pro-19044cb6062b
- 6: https://www.linkedin.com/posts/ddesanto_litellm-activity-7445128918690430977-kvdQ
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
echo "----"
find . -maxdepth 2 -type f \( -iname '*lock*' -o -iname 'requirements*.txt' -o -iname '*pipfile*' \) | sed 's|^\./||'Repository: sreerevanth/AgentWatch
Length of output: 2429
🌐 Web query:
Which FastAPI version depends on Starlette >= 1.0.1 (CVE-2026-48710 remediation)?
💡 Result:
FastAPI versions that depend on Starlette >= 1.0.1 (the CVE-2026-48710 / BadHost remediation requirement) are the FastAPI releases whose dependency constraint allows Starlette >= 1.0.1. However, from the authoritative evidence found here, FastAPI’s dependency range is not “Starlette >= 1.0.1” directly; instead, FastAPI historically pins Starlette to a bounded range (example: Starlette>=0.40.0,<0.47.0 in a FastAPI pyproject.toml snapshot) [1]. A separate FastAPI discussion confirms that FastAPI delegates request handling to Starlette, and that any FastAPI application running on Starlette <= 1.0.0 is exposed, and the mitigation is upgrading Starlette to >= 1.0.1 [2]. Therefore, the correct mapping is: - Any FastAPI version will be “fixed for CVE-2026-48710” if (and only if) the resolved Starlette version is >= 1.0.1 [3][2]. - Any FastAPI version that can resolve Starlette <= 1.0.0 is potentially affected, because the fix is in Starlette starting at 1.0.1 [3][2]. Net: determine your FastAPI version’s Starlette dependency constraint from its pyproject/requirements, then confirm the installed Starlette version is >= 1.0.1 (CVE-2026-48710 remediation) [1][3][2].
Citations:
- 1: https://github.com/FastAPI/FastAPI/blob/5ba94612/pyproject.toml
- 2: Does Starlette GHSA-86qp-5c8j-p5mr affect FastAPI installations using Starlette <= 1.0.0? fastapi/fastapi#15593
- 3: https://cvefeed.io/vuln/detail/CVE-2026-48710
🌐 Web query:
Does the latest FastAPI (as of May 2026) depend on Starlette 1.x? If so, what range?
💡 Result:
As of May 2026, the latest versions of FastAPI (e.g., 0.136.3) depend on Starlette with a constraint of >=0.46.0 [1]. FastAPI does not currently require or depend on Starlette 1.x; its dependency management remains focused on the 0.x release series [1][2]. For context, recent security advisories in May 2026 highlighted a vulnerability (CVE-2026-48710) affecting Starlette versions prior to 1.0.1 [3][4]. While some reports recommended upgrading to Starlette 1.0.1 or later to mitigate this issue [3][5], FastAPI users should continue to let the framework manage its Starlette dependencies to ensure compatibility [6][7]. FastAPI maintains its own version ranges to handle breaking changes and security updates, so users should not manually force upgrades to Starlette 1.x unless specified by a new FastAPI release [6][7].
Citations:
- 1: https://pypi.org/project/fastapi/
- 2: https://github.com/fastapi/fastapi/blob/master/pyproject.toml
- 3: https://www.infoworld.com/article/4177714/fastapi-based-ai-tools-exposed-to-authentication-bypass-by-flaw-in-starlette-framework-2.html
- 4: https://arstechnica.com/information-technology/2026/05/millions-of-ai-agents-imperiled-by-critical-vulnerability-in-open-source-package/
- 5: https://cryptobriefing.com/starlette-badhost-vulnerability-ai-agents/
- 6: https://fastapi.tiangolo.com/deployment/versions/
- 7: https://docs.bswen.com/blog/2026-02-27-starlette-fastapi-relationship/
🌐 Web query:
fastapi 0.115.0 starlette range <0.39.0 why; does later fastapi allow starlette 1.0.1?
💡 Result:
FastAPI 0.115.0 imposed a version constraint on Starlette (typically <0.42.0 or similar ranges depending on the exact minor/patch version) to ensure stability, as FastAPI relies heavily on Starlette's internal APIs [1][2][3]. FastAPI versions are tightly coupled with specific ranges of Starlette to prevent breaking changes in Starlette from destabilizing FastAPI applications [4][5]. Regarding your question about later FastAPI versions: Yes, later versions of FastAPI do allow (and require) Starlette 1.0.1 or later [1][6]. Support for Starlette 1.0.0+ was formally added to FastAPI in early 2026 [6]. This update is particularly important because Starlette versions prior to 1.0.1 were found to be vulnerable to a critical security flaw (CVE-2026-48710), which allowed attackers to bypass authentication via malformed Host headers [7][8][9]. If you are using an older version of FastAPI, you should upgrade to a version that supports Starlette 1.0.1+ to mitigate this vulnerability [10][8]. You can check your current FastAPI and Starlette versions by running pip show fastapi starlette.
Citations:
- 1: https://fastapi.tiangolo.com/release-notes/
- 2: Dependency Conflict: Starlette Version Requirement Causes Conflicts with FastAPI openinterpreter/open-interpreter#1505
- 3: https://pypi.org/project/fastapi/
- 4: https://fastapi.tiangolo.com/deployment/versions/
- 5: starlette dependnecy seems to have been found with a CVE-2025-62727. Why does fastapi still allow the vulnerable version? fastapi/fastapi#14424
- 6: ⬆️ Add support for Starlette 1.0.0+ fastapi/fastapi#14987
- 7: https://www.csoonline.com/article/4177711/fastapi-based-ai-tools-exposed-to-authentication-bypass-by-flaw-in-starlette-framework.html
- 8: https://arstechnica.com/information-technology/2026/05/millions-of-ai-agents-imperiled-by-critical-vulnerability-in-open-source-package/
- 9: https://www.opensourceforu.com/2026/05/starlette-host-header-flaw-threatens-400000-dependent-github-projects/
- 10: https://b2bdaily.com/it/starlette-vulnerability-puts-ai-and-fastapi-apps-at-risk/
slowapi dependency looks fine; ensure Starlette is patched for CVE-2026-48710
slowapi>=0.1.9: 0.1.9 is the latest SlowAPI release and Snyk reports no direct vulnerabilities for the SlowAPI package.- Security note: SlowAPI runs on top of FastAPI/Starlette; CVE-2026-48710 (“BadHost”) affects Starlette versions < 1.0.1 (fixed in >= 1.0.1). With
fastapi>=0.115.0, FastAPI 0.115.0 can resolve Starlette to<0.39.0, so confirm the resolved Starlette version is>=1.0.1(or pin FastAPI/Starlette accordingly).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pyproject.toml` at line 38, Summary: Ensure the Starlette version in your
dependency resolution is not vulnerable to CVE-2026-48710. Fix: add an explicit
constraint for Starlette (e.g., declare starlette>=1.0.1) in pyproject.toml or
pin FastAPI to a release that depends on Starlette>=1.0.1 so the resolver cannot
pull Starlette <1.0.1; then regenerate your lock file and verify the resolved
Starlette version in the lock (and CI) to confirm it is >=1.0.1. Reference
symbols: pyproject.toml, slowapi, fastapi, starlette, CVE-2026-48710.
There was a problem hiding this comment.
@harika880 Thanks for addressing the dependency issue and updating the rate-limit tests.
Before merge:
Resolve the remaining merge conflicts in:
agentwatch/main.py
agentwatch/api/server.py
agentwatch/core/http_forwarder.py
Confirm that forwarded events continue to work when API-key protection is enabled. The earlier review identified a potential authentication mismatch in http_forwarder.py, and I don't see evidence that this concern has been addressed.
Once conflicts are resolved and the forwarding/authentication path is verified, this can be reviewed again.
Labels:
NSoC 26'
level3
level: advanced
security
help wanted
Closes #56
What this PR does
Adds per-IP rate limiting to the AgentWatch API using
slowapito preventmemory exhaustion from request floods on
/api/v1/eventsand other endpoints.Changes
slowapiLimiterwithSlowAPIMiddlewarefor per-IP rate limiting1000/minutefor read endpoints,200/minutefor write endpointsAPI_RATE_LIMIT_READandAPI_RATE_LIMIT_WRITE{"error": "rate_limit_exceeded"}JSON bodyX-RateLimit-LimitX-RateLimit-Remaining: 0Retry-AfterTests
All 8 tests in
tests/test_rate_limiting.pypass, covering:X-RateLimit-Remainingis0on 429Retry-Afteris a positive integerSummary by CodeRabbit
New Features
Tests
Chores