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:
1 parent
b5a83e4057
commit
9b8e82cdb2
6 files changed
+164
-17
No files matched your search
@@ -0,0 +1,83 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func policyFor(t *testing.T, csp CSP) string {
|
||||
t.Helper()
|
||||
h := SecurityHeaders(csp)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
|
||||
return rec.Header().Get("Content-Security-Policy")
|
||||
}
|
||||
|
||||
// Ohne Ausnahmen darf nichts Fremdes erlaubt sein — das ist der Normalfall
|
||||
// und der Zustand, in dem eine Instanz ohne Zusatzkonfiguration läuft.
|
||||
func TestCSPOhneAusnahmenBleibtEng(t *testing.T) {
|
||||
got := policyFor(t, CSP{})
|
||||
for _, want := range []string{
|
||||
"default-src 'self'",
|
||||
"script-src 'self';",
|
||||
"connect-src 'self';",
|
||||
"frame-src 'self'",
|
||||
} {
|
||||
if !strings.Contains(got+";", want) {
|
||||
t.Errorf("Regel %q fehlt in %q", want, got)
|
||||
}
|
||||
}
|
||||
if strings.Contains(got, "*") {
|
||||
t.Errorf("Platzhalter in der Regel: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Die eingebettete Leiste braucht zwei Richtungen: das Skript selbst und die
|
||||
// Anfrage, mit der es sich seine Daten holt.
|
||||
func TestCSPLeisteBekommtSkriptUndVerbindung(t *testing.T) {
|
||||
const bar = "https://os.example.com"
|
||||
got := policyFor(t, CSP{ScriptSrc: []string{bar}, ConnectSrc: []string{bar}})
|
||||
|
||||
if !strings.Contains(got, "script-src 'self' "+bar) {
|
||||
t.Errorf("script-src erlaubt die Leiste nicht: %q", got)
|
||||
}
|
||||
if !strings.Contains(got, "connect-src 'self' "+bar) {
|
||||
t.Errorf("connect-src erlaubt die Anfrage nicht: %q", got)
|
||||
}
|
||||
// Die Ausnahme gilt nur, wofür sie gedacht ist.
|
||||
if strings.Contains(got, "frame-src 'self' "+bar) {
|
||||
t.Errorf("die Leiste darf keine Rahmen-Freigabe erben: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Ein Pfad in der Konfiguration wäre enger als gemeint; ein Tippfehler ohne
|
||||
// Schema soll gar nicht erst in die Regel geraten.
|
||||
func TestCSPKürztAufDenUrsprungUndVerwirftUnsinn(t *testing.T) {
|
||||
got := policyFor(t, CSP{ScriptSrc: []string{"https://os.example.com/honeybar/honeybar.js"}})
|
||||
if !strings.Contains(got, "script-src 'self' https://os.example.com;") {
|
||||
t.Errorf("Pfad nicht abgeschnitten: %q", got)
|
||||
}
|
||||
|
||||
got = policyFor(t, CSP{ScriptSrc: []string{"os.example.com", "", " "}})
|
||||
if !strings.Contains(got+";", "script-src 'self';") {
|
||||
t.Errorf("Adresse ohne Schema wurde übernommen: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSicherheitsköpfeStehen(t *testing.T) {
|
||||
h := SecurityHeaders(CSP{})(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
|
||||
|
||||
for k, want := range map[string]string{
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Frame-Options": "DENY",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
} {
|
||||
if got := rec.Header().Get(k); got != want {
|
||||
t.Errorf("%s = %q, want %q", k, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user