WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto

fix(web): sanitize token values on save + add PUT body forwarding test (ATB-59)

+37 -2
+33
apps/web/src/routes/__tests__/admin-themes.test.tsx
··· 506 506 expect(location).toContain("error="); 507 507 expect(decodeURIComponent(location).toLowerCase()).toContain("unavailable"); 508 508 }); 509 + 510 + // ── PUT body forwarding ──────────────────────────────────────────────────── 511 + 512 + it("forwards name, colorScheme, tokens, and fontUrls to the AppView PUT call", async () => { 513 + setupAuthenticatedSession([MANAGE_THEMES]); 514 + mockFetch.mockResolvedValueOnce(mockResponse({ id: "1" })); 515 + 516 + const routes = await loadThemeRoutes(); 517 + await routes.request("/admin/themes/abc123/save", { 518 + method: "POST", 519 + headers: { 520 + "content-type": "application/x-www-form-urlencoded", 521 + cookie: "atbb_session=token", 522 + }, 523 + body: new URLSearchParams({ 524 + name: "Saved Theme", 525 + colorScheme: "dark", 526 + fontUrls: "https://fonts.example.com/font.css\nhttps://fonts.example.com/font2.css", 527 + "color-bg": "#000000", 528 + }).toString(), 529 + }); 530 + 531 + // Calls: 1 = session, 2 = members/me, 3 = AppView PUT 532 + const putCall = mockFetch.mock.calls[2]; 533 + const putBody = JSON.parse(putCall[1].body as string); 534 + expect(putBody.name).toBe("Saved Theme"); 535 + expect(putBody.colorScheme).toBe("dark"); 536 + expect(putBody.fontUrls).toEqual([ 537 + "https://fonts.example.com/font.css", 538 + "https://fonts.example.com/font2.css", 539 + ]); 540 + expect(putBody.tokens["color-bg"]).toBe("#000000"); 541 + }); 509 542 });
+4 -2
apps/web/src/routes/admin-themes.tsx
··· 1043 1043 const tokens: Record<string, string> = {}; 1044 1044 for (const tokenName of ALL_KNOWN_TOKENS) { 1045 1045 const raw = rawBody[tokenName]; 1046 - if (typeof raw === "string" && raw.trim()) { 1047 - tokens[tokenName] = raw.trim(); 1046 + if (typeof raw !== "string") continue; 1047 + const safe = sanitizeTokenValue(raw.trim()); 1048 + if (safe !== null && safe !== "") { 1049 + tokens[tokenName] = safe; 1048 1050 } 1049 1051 } 1050 1052