mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-03 00:18:32 +00:00
e6ffbea888
The `gitea.com/go-chi/binding` package only exists for Gitea, so it moves into `modules/web/binding` to fix its bugs directly. Split out of https://github.com/go-gitea/gitea/pull/39504. - GET and HEAD always bind the query - JSON `null` slice elements and nested `TrimSpace` fields bind correctly - Integer fields reject out-of-range values instead of wrapping - An empty JSON body binds nothing and an unknown binding rule is an error Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: bircni <bircni@icloud.com>
146 lines
4.3 KiB
Go
146 lines
4.3 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package validation
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
|
|
"gitea.dev/modules/auth"
|
|
"gitea.dev/modules/git"
|
|
"gitea.dev/modules/glob"
|
|
"gitea.dev/modules/web/binding"
|
|
)
|
|
|
|
const (
|
|
ErrCustomMessage = "CustomMessage"
|
|
ErrEmail = "EmailError"
|
|
ErrURL = "UrlError"
|
|
ErrGitRefName = "GitRefNameError"
|
|
ErrGlobPattern = "GlobPattern"
|
|
ErrRegexPattern = "RegexPattern"
|
|
ErrUsername = "UsernameError"
|
|
ErrInvalidGroupTeamMap = "InvalidGroupTeamMap"
|
|
ErrInvalidBadgeSlug = "InvalidBadgeSlug"
|
|
)
|
|
|
|
func newFieldError(field reflect.StructField, cls, msg string) *BindingError {
|
|
return &BindingError{FieldNames: []string{field.Name}, Classification: cls, Message: msg}
|
|
}
|
|
|
|
func AddValidationError(errs BindingErrors, fieldName, errorMsg string) BindingErrors {
|
|
return append(errs, BindingError{FieldNames: []string{fieldName}, Classification: ErrCustomMessage, Message: errorMsg})
|
|
}
|
|
|
|
// AddBindingRules adds additional binding rules
|
|
func AddBindingRules(b *binding.Binder) {
|
|
b.AddRuleNonZero("Email", func(_ context.Context, f *binding.ValidationField) *binding.Error {
|
|
if !IsEmailAddressValid(f.ValueMustString()) {
|
|
return newFieldError(f.StructField, ErrEmail, "invalid email")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
b.AddRuleNonZero("GitRefName", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
if !git.IsValidRefPattern(f.ValueMustString()) {
|
|
return newFieldError(f.StructField, ErrGitRefName, "GitRefName")
|
|
}
|
|
return nil
|
|
})
|
|
b.AddRuleNonZero("ValidUrl", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
if !IsValidURL(f.ValueMustString()) {
|
|
return newFieldError(f.StructField, ErrURL, "Url")
|
|
}
|
|
return nil
|
|
})
|
|
b.AddRuleNonZero("ValidSiteUrl", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
if !IsValidSiteURL(f.ValueMustString()) {
|
|
return newFieldError(f.StructField, ErrURL, "Url")
|
|
}
|
|
return nil
|
|
})
|
|
b.AddRuleNonZero("BadgeSlug", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
if !IsValidBadgeSlug(f.ValueMustString()) {
|
|
return newFieldError(f.StructField, ErrInvalidBadgeSlug, "invalid badge slug")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
ruleGlobPattern := func(_ context.Context, f *binding.ValidationField) *binding.Error {
|
|
if _, err := glob.Compile(f.ValueMustString()); err != nil {
|
|
return newFieldError(f.StructField, ErrGlobPattern, err.Error())
|
|
}
|
|
return nil
|
|
}
|
|
b.AddRuleNonZero("GlobPattern", ruleGlobPattern)
|
|
ruleRegexPattern := func(_ context.Context, f *binding.ValidationField, val string) *binding.Error {
|
|
if _, err := regexp.Compile(val); err != nil {
|
|
return newFieldError(f.StructField, ErrRegexPattern, err.Error())
|
|
}
|
|
return nil
|
|
}
|
|
b.AddRuleNonZero("RegexPattern", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
return ruleRegexPattern(ctx, f, f.ValueMustString())
|
|
})
|
|
b.AddRuleNonZero("GlobOrRegexPattern", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
str := f.ValueMustString()
|
|
if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
|
|
return ruleRegexPattern(ctx, f, str[1:len(str)-1])
|
|
}
|
|
return ruleGlobPattern(ctx, f)
|
|
})
|
|
|
|
b.AddRuleNonZero("Username", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
if !IsValidUsername(f.ValueMustString()) {
|
|
return newFieldError(f.StructField, ErrUsername, "invalid username")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
b.AddRuleNonZero("ValidGroupTeamMap", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
|
_, err := auth.UnmarshalGroupTeamMapping(f.ValueMustString())
|
|
if err != nil {
|
|
return newFieldError(f.StructField, ErrInvalidGroupTeamMap, err.Error())
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func portOnly(hostport string) string {
|
|
_, after, ok := strings.Cut(hostport, ":")
|
|
if !ok {
|
|
return ""
|
|
}
|
|
if _, after2, ok2 := strings.Cut(hostport, "]:"); ok2 {
|
|
return after2
|
|
}
|
|
if strings.Contains(hostport, "]") {
|
|
return ""
|
|
}
|
|
return after
|
|
}
|
|
|
|
func validPort(p string) bool {
|
|
for _, r := range []byte(p) {
|
|
if r < '0' || r > '9' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
var Binder = sync.OnceValue(func() *binding.Binder {
|
|
b := binding.NewBinder()
|
|
AddBindingRules(b)
|
|
return b
|
|
})
|
|
|
|
type (
|
|
BindingErrors = binding.Errors
|
|
BindingError = binding.Error //exhaustruct:enforce
|
|
)
|