mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-18 01:11:26 +03:00
fix: ensure user inputs are normalized (#724)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
committed by
GitHub
parent
f145903eb0
commit
7b4ccd1f30
94
backend/internal/dto/dto_normalize.go
Normal file
94
backend/internal/dto/dto_normalize.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// Normalize iterates through an object and performs Unicode normalization on all string fields with the `unorm` tag.
|
||||
func Normalize(obj any) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||
return
|
||||
}
|
||||
v = v.Elem()
|
||||
|
||||
// Handle case where obj is a slice of models
|
||||
if v.Kind() == reflect.Slice {
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
elem := v.Index(i)
|
||||
if elem.Kind() == reflect.Ptr && !elem.IsNil() && elem.Elem().Kind() == reflect.Struct {
|
||||
Normalize(elem.Interface())
|
||||
} else if elem.Kind() == reflect.Struct && elem.CanAddr() {
|
||||
Normalize(elem.Addr().Interface())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
// Iterate through all fields looking for those with the "unorm" tag
|
||||
t := v.Type()
|
||||
loop:
|
||||
for i := range t.NumField() {
|
||||
field := t.Field(i)
|
||||
|
||||
unormTag := field.Tag.Get("unorm")
|
||||
if unormTag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fv := v.Field(i)
|
||||
if !fv.CanSet() || fv.Kind() != reflect.String {
|
||||
continue
|
||||
}
|
||||
|
||||
var form norm.Form
|
||||
switch unormTag {
|
||||
case "nfc":
|
||||
form = norm.NFC
|
||||
case "nfkc":
|
||||
form = norm.NFKC
|
||||
case "nfd":
|
||||
form = norm.NFD
|
||||
case "nfkd":
|
||||
form = norm.NFKD
|
||||
default:
|
||||
continue loop
|
||||
}
|
||||
|
||||
val := fv.String()
|
||||
val = form.String(val)
|
||||
fv.SetString(val)
|
||||
}
|
||||
}
|
||||
|
||||
func ShouldBindWithNormalizedJSON(ctx *gin.Context, obj any) error {
|
||||
return ctx.ShouldBindWith(obj, binding.JSON)
|
||||
}
|
||||
|
||||
type NormalizerJSONBinding struct{}
|
||||
|
||||
func (NormalizerJSONBinding) Name() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
func (NormalizerJSONBinding) Bind(req *http.Request, obj any) error {
|
||||
// Use the default JSON binder
|
||||
err := binding.JSON.Bind(req, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform normalization
|
||||
Normalize(obj)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user