audio streaming app plyr.fm
38
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #1106 from zzstoatzz/cors/allow-all-origins

cors: allow any HTTPS origin to call the API

authored by

nate nowack and committed by
GitHub
671d2bb2 8ad48296

+18 -18
+8 -13
backend/src/backend/config.py
··· 189 189 cors_origin_regex: str | None = Field( 190 190 default=None, 191 191 validation_alias="FRONTEND_CORS_ORIGIN_REGEX", 192 - description="CORS origin regex pattern (if not set, uses default for plyr.fm and relay-4i6.pages.dev)", 192 + description="CORS origin regex pattern (if not set, allows any HTTPS origin in prod/staging)", 193 193 ) 194 194 195 195 @computed_field ··· 208 208 if self.cors_origin_regex is not None: 209 209 return self.cors_origin_regex 210 210 211 - # derive allowed origins based on FRONTEND_URL 212 - # production: FRONTEND_URL=https://plyr.fm → allow plyr.fm + www.plyr.fm 213 - # staging: FRONTEND_URL=https://stg.plyr.fm → allow stg.plyr.fm 214 - # always allow localhost for local dev and cloudflare preview deployments 211 + # the API is public — allow any HTTPS origin (plus localhost for dev). 212 + # auth uses HttpOnly cookies scoped to plyr.fm, so credentialed 213 + # cross-origin requests from third-party sites won't carry session cookies. 215 214 216 215 from urllib.parse import urlparse 217 216 218 217 parsed = urlparse(self.url) 219 218 hostname = parsed.hostname or "localhost" 220 219 221 - if hostname == "stg.plyr.fm": 222 - # staging: allow stg.plyr.fm and *.stg.plyr.fm subdomains 223 - return r"^(https://([a-z0-9-]+\.)?stg\.plyr\.fm|https://([a-z0-9]+\.)?relay-4i6\.pages\.dev|http://localhost:5173)$" 224 - elif hostname in ("plyr.fm", "www.plyr.fm"): 225 - # production: allow plyr.fm, *.plyr.fm subdomains, and embed consumers 226 - return r"^(https://([a-z0-9-]+\.)?plyr\.fm|https://zzstoatzz\.(github\.io|io)|https://([a-z0-9]+\.)?relay-4i6\.pages\.dev|http://localhost:5173)$" 220 + if hostname in ("localhost", "127.0.0.1"): 221 + return r"^http://localhost:\d+$" 227 222 else: 228 - # local dev: allow localhost 229 - return r"^(http://localhost:5173)$" 223 + # production / staging: any HTTPS origin + localhost for dev 224 + return r"^(https://.+|http://localhost:\d+)$" 230 225 231 226 232 227 class DatabaseSettings(AppSettingsSection):
+10 -5
backend/tests/test_cors_origins.py
··· 37 37 "https://stg.plyr.fm", 38 38 "https://zzstoatzz.github.io", 39 39 "https://zzstoatzz.io", 40 + "https://montoulieu.dev", 41 + "https://example.com", 40 42 "http://localhost:5173", 43 + "http://localhost:3000", 41 44 ], 42 45 ) 43 46 def test_allowed(self, regex: str, origin: str) -> None: ··· 46 49 @pytest.mark.parametrize( 47 50 "origin", 48 51 [ 49 - "https://evil-plyr.fm", 50 - "https://notplyr.fm", 51 - "http://plyr.fm", 52 + "http://plyr.fm", # non-HTTPS remote 53 + "http://example.com", # non-HTTPS remote 52 54 ], 53 55 ) 54 56 def test_rejected(self, regex: str, origin: str) -> None: ··· 67 69 [ 68 70 "https://stg.plyr.fm", 69 71 "https://docs.stg.plyr.fm", 72 + "https://any-site.example.com", 70 73 "http://localhost:5173", 71 74 ], 72 75 ) ··· 76 79 @pytest.mark.parametrize( 77 80 "origin", 78 81 [ 79 - "https://plyr.fm", 80 - "https://evil-stg.plyr.fm", 82 + "http://plyr.fm", # non-HTTPS remote 81 83 ], 82 84 ) 83 85 def test_rejected(self, regex: str, origin: str) -> None: ··· 93 95 94 96 def test_allows_localhost(self, regex: str) -> None: 95 97 assert _matches(regex, "http://localhost:5173") 98 + 99 + def test_allows_other_localhost_ports(self, regex: str) -> None: 100 + assert _matches(regex, "http://localhost:3000") 96 101 97 102 def test_rejects_remote(self, regex: str) -> None: 98 103 assert not _matches(regex, "https://plyr.fm")