-
Notifications
You must be signed in to change notification settings - Fork 55
LCORE-405: configurable CORS settings #406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,6 +24,22 @@ def check_tls_configuration(self) -> Self: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class CORSConfiguration(BaseModel): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """CORS configuration.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allow_origins: list[str] = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "*" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] # not AnyHttpUrl: we need to support "*" that is not valid URL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allow_credentials: bool = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allow_methods: list[str] = ["*"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allow_headers: list[str] = ["*"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @model_validator(mode="after") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def check_cors_configuration(self) -> Self: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Check CORS configuration.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+41
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block invalid/insecure combo: '*' origins with allow_credentials=True Browsers reject credentialed requests when Access-Control-Allow-Origin is ''. Shipping defaults as allow_origins [''] and allow_credentials True is both insecure and functionally broken. Apply this diff to use a safe default and enforce validation: class CORSConfiguration(BaseModel):
"""CORS configuration."""
- allow_origins: list[str] = [
- "*"
- ] # not AnyHttpUrl: we need to support "*" that is not valid URL
- allow_credentials: bool = True
+ allow_origins: list[str] = [
+ "*"
+ ] # not AnyHttpUrl: we need to support "*" that is not valid URL
+ allow_credentials: bool = False
allow_methods: list[str] = ["*"]
allow_headers: list[str] = ["*"]
@model_validator(mode="after")
def check_cors_configuration(self) -> Self:
- """Check CORS configuration."""
- return self
+ """Check CORS configuration."""
+ # Credentials are not allowed with wildcard origins per CORS/Fetch spec.
+ if self.allow_credentials and "*" in self.allow_origins:
+ raise ValueError(
+ "Invalid CORS config: allow_credentials cannot be true when allow_origins contains '*'. "
+ "Use explicit origins or disable credentials."
+ )
+ return self📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class SQLiteDatabaseConfiguration(BaseModel): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """SQLite database configuration.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -106,6 +122,7 @@ class ServiceConfiguration(BaseModel): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| color_log: bool = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| access_log: bool = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tls_config: TLSConfiguration = TLSConfiguration() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cors: CORSConfiguration = CORSConfiguration() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @model_validator(mode="after") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def check_service_configuration(self) -> Self: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,13 @@ def test_loading_proper_configuration(configuration_filename: str) -> None: | |
| assert svc_config.color_log is True | ||
| assert svc_config.access_log is True | ||
|
|
||
| # check 'service.cors' section | ||
| cors_config = cfg.service_configuration.cors | ||
| assert cors_config.allow_origins == ["foo_origin", "bar_origin", "baz_origin"] | ||
| assert cors_config.allow_credentials is False | ||
| assert cors_config.allow_methods == ["foo_method", "bar_method", "baz_method"] | ||
| assert cors_config.allow_headers == ["foo_header", "bar_header", "baz_header"] | ||
|
|
||
|
Comment on lines
+50
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainGuard against import-time access to configuration in app startup. Per src/configuration.py (lines 62-67), accessing service_configuration asserts that configuration is loaded. If src/app/main.py reads configuration.service_configuration.cors at module import time (as stated in the PR summary), startup can fail unless configuration is loaded beforehand. Consider deferring the CORS read to runtime (e.g., during app factory, lifespan event) or ensuring configuration is loaded before main is imported. [offer_architecture_advice] 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Searching for module-level access to configuration.service_configuration.cors:"
rg -n -C3 -P 'configuration\.service_configuration\.cors' --type=py
echo
echo "Searching for calls that load configuration (order review):"
rg -n -C3 -P '\bload_configuration\s*\(' --type=pyLength of output: 4482 Defer import-time CORS setup in src/app/main.py The middleware is pulling in • Location to fix: 🤖 Prompt for AI Agents |
||
| # check 'llama_stack' section | ||
| ls_config = cfg.llama_stack_configuration | ||
| assert ls_config.use_as_library_client is False | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against invalid CORS at runtime (credentials + '*')
Given current defaults, this will configure CORSMiddleware with allow_credentials=True and wildcard origins, which browsers will reject. Either rely on config-model validation (preferred, see suggested change in config.py) or defensively disable credentials when wildcards are present.
Apply this diff to add a runtime guard:
📝 Committable suggestion
🤖 Prompt for AI Agents