From ae71abda0bd60b521a2bc1b15580b761a17fac11 Mon Sep 17 00:00:00 2001
From: m15o
Date: Mon, 6 Dec 2021 07:14:20 +0100
Subject: [PATCH] security
---
web/handler/handler.go | 35 ++++++++---------
web/handler/html.go | 17 ++++++++-
web/handler/html/status-updater.html | 2 +-
web/handler/html/status.html | 15 ++++++++
web/handler/index_show.go | 1 +
web/handler/settings_show.go | 1 +
web/handler/status_create.go | 1 +
web/handler/status_edit.go | 1 +
web/handler/status_remove.go | 1 +
web/handler/tpl.go | 2 +-
web/handler/user_show.go | 56 +++++++++++++++++++++++++++-
web/handler/widget_show.go | 2 +-
web/session/session.go | 1 +
13 files changed, 113 insertions(+), 22 deletions(-)
create mode 100644 web/handler/html/status.html
diff --git a/web/handler/handler.go b/web/handler/handler.go
index f8ba2e4..aa5214d 100644
--- a/web/handler/handler.go
+++ b/web/handler/handler.go
@@ -1,13 +1,16 @@
package handler
import (
+ "errors"
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
+ "net/url"
"status/config"
"status/storage"
"status/web/session"
+ "strings"
)
func serverError(w http.ResponseWriter, err error) {
@@ -30,28 +33,24 @@ type Handler struct {
sess *session.Session
}
-//func (h *Handler) getUser(r *http.Request) (string, error) {
-// user, err := h.sess.Get(r)
-// if err != nil {
-// return "", err
-// }
-// if h.cfg.Env != "PROD" {
-// return user, err
-// }
-// // Removes "https://" from referer before checking.
-// // Thank you crussel for this fix!
-// if !strings.HasPrefix(r.Referer()[8:], h.cfg.Host) && !strings.HasPrefix(r.Referer()[8:], user+h.cfg.Host) {
-// err = errors.New("wrong referer")
-// }
-// return user, err
-//}
+func protectClickJacking(w http.ResponseWriter) {
+ w.Header().Set("X-Frame-Options", "DENY")
+ w.Header().Set("Content-Security-Policy", "frame-ancestors 'none'")
+}
func (h *Handler) getUser(r *http.Request) (string, error) {
user, err := h.sess.Get(r)
if err != nil {
return "", err
}
- return user, nil
+ u, err := url.Parse(r.Referer())
+ if err != nil {
+ return "", err
+ }
+ if strings.HasPrefix(u.Path, "/users") {
+ err = errors.New("forbidden access")
+ }
+ return user, err
}
func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http.Handler, error) {
@@ -84,9 +83,11 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
router.HandleFunc("/about/status-updater", h.showStatusUpdaterView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}.atom", h.showAtomView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet)
- router.HandleFunc("/users/{user}/status.json", h.showUserStatusView).Methods(http.MethodGet)
+ router.HandleFunc("/users/{user}/status", h.showUserStatusView).Methods(http.MethodGet)
+ router.HandleFunc("/users/{user}/status.json", h.showUserStatusJSONView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet)
+ router.HandleFunc("/users/{user}/badge.png", h.showUserStatusBadgeView).Methods(http.MethodGet)
router.PathPrefix("/assets/").Handler(
http.StripPrefix("/assets/",
http.FileServer(
diff --git a/web/handler/html.go b/web/handler/html.go
index 7baa4fa..5b4a295 100644
--- a/web/handler/html.go
+++ b/web/handler/html.go
@@ -297,6 +297,21 @@ var TplMap = map[string]string{
{{ end }}`,
+ "status": `
+
+
+
+
+ status cafe
+
+
+
+
+{{ if .status.Content }}
+{{ template "status" .status }}
+{{ end }}
+
+`,
"status-updater": `{{ define "content" }}
Status Updater
@@ -314,7 +329,7 @@ var TplMap = map[string]string{
Drag the following link to your bookmarks toolbar:
- status updater
+ status updater
That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.
diff --git a/web/handler/html/status-updater.html b/web/handler/html/status-updater.html
index 6d4c0e4..b859dca 100644
--- a/web/handler/html/status-updater.html
+++ b/web/handler/html/status-updater.html
@@ -15,7 +15,7 @@
Drag the following link to your bookmarks toolbar:
- status updater
+ status updater
That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.
diff --git a/web/handler/html/status.html b/web/handler/html/status.html
new file mode 100644
index 0000000..0f11011
--- /dev/null
+++ b/web/handler/html/status.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+ status cafe
+
+
+
+
+{{ if .status.Content }}
+{{ template "status" .status }}
+{{ end }}
+
+
\ No newline at end of file
diff --git a/web/handler/index_show.go b/web/handler/index_show.go
index a8cee64..2b6133e 100644
--- a/web/handler/index_show.go
+++ b/web/handler/index_show.go
@@ -11,6 +11,7 @@ type Update struct {
}
func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
+ protectClickJacking(w)
user, _ := h.sess.Get(r)
statuses, err := h.storage.LatestStatuses()
if err != nil {
diff --git a/web/handler/settings_show.go b/web/handler/settings_show.go
index 957ab86..b578c05 100644
--- a/web/handler/settings_show.go
+++ b/web/handler/settings_show.go
@@ -3,6 +3,7 @@ package handler
import "net/http"
func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
+ protectClickJacking(w)
username, err := h.getUser(r)
if err != nil {
unauthorized(w)
diff --git a/web/handler/status_create.go b/web/handler/status_create.go
index 0484bfa..281f3f7 100644
--- a/web/handler/status_create.go
+++ b/web/handler/status_create.go
@@ -6,6 +6,7 @@ import (
)
func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) {
+ protectClickJacking(w)
_, err := h.getUser(r)
if err != nil {
unauthorized(w)
diff --git a/web/handler/status_edit.go b/web/handler/status_edit.go
index c5a2ed3..7a70eff 100644
--- a/web/handler/status_edit.go
+++ b/web/handler/status_edit.go
@@ -19,6 +19,7 @@ func RouteInt64Param(r *http.Request, param string) int64 {
}
func (h *Handler) showEditStatusView(w http.ResponseWriter, r *http.Request) {
+ protectClickJacking(w)
user, err := h.getUser(r)
if err != nil {
unauthorized(w)
diff --git a/web/handler/status_remove.go b/web/handler/status_remove.go
index cf19731..5c498a1 100644
--- a/web/handler/status_remove.go
+++ b/web/handler/status_remove.go
@@ -6,6 +6,7 @@ import (
)
func (h *Handler) handleRemoveStatus(w http.ResponseWriter, r *http.Request) {
+ protectClickJacking(w)
user, err := h.getUser(r)
if err != nil {
unauthorized(w)
diff --git a/web/handler/tpl.go b/web/handler/tpl.go
index 3793067..8cb7856 100644
--- a/web/handler/tpl.go
+++ b/web/handler/tpl.go
@@ -16,7 +16,7 @@ func (h *Handler) initTpl() {
for name, content := range TplMap {
views[name] = template.Must(template.New("main").Funcs(template.FuncMap{
"faces": func() []string {
- return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺"}
+ return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺", "📖", "🔥", "❄️", "✨", "💡", "🎼"}
}}).Parse(commonTemplates + content))
}
}
diff --git a/web/handler/user_show.go b/web/handler/user_show.go
index 414c56d..e254bb7 100644
--- a/web/handler/user_show.go
+++ b/web/handler/user_show.go
@@ -15,6 +15,7 @@ import (
"image"
"image/color"
"image/draw"
+ "status/model"
"strconv"
"time"
@@ -96,7 +97,7 @@ type statusjson struct {
TimeAgo string `json:"timeAgo"`
}
-func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
+func (h *Handler) showUserStatusJSONView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) {
notFound(w)
@@ -119,6 +120,19 @@ func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(res)
}
+func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
+ statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
+ if err != nil {
+ serverError(w, err)
+ return
+ }
+ var status model.Status
+ if len(statuses) > 0 {
+ status = statuses[0]
+ }
+ h.view("status").Execute(w, map[string]interface{}{"status": status})
+}
+
func drawText(canvas *image.RGBA, text string) error {
var (
fgColor image.Image
@@ -162,6 +176,46 @@ func createAvatar(text string) *image.RGBA {
return background
}
+func (h *Handler) showUserStatusBadgeView(w http.ResponseWriter, r *http.Request) {
+ user := mux.Vars(r)["user"]
+ if !h.storage.UserExists(user) {
+ notFound(w)
+ return
+ }
+ statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
+ if err != nil {
+ serverError(w, err)
+ return
+ }
+
+ var username string
+ if len(statuses) > 0 {
+ username = statuses[0].User
+ }
+
+ background := "#f0ffff"
+
+ dc := gg.NewContext(88, 31)
+ dc.SetHexColor(background)
+ dc.Clear()
+ dc.SetHexColor("#191970")
+ font, err := truetype.Parse(goregular.TTF)
+ if err != nil {
+ panic("")
+ }
+ face := truetype.NewFace(font, &truetype.Options{
+ Size: 12,
+ })
+ dc.SetFontFace(face)
+ if err != nil {
+ fmt.Println(err)
+ }
+ //dc.DrawString(username, 10, 14)
+ dc.DrawStringAnchored(username, 88/2, 8, 0.5, 0.5)
+ dc.DrawStringAnchored("status.cafe", 88/2, 20, 0.5, 0.5)
+ dc.EncodePNG(w)
+}
+
func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) {
diff --git a/web/handler/widget_show.go b/web/handler/widget_show.go
index 5bdbd26..38581b8 100644
--- a/web/handler/widget_show.go
+++ b/web/handler/widget_show.go
@@ -15,7 +15,7 @@ fetch("https://status.cafe/users/` + name + `/status.json")
document.getElementById("statuscafe-content").innerHTML = "No status yet."
return
}
- document.getElementById("statuscafe-username").innerHTML = '' + r.author + ' ' + r.face + ' ' + r.timeAgo
+ document.getElementById("statuscafe-username").innerHTML = '' + r.author + ' ' + r.face + ' ' + r.timeAgo
document.getElementById("statuscafe-content").innerHTML = r.content
})
`))
diff --git a/web/session/session.go b/web/session/session.go
index a78a624..579e661 100644
--- a/web/session/session.go
+++ b/web/session/session.go
@@ -18,6 +18,7 @@ func New(key string, storage *storage.Storage) *Session {
store := sessions.NewCookieStore([]byte(key))
store.Options = &sessions.Options{
HttpOnly: true,
+ Secure: true,
MaxAge: 86400 * 30,
}
return &Session{