package pwa import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) func exampleManifest() Manifest { return Manifest{ID: "/app/", Name: "Example", StartURL: "/app/inbox", Scope: "/app/", Icons: []Icon{{Src: "/icon.png", Sizes: "192x192", Type: "image/png"}}} } func TestManifestIdentityAndHeaders(t *testing.T) { m := exampleManifest() m.Name = `Example "quoted" ` h, err := m.Handler() if err != nil { t.Fatal(err) } w := httptest.NewRecorder() h.ServeHTTP(w, httptest.NewRequest("GET", "/manifest.webmanifest", nil)) var got Manifest if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatal(err) } if got.ID != m.ID || got.Name != m.Name || got.Display != "standalone" { t.Fatalf("identity changed: %+v", got) } if w.Header().Get("Content-Type") != "application/manifest+json" || w.Header().Get("Cache-Control") != "no-cache" { t.Fatal(w.Header()) } for _, method := range []string{"HEAD", "POST"} { w := httptest.NewRecorder() h.ServeHTTP(w, httptest.NewRequest(method, "/manifest.webmanifest", nil)) if method == "HEAD" && w.Body.Len() != 0 { t.Fatal("HEAD returned a body") } if method == "POST" && w.Code != http.StatusMethodNotAllowed { t.Fatal("manifest accepted a mutation") } } } func TestManifestRejectsScopeEscapes(t *testing.T) { for _, start := range []string{"/other/", "//evil.example/", "/app/../outside", "/app/%2e%2e/outside", "/app/%5c../outside", "/app/./inbox", "/app/\n/", "https://example.com/app/"} { t.Run(start, func(t *testing.T) { m := exampleManifest() m.StartURL = start if _, err := m.Handler(); err == nil { t.Fatalf("accepted ambiguous or out-of-scope start URL %q", start) } }) } } func TestAssetsAreScriptsAndNeverDirectoryListings(t *testing.T) { h := http.StripPrefix("/pwa", Assets()) for _, path := range []string{"/pwa/client.mjs", "/pwa/worker.js"} { w := httptest.NewRecorder() h.ServeHTTP(w, httptest.NewRequest("GET", path, nil)) if w.Code != 200 || !strings.HasPrefix(w.Header().Get("Content-Type"), "text/javascript") || w.Body.Len() == 0 { t.Fatalf("%s: %d %v", path, w.Code, w.Header()) } } w := httptest.NewRecorder() h.ServeHTTP(w, httptest.NewRequest("GET", "/pwa/", nil)) if w.Code != 404 { t.Fatal("assets exposed a directory listing") } }