Die Leiste kommt als Skript von der HoneyOS-Domain und holt sich von dort ihre App-Liste. Beides blockte die CSP bisher (script-src / connect-src fielen auf 'self' zurueck) – ohne Ausnahme erschien sie gar nicht. - HONEYBAR_ORIGIN nimmt genau diesen einen Ursprung in script-src und connect-src auf. Leer heisst: keine Leiste, und die Regel bleibt so eng wie bisher. Frame-Freigabe erbt sie ausdruecklich nicht. - SecurityHeaders nimmt die Ausnahmen jetzt als CSP-Struct statt als variadische frameSrc-Liste – mit drei Richtungen wurde das sonst unklar. - Das <script>-Tag steht in der index.html und nicht serverseitig eingespritzt: der Service Worker legt die index.html in seinen Vorrat (navigateFallback), was zur Bauzeit fehlt, kaeme im Browser nie an. - defer, damit ein langsames oder abgeschaltetes HoneyOS den Start von Aether nicht ausbremst. Tests fuer die neue Regel: eng ohne Ausnahme, beide Richtungen mit, Pfad wird auf den Ursprung gekuerzt, Adresse ohne Schema faellt raus. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
package middleware
|
||
|
||
import (
|
||
"net/http"
|
||
"net/url"
|
||
"strings"
|
||
)
|
||
|
||
// CSP nennt die Ursprünge, die zusätzlich zu 'self' erlaubt sind.
|
||
//
|
||
// Jedes Feld ist eine bewusst enge Ausnahme mit einem konkreten Anlass. Leer
|
||
// heißt: keine Ausnahme — dann gilt für diese Richtung allein 'self'.
|
||
type CSP struct {
|
||
// FrameSrc: Seiten, die in einem <iframe> erscheinen dürfen.
|
||
// Gebraucht für den Office-Server; `default-src 'self'` deckt auch
|
||
// `frame-src` ab, ohne Ausnahme blockt der Browser ihn also, noch bevor
|
||
// eine Anfrage hinausgeht. Gemessen als „Framing 'http://…:9980/'
|
||
// violates … default-src 'self'".
|
||
FrameSrc []string
|
||
|
||
// ScriptSrc: fremde Ursprünge, von denen Skripte geladen werden dürfen.
|
||
// ConnectSrc: fremde Ursprünge, die per fetch/XHR erreichbar sein sollen.
|
||
// Beide gebraucht für eine eingebettete Leiste, die von woanders kommt und
|
||
// sich ihre Daten selbst holt — das Skript über das eine, die Anfrage über
|
||
// das andere. Nur eines von beiden zu setzen ergibt selten Sinn.
|
||
ScriptSrc []string
|
||
ConnectSrc []string
|
||
}
|
||
|
||
// SecurityHeaders adds security headers that the app controls.
|
||
// HSTS is intentionally omitted — it belongs on the edge reverse proxy.
|
||
//
|
||
// Ausnahmen werden **einzeln benannt** und nie als Platzhalter aufgenommen:
|
||
// Sie sollen so schmal sein wie ihr Anlass. Ein Eintrag, der sich nicht als
|
||
// Adresse lesen lässt, wird verworfen statt durchgereicht.
|
||
func SecurityHeaders(csp CSP) func(http.Handler) http.Handler {
|
||
directive := func(name string, extra []string) string {
|
||
out := name + " 'self'"
|
||
for _, raw := range extra {
|
||
if origin := originOf(raw); origin != "" {
|
||
out += " " + origin
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
policy := strings.Join([]string{
|
||
"default-src 'self'",
|
||
directive("script-src", csp.ScriptSrc),
|
||
"style-src 'self' 'unsafe-inline'",
|
||
"img-src 'self' data: blob:",
|
||
directive("connect-src", csp.ConnectSrc),
|
||
directive("frame-src", csp.FrameSrc),
|
||
}, "; ")
|
||
|
||
return func(next http.Handler) http.Handler {
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
h := w.Header()
|
||
h.Set("X-Content-Type-Options", "nosniff")
|
||
h.Set("X-Frame-Options", "DENY")
|
||
h.Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||
h.Set("Content-Security-Policy", policy)
|
||
next.ServeHTTP(w, r)
|
||
})
|
||
}
|
||
}
|
||
|
||
// originOf reduziert eine Adresse auf Schema und Host. Ein Pfad in einer
|
||
// CSP-Quelle wäre nicht falsch, aber enger als gemeint – und ein Tippfehler
|
||
// darin fiele erst auf, wenn der Rahmen leer bleibt.
|
||
func originOf(raw string) string {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return ""
|
||
}
|
||
u, err := url.Parse(raw)
|
||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||
return ""
|
||
}
|
||
return u.Scheme + "://" + u.Host
|
||
}
|