Make web tests database-free and finish review hardening.
Introduce a Store interface with Postgres and in-memory backends, cover mutations/CSRF/session rotation without Postgres, bound avatar decode dimensions, add truncate/prepareAvatar unit tests, and run go test -race in CI.
This commit is contained in:
+23
-7
@@ -99,7 +99,7 @@ func (s *Server) handleProfile(w http.ResponseWriter, r *http.Request) {
|
||||
if avatarURL != "" {
|
||||
u.AvatarURL = avatarURL
|
||||
}
|
||||
if err := u.SaveProfile(r.Context()); err != nil {
|
||||
if err := s.store.SaveUserProfile(r.Context(), u); err != nil {
|
||||
http.Error(w, "could not save profile", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -131,13 +131,29 @@ func prepareAvatar(r io.Reader, maxBytes int64) (body []byte, ext, contentType s
|
||||
return nil, "", "", fmt.Errorf("unsupported type %s", sniff)
|
||||
}
|
||||
|
||||
img, format, err := image.Decode(bytes.NewReader(raw))
|
||||
cfg, format, err := image.DecodeConfig(bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
const maxDim = 4096
|
||||
const maxPixels = 4096 * 4096
|
||||
if cfg.Width <= 0 || cfg.Height <= 0 || cfg.Width > maxDim || cfg.Height > maxDim {
|
||||
return nil, "", "", fmt.Errorf("image dimensions out of range")
|
||||
}
|
||||
if int64(cfg.Width)*int64(cfg.Height) > maxPixels {
|
||||
return nil, "", "", fmt.Errorf("image too many pixels")
|
||||
}
|
||||
|
||||
img, decodedFormat, err := image.Decode(bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if format != "" {
|
||||
decodedFormat = format
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
switch format {
|
||||
switch decodedFormat {
|
||||
case "jpeg":
|
||||
if err := jpeg.Encode(&out, img, &jpeg.Options{Quality: 90}); err != nil {
|
||||
return nil, "", "", err
|
||||
@@ -149,7 +165,7 @@ func prepareAvatar(r io.Reader, maxBytes int64) (body []byte, ext, contentType s
|
||||
}
|
||||
return out.Bytes(), ".png", "image/png", nil
|
||||
default:
|
||||
return nil, "", "", fmt.Errorf("unsupported format %s", format)
|
||||
return nil, "", "", fmt.Errorf("unsupported format %s", decodedFormat)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,16 +177,16 @@ func (s *Server) renderProfile(w http.ResponseWriter, r *http.Request, u *store.
|
||||
)
|
||||
if u.Admin() {
|
||||
label = "Questions you answered"
|
||||
questions, err = store.ListQuestionsAnsweredBy(r.Context(), s.db, u.ID)
|
||||
questions, err = s.store.ListQuestionsAnsweredBy(r.Context(), u.ID)
|
||||
} else {
|
||||
label = "Your questions"
|
||||
questions, err = store.ListQuestionsByAuthor(r.Context(), s.db, u.ID)
|
||||
questions, err = s.store.ListQuestionsByAuthor(r.Context(), u.ID)
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "could not load questions", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if fresh, e := store.UserByID(r.Context(), s.db, u.ID); e == nil {
|
||||
if fresh, e := s.store.UserByID(r.Context(), u.ID); e == nil {
|
||||
u = fresh
|
||||
}
|
||||
p := s.basePage(r, "Profile")
|
||||
|
||||
Reference in New Issue
Block a user