diff --git a/bloat.conf b/bloat.conf index e583988..20d5fef 100644 --- a/bloat.conf +++ b/bloat.conf @@ -38,5 +38,4 @@ post_formats=PlainText:text/plain,HTML:text/html,Markdown:text/markdown,BBCode:t # single_instance=pl.mydomain.com # Path to custom CSS. Value can be a file path relative to the static directory. -# or a URL starting with either "http://" or "https://". # custom_css=custom.css diff --git a/main.go b/main.go index 657912d..9e9ba4e 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "net/http" "os" "path/filepath" - "strings" "bloat/config" "bloat/renderer" @@ -47,14 +46,8 @@ func main() { errExit(err) } - customCSS := config.CustomCSS - if len(customCSS) > 0 && !strings.HasPrefix(customCSS, "http://") && - !strings.HasPrefix(customCSS, "https://") { - customCSS = "/static/" + customCSS - } - s := service.NewService(config.ClientName, config.ClientScope, - config.ClientWebsite, customCSS, config.SingleInstance, + config.ClientWebsite, config.CustomCSS, config.SingleInstance, config.PostFormats, renderer) handler := service.NewHandler(s, *verbose, config.StaticDirectory) diff --git a/model/session.go b/model/session.go index 7c4ff27..cb634ee 100644 --- a/model/session.go +++ b/model/session.go @@ -1,7 +1,6 @@ package model type Session struct { - ID string `json:"id,omitempty"` UserID string `json:"uid,omitempty"` Instance string `json:"ins,omitempty"` ClientID string `json:"cid,omitempty"` @@ -30,6 +29,7 @@ type Settings struct { InstanceEmojiFilter string `json:"iemojfilter,omitempty"` AddReactionsFilter string `json:"reactionfilter,omitempty"` CSS string `json:"css,omitempty"` + CSSHash string `json:"cssh,omitempty"` } func NewSettings() *Settings { @@ -48,5 +48,6 @@ func NewSettings() *Settings { InstanceEmojiFilter: "", AddReactionsFilter: "", CSS: "", + CSSHash: "", } } diff --git a/service/client.go b/service/client.go index 7c5c33c..49bc145 100644 --- a/service/client.go +++ b/service/client.go @@ -33,9 +33,11 @@ func (c *client) setSession(sess *model.Session) error { return err } http.SetCookie(c.w, &http.Cookie{ - Name: "session", - Value: sb.String(), - Expires: time.Now().Add(365 * 24 * time.Hour), + Name: "session", + Path: "/", + HttpOnly: true, + Value: sb.String(), + Expires: time.Now().Add(365 * 24 * time.Hour), }) return nil } @@ -53,6 +55,7 @@ func (c *client) getSession() (sess *model.Session, err error) { func (c *client) unsetSession() { http.SetCookie(c.w, &http.Cookie{ Name: "session", + Path: "/", Value: "", Expires: time.Now(), }) diff --git a/service/service.go b/service/service.go index 6870f09..7c9d704 100644 --- a/service/service.go +++ b/service/service.go @@ -1,6 +1,8 @@ package service import ( + "crypto/sha256" + "encoding/base64" "errors" "fmt" "mime/multipart" @@ -936,10 +938,6 @@ func (s *service) NewSession(c *client, instance string) (rurl string, sess *mod instanceURL = "https://" + instance } - sid, err := util.NewSessionID() - if err != nil { - return - } csrf, err := util.NewCSRFToken() if err != nil { return @@ -955,28 +953,14 @@ func (s *service) NewSession(c *client, instance string) (rurl string, sess *mod if err != nil { return } + rurl = app.AuthURI sess = &model.Session{ - ID: sid, Instance: instance, ClientID: app.ClientID, ClientSecret: app.ClientSecret, CSRFToken: csrf, Settings: *model.NewSettings(), } - - u, err := url.Parse("/oauth/authorize") - if err != nil { - return - } - - q := make(url.Values) - q.Set("scope", "read write follow") - q.Set("client_id", app.ClientID) - q.Set("response_type", "code") - q.Set("redirect_uri", s.cwebsite+"/oauth_callback") - u.RawQuery = q.Encode() - - rurl = instanceURL + u.String() return } @@ -1213,8 +1197,18 @@ func (s *service) SaveSettings(c *client, settings *model.Settings) (err error) default: return errInvalidArgument } - if len(settings.CSS) > 1<<20 { - return errInvalidArgument + if len(settings.CSS) > 0 { + if len(settings.CSS) > 1<<20 { + return errInvalidArgument + } + // For some reason, browsers convert CRLF to LF before calculating + // the hash of the inline resources. + settings.CSS = strings.Replace(settings.CSS, "\x0d\x0a", "\x0a", -1) + + h := sha256.Sum256([]byte(settings.CSS)) + settings.CSSHash = base64.StdEncoding.EncodeToString(h[:]) + } else { + settings.CSSHash = "" } c.s.Settings = *settings return c.setSession(c.s) diff --git a/service/transport.go b/service/transport.go index c5bdde7..2984bd7 100644 --- a/service/transport.go +++ b/service/transport.go @@ -26,6 +26,15 @@ const ( CSRF ) +const csp = "default-src 'none';" + + " img-src *;" + + " media-src *;" + + " font-src *;" + + " child-src *;" + + " connect-src 'self';" + + " script-src 'self';" + + " style-src 'self'" + func NewHandler(s *service, verbose bool, staticDir string) http.Handler { r := mux.NewRouter() @@ -58,14 +67,14 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler { }(time.Now()) } - var ct string + h := c.w.Header() switch rt { case HTML: - ct = "text/html; charset=utf-8" + h.Set("Content-Type", "text/html; charset=utf-8") + h.Set("Content-Security-Policy", csp) case JSON: - ct = "application/json" + h.Set("Content-Type", "application/json") } - c.w.Header().Add("Content-Type", ct) err = c.authenticate(at, s.instance) if err != nil { @@ -73,6 +82,13 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler { return } + // Override the CSP header to allow custom CSS + if rt == HTML && len(c.s.Settings.CSS) > 0 && + len(c.s.Settings.CSSHash) > 0 { + v := fmt.Sprintf("%s 'sha256-%s'", csp, c.s.Settings.CSSHash) + h.Set("Content-Security-Policy", v) + } + err = f(c) if err != nil { writeError(c, err, rt, req.Method == http.MethodGet) diff --git a/static/fluoride.js b/static/fluoride.js index ee3e3d8..70379de 100644 --- a/static/fluoride.js +++ b/static/fluoride.js @@ -326,7 +326,7 @@ document.addEventListener("DOMContentLoaded", function() { links[j].target = "_blank"; } - var links = document.querySelectorAll(".status-media-container .img-link"); + var links = document.querySelectorAll(".status-media-container .img-link, .user-profile-img-container .img-link"); for (var j = 0; j < links.length; j++) { handleImgPreview(links[j]); } diff --git a/templates/header.tmpl b/templates/header.tmpl index 8a1b0ca..7ebf65e 100644 --- a/templates/header.tmpl +++ b/templates/header.tmpl @@ -20,7 +20,7 @@ {{if gt .Count 0}}({{.Count}}){{end}} {{.Title}} {{if .CustomCSS}} - + {{end}} {{if $.Ctx.FluorideMode}} diff --git a/util/rand.go b/util/rand.go index 90e66a5..f1692b9 100644 --- a/util/rand.go +++ b/util/rand.go @@ -16,10 +16,6 @@ func NewRandID(n int) (string, error) { return enc.EncodeToString(data), nil } -func NewSessionID() (string, error) { - return NewRandID(24) -} - func NewCSRFToken() (string, error) { return NewRandID(24) }