feat(honeybar): HoneyOS-Leiste einbinden, CSP-Ausnahme dafür

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>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-24 21:03:01 +02:00
1 parent b5a83e4057
commit 9b8e82cdb2
6 files changed
+164 -17

No files matched your search

+42 -16
View File
@@ -6,26 +6,52 @@ import (
"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.
//
// frameSrc nennt zusätzliche Ursprünge, die in einem Rahmen erscheinen dürfen.
// Gebraucht für den Office-Server: Seine Bedienoberfläche läuft in einem
// <iframe>, und `default-src 'self'` deckt auch `frame-src` ab – ohne
// Ausnahme blockt der Browser sie, noch bevor eine Anfrage hinausgeht.
// Gemessen als „Framing 'http://…:9980/' violates … default-src 'self'".
//
// Bewusst **nur der eine konfigurierte Ursprung** und kein Platzhalter: Die
// Ausnahme soll so schmal sein wie der Anlass.
func SecurityHeaders(frameSrc ...string) func(http.Handler) http.Handler {
frame := "frame-src 'self'"
for _, raw := range frameSrc {
if origin := originOf(raw); origin != "" {
frame += " " + origin
// 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
}
csp := "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: blob:; " + frame
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) {
@@ -33,7 +59,7 @@ func SecurityHeaders(frameSrc ...string) func(http.Handler) http.Handler {
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", csp)
h.Set("Content-Security-Policy", policy)
next.ServeHTTP(w, r)
})
}