- One base layout (layout.html) with the header, the nav from Registry.Nav()
(D-038), a content slot and the CSRF token; every page renders inside it and
a module supplies content only
- Embedded same-origin assets (app.css · app.js · webauthn.js · sw.js ·
manifest.webmanifest · icons) served by the reserved `static` workflow
- webauthn.js drives the three ceremonies from data-* attributes — no inline
script, so the CSP `script-src 'self'` holds (AC-3)
- manifest start_url "/" (D-011); the service worker never caches an
authenticated response or /{GUID} (A5)
45 lines
1.5 KiB
Go
45 lines
1.5 KiB
Go
package web
|
|
|
|
import (
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
// ── the service worker's cache policy (A5, SPEC-009 §6) ──────────────────────
|
|
//
|
|
// THE RULE: the service worker may cache STATIC ASSETS — and nothing else. It
|
|
// must never cache an authenticated response and never a /{GUID} page (A5), so
|
|
// it does not intercept such a request at all.
|
|
//
|
|
// StaticCachePrefixes is the ONE source of truth for "static". The embedded
|
|
// service worker (static/sw.js) declares the same list; a test asserts the two
|
|
// agree, so the policy cannot drift (AC-5).
|
|
|
|
// StaticCachePrefixes are the only request-path prefixes the service worker may
|
|
// cache. Authenticated responses and /{GUID} pages are never in it.
|
|
var StaticCachePrefixes = []string{StaticPath}
|
|
|
|
// CacheablePath reports whether a same-origin request may be cached by the
|
|
// service worker (A5): a GET of a static asset, and nothing else. The path is
|
|
// CLEANED first, so "/static/../{GUID}" is judged as "/{GUID}" — a traversal
|
|
// cannot smuggle an authenticated path into the cache.
|
|
//
|
|
// It is the policy the embedded worker implements (static/sw.js) and the shape
|
|
// its test asserts; no caching decision is taken anywhere else.
|
|
func CacheablePath(method, requestPath string) bool {
|
|
if method != http.MethodGet {
|
|
return false
|
|
}
|
|
p := requestPath
|
|
if !strings.HasPrefix(p, "/") {
|
|
p = "/" + p
|
|
}
|
|
clean := path.Clean(p)
|
|
for _, prefix := range StaticCachePrefixes {
|
|
if strings.HasPrefix(clean, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|