// 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 )