Introduce SafeTemplates.Render to execute templates into a buffer and prevent partial HTML on errors. Replace direct ExecuteTemplate calls in partial handlers and add a make lint-templates target to catch bypasses. Update operator sites template/view model to use OwnerOrgName. Guard the FedWiki sync by skipping inserts when DefaultWorkspaceID is empty and scope deletes to the configured default workspace only.
45 lines
1.7 KiB
Go
45 lines
1.7 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"html/template"
|
|
"log/slog"
|
|
"net/http"
|
|
)
|
|
|
|
// SafeTemplates wraps *html/template.Template to ensure all rendering goes
|
|
// through a buffer. This prevents partial HTML from being written to the
|
|
// response if template execution fails mid-way — the stdlib streams directly
|
|
// to the writer and cannot roll back a partial write.
|
|
//
|
|
// Handlers hold a *SafeTemplates instead of *template.Template. The underlying
|
|
// template set is unexported, so callers cannot bypass the buffer by calling
|
|
// ExecuteTemplate on the ResponseWriter directly — the compiler prevents it.
|
|
type SafeTemplates struct {
|
|
tmpl *template.Template
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// NewSafeTemplates wraps tmpl and logger for safe buffered rendering.
|
|
func NewSafeTemplates(tmpl *template.Template, logger *slog.Logger) *SafeTemplates {
|
|
return &SafeTemplates{tmpl: tmpl, logger: logger}
|
|
}
|
|
|
|
// Render executes the named template into a buffer and writes it to w only on
|
|
// success. On failure it logs the error and writes a 500 error fragment,
|
|
// guaranteeing the response is never partial or truncated.
|
|
func (s *SafeTemplates) Render(w http.ResponseWriter, name string, data any) {
|
|
var buf bytes.Buffer
|
|
if err := s.tmpl.ExecuteTemplate(&buf, name, data); err != nil {
|
|
s.logger.Error("template execution failed",
|
|
slog.String("template", name),
|
|
slog.Any("error", err))
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`<div class="alert alert-danger" role="alert"><strong>Error:</strong> Failed to render content. Please try again.</div>`))
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write(buf.Bytes())
|
|
}
|