Seed from go-template-repo, renamed to simplexcalc

The template's files at a77fd30, without its history or LICENSE, after
script/rename simplexcalc.

Model: opus-5-5
This commit is contained in:
clawbot
2026-09-26 21:38:57 +00:00
parent e336f46e34
commit f8ce8cef83
79 changed files with 7131 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
package middleware
import (
"crypto/rand"
"html/template"
"net/http"
"strings"
"github.com/gorilla/csrf"
)
// csrfCookieName is deliberately not the library default: a name that
// says which service issued it makes a cookie jar readable, and two
// services on sibling hosts do not fight over one name.
const csrfCookieName = "simplexcalc_csrf"
// csrfMaxAge bounds how long a token stays valid, in seconds.
const csrfMaxAge = 12 * 60 * 60
// csrfKeyBytes is the key length gorilla/csrf requires.
const csrfKeyBytes = 32
// CSRF protects state-changing routes (POST, PUT, PATCH, DELETE). Safe
// methods pass through and are issued a token.
//
// The key comes from config: CSRF_KEY when set, otherwise a random key
// generated here and logged as such. An ephemeral key is correct for
// development and wrong for anything with more than one replica or more
// than one process lifetime, because a token issued by one key is
// rejected by another — the user sees a failed form submission, not a
// security event. That is why it is a warning at startup and a
// documented configuration key rather than a silent default.
func (m *Middleware) CSRF() func(http.Handler) http.Handler {
key := m.cfg.CSRFKey
if m.cfg.CSRFKeyEphemeral {
key = make([]byte, csrfKeyBytes)
// crypto/rand.Read cannot fail on any supported platform; it
// panics internally rather than returning an error a caller
// might ignore. A key that is not random is not a key, so
// there is nothing to fall back to here anyway.
_, _ = rand.Read(key)
m.log.Warn("CSRF_KEY is not set; using a random key for this process",
"consequence", "tokens do not survive a restart and are not shared between replicas")
}
protect := csrf.Protect(
key,
// Secure cookies require TLS, which is absent in local
// development; tying the flag to the same switch that governs
// HSTS keeps "is this a production deployment" a single
// decision rather than two that can disagree.
csrf.Secure(m.cfg.HSTS),
csrf.HttpOnly(true),
csrf.SameSite(csrf.SameSiteLaxMode),
csrf.Path("/"),
csrf.CookieName(csrfCookieName),
csrf.MaxAge(csrfMaxAge),
csrf.ErrorHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
m.log.Warn("csrf rejection",
"id", RequestIDFrom(r.Context()),
"path", r.URL.Path,
"reason", csrf.FailureReason(r).Error(),
)
http.Error(w, "invalid CSRF token", http.StatusForbidden)
})),
)
// markScheme must be OUTSIDE protect: it sets a context value that
// protect reads, so it has to run first.
return func(next http.Handler) http.Handler {
return markScheme(protect(next))
}
}
// markScheme tells gorilla/csrf whether the browser's connection was
// plaintext, because the library cannot tell and assumes it was not.
//
// Its strict Referer check is for TLS only, and it treats every request
// as TLS unless a context value says otherwise. A service behind a
// TLS-terminating reverse proxy receives plaintext HTTP with an
// https:// Referer — the library then applies the TLS rules to a
// plaintext connection and rejects every form submission, which is a
// total outage of every state-changing route rather than a subtle bug.
// Left alone, the same misreading rejects plain HTTP in development for
// the mirror-image reason.
//
// The rule: HTTPS if the connection is TLS, or if a proxy said so with
// X-Forwarded-Proto. Trusting that header is safe in this one
// direction — the only thing an attacker gains by setting it is
// STRICTER checking of their own request. The reverse (inferring
// plaintext) is what would weaken the check, and nothing a client sends
// can cause it.
//
// A deployment behind a proxy that does not set X-Forwarded-Proto gets
// the plaintext ruleset: tokens still work, and the extra Referer check
// TLS would have added is not applied. Configure the proxy.
func markScheme(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil && !strings.EqualFold(r.Header.Get("X-Forwarded-Proto"), "https") {
r = csrf.PlaintextHTTPRequest(r)
}
next.ServeHTTP(w, r)
})
}
// CSRFField returns the hidden input for r's token, for a template to
// place inside a form. Handlers call this rather than importing
// gorilla/csrf, so the library stays swappable behind this package.
func CSRFField(r *http.Request) template.HTML {
return csrf.TemplateField(r)
}