server: fetch service sends Access-Control-Allow-Origin: * (#1251)
Every caller is a browser on some other origin (each space is its own), so without CORS headers fetch() could never read a response — every client silently fell back to its instance path and the whole client flip was a no-op in real browsers (curl checks hid it; <img> loads aren't CORS-gated, which is why proxied images looked fine). '*' is the honest scope: the service is anonymous by design — no cookies, no credentials, nothing origin-scoped to protect.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+15
−1
fetch.go+9 −1fetch_test.go+6 −0
diff --git a/fetch.go b/fetch.go| index ad67897..f4bb433 100644 |
| --- a/fetch.go |
| +++ b/fetch.go |
| @@ -170,7 +170,15 @@ func (g *fetchServer) handler() http.Handler { |
| mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
| http.Redirect(w, r, "https://elevenmessenger.com", http.StatusFound) |
| }) |
| - return mux |
| + // Every caller is a browser on some OTHER origin (each space is its own |
| + // origin; the service serves them all), so without CORS headers fetch() |
| + // can never read a response and every client silently falls back to its |
| + // instance path. `*` is the honest scope: the service is anonymous by |
| + // design — no cookies, no credentials, nothing origin-scoped to protect. |
| + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| + w.Header().Set("Access-Control-Allow-Origin", "*") |
| + mux.ServeHTTP(w, r) |
| + }) |
| } |
| // fetchCallerKey is the rate-limit bucket key. Mirrors linkscmd.go — behind |
diff --git a/fetch_test.go b/fetch_test.go| index ce2c047..38a91b6 100644 |
| --- a/fetch_test.go |
| +++ b/fetch_test.go |
| @@ -48,6 +48,12 @@ func TestFetchUnfurlAndImageProxy(t *testing.T) { |
| if err != nil { |
| t.Fatalf("unfurl: %v", err) |
| } |
| + // Callers are browsers on OTHER origins (every space is one): without |
| + // this header fetch() can't read the response and clients silently fall |
| + // back to their instance path — the bug that made the client flip a no-op. |
| + if r.Header.Get("Access-Control-Allow-Origin") != "*" { |
| + t.Fatal("missing Access-Control-Allow-Origin — cross-origin clients can't read the service") |
| + } |
| var pv LinkPreview |
| json.NewDecoder(r.Body).Decode(&pv) |
| r.Body.Close() |