From a44a4710227221a46a94cb4b268da5f99328f0e9 Mon Sep 17 00:00:00 2001 From: ellieayla <1447600+me@users.noreply.github.com> Date: Sat, 14 Feb 2026 22:41:53 -0500 Subject: [PATCH] Safari support - initial spike, add required CORS headers The chrome manifest v3 can be converted automatically to a Safari Web Extention with safari-web-extension-packager, though it uses default icons and description. Effort is needed to package it up pretty. The safari web extension appears to have a similar behaviour to firefox, with the Origin url containing a UUID unique to the installation. --- manage.sh | 1 + server/server.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/manage.sh b/manage.sh index eadb6443..e87b5953 100755 --- a/manage.sh +++ b/manage.sh @@ -56,6 +56,7 @@ build_addon() { echo "[!] Warning: The default manifest.json is for chrome browsers, overwrite it with manifest_ff.json for firefox" cd ext npm run build + xcrun safari-web-extension-packager ./dist --project-location ./xc-project --app-name Hister cd .. } diff --git a/server/server.go b/server/server.go index e3224ea5..d7493461 100644 --- a/server/server.go +++ b/server/server.go @@ -70,6 +70,8 @@ func (lrw *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) func (c *csrfExceptionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/add" && strings.HasPrefix(r.Header.Get("Origin"), "moz-extension://") { c.origHandler.ServeHTTP(w, r) + } else if r.URL.Path == "/add" && strings.HasPrefix(r.Header.Get("Origin"), "safari-web-extension://") { + c.origHandler.ServeHTTP(w, r) } else { c.csrfHandler.ServeHTTP(w, r) } @@ -337,6 +339,21 @@ func serveAdd(c *webContext) { c.Render("add", nil) return } + if m == http.MethodOptions { + if c.Request.Header.Get("Access-Control-Request-Method") != http.MethodPost { + c.Response.WriteHeader(http.StatusForbidden) + return + } + origin := c.Request.Header.Get("Origin") + if strings.HasPrefix(origin, "safari-web-extension://") { + c.Response.Header().Add("Access-Control-Allow-Origin", origin) + } + c.Response.Header().Add("Access-Control-Allow-Methods", http.MethodPost) + c.Response.Header().Add("Access-Control-Allow-Headers", "Content-Type") + c.Response.Header().Add("Access-Control-Max-Age", "86400") + c.Response.WriteHeader(http.StatusNoContent) + return + } if m != http.MethodPost { serve500(c) return @@ -361,6 +378,7 @@ func serveAdd(c *webContext) { d.Title = f.Get("title") d.Text = f.Get("text") } + if !c.Config.Rules.IsSkip(d.URL) && !strings.HasPrefix(d.URL, c.Config.BaseURL("/")) { if err := d.Process(); err != nil { log.Error().Err(err).Str("URL", d.URL).Msg("failed to process document") @@ -373,6 +391,10 @@ func serveAdd(c *webContext) { serve500(c) return } + origin := c.Request.Header.Get("Origin") + if strings.HasPrefix(origin, "safari-web-extension://") { + c.Response.Header().Add("Access-Control-Allow-Origin", origin) + } c.Response.WriteHeader(http.StatusCreated) } else { log.Debug().Str("url", d.URL).Msg("skip indexing")