Repository navigation
Conversation
Off by default (OPENENV_GUARD_ENABLED=1 to enable): IP block/allow lists, rate limiting with auto-ban, user-agent blocking, and penetration detection, wired into both create_fastapi_app and the gradio web-interface path. Ships as a 'security' extra; in-memory state by default, Redis via OPENENV_GUARD_REDIS_URL.
| # penetration detection) for env servers. No effect unless | ||
| # OPENENV_GUARD_ENABLED=1. | ||
| "fastapi-guard>=8.0.3,<9.0.0", | ||
| ] |
There was a problem hiding this comment.
Redis extra dependency missing
Medium Severity
Setting OPENENV_GUARD_REDIS_URL turns on enable_redis, but the security extra only installs fastapi-guard. Redis support lives in guard-core[redis], and enabling Redis without that extra raises a SecurityConfig validation error at startup. The documented shared-worker Redis path cannot work from pip install "openenv[security]" alone.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 9e3c9db. Configure here.
There was a problem hiding this comment.
Not actually needed: redis is a core dependency of guard-core, which fastapi-guard depends on - I verified against the installed packages: guard-core's base metadata lists redis (the guard-core[redis] extra is a no-op alias for the same package), and pip install fastapi-guard pulls redis 8.1.0 transitively. SecurityConfig(enable_redis=True, redis_url=...) constructs cleanly from the plain security extra. The documented Redis path works exactly as written.
…loud blocking, and log options Brings the OPENENV_GUARD_* surface to parity: passive/log-only mode, optional security headers, enforce-HTTPS, blocked/allowed countries and cloud-provider blocking, custom log file/format, and IPINFO_TOKEN geo lookups. Country rules require IPINFO_TOKEN (guard-core resolves a geo handler from it).
| kwargs["block_cloud_providers"] = frozenset(cloud_providers) | ||
|
|
||
| if ipinfo_token := os.environ.get("IPINFO_TOKEN"): | ||
| kwargs["ipinfo_token"] = ipinfo_token |
There was a problem hiding this comment.
Geo rules skip required handler
Medium Severity
Country and cloud-provider lists are applied without constructing geo_ip_handler as IPInfoManager. fastapi-guard skips those checks when the handler is missing, so OPENENV_GUARD_BLOCKED_COUNTRIES, OPENENV_GUARD_ALLOWED_COUNTRIES, and OPENENV_GUARD_BLOCK_CLOUD_PROVIDERS can take no effect even when IPINFO_TOKEN is set via the deprecated ipinfo_token field.
Reviewed by Cursor Bugbot for commit c5ff4c7. Configure here.
There was a problem hiding this comment.
Valid - fixed. trust_x_forwarded_proto now defaults to following enforce_https (exactly the case you describe: a TLS-terminating proxy makes every request look like plain HTTP otherwise), with an explicit OPENENV_GUARD_TRUST_X_FORWARDED_PROTO override.
…enforcement, strict booleans, rate-limit auto-ban knob - web_interface no longer attaches guard twice (create_fastapi_app already covers it) and attach_guard is idempotent via app.state - enforce_https now sets trust_x_forwarded_proto by default so TLS-terminating proxies are read correctly (explicit override via OPENENV_GUARD_TRUST_X_FORWARDED_PROTO) - invalid boolean env values now fail loudly instead of silently disabling security - new OPENENV_GUARD_RATE_LIMIT_AUTO_BAN knob
| enabled = os.environ.get("OPENENV_GUARD_ENABLED", "").strip().lower() | ||
| if enabled not in ("1", "true", "yes"): | ||
| return | ||
| if getattr(app.state, "guard_attached", False): |
There was a problem hiding this comment.
Enable flag ignores on value
Low Severity
attach_guard still treats only 1/true/yes as enabled, while _env_bool now accepts on for every other OPENENV_GUARD_* flag. OPENENV_GUARD_ENABLED=on therefore leaves the middleware off with no error, so a consistent boolean spelling fails to enable protection.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 5b767da. Configure here.
…security-middleware
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9278c59. Configure here.
| ) from exc | ||
|
|
||
| app.add_middleware(SecurityMiddleware, config=_build_security_config()) | ||
| app.state.guard_attached = True |
There was a problem hiding this comment.
WebSocket routes bypass guard protections
High Severity
attach_guard only installs SecurityMiddleware, which fastapi-guard documents as HTTP-only. Env-server traffic on /ws, /mcp, /harness, and /ws/ui never hits IP lists, rate limits, bans, or detection. Those checks require Depends(guard_websocket) on each WebSocket route, which is not applied, so a blocked or limited client can still drive the primary Gym/MCP/harness APIs.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 9278c59. Configure here.
- Trusted proxies default to empty: trusting RFC1918 ranges let any LAN peer spoof X-Forwarded-For past the blocklist and rate limits. Behind a reverse proxy, list it in OPENENV_GUARD_TRUSTED_PROXIES. - Penetration-detection body scanning is now opt-in via OPENENV_GUARD_SCAN_BODY: request bodies are model input (observations, prompts, completions), not commands, so signature scans false-positived on ordinary content and rejected legitimate requests with 400. URL, query, and header screening stay on. - Rate-limit auto-ban defaults on; OPENENV_GUARD_RATE_LIMIT_AUTO_BAN=0 keeps 429-only enforcement. _env_bool gained a keyword-only default.


Closes #1360
Implementation for the discussion in #1360.
Summary
Opt-in fastapi-guard middleware wired into both app factories (
create_fastapi_appand the gradio web-interface path). Default off: withOPENENV_GUARD_ENABLEDunset,attach_guard()is a no-op and env servers run exactly as before. Enabling without the extra installed raises a clear ImportError with the install command, so misconfiguration is loud instead of silent.Type of Change
Alignment Checklist
.claude/docs/PRINCIPLES.mdand this PR aligns with our principles.claude/docs/INVARIANTS.mdand no invariants are violated (no API signature or wire-type change, env-var only config, nothing secret logged)/pre-submit-pr(orbash .claude/hooks/lint.shand tests) and addressed all issuesRFC Status
What you get when enabled
pip install "openenv[security]"plusOPENENV_GUARD_ENABLED=1:OPENENV_GUARD_RATE_LIMIT/_RATE_LIMIT_WINDOW(defaults 100 req / 60s),_AUTO_BAN_THRESHOLD/_AUTO_BAN_DURATION(defaults 10 / 300s)OPENENV_GUARD_BLOCKED_IPS/_ALLOWED_IPSOPENENV_GUARD_BLOCKED_USER_AGENTSOPENENV_GUARD_PASSIVE_MODE), optional security headers (OPENENV_GUARD_SECURITY_HEADERS), enforce-HTTPS (OPENENV_GUARD_ENFORCE_HTTPS), custom log file/format, and blocked/allowed countries plus cloud-provider blocking (countries requireIPINFO_TOKEN)OPENENV_GUARD_TRUSTED_PROXIES/_TRUSTED_PROXY_DEPTH(private ranges by default, matching container and Space deployments)OPENENV_GUARD_REDIS_URL, in-memory otherwise. I explicitly disabled the library's localhost default; an opt-in feature shouldn't require a running Redis./health,/schema,/metadataand the docs paths are excluded from detection checks so orchestrators can keep probing, but IP lists and rate limiting still apply on them. That's fastapi-guard'senforced_on_excluded_pathsdesign and I think it's the right call: excluded shouldn't mean free pass.Test Plan
uv run --extra security pytest tests/core/test_guard_security.py: 5 tests, all green (default-off no-op, middleware wiring, blocked IP 403, IP lists on excluded paths, rate-limit 429). Rate-limit state in the library is process-wide, so each test drives its own TEST-NET client IP to stay hermetic.test_task_api,test_state_schema_subclassandtest_reset_observation_schemapass. One adjacent test (test_reference_echo_explicitly_advertises_its_base_reset_observation) fails on main with this PR's changes stashed too, so it's pre-existing and unrelated.src/andtests/. Repo-wide.claude/hooks/lint.shalso flags ~58 files underenvs/that already fail on main; I left those alone.Claude Code Review
N/A