Sandbox artifact rendering via iframe + CSP
Splits /p/{slug} into a trusted wrapper (HTML with a sandboxed iframe)
and /p/{slug}/raw (the artifact itself, served with Content-Security-Policy:
sandbox). Artifact JS now runs in an opaque origin and can't read admin
cookies or make same-origin credentialed requests to /a/* or /api/*.
Password gating is enforced on both routes so /raw can't be used to bypass
the unlock flow.
This commit is contained in:
@@ -33,17 +33,42 @@ func (s *Server) getArtifact(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.render(w, "artifact_wrapper", struct{ Slug string }{Slug: slug})
|
||||
|
||||
// Log view asynchronously. Failures are non-fatal.
|
||||
go s.logView(r, slug)
|
||||
}
|
||||
|
||||
func (s *Server) getArtifactRaw(w http.ResponseWriter, r *http.Request) {
|
||||
slug := chi.URLParam(r, "slug")
|
||||
art, err := s.store.GetArtifact(slug)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if art.ExpiresAt != nil && time.Now().After(*art.ExpiresAt) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if art.HasPassword && !s.artifact.Verify(r, slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := os.ReadFile(s.store.ArtifactPath(slug))
|
||||
if err != nil {
|
||||
http.Error(w, "artifact missing on disk", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// CSP: sandbox — if this URL is loaded directly (not via the wrapper
|
||||
// iframe), the browser applies the same restrictions as an iframe
|
||||
// sandbox, preventing artifact JS from reading cookies or making
|
||||
// same-origin credentialed requests to the admin surface.
|
||||
w.Header().Set("Content-Security-Policy",
|
||||
"sandbox allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-modals")
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Write(body)
|
||||
|
||||
// Log view asynchronously. Failures are non-fatal.
|
||||
go s.logView(r, slug)
|
||||
}
|
||||
|
||||
func (s *Server) postUnlock(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user