package web
import (
"net/http"
"net/url"
)
// checkSameOrigin is dolt.sr.ht's CSRF defence for state-changing POSTs. core-go
// ships no CSRF helper, so we keep it simple and explicit: a mutating request
// must carry an Origin (or, failing that, a Referer) header whose scheme+host
// matches our own configured origin. Cross-site form posts from a browser always
// send an Origin that differs from ours, so this blocks them; same-origin form
// submissions from our own pages always match.
//
// Rationale and limits (documented deliberately): we trust the Origin/Referer
// header, which browsers set and script cannot forge cross-origin. A request
// with NEITHER header is rejected — our own forms are same-origin and browsers
// send Origin on form POSTs, so a missing header signals a non-browser or
// stripped request, which we decline rather than wave through. This is a
// header-check, not a token scheme; it is sufficient because dolt.sr.ht uses the
// shared unified-login cookie (SameSite handling lives in meta) and has no
// cross-origin embedding.
func (a *app) checkSameOrigin(r *http.Request) bool {
self, err := url.Parse(a.chrome.SelfOrigin())
if err != nil || self.Host == "" {
return false
}
if origin := r.Header.Get("Origin"); origin != "" {
return originMatches(origin, self)
}
if referer := r.Header.Get("Referer"); referer != "" {
return originMatches(referer, self)
}
// No Origin and no Referer: refuse rather than assume same-origin.
return false
}
// originMatches reports whether raw (a full URL from an Origin or Referer
// header) has the same scheme and host as self.
func originMatches(raw string, self *url.URL) bool {
u, err := url.Parse(raw)
if err != nil {
return false
}
return u.Scheme == self.Scheme && u.Host == self.Host
}