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 { selfOrigin := a.newBasePage(r, "").SelfOrigin self, err := url.Parse(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 }