fix: validate member name contents

This commit is contained in:
Sam 2023-03-18 23:00:44 +01:00
parent fe0680d587
commit d223cd89e8
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
4 changed files with 53 additions and 1 deletions

View file

@ -2,6 +2,7 @@ package db
import ( import (
"context" "context"
"regexp"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/georgysavva/scany/pgxscan" "github.com/georgysavva/scany/pgxscan"
@ -32,6 +33,13 @@ const (
ErrMemberNameInUse = errors.Sentinel("member name already in use") ErrMemberNameInUse = errors.Sentinel("member name already in use")
) )
// member names must match this regex
var memberNameRegex = regexp.MustCompile("^[^@\\?!#/\\\\[\\]\"'$%&()+<=>^|~`,]{1,100}$")
func MemberNameValid(name string) bool {
return memberNameRegex.MatchString(name)
}
func (db *DB) Member(ctx context.Context, id xid.ID) (m Member, err error) { func (db *DB) Member(ctx context.Context, id xid.ID) (m Member, err error) {
sql, args, err := sq.Select("*").From("members").Where("id = ?", id).ToSql() sql, args, err := sq.Select("*").From("members").Where("id = ?", id).ToSql()
if err != nil { if err != nil {

View file

@ -3,6 +3,7 @@ package member
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/log"
@ -51,6 +52,13 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error
return server.APIError{Code: server.ErrBadRequest} return server.APIError{Code: server.ErrBadRequest}
} }
// remove whitespace from all fields
cmr.Name = strings.TrimSpace(cmr.Name)
cmr.Bio = strings.TrimSpace(cmr.Bio)
if cmr.DisplayName != nil {
*cmr.DisplayName = strings.TrimSpace(*cmr.DisplayName)
}
// validate everything // validate everything
if cmr.Name == "" { if cmr.Name == "" {
return server.APIError{ return server.APIError{
@ -64,6 +72,13 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error
} }
} }
if !db.MemberNameValid(cmr.Name) {
return server.APIError{
Code: server.ErrBadRequest,
Details: "Member name cannot contain any of the following: @, \\, ?, !, #, /, \\, [, ], \", ', $, %, &, (, ), +, <, =, >, ^, |, ~, `, ,",
}
}
if err := validateSlicePtr("name", &cmr.Names); err != nil { if err := validateSlicePtr("name", &cmr.Names); err != nil {
return *err return *err
} }

View file

@ -3,6 +3,7 @@ package member
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"codeberg.org/u1f320/pronouns.cc/backend/db" "codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/log"
@ -68,11 +69,37 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error {
} }
} }
// trim whitespace from strings
if req.Name != nil {
*req.Name = strings.TrimSpace(*req.Name)
}
if req.DisplayName != nil {
*req.DisplayName = strings.TrimSpace(*req.Name)
}
if req.Bio != nil {
*req.Bio = strings.TrimSpace(*req.Bio)
}
if req.Name != nil && *req.Name == "" { if req.Name != nil && *req.Name == "" {
return server.APIError{ return server.APIError{
Code: server.ErrBadRequest, Code: server.ErrBadRequest,
Details: "Name must not be empty", Details: "Name must not be empty",
} }
} else if req.Name != nil && len(*req.Name) > 100 {
return server.APIError{
Code: server.ErrBadRequest,
Details: "Name may not be longer than 100 characters",
}
}
// validate member name
if req.Name != nil {
if !db.MemberNameValid(*req.Name) {
return server.APIError{
Code: server.ErrBadRequest,
Details: "Member name cannot contain any of the following: @, \\, ?, !, #, /, \\, [, ], \", ', $, %, &, (, ), +, <, =, >, ^, |, ~, `, ,",
}
}
} }
// validate display name/bio // validate display name/bio

View file

@ -61,11 +61,12 @@
let modified = false; let modified = false;
$: modified = isModified(bio, display_name, links, names, pronouns, fields, avatar); $: modified = isModified(bio, name, display_name, links, names, pronouns, fields, avatar);
$: getAvatar(avatar_files).then((b64) => (avatar = b64)); $: getAvatar(avatar_files).then((b64) => (avatar = b64));
const isModified = ( const isModified = (
bio: string, bio: string,
name: string,
display_name: string, display_name: string,
links: string[], links: string[],
names: FieldEntry[], names: FieldEntry[],
@ -73,6 +74,7 @@
fields: Field[], fields: Field[],
avatar: string | null, avatar: string | null,
) => { ) => {
if (name !== data.member.name) return true;
if (bio !== data.member.bio) return true; if (bio !== data.member.bio) return true;
if (display_name !== data.member.display_name) return true; if (display_name !== data.member.display_name) return true;
if (!linksEqual(links, data.member.links)) return true; if (!linksEqual(links, data.member.links)) return true;