From b6eba66801278f3282f3b6895dbd23f138d68546 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 25 Oct 2026 11:24:25 -0400 Subject: [PATCH] fix(web): restore Alpine theme members and allow eval for Alpine expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two regressions from the cleanup cycle, caught in E2E: 1. The unexport pass stripped loadTheme, loadUserTheme, and updateThemeIndicators from the Alpine.data("theme") registration — the member-removal pass could not tell export-block lines from Alpine component object lines. Theme dropdowns on docs and non-app pages stopped reflecting the active theme. Members restored (functions were kept, only their scope registration was lost). 2. The new Content-Security-Policy shipped script-src without 'unsafe-eval'. Alpine evaluates every @click / x-init / x-show expression through new Function(), so the CSP silently disabled ALL Alpine interactivity app-wide: sidebar categories stuck open, modals rendering unhidden and unresponsive, dropdowns inert. script-src now includes 'unsafe-eval' with a comment explaining the Alpine requirement. --- internal/middleware/security_headers.go | 2 +- web/src/theme.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/middleware/security_headers.go b/internal/middleware/security_headers.go index b6c0320c..27447ead 100644 --- a/internal/middleware/security_headers.go +++ b/internal/middleware/security_headers.go @@ -13,7 +13,7 @@ func SecurityHeadersMiddleware() echo.MiddlewareFunc { c.Response().Header().Set("X-Frame-Options", "DENY") c.Response().Header().Set("X-XSS-Protection", "1; mode=block") c.Response().Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload") - c.Response().Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:") + c.Response().Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:") c.Response().Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") c.Response().Header().Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()") diff --git a/web/src/theme.ts b/web/src/theme.ts index 4b61b594..d0b39c27 100644 --- a/web/src/theme.ts +++ b/web/src/theme.ts @@ -288,8 +288,11 @@ Alpine.data("theme", () => ({ changeTheme, changeWoodPaneling, initializeTheme, + loadTheme, + loadUserTheme, loadWoodPaneling, revealCurrentTheme, + updateThemeIndicators, updateWoodPanelingIndicators, }));