fix(csp): support CivitAI CDN subdomains for example images (#822)

- Update CSP whitelist to use wildcard *.civitai.com for all CDN subdomains
- Fix hostname parsing to use parsed.hostname instead of parsed.netloc (handles ports)
- Update rewrite_preview_url() to support all CivitAI CDN subdomains
- Update rewriteCivitaiUrl() frontend function to support subdomains
- Add comprehensive tests for edge cases (ports, subdomains, invalid URLs)
- Add security note explaining wildcard CSP design decision

Fixes CSP blocking of images from image-b2.civitai.com and other CDN subdomains
This commit is contained in:
Will Miao
2026-04-03 09:40:15 +08:00
parent 05636712f0
commit 30db8c3d1d
6 changed files with 236 additions and 17 deletions

View File

@@ -22,7 +22,9 @@ def _normalize_commercial_values(value: Any) -> Sequence[str]:
def _split_aggregate(value_str: str) -> list[str]:
stripped = value_str.strip()
looks_aggregate = "," in stripped or (stripped.startswith("{") and stripped.endswith("}"))
looks_aggregate = "," in stripped or (
stripped.startswith("{") and stripped.endswith("}")
)
if not looks_aggregate:
return [value_str]
@@ -141,14 +143,18 @@ def build_license_flags(payload: Mapping[str, Any] | None) -> int:
return flags
def resolve_license_info(model_data: Mapping[str, Any] | None) -> tuple[Dict[str, Any], int]:
def resolve_license_info(
model_data: Mapping[str, Any] | None,
) -> tuple[Dict[str, Any], int]:
"""Return normalized license payload and its encoded bitset."""
payload = resolve_license_payload(model_data)
return payload, build_license_flags(payload)
def rewrite_preview_url(source_url: str | None, media_type: str | None = None) -> tuple[str | None, bool]:
def rewrite_preview_url(
source_url: str | None, media_type: str | None = None
) -> tuple[str | None, bool]:
"""Rewrite Civitai preview URLs to use optimized renditions.
Args:
@@ -168,7 +174,12 @@ def rewrite_preview_url(source_url: str | None, media_type: str | None = None) -
except ValueError:
return source_url, False
if parsed.netloc.lower() != "image.civitai.com":
hostname = parsed.hostname
if hostname is None:
return source_url, False
hostname = hostname.lower()
if hostname == "civitai.com" or not hostname.endswith(".civitai.com"):
return source_url, False
replacement = "/width=450,optimized=true"