From d297eb565814e1ab3d350b9eefc35a219fb51a88 Mon Sep 17 00:00:00 2001
From: r
Date: Sat, 7 Oct 2023 09:11:43 +0000
Subject: [PATCH 01/11] Use stricter cookie attributes
---
service/client.go | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/service/client.go b/service/client.go
index e4ab8cb..18ebb52 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(),
})
From c7f40c1e1568bf3f0fcb15c0fc0d01f0f253675d Mon Sep 17 00:00:00 2001
From: r
Date: Sat, 7 Oct 2023 09:19:56 +0000
Subject: [PATCH 02/11] Cleanup oauth redirect URL generation
---
service/service.go | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)
diff --git a/service/service.go b/service/service.go
index 6b8d0ee..ca80a2b 100644
--- a/service/service.go
+++ b/service/service.go
@@ -859,6 +859,7 @@ 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,
@@ -867,20 +868,6 @@ func (s *service) NewSession(c *client, instance string) (rurl string, sess *mod
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
}
From 927072e26a470ed3084e36ae2413f4dfd088905f Mon Sep 17 00:00:00 2001
From: r
Date: Sat, 7 Oct 2023 10:20:11 +0000
Subject: [PATCH 03/11] Remove unused session ID field
---
model/session.go | 1 -
service/service.go | 5 -----
util/rand.go | 4 ----
3 files changed, 10 deletions(-)
diff --git a/model/session.go b/model/session.go
index 6ada4aa..f9e4287 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"`
diff --git a/service/service.go b/service/service.go
index ca80a2b..2f87fa3 100644
--- a/service/service.go
+++ b/service/service.go
@@ -840,10 +840,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
@@ -861,7 +857,6 @@ func (s *service) NewSession(c *client, instance string) (rurl string, sess *mod
}
rurl = app.AuthURI
sess = &model.Session{
- ID: sid,
Instance: instance,
ClientID: app.ClientID,
ClientSecret: app.ClientSecret,
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)
}
From ed521dd33d0d002c577a75e349136fed25b7fda5 Mon Sep 17 00:00:00 2001
From: r
Date: Sun, 15 Oct 2023 15:46:54 +0000
Subject: [PATCH 04/11] Restrict instance level custom CSS to static directory
---
bloat.conf | 1 -
main.go | 9 +--------
templates/header.tmpl | 2 +-
3 files changed, 2 insertions(+), 10 deletions(-)
diff --git a/bloat.conf b/bloat.conf
index f29e553..9bd2781 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/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}}
From 67b13c71baea56eeb15532ca1b1377f6da8d18ac Mon Sep 17 00:00:00 2001
From: r
Date: Sun, 15 Oct 2023 15:53:44 +0000
Subject: [PATCH 05/11] Use CSP header to restrict resource loading
This helps mitigate XSS exploits.
Users will have to save the settings again to make the custom CSS
work.
---
model/session.go | 2 ++
service/service.go | 16 ++++++++++++++--
service/transport.go | 25 +++++++++++++++++++++----
3 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/model/session.go b/model/session.go
index f9e4287..61a409c 100644
--- a/model/session.go
+++ b/model/session.go
@@ -27,6 +27,7 @@ type Settings struct {
AntiDopamineMode bool `json:"adm,omitempty"`
HideUnsupportedNotifs bool `json:"hun,omitempty"`
CSS string `json:"css,omitempty"`
+ CSSHash string `json:"cssh,omitempty"`
}
func NewSettings() *Settings {
@@ -43,5 +44,6 @@ func NewSettings() *Settings {
AntiDopamineMode: false,
HideUnsupportedNotifs: false,
CSS: "",
+ CSSHash: "",
}
}
diff --git a/service/service.go b/service/service.go
index 2f87fa3..c925b83 100644
--- a/service/service.go
+++ b/service/service.go
@@ -1,6 +1,8 @@
package service
import (
+ "crypto/sha256"
+ "encoding/base64"
"errors"
"fmt"
"mime/multipart"
@@ -1014,8 +1016,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.ReplaceAll(settings.CSS, "\x0d\x0a", "\x0a")
+
+ 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 1182d6c..d032cce 100644
--- a/service/transport.go
+++ b/service/transport.go
@@ -26,6 +26,16 @@ const (
CSRF
)
+const csp = "default-src 'none';" +
+ " img-src *;" +
+ " media-src *;" +
+ " font-src *;" +
+ " child-src *;" +
+ " connect-src 'self';" +
+ " form-action 'self';" +
+ " script-src 'self';" +
+ " style-src 'self'"
+
func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
r := mux.NewRouter()
@@ -58,14 +68,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 +83,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)
From 9b053e32ec8a42c74f3c09d28a8df4086b5b2945 Mon Sep 17 00:00:00 2001
From: r
Date: Sun, 22 Oct 2023 11:11:21 +0000
Subject: [PATCH 06/11] Fix replace syntax
---
service/service.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/service/service.go b/service/service.go
index c925b83..db309ff 100644
--- a/service/service.go
+++ b/service/service.go
@@ -1022,7 +1022,7 @@ func (s *service) SaveSettings(c *client, settings *model.Settings) (err error)
}
// For some reason, browsers convert CRLF to LF before calculating
// the hash of the inline resources.
- settings.CSS = strings.ReplaceAll(settings.CSS, "\x0d\x0a", "\x0a")
+ settings.CSS = strings.Replace(settings.CSS, "\x0d\x0a", "\x0a", -1)
h := sha256.Sum256([]byte(settings.CSS))
settings.CSSHash = base64.StdEncoding.EncodeToString(h[:])
From 597cfc6b1ed23dc85774a43055416c98b77cae67 Mon Sep 17 00:00:00 2001
From: r
Date: Sun, 22 Oct 2023 11:12:27 +0000
Subject: [PATCH 07/11] fluoride: Add image preview for profile image
---
static/fluoride.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/static/fluoride.js b/static/fluoride.js
index abeed21..d5c5b5d 100644
--- a/static/fluoride.js
+++ b/static/fluoride.js
@@ -325,7 +325,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]);
}
From f4881e72675e87a9eae716436c3ac18a788d596d Mon Sep 17 00:00:00 2001
From: r
Date: Wed, 25 Oct 2023 06:40:34 +0000
Subject: [PATCH 08/11] Remove form-action CSP directive
Chrome incorrectly restricts the redirect URL to the sources specified
in the form-action value, which prevents the instance oauth page from
loading.
---
service/transport.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/service/transport.go b/service/transport.go
index d032cce..f7e31d6 100644
--- a/service/transport.go
+++ b/service/transport.go
@@ -32,7 +32,6 @@ const csp = "default-src 'none';" +
" font-src *;" +
" child-src *;" +
" connect-src 'self';" +
- " form-action 'self';" +
" script-src 'self';" +
" style-src 'self'"
From 53dd0c50ef823326f19311ca6a48d886e3636e66 Mon Sep 17 00:00:00 2001
From: localhost_frssoft
Date: Mon, 6 Nov 2023 12:17:09 +0300
Subject: [PATCH 09/11] removed session id from register
---
service/service.go | 5 -----
1 file changed, 5 deletions(-)
diff --git a/service/service.go b/service/service.go
index 7c9d704..e8d0821 100644
--- a/service/service.go
+++ b/service/service.go
@@ -991,10 +991,6 @@ func (s *service) NewSessionRegister(c *client, instance string, reason string,
instanceURL = "https://" + instance
}
- sid, err := util.NewSessionID()
- if err != nil {
- return
- }
csrf, err := util.NewCSRFToken()
if err != nil {
return
@@ -1021,7 +1017,6 @@ func (s *service) NewSessionRegister(c *client, instance string, reason string,
return
}
sess = &model.Session{
- ID: sid,
Instance: instance,
UserID: "1",
ClientID: app.ClientID,
From 1c3cb0f3585ad76c0e8f0c03998637ebbcf57678 Mon Sep 17 00:00:00 2001
From: localhost_frssoft
Date: Mon, 6 Nov 2023 12:46:18 +0300
Subject: [PATCH 10/11] footer sign-in changed
---
templates/signin.tmpl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/signin.tmpl b/templates/signin.tmpl
index 9dcec63..af46fe8 100644
--- a/templates/signin.tmpl
+++ b/templates/signin.tmpl
@@ -53,7 +53,7 @@
git.freesoftwareextremist.com/bloat
for more details.
- bloat-gts fork branch
+ localhost_custom fork branch
{{template "footer.tmpl"}}
From 6b3240dd9a5a853cd110347127864c7fd6c9ec73 Mon Sep 17 00:00:00 2001
From: localhost_frssoft
Date: Mon, 6 Nov 2023 13:11:16 +0300
Subject: [PATCH 11/11] remove unusable signup feature
---
mastodon/apps.go | 47 ---------------------------------
service/service.go | 60 -------------------------------------------
service/transport.go | 27 -------------------
templates/signin.tmpl | 35 -------------------------
4 files changed, 169 deletions(-)
diff --git a/mastodon/apps.go b/mastodon/apps.go
index 1ce894f..652be6e 100644
--- a/mastodon/apps.go
+++ b/mastodon/apps.go
@@ -92,50 +92,3 @@ func RegisterApp(ctx context.Context, appConfig *AppConfig) (*Application, error
return &app, nil
}
-
-type AppAuth struct {
- http.Client
-}
-
-// RegisterApp make auth application and return app token.
-func AuthApp(ctx context.Context, appConfig *Application, instance string) (*string, error) {
- var appAuth AppAuth
- params := url.Values{}
- params.Set("client_id", appConfig.ClientID)
- params.Set("client_secret", appConfig.ClientSecret)
- params.Set("redirect_uris", "urn:ietf:wg:oauth:2.0:oob")
- params.Set("grant_type", "client_credentials")
- params.Set("scope", "read write follow")
-
- u, err := url.Parse("https://" + instance)
- if err != nil {
- return nil, err
- }
- u.Path = path.Join(u.Path, "/oauth/token")
-
- req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(params.Encode()))
- if err != nil {
- return nil, err
- }
- req = req.WithContext(ctx)
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- resp, err := appAuth.Do(req)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != http.StatusOK {
- return nil, parseAPIError("bad request", resp)
- }
-
- var res struct {
- AccessToken string `json:"access_token"`
- }
- err = json.NewDecoder(resp.Body).Decode(&res)
- if err != nil {
- return nil, err
- }
-
- return &res.AccessToken, nil
-}
diff --git a/service/service.go b/service/service.go
index e8d0821..7ac5624 100644
--- a/service/service.go
+++ b/service/service.go
@@ -982,66 +982,6 @@ func (s *service) Signin(c *client, code string) (err error) {
return c.setSession(c.s)
}
-func (s *service) NewSessionRegister(c *client, instance string, reason string, username string, email string, password string, agreement bool, locale string, registerCredintals mastodon.RegisterCredintals) (rurl string, sess *model.Session, err error) {
- var instanceURL string
- if strings.HasPrefix(instance, "https://") {
- instanceURL = instance
- instance = strings.TrimPrefix(instance, "https://")
- } else {
- instanceURL = "https://" + instance
- }
-
- csrf, err := util.NewCSRFToken()
- if err != nil {
- return
- }
-
- app, err := mastodon.RegisterApp(c.ctx, &mastodon.AppConfig{
- Server: instanceURL,
- ClientName: s.cname,
- Scopes: s.cscope,
- Website: s.cwebsite,
- RedirectURIs: s.cwebsite + "/oauth_callback",
- })
- if err != nil {
- return
- }
- registerCredintals.App = app
- bearer, err := mastodon.AuthApp(c.ctx, app, instance)
- if err != nil {
- return
- }
- token, err := mastodon.RegisterAccount(c.ctx, instance, reason, username, email, password, agreement, locale, registerCredintals, *bearer)
-
- if err != nil {
- return
- }
- sess = &model.Session{
- Instance: instance,
- UserID: "1",
- ClientID: app.ClientID,
- ClientSecret: app.ClientSecret,
- AccessToken: *token,
- 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
-}
-
func (s *service) Signout(c *client) (err error) {
return c.RevokeToken(c.ctx)
}
diff --git a/service/transport.go b/service/transport.go
index 2984bd7..2b579e4 100644
--- a/service/transport.go
+++ b/service/transport.go
@@ -293,32 +293,6 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
return nil
}, NOAUTH, HTML)
- signup := handle(func(c *client) error {
- instance := c.r.FormValue("instanceup")
- reason := c.r.FormValue("reason")
- username := c.r.FormValue("username")
- email := c.r.FormValue("email")
- password := c.r.FormValue("password")
- agreement := c.r.FormValue("agreement") == "true"
- locale := c.r.FormValue("locale")
- url, sess, err := s.NewSessionRegister(c, instance, reason, username, email, password, agreement, locale, mastodon.RegisterCredintals{
- Server: "https://"+instance,
- Reason: reason,
- Username: username,
- Email: email,
- Password: password,
- Agreement: agreement,
- Locale: locale,
- })
- if err != nil {
- return err
- }
- c.setSession(sess)
- url = "/confirmation"
- c.redirect(url)
- return nil
- }, NOAUTH, HTML)
-
oauthCallback := handle(func(c *client) error {
q := c.r.URL.Query()
token := q.Get("code")
@@ -856,7 +830,6 @@ func NewHandler(s *service, verbose bool, staticDir string) http.Handler {
r.HandleFunc("/profile/delavatar", profileDelAvatar).Methods(http.MethodPost)
r.HandleFunc("/profile/delbanner", profileDelBanner).Methods(http.MethodPost)
r.HandleFunc("/signin", signin).Methods(http.MethodPost)
- r.HandleFunc("/signup", signup).Methods(http.MethodPost)
r.HandleFunc("/oauth_callback", oauthCallback).Methods(http.MethodGet)
r.HandleFunc("/post", post).Methods(http.MethodPost)
r.HandleFunc("/like/{id}", like).Methods(http.MethodPost)
diff --git a/templates/signin.tmpl b/templates/signin.tmpl
index af46fe8..7726508 100644
--- a/templates/signin.tmpl
+++ b/templates/signin.tmpl
@@ -13,41 +13,6 @@
-
-Sign up
-
-
-
-
-
-
See
git.freesoftwareextremist.com/bloat