Hardening: safer CORS config + localhost bind defaults#2767
Hardening: safer CORS config + localhost bind defaults#2767crivetimihai merged 3 commits intoIBM:mainfrom
Conversation
722d4c0 to
2850c4a
Compare
|
Thanks for this hardening PR, @TheodorNEngoy! Solid security improvements:
A couple of minor suggestions:
Neither is a blocker. LGTM! |
|
Thanks! Addressed both nits:
|
crivetimihai
left a comment
There was a problem hiding this comment.
Thanks @TheodorNEngoy — this is well-executed. Both suggestions from the previous review have been properly addressed:
- DRY the port parsing — extracted
_env_bool,_env_int,_parse_csvintoenv_utils.py. Clean. - Makefile host override —
run/devtargets now use$${HOST:-127.0.0.1}. Works correctly.
The CORS hardening is sound (empty default = disabled, wildcard+credentials guard with warning, defense-in-depth), and the localhost bind default is a good security improvement.
One edge case to consider: the wildcard-with-credentials guard only fires when CORS_ORIGINS is exactly "*", not when "*" appears among a comma-separated list like "*,http://example.com". While unlikely, checking the parsed list instead of the raw string would make the guard more robust:
cors_allow_origins = _parse_csv(cors_origins_raw)
if "*" in cors_allow_origins:
cors_allow_origins = ["*"]
if cors_allow_credentials:
logger.warning("...")
cors_allow_credentials = FalseThis is a minor suggestion — the PR is solid as-is. LGTM.
Signed-off-by: Theodor N. Engøy <theodornengoy@Mac.home>
Signed-off-by: Theodor N. Engøy <theodornengoy@eduroam-193-157-246-146.wlan.uio.no>
- Fix CORS wildcard bypass: check parsed origin list instead of raw string so '*,https://example.com' is caught - Validate LOG_LEVEL against allowed uvicorn levels with fallback - Add 44 differential tests for env_utils and CORS configuration - Remove unused pytest import (Ruff F401) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
2412644 to
6874ec0
Compare
crivetimihai
left a comment
There was a problem hiding this comment.
Thanks @TheodorNEngoy — this is a solid security hardening contribution!
What was done
Rebased onto main (clean, no conflicts) and applied three fixes during review:
-
CORS wildcard bypass fix (High): The original guard (
cors_origins_raw == "*") only caught the exact*string. Mixed inputs like*,https://example.combypassed the safety check while Starlette still treated the list as allow-all. Fixed by checking"*" in cors_allow_originson the parsed list, and normalizing any wildcard-containing list to["*"]. -
LOG_LEVEL validation (Medium):
LOG_LEVELwas passed to uvicorn without validation — invalid values likewarnor" info "would crash at startup. Added strip/lowercase/validation against allowed uvicorn levels with a warning fallback. -
Unused import (Low): Removed unused
pytestimport in test file.
Tests added
44 differential tests covering all new code:
test_env_utils.py(32 tests):_env_bool,_env_int,_parse_csv— truthy/falsy/default/whitespace/edge casestest_cors_config.py(12 tests): CORS disabled by default, explicit origins, wildcard safety, mixed wildcard bypass (the new regression tests), edge cases
Verification
All pass:
make doctest— 1193 passedmake test— 12447 passedmake pylint— 10.00/10make flake8— clean
* Hardening: safer CORS + localhost bind defaults Signed-off-by: Theodor N. Engøy <theodornengoy@Mac.home> * langchain agent: DRY env parsing + Makefile HOST override Signed-off-by: Theodor N. Engøy <theodornengoy@eduroam-193-157-246-146.wlan.uio.no> * fix(security): harden CORS wildcard guard, validate LOG_LEVEL, add tests - Fix CORS wildcard bypass: check parsed origin list instead of raw string so '*,https://example.com' is caught - Validate LOG_LEVEL against allowed uvicorn levels with fallback - Add 44 differential tests for env_utils and CORS configuration - Remove unused pytest import (Ruff F401) Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: Theodor N. Engøy <theodornengoy@Mac.home> Signed-off-by: Theodor N. Engøy <theodornengoy@eduroam-193-157-246-146.wlan.uio.no> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Theodor N. Engøy <theodornengoy@Mac.home> Co-authored-by: Theodor N. Engøy <theodornengoy@eduroam-193-157-246-146.wlan.uio.no> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
This hardens the LangChain agent runtime defaults to avoid two common security footguns for network-exposed MCP-adjacent servers:
CORS_ORIGINS=*with credentials. The app now readsCORS_ORIGINS/CORS_CREDENTIALSand forces credentials off whenCORS_ORIGINS=*(with a warning), since wildcard+credentials is unsafe.127.0.0.1instead of0.0.0.0inMakefileandstart_agent.py(still configurable viaHOST/PORT). The containerDockerfileremains--host 0.0.0.0.Also updates
.env.exampleto reflect safer defaults (CORS disabled by default; loopback bind by default).Rationale: reduces accidental remote exposure + credentialed cross-origin access while keeping intentional remote deployments opt-in.