podman
2955 строк · 84.6 Кб
1package validator
2
3import (
4"bytes"
5"context"
6"crypto/sha256"
7"encoding/hex"
8"encoding/json"
9"fmt"
10"io/fs"
11"net"
12"net/url"
13"os"
14"reflect"
15"strconv"
16"strings"
17"sync"
18"syscall"
19"time"
20"unicode/utf8"
21
22"golang.org/x/crypto/sha3"
23"golang.org/x/text/language"
24
25"github.com/gabriel-vasile/mimetype"
26urn "github.com/leodido/go-urn"
27)
28
29// Func accepts a FieldLevel interface for all validation needs. The return
30// value should be true when validation succeeds.
31type Func func(fl FieldLevel) bool
32
33// FuncCtx accepts a context.Context and FieldLevel interface for all
34// validation needs. The return value should be true when validation succeeds.
35type FuncCtx func(ctx context.Context, fl FieldLevel) bool
36
37// wrapFunc wraps normal Func makes it compatible with FuncCtx
38func wrapFunc(fn Func) FuncCtx {
39if fn == nil {
40return nil // be sure not to wrap a bad function.
41}
42return func(ctx context.Context, fl FieldLevel) bool {
43return fn(fl)
44}
45}
46
47var (
48restrictedTags = map[string]struct{}{
49diveTag: {},
50keysTag: {},
51endKeysTag: {},
52structOnlyTag: {},
53omitempty: {},
54omitnil: {},
55skipValidationTag: {},
56utf8HexComma: {},
57utf8Pipe: {},
58noStructLevelTag: {},
59requiredTag: {},
60isdefault: {},
61}
62
63// bakedInAliases is a default mapping of a single validation tag that
64// defines a common or complex set of validation(s) to simplify
65// adding validation to structs.
66bakedInAliases = map[string]string{
67"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
68"country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
69}
70
71// bakedInValidators is the default map of ValidationFunc
72// you can add, remove or even replace items to suite your needs,
73// or even disregard and use your own map if so desired.
74bakedInValidators = map[string]Func{
75"required": hasValue,
76"required_if": requiredIf,
77"required_unless": requiredUnless,
78"skip_unless": skipUnless,
79"required_with": requiredWith,
80"required_with_all": requiredWithAll,
81"required_without": requiredWithout,
82"required_without_all": requiredWithoutAll,
83"excluded_if": excludedIf,
84"excluded_unless": excludedUnless,
85"excluded_with": excludedWith,
86"excluded_with_all": excludedWithAll,
87"excluded_without": excludedWithout,
88"excluded_without_all": excludedWithoutAll,
89"isdefault": isDefault,
90"len": hasLengthOf,
91"min": hasMinOf,
92"max": hasMaxOf,
93"eq": isEq,
94"eq_ignore_case": isEqIgnoreCase,
95"ne": isNe,
96"ne_ignore_case": isNeIgnoreCase,
97"lt": isLt,
98"lte": isLte,
99"gt": isGt,
100"gte": isGte,
101"eqfield": isEqField,
102"eqcsfield": isEqCrossStructField,
103"necsfield": isNeCrossStructField,
104"gtcsfield": isGtCrossStructField,
105"gtecsfield": isGteCrossStructField,
106"ltcsfield": isLtCrossStructField,
107"ltecsfield": isLteCrossStructField,
108"nefield": isNeField,
109"gtefield": isGteField,
110"gtfield": isGtField,
111"ltefield": isLteField,
112"ltfield": isLtField,
113"fieldcontains": fieldContains,
114"fieldexcludes": fieldExcludes,
115"alpha": isAlpha,
116"alphanum": isAlphanum,
117"alphaunicode": isAlphaUnicode,
118"alphanumunicode": isAlphanumUnicode,
119"boolean": isBoolean,
120"numeric": isNumeric,
121"number": isNumber,
122"hexadecimal": isHexadecimal,
123"hexcolor": isHEXColor,
124"rgb": isRGB,
125"rgba": isRGBA,
126"hsl": isHSL,
127"hsla": isHSLA,
128"e164": isE164,
129"email": isEmail,
130"url": isURL,
131"http_url": isHttpURL,
132"uri": isURI,
133"urn_rfc2141": isUrnRFC2141, // RFC 2141
134"file": isFile,
135"filepath": isFilePath,
136"base64": isBase64,
137"base64url": isBase64URL,
138"base64rawurl": isBase64RawURL,
139"contains": contains,
140"containsany": containsAny,
141"containsrune": containsRune,
142"excludes": excludes,
143"excludesall": excludesAll,
144"excludesrune": excludesRune,
145"startswith": startsWith,
146"endswith": endsWith,
147"startsnotwith": startsNotWith,
148"endsnotwith": endsNotWith,
149"image": isImage,
150"isbn": isISBN,
151"isbn10": isISBN10,
152"isbn13": isISBN13,
153"issn": isISSN,
154"eth_addr": isEthereumAddress,
155"eth_addr_checksum": isEthereumAddressChecksum,
156"btc_addr": isBitcoinAddress,
157"btc_addr_bech32": isBitcoinBech32Address,
158"uuid": isUUID,
159"uuid3": isUUID3,
160"uuid4": isUUID4,
161"uuid5": isUUID5,
162"uuid_rfc4122": isUUIDRFC4122,
163"uuid3_rfc4122": isUUID3RFC4122,
164"uuid4_rfc4122": isUUID4RFC4122,
165"uuid5_rfc4122": isUUID5RFC4122,
166"ulid": isULID,
167"md4": isMD4,
168"md5": isMD5,
169"sha256": isSHA256,
170"sha384": isSHA384,
171"sha512": isSHA512,
172"ripemd128": isRIPEMD128,
173"ripemd160": isRIPEMD160,
174"tiger128": isTIGER128,
175"tiger160": isTIGER160,
176"tiger192": isTIGER192,
177"ascii": isASCII,
178"printascii": isPrintableASCII,
179"multibyte": hasMultiByteCharacter,
180"datauri": isDataURI,
181"latitude": isLatitude,
182"longitude": isLongitude,
183"ssn": isSSN,
184"ipv4": isIPv4,
185"ipv6": isIPv6,
186"ip": isIP,
187"cidrv4": isCIDRv4,
188"cidrv6": isCIDRv6,
189"cidr": isCIDR,
190"tcp4_addr": isTCP4AddrResolvable,
191"tcp6_addr": isTCP6AddrResolvable,
192"tcp_addr": isTCPAddrResolvable,
193"udp4_addr": isUDP4AddrResolvable,
194"udp6_addr": isUDP6AddrResolvable,
195"udp_addr": isUDPAddrResolvable,
196"ip4_addr": isIP4AddrResolvable,
197"ip6_addr": isIP6AddrResolvable,
198"ip_addr": isIPAddrResolvable,
199"unix_addr": isUnixAddrResolvable,
200"mac": isMAC,
201"hostname": isHostnameRFC952, // RFC 952
202"hostname_rfc1123": isHostnameRFC1123, // RFC 1123
203"fqdn": isFQDN,
204"unique": isUnique,
205"oneof": isOneOf,
206"html": isHTML,
207"html_encoded": isHTMLEncoded,
208"url_encoded": isURLEncoded,
209"dir": isDir,
210"dirpath": isDirPath,
211"json": isJSON,
212"jwt": isJWT,
213"hostname_port": isHostnamePort,
214"lowercase": isLowercase,
215"uppercase": isUppercase,
216"datetime": isDatetime,
217"timezone": isTimeZone,
218"iso3166_1_alpha2": isIso3166Alpha2,
219"iso3166_1_alpha3": isIso3166Alpha3,
220"iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
221"iso3166_2": isIso31662,
222"iso4217": isIso4217,
223"iso4217_numeric": isIso4217Numeric,
224"bcp47_language_tag": isBCP47LanguageTag,
225"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
226"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
227"bic": isIsoBicFormat,
228"semver": isSemverFormat,
229"dns_rfc1035_label": isDnsRFC1035LabelFormat,
230"credit_card": isCreditCard,
231"cve": isCveFormat,
232"luhn_checksum": hasLuhnChecksum,
233"mongodb": isMongoDB,
234"cron": isCron,
235"spicedb": isSpiceDB,
236}
237)
238
239var (
240oneofValsCache = map[string][]string{}
241oneofValsCacheRWLock = sync.RWMutex{}
242)
243
244func parseOneOfParam2(s string) []string {
245oneofValsCacheRWLock.RLock()
246vals, ok := oneofValsCache[s]
247oneofValsCacheRWLock.RUnlock()
248if !ok {
249oneofValsCacheRWLock.Lock()
250vals = splitParamsRegex.FindAllString(s, -1)
251for i := 0; i < len(vals); i++ {
252vals[i] = strings.Replace(vals[i], "'", "", -1)
253}
254oneofValsCache[s] = vals
255oneofValsCacheRWLock.Unlock()
256}
257return vals
258}
259
260func isURLEncoded(fl FieldLevel) bool {
261return uRLEncodedRegex.MatchString(fl.Field().String())
262}
263
264func isHTMLEncoded(fl FieldLevel) bool {
265return hTMLEncodedRegex.MatchString(fl.Field().String())
266}
267
268func isHTML(fl FieldLevel) bool {
269return hTMLRegex.MatchString(fl.Field().String())
270}
271
272func isOneOf(fl FieldLevel) bool {
273vals := parseOneOfParam2(fl.Param())
274
275field := fl.Field()
276
277var v string
278switch field.Kind() {
279case reflect.String:
280v = field.String()
281case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
282v = strconv.FormatInt(field.Int(), 10)
283case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
284v = strconv.FormatUint(field.Uint(), 10)
285default:
286panic(fmt.Sprintf("Bad field type %T", field.Interface()))
287}
288for i := 0; i < len(vals); i++ {
289if vals[i] == v {
290return true
291}
292}
293return false
294}
295
296// isUnique is the validation function for validating if each array|slice|map value is unique
297func isUnique(fl FieldLevel) bool {
298field := fl.Field()
299param := fl.Param()
300v := reflect.ValueOf(struct{}{})
301
302switch field.Kind() {
303case reflect.Slice, reflect.Array:
304elem := field.Type().Elem()
305if elem.Kind() == reflect.Ptr {
306elem = elem.Elem()
307}
308
309if param == "" {
310m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
311
312for i := 0; i < field.Len(); i++ {
313m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
314}
315return field.Len() == m.Len()
316}
317
318sf, ok := elem.FieldByName(param)
319if !ok {
320panic(fmt.Sprintf("Bad field name %s", param))
321}
322
323sfTyp := sf.Type
324if sfTyp.Kind() == reflect.Ptr {
325sfTyp = sfTyp.Elem()
326}
327
328m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
329var fieldlen int
330for i := 0; i < field.Len(); i++ {
331key := reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param))
332if key.IsValid() {
333fieldlen++
334m.SetMapIndex(key, v)
335}
336}
337return fieldlen == m.Len()
338case reflect.Map:
339var m reflect.Value
340if field.Type().Elem().Kind() == reflect.Ptr {
341m = reflect.MakeMap(reflect.MapOf(field.Type().Elem().Elem(), v.Type()))
342} else {
343m = reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
344}
345
346for _, k := range field.MapKeys() {
347m.SetMapIndex(reflect.Indirect(field.MapIndex(k)), v)
348}
349
350return field.Len() == m.Len()
351default:
352if parent := fl.Parent(); parent.Kind() == reflect.Struct {
353uniqueField := parent.FieldByName(param)
354if uniqueField == reflect.ValueOf(nil) {
355panic(fmt.Sprintf("Bad field name provided %s", param))
356}
357
358if uniqueField.Kind() != field.Kind() {
359panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface()))
360}
361
362return field.Interface() != uniqueField.Interface()
363}
364
365panic(fmt.Sprintf("Bad field type %T", field.Interface()))
366}
367}
368
369// isMAC is the validation function for validating if the field's value is a valid MAC address.
370func isMAC(fl FieldLevel) bool {
371_, err := net.ParseMAC(fl.Field().String())
372
373return err == nil
374}
375
376// isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
377func isCIDRv4(fl FieldLevel) bool {
378ip, net, err := net.ParseCIDR(fl.Field().String())
379
380return err == nil && ip.To4() != nil && net.IP.Equal(ip)
381}
382
383// isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
384func isCIDRv6(fl FieldLevel) bool {
385ip, _, err := net.ParseCIDR(fl.Field().String())
386
387return err == nil && ip.To4() == nil
388}
389
390// isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
391func isCIDR(fl FieldLevel) bool {
392_, _, err := net.ParseCIDR(fl.Field().String())
393
394return err == nil
395}
396
397// isIPv4 is the validation function for validating if a value is a valid v4 IP address.
398func isIPv4(fl FieldLevel) bool {
399ip := net.ParseIP(fl.Field().String())
400
401return ip != nil && ip.To4() != nil
402}
403
404// isIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
405func isIPv6(fl FieldLevel) bool {
406ip := net.ParseIP(fl.Field().String())
407
408return ip != nil && ip.To4() == nil
409}
410
411// isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
412func isIP(fl FieldLevel) bool {
413ip := net.ParseIP(fl.Field().String())
414
415return ip != nil
416}
417
418// isSSN is the validation function for validating if the field's value is a valid SSN.
419func isSSN(fl FieldLevel) bool {
420field := fl.Field()
421
422if field.Len() != 11 {
423return false
424}
425
426return sSNRegex.MatchString(field.String())
427}
428
429// isLongitude is the validation function for validating if the field's value is a valid longitude coordinate.
430func isLongitude(fl FieldLevel) bool {
431field := fl.Field()
432
433var v string
434switch field.Kind() {
435case reflect.String:
436v = field.String()
437case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
438v = strconv.FormatInt(field.Int(), 10)
439case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
440v = strconv.FormatUint(field.Uint(), 10)
441case reflect.Float32:
442v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
443case reflect.Float64:
444v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
445default:
446panic(fmt.Sprintf("Bad field type %T", field.Interface()))
447}
448
449return longitudeRegex.MatchString(v)
450}
451
452// isLatitude is the validation function for validating if the field's value is a valid latitude coordinate.
453func isLatitude(fl FieldLevel) bool {
454field := fl.Field()
455
456var v string
457switch field.Kind() {
458case reflect.String:
459v = field.String()
460case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
461v = strconv.FormatInt(field.Int(), 10)
462case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
463v = strconv.FormatUint(field.Uint(), 10)
464case reflect.Float32:
465v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
466case reflect.Float64:
467v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
468default:
469panic(fmt.Sprintf("Bad field type %T", field.Interface()))
470}
471
472return latitudeRegex.MatchString(v)
473}
474
475// isDataURI is the validation function for validating if the field's value is a valid data URI.
476func isDataURI(fl FieldLevel) bool {
477uri := strings.SplitN(fl.Field().String(), ",", 2)
478
479if len(uri) != 2 {
480return false
481}
482
483if !dataURIRegex.MatchString(uri[0]) {
484return false
485}
486
487return base64Regex.MatchString(uri[1])
488}
489
490// hasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character.
491func hasMultiByteCharacter(fl FieldLevel) bool {
492field := fl.Field()
493
494if field.Len() == 0 {
495return true
496}
497
498return multibyteRegex.MatchString(field.String())
499}
500
501// isPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character.
502func isPrintableASCII(fl FieldLevel) bool {
503return printableASCIIRegex.MatchString(fl.Field().String())
504}
505
506// isASCII is the validation function for validating if the field's value is a valid ASCII character.
507func isASCII(fl FieldLevel) bool {
508return aSCIIRegex.MatchString(fl.Field().String())
509}
510
511// isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
512func isUUID5(fl FieldLevel) bool {
513return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
514}
515
516// isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
517func isUUID4(fl FieldLevel) bool {
518return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
519}
520
521// isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
522func isUUID3(fl FieldLevel) bool {
523return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
524}
525
526// isUUID is the validation function for validating if the field's value is a valid UUID of any version.
527func isUUID(fl FieldLevel) bool {
528return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
529}
530
531// isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
532func isUUID5RFC4122(fl FieldLevel) bool {
533return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
534}
535
536// isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
537func isUUID4RFC4122(fl FieldLevel) bool {
538return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
539}
540
541// isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
542func isUUID3RFC4122(fl FieldLevel) bool {
543return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
544}
545
546// isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
547func isUUIDRFC4122(fl FieldLevel) bool {
548return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
549}
550
551// isULID is the validation function for validating if the field's value is a valid ULID.
552func isULID(fl FieldLevel) bool {
553return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
554}
555
556// isMD4 is the validation function for validating if the field's value is a valid MD4.
557func isMD4(fl FieldLevel) bool {
558return md4Regex.MatchString(fl.Field().String())
559}
560
561// isMD5 is the validation function for validating if the field's value is a valid MD5.
562func isMD5(fl FieldLevel) bool {
563return md5Regex.MatchString(fl.Field().String())
564}
565
566// isSHA256 is the validation function for validating if the field's value is a valid SHA256.
567func isSHA256(fl FieldLevel) bool {
568return sha256Regex.MatchString(fl.Field().String())
569}
570
571// isSHA384 is the validation function for validating if the field's value is a valid SHA384.
572func isSHA384(fl FieldLevel) bool {
573return sha384Regex.MatchString(fl.Field().String())
574}
575
576// isSHA512 is the validation function for validating if the field's value is a valid SHA512.
577func isSHA512(fl FieldLevel) bool {
578return sha512Regex.MatchString(fl.Field().String())
579}
580
581// isRIPEMD128 is the validation function for validating if the field's value is a valid PIPEMD128.
582func isRIPEMD128(fl FieldLevel) bool {
583return ripemd128Regex.MatchString(fl.Field().String())
584}
585
586// isRIPEMD160 is the validation function for validating if the field's value is a valid PIPEMD160.
587func isRIPEMD160(fl FieldLevel) bool {
588return ripemd160Regex.MatchString(fl.Field().String())
589}
590
591// isTIGER128 is the validation function for validating if the field's value is a valid TIGER128.
592func isTIGER128(fl FieldLevel) bool {
593return tiger128Regex.MatchString(fl.Field().String())
594}
595
596// isTIGER160 is the validation function for validating if the field's value is a valid TIGER160.
597func isTIGER160(fl FieldLevel) bool {
598return tiger160Regex.MatchString(fl.Field().String())
599}
600
601// isTIGER192 is the validation function for validating if the field's value is a valid isTIGER192.
602func isTIGER192(fl FieldLevel) bool {
603return tiger192Regex.MatchString(fl.Field().String())
604}
605
606// isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
607func isISBN(fl FieldLevel) bool {
608return isISBN10(fl) || isISBN13(fl)
609}
610
611// isISBN13 is the validation function for validating if the field's value is a valid v13 ISBN.
612func isISBN13(fl FieldLevel) bool {
613s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4)
614
615if !iSBN13Regex.MatchString(s) {
616return false
617}
618
619var checksum int32
620var i int32
621
622factor := []int32{1, 3}
623
624for i = 0; i < 12; i++ {
625checksum += factor[i%2] * int32(s[i]-'0')
626}
627
628return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0
629}
630
631// isISBN10 is the validation function for validating if the field's value is a valid v10 ISBN.
632func isISBN10(fl FieldLevel) bool {
633s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3)
634
635if !iSBN10Regex.MatchString(s) {
636return false
637}
638
639var checksum int32
640var i int32
641
642for i = 0; i < 9; i++ {
643checksum += (i + 1) * int32(s[i]-'0')
644}
645
646if s[9] == 'X' {
647checksum += 10 * 10
648} else {
649checksum += 10 * int32(s[9]-'0')
650}
651
652return checksum%11 == 0
653}
654
655// isISSN is the validation function for validating if the field's value is a valid ISSN.
656func isISSN(fl FieldLevel) bool {
657s := fl.Field().String()
658
659if !iSSNRegex.MatchString(s) {
660return false
661}
662s = strings.ReplaceAll(s, "-", "")
663
664pos := 8
665checksum := 0
666
667for i := 0; i < 7; i++ {
668checksum += pos * int(s[i]-'0')
669pos--
670}
671
672if s[7] == 'X' {
673checksum += 10
674} else {
675checksum += int(s[7] - '0')
676}
677
678return checksum%11 == 0
679}
680
681// isEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
682func isEthereumAddress(fl FieldLevel) bool {
683address := fl.Field().String()
684
685return ethAddressRegex.MatchString(address)
686}
687
688// isEthereumAddressChecksum is the validation function for validating if the field's value is a valid checksumed Ethereum address.
689func isEthereumAddressChecksum(fl FieldLevel) bool {
690address := fl.Field().String()
691
692if !ethAddressRegex.MatchString(address) {
693return false
694}
695// Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
696address = address[2:] // Skip "0x" prefix.
697h := sha3.NewLegacyKeccak256()
698// hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash
699_, _ = h.Write([]byte(strings.ToLower(address)))
700hash := hex.EncodeToString(h.Sum(nil))
701
702for i := 0; i < len(address); i++ {
703if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case.
704continue
705}
706if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' {
707return false
708}
709}
710
711return true
712}
713
714// isBitcoinAddress is the validation function for validating if the field's value is a valid btc address
715func isBitcoinAddress(fl FieldLevel) bool {
716address := fl.Field().String()
717
718if !btcAddressRegex.MatchString(address) {
719return false
720}
721
722alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
723
724decode := [25]byte{}
725
726for _, n := range []byte(address) {
727d := bytes.IndexByte(alphabet, n)
728
729for i := 24; i >= 0; i-- {
730d += 58 * int(decode[i])
731decode[i] = byte(d % 256)
732d /= 256
733}
734}
735
736h := sha256.New()
737_, _ = h.Write(decode[:21])
738d := h.Sum([]byte{})
739h = sha256.New()
740_, _ = h.Write(d)
741
742validchecksum := [4]byte{}
743computedchecksum := [4]byte{}
744
745copy(computedchecksum[:], h.Sum(d[:0]))
746copy(validchecksum[:], decode[21:])
747
748return validchecksum == computedchecksum
749}
750
751// isBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address
752func isBitcoinBech32Address(fl FieldLevel) bool {
753address := fl.Field().String()
754
755if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) {
756return false
757}
758
759am := len(address) % 8
760
761if am == 0 || am == 3 || am == 5 {
762return false
763}
764
765address = strings.ToLower(address)
766
767alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
768
769hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc
770addr := address[3:]
771dp := make([]int, 0, len(addr))
772
773for _, c := range addr {
774dp = append(dp, strings.IndexRune(alphabet, c))
775}
776
777ver := dp[0]
778
779if ver < 0 || ver > 16 {
780return false
781}
782
783if ver == 0 {
784if len(address) != 42 && len(address) != 62 {
785return false
786}
787}
788
789values := append(hr, dp...)
790
791GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
792
793p := 1
794
795for _, v := range values {
796b := p >> 25
797p = (p&0x1ffffff)<<5 ^ v
798
799for i := 0; i < 5; i++ {
800if (b>>uint(i))&1 == 1 {
801p ^= GEN[i]
802}
803}
804}
805
806if p != 1 {
807return false
808}
809
810b := uint(0)
811acc := 0
812mv := (1 << 5) - 1
813var sw []int
814
815for _, v := range dp[1 : len(dp)-6] {
816acc = (acc << 5) | v
817b += 5
818for b >= 8 {
819b -= 8
820sw = append(sw, (acc>>b)&mv)
821}
822}
823
824if len(sw) < 2 || len(sw) > 40 {
825return false
826}
827
828return true
829}
830
831// excludesRune is the validation function for validating that the field's value does not contain the rune specified within the param.
832func excludesRune(fl FieldLevel) bool {
833return !containsRune(fl)
834}
835
836// excludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param.
837func excludesAll(fl FieldLevel) bool {
838return !containsAny(fl)
839}
840
841// excludes is the validation function for validating that the field's value does not contain the text specified within the param.
842func excludes(fl FieldLevel) bool {
843return !contains(fl)
844}
845
846// containsRune is the validation function for validating that the field's value contains the rune specified within the param.
847func containsRune(fl FieldLevel) bool {
848r, _ := utf8.DecodeRuneInString(fl.Param())
849
850return strings.ContainsRune(fl.Field().String(), r)
851}
852
853// containsAny is the validation function for validating that the field's value contains any of the characters specified within the param.
854func containsAny(fl FieldLevel) bool {
855return strings.ContainsAny(fl.Field().String(), fl.Param())
856}
857
858// contains is the validation function for validating that the field's value contains the text specified within the param.
859func contains(fl FieldLevel) bool {
860return strings.Contains(fl.Field().String(), fl.Param())
861}
862
863// startsWith is the validation function for validating that the field's value starts with the text specified within the param.
864func startsWith(fl FieldLevel) bool {
865return strings.HasPrefix(fl.Field().String(), fl.Param())
866}
867
868// endsWith is the validation function for validating that the field's value ends with the text specified within the param.
869func endsWith(fl FieldLevel) bool {
870return strings.HasSuffix(fl.Field().String(), fl.Param())
871}
872
873// startsNotWith is the validation function for validating that the field's value does not start with the text specified within the param.
874func startsNotWith(fl FieldLevel) bool {
875return !startsWith(fl)
876}
877
878// endsNotWith is the validation function for validating that the field's value does not end with the text specified within the param.
879func endsNotWith(fl FieldLevel) bool {
880return !endsWith(fl)
881}
882
883// fieldContains is the validation function for validating if the current field's value contains the field specified by the param's value.
884func fieldContains(fl FieldLevel) bool {
885field := fl.Field()
886
887currentField, _, ok := fl.GetStructFieldOK()
888
889if !ok {
890return false
891}
892
893return strings.Contains(field.String(), currentField.String())
894}
895
896// fieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value.
897func fieldExcludes(fl FieldLevel) bool {
898field := fl.Field()
899
900currentField, _, ok := fl.GetStructFieldOK()
901if !ok {
902return true
903}
904
905return !strings.Contains(field.String(), currentField.String())
906}
907
908// isNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value.
909func isNeField(fl FieldLevel) bool {
910field := fl.Field()
911kind := field.Kind()
912
913currentField, currentKind, ok := fl.GetStructFieldOK()
914
915if !ok || currentKind != kind {
916return true
917}
918
919switch kind {
920
921case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
922return field.Int() != currentField.Int()
923
924case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
925return field.Uint() != currentField.Uint()
926
927case reflect.Float32, reflect.Float64:
928return field.Float() != currentField.Float()
929
930case reflect.Slice, reflect.Map, reflect.Array:
931return int64(field.Len()) != int64(currentField.Len())
932
933case reflect.Bool:
934return field.Bool() != currentField.Bool()
935
936case reflect.Struct:
937
938fieldType := field.Type()
939
940if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
941
942t := currentField.Interface().(time.Time)
943fieldTime := field.Interface().(time.Time)
944
945return !fieldTime.Equal(t)
946}
947
948// Not Same underlying type i.e. struct and time
949if fieldType != currentField.Type() {
950return true
951}
952}
953
954// default reflect.String:
955return field.String() != currentField.String()
956}
957
958// isNe is the validation function for validating that the field's value does not equal the provided param value.
959func isNe(fl FieldLevel) bool {
960return !isEq(fl)
961}
962
963// isNeIgnoreCase is the validation function for validating that the field's string value does not equal the
964// provided param value. The comparison is case-insensitive
965func isNeIgnoreCase(fl FieldLevel) bool {
966return !isEqIgnoreCase(fl)
967}
968
969// isLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value.
970func isLteCrossStructField(fl FieldLevel) bool {
971field := fl.Field()
972kind := field.Kind()
973
974topField, topKind, ok := fl.GetStructFieldOK()
975if !ok || topKind != kind {
976return false
977}
978
979switch kind {
980
981case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
982return field.Int() <= topField.Int()
983
984case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
985return field.Uint() <= topField.Uint()
986
987case reflect.Float32, reflect.Float64:
988return field.Float() <= topField.Float()
989
990case reflect.Slice, reflect.Map, reflect.Array:
991return int64(field.Len()) <= int64(topField.Len())
992
993case reflect.Struct:
994
995fieldType := field.Type()
996
997if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
998
999fieldTime := field.Convert(timeType).Interface().(time.Time)
1000topTime := topField.Convert(timeType).Interface().(time.Time)
1001
1002return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
1003}
1004
1005// Not Same underlying type i.e. struct and time
1006if fieldType != topField.Type() {
1007return false
1008}
1009}
1010
1011// default reflect.String:
1012return field.String() <= topField.String()
1013}
1014
1015// isLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value.
1016// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
1017func isLtCrossStructField(fl FieldLevel) bool {
1018field := fl.Field()
1019kind := field.Kind()
1020
1021topField, topKind, ok := fl.GetStructFieldOK()
1022if !ok || topKind != kind {
1023return false
1024}
1025
1026switch kind {
1027
1028case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1029return field.Int() < topField.Int()
1030
1031case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1032return field.Uint() < topField.Uint()
1033
1034case reflect.Float32, reflect.Float64:
1035return field.Float() < topField.Float()
1036
1037case reflect.Slice, reflect.Map, reflect.Array:
1038return int64(field.Len()) < int64(topField.Len())
1039
1040case reflect.Struct:
1041
1042fieldType := field.Type()
1043
1044if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1045
1046fieldTime := field.Convert(timeType).Interface().(time.Time)
1047topTime := topField.Convert(timeType).Interface().(time.Time)
1048
1049return fieldTime.Before(topTime)
1050}
1051
1052// Not Same underlying type i.e. struct and time
1053if fieldType != topField.Type() {
1054return false
1055}
1056}
1057
1058// default reflect.String:
1059return field.String() < topField.String()
1060}
1061
1062// isGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value.
1063func isGteCrossStructField(fl FieldLevel) bool {
1064field := fl.Field()
1065kind := field.Kind()
1066
1067topField, topKind, ok := fl.GetStructFieldOK()
1068if !ok || topKind != kind {
1069return false
1070}
1071
1072switch kind {
1073
1074case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1075return field.Int() >= topField.Int()
1076
1077case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1078return field.Uint() >= topField.Uint()
1079
1080case reflect.Float32, reflect.Float64:
1081return field.Float() >= topField.Float()
1082
1083case reflect.Slice, reflect.Map, reflect.Array:
1084return int64(field.Len()) >= int64(topField.Len())
1085
1086case reflect.Struct:
1087
1088fieldType := field.Type()
1089
1090if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1091
1092fieldTime := field.Convert(timeType).Interface().(time.Time)
1093topTime := topField.Convert(timeType).Interface().(time.Time)
1094
1095return fieldTime.After(topTime) || fieldTime.Equal(topTime)
1096}
1097
1098// Not Same underlying type i.e. struct and time
1099if fieldType != topField.Type() {
1100return false
1101}
1102}
1103
1104// default reflect.String:
1105return field.String() >= topField.String()
1106}
1107
1108// isGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value.
1109func isGtCrossStructField(fl FieldLevel) bool {
1110field := fl.Field()
1111kind := field.Kind()
1112
1113topField, topKind, ok := fl.GetStructFieldOK()
1114if !ok || topKind != kind {
1115return false
1116}
1117
1118switch kind {
1119
1120case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1121return field.Int() > topField.Int()
1122
1123case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1124return field.Uint() > topField.Uint()
1125
1126case reflect.Float32, reflect.Float64:
1127return field.Float() > topField.Float()
1128
1129case reflect.Slice, reflect.Map, reflect.Array:
1130return int64(field.Len()) > int64(topField.Len())
1131
1132case reflect.Struct:
1133
1134fieldType := field.Type()
1135
1136if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1137
1138fieldTime := field.Convert(timeType).Interface().(time.Time)
1139topTime := topField.Convert(timeType).Interface().(time.Time)
1140
1141return fieldTime.After(topTime)
1142}
1143
1144// Not Same underlying type i.e. struct and time
1145if fieldType != topField.Type() {
1146return false
1147}
1148}
1149
1150// default reflect.String:
1151return field.String() > topField.String()
1152}
1153
1154// isNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value.
1155func isNeCrossStructField(fl FieldLevel) bool {
1156field := fl.Field()
1157kind := field.Kind()
1158
1159topField, currentKind, ok := fl.GetStructFieldOK()
1160if !ok || currentKind != kind {
1161return true
1162}
1163
1164switch kind {
1165
1166case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1167return topField.Int() != field.Int()
1168
1169case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1170return topField.Uint() != field.Uint()
1171
1172case reflect.Float32, reflect.Float64:
1173return topField.Float() != field.Float()
1174
1175case reflect.Slice, reflect.Map, reflect.Array:
1176return int64(topField.Len()) != int64(field.Len())
1177
1178case reflect.Bool:
1179return topField.Bool() != field.Bool()
1180
1181case reflect.Struct:
1182
1183fieldType := field.Type()
1184
1185if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1186
1187t := field.Convert(timeType).Interface().(time.Time)
1188fieldTime := topField.Convert(timeType).Interface().(time.Time)
1189
1190return !fieldTime.Equal(t)
1191}
1192
1193// Not Same underlying type i.e. struct and time
1194if fieldType != topField.Type() {
1195return true
1196}
1197}
1198
1199// default reflect.String:
1200return topField.String() != field.String()
1201}
1202
1203// isEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value.
1204func isEqCrossStructField(fl FieldLevel) bool {
1205field := fl.Field()
1206kind := field.Kind()
1207
1208topField, topKind, ok := fl.GetStructFieldOK()
1209if !ok || topKind != kind {
1210return false
1211}
1212
1213switch kind {
1214
1215case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1216return topField.Int() == field.Int()
1217
1218case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1219return topField.Uint() == field.Uint()
1220
1221case reflect.Float32, reflect.Float64:
1222return topField.Float() == field.Float()
1223
1224case reflect.Slice, reflect.Map, reflect.Array:
1225return int64(topField.Len()) == int64(field.Len())
1226
1227case reflect.Bool:
1228return topField.Bool() == field.Bool()
1229
1230case reflect.Struct:
1231
1232fieldType := field.Type()
1233
1234if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1235
1236t := field.Convert(timeType).Interface().(time.Time)
1237fieldTime := topField.Convert(timeType).Interface().(time.Time)
1238
1239return fieldTime.Equal(t)
1240}
1241
1242// Not Same underlying type i.e. struct and time
1243if fieldType != topField.Type() {
1244return false
1245}
1246}
1247
1248// default reflect.String:
1249return topField.String() == field.String()
1250}
1251
1252// isEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value.
1253func isEqField(fl FieldLevel) bool {
1254field := fl.Field()
1255kind := field.Kind()
1256
1257currentField, currentKind, ok := fl.GetStructFieldOK()
1258if !ok || currentKind != kind {
1259return false
1260}
1261
1262switch kind {
1263
1264case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1265return field.Int() == currentField.Int()
1266
1267case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1268return field.Uint() == currentField.Uint()
1269
1270case reflect.Float32, reflect.Float64:
1271return field.Float() == currentField.Float()
1272
1273case reflect.Slice, reflect.Map, reflect.Array:
1274return int64(field.Len()) == int64(currentField.Len())
1275
1276case reflect.Bool:
1277return field.Bool() == currentField.Bool()
1278
1279case reflect.Struct:
1280
1281fieldType := field.Type()
1282
1283if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
1284
1285t := currentField.Convert(timeType).Interface().(time.Time)
1286fieldTime := field.Convert(timeType).Interface().(time.Time)
1287
1288return fieldTime.Equal(t)
1289}
1290
1291// Not Same underlying type i.e. struct and time
1292if fieldType != currentField.Type() {
1293return false
1294}
1295}
1296
1297// default reflect.String:
1298return field.String() == currentField.String()
1299}
1300
1301// isEq is the validation function for validating if the current field's value is equal to the param's value.
1302func isEq(fl FieldLevel) bool {
1303field := fl.Field()
1304param := fl.Param()
1305
1306switch field.Kind() {
1307
1308case reflect.String:
1309return field.String() == param
1310
1311case reflect.Slice, reflect.Map, reflect.Array:
1312p := asInt(param)
1313
1314return int64(field.Len()) == p
1315
1316case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1317p := asIntFromType(field.Type(), param)
1318
1319return field.Int() == p
1320
1321case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1322p := asUint(param)
1323
1324return field.Uint() == p
1325
1326case reflect.Float32:
1327p := asFloat32(param)
1328
1329return field.Float() == p
1330
1331case reflect.Float64:
1332p := asFloat64(param)
1333
1334return field.Float() == p
1335
1336case reflect.Bool:
1337p := asBool(param)
1338
1339return field.Bool() == p
1340}
1341
1342panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1343}
1344
1345// isEqIgnoreCase is the validation function for validating if the current field's string value is
1346// equal to the param's value.
1347// The comparison is case-insensitive.
1348func isEqIgnoreCase(fl FieldLevel) bool {
1349field := fl.Field()
1350param := fl.Param()
1351
1352switch field.Kind() {
1353
1354case reflect.String:
1355return strings.EqualFold(field.String(), param)
1356}
1357
1358panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1359}
1360
1361// isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2
1362// example: `postcode_iso3166_alpha2=US`
1363func isPostcodeByIso3166Alpha2(fl FieldLevel) bool {
1364field := fl.Field()
1365param := fl.Param()
1366
1367reg, found := postCodeRegexDict[param]
1368if !found {
1369return false
1370}
1371
1372return reg.MatchString(field.String())
1373}
1374
1375// isPostcodeByIso3166Alpha2Field validates by field which represents for a value of country code in iso 3166 alpha 2
1376// example: `postcode_iso3166_alpha2_field=CountryCode`
1377func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
1378field := fl.Field()
1379params := parseOneOfParam2(fl.Param())
1380
1381if len(params) != 1 {
1382return false
1383}
1384
1385currentField, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), params[0])
1386if !found {
1387return false
1388}
1389
1390if kind != reflect.String {
1391panic(fmt.Sprintf("Bad field type %T", currentField.Interface()))
1392}
1393
1394reg, found := postCodeRegexDict[currentField.String()]
1395if !found {
1396return false
1397}
1398
1399return reg.MatchString(field.String())
1400}
1401
1402// isBase64 is the validation function for validating if the current field's value is a valid base 64.
1403func isBase64(fl FieldLevel) bool {
1404return base64Regex.MatchString(fl.Field().String())
1405}
1406
1407// isBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string.
1408func isBase64URL(fl FieldLevel) bool {
1409return base64URLRegex.MatchString(fl.Field().String())
1410}
1411
1412// isBase64RawURL is the validation function for validating if the current field's value is a valid base64 URL safe string without '=' padding.
1413func isBase64RawURL(fl FieldLevel) bool {
1414return base64RawURLRegex.MatchString(fl.Field().String())
1415}
1416
1417// isURI is the validation function for validating if the current field's value is a valid URI.
1418func isURI(fl FieldLevel) bool {
1419field := fl.Field()
1420
1421switch field.Kind() {
1422case reflect.String:
1423
1424s := field.String()
1425
1426// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
1427// emulate browser and strip the '#' suffix prior to validation. see issue-#237
1428if i := strings.Index(s, "#"); i > -1 {
1429s = s[:i]
1430}
1431
1432if len(s) == 0 {
1433return false
1434}
1435
1436_, err := url.ParseRequestURI(s)
1437
1438return err == nil
1439}
1440
1441panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1442}
1443
1444// isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
1445func isFileURL(path string) bool {
1446if !strings.HasPrefix(path, "file:/") {
1447return false
1448}
1449_, err := url.ParseRequestURI(path)
1450return err == nil
1451}
1452
1453// isURL is the validation function for validating if the current field's value is a valid URL.
1454func isURL(fl FieldLevel) bool {
1455field := fl.Field()
1456
1457switch field.Kind() {
1458case reflect.String:
1459
1460s := strings.ToLower(field.String())
1461
1462if len(s) == 0 {
1463return false
1464}
1465
1466if isFileURL(s) {
1467return true
1468}
1469
1470url, err := url.Parse(s)
1471if err != nil || url.Scheme == "" {
1472return false
1473}
1474
1475if url.Host == "" && url.Fragment == "" && url.Opaque == "" {
1476return false
1477}
1478
1479return true
1480}
1481
1482panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1483}
1484
1485// isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL.
1486func isHttpURL(fl FieldLevel) bool {
1487if !isURL(fl) {
1488return false
1489}
1490
1491field := fl.Field()
1492switch field.Kind() {
1493case reflect.String:
1494
1495s := strings.ToLower(field.String())
1496
1497url, err := url.Parse(s)
1498if err != nil || url.Host == "" {
1499return false
1500}
1501
1502return url.Scheme == "http" || url.Scheme == "https"
1503}
1504
1505panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1506}
1507
1508// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141.
1509func isUrnRFC2141(fl FieldLevel) bool {
1510field := fl.Field()
1511
1512switch field.Kind() {
1513case reflect.String:
1514
1515str := field.String()
1516
1517_, match := urn.Parse([]byte(str))
1518
1519return match
1520}
1521
1522panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1523}
1524
1525// isFile is the validation function for validating if the current field's value is a valid existing file path.
1526func isFile(fl FieldLevel) bool {
1527field := fl.Field()
1528
1529switch field.Kind() {
1530case reflect.String:
1531fileInfo, err := os.Stat(field.String())
1532if err != nil {
1533return false
1534}
1535
1536return !fileInfo.IsDir()
1537}
1538
1539panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1540}
1541
1542// isImage is the validation function for validating if the current field's value contains the path to a valid image file
1543func isImage(fl FieldLevel) bool {
1544mimetypes := map[string]bool{
1545"image/bmp": true,
1546"image/cis-cod": true,
1547"image/gif": true,
1548"image/ief": true,
1549"image/jpeg": true,
1550"image/jp2": true,
1551"image/jpx": true,
1552"image/jpm": true,
1553"image/pipeg": true,
1554"image/png": true,
1555"image/svg+xml": true,
1556"image/tiff": true,
1557"image/webp": true,
1558"image/x-cmu-raster": true,
1559"image/x-cmx": true,
1560"image/x-icon": true,
1561"image/x-portable-anymap": true,
1562"image/x-portable-bitmap": true,
1563"image/x-portable-graymap": true,
1564"image/x-portable-pixmap": true,
1565"image/x-rgb": true,
1566"image/x-xbitmap": true,
1567"image/x-xpixmap": true,
1568"image/x-xwindowdump": true,
1569}
1570field := fl.Field()
1571
1572switch field.Kind() {
1573case reflect.String:
1574filePath := field.String()
1575fileInfo, err := os.Stat(filePath)
1576
1577if err != nil {
1578return false
1579}
1580
1581if fileInfo.IsDir() {
1582return false
1583}
1584
1585file, err := os.Open(filePath)
1586if err != nil {
1587return false
1588}
1589defer file.Close()
1590
1591mime, err := mimetype.DetectReader(file)
1592if err != nil {
1593return false
1594}
1595
1596if _, ok := mimetypes[mime.String()]; ok {
1597return true
1598}
1599}
1600return false
1601}
1602
1603// isFilePath is the validation function for validating if the current field's value is a valid file path.
1604func isFilePath(fl FieldLevel) bool {
1605
1606var exists bool
1607var err error
1608
1609field := fl.Field()
1610
1611// Not valid if it is a directory.
1612if isDir(fl) {
1613return false
1614}
1615// If it exists, it obviously is valid.
1616// This is done first to avoid code duplication and unnecessary additional logic.
1617if exists = isFile(fl); exists {
1618return true
1619}
1620
1621// It does not exist but may still be a valid filepath.
1622switch field.Kind() {
1623case reflect.String:
1624// Every OS allows for whitespace, but none
1625// let you use a file with no filename (to my knowledge).
1626// Unless you're dealing with raw inodes, but I digress.
1627if strings.TrimSpace(field.String()) == "" {
1628return false
1629}
1630// We make sure it isn't a directory.
1631if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
1632return false
1633}
1634if _, err = os.Stat(field.String()); err != nil {
1635switch t := err.(type) {
1636case *fs.PathError:
1637if t.Err == syscall.EINVAL {
1638// It's definitely an invalid character in the filepath.
1639return false
1640}
1641// It could be a permission error, a does-not-exist error, etc.
1642// Out-of-scope for this validation, though.
1643return true
1644default:
1645// Something went *seriously* wrong.
1646/*
1647Per https://pkg.go.dev/os#Stat:
1648"If there is an error, it will be of type *PathError."
1649*/
1650panic(err)
1651}
1652}
1653}
1654
1655panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1656}
1657
1658// isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
1659func isE164(fl FieldLevel) bool {
1660return e164Regex.MatchString(fl.Field().String())
1661}
1662
1663// isEmail is the validation function for validating if the current field's value is a valid email address.
1664func isEmail(fl FieldLevel) bool {
1665return emailRegex.MatchString(fl.Field().String())
1666}
1667
1668// isHSLA is the validation function for validating if the current field's value is a valid HSLA color.
1669func isHSLA(fl FieldLevel) bool {
1670return hslaRegex.MatchString(fl.Field().String())
1671}
1672
1673// isHSL is the validation function for validating if the current field's value is a valid HSL color.
1674func isHSL(fl FieldLevel) bool {
1675return hslRegex.MatchString(fl.Field().String())
1676}
1677
1678// isRGBA is the validation function for validating if the current field's value is a valid RGBA color.
1679func isRGBA(fl FieldLevel) bool {
1680return rgbaRegex.MatchString(fl.Field().String())
1681}
1682
1683// isRGB is the validation function for validating if the current field's value is a valid RGB color.
1684func isRGB(fl FieldLevel) bool {
1685return rgbRegex.MatchString(fl.Field().String())
1686}
1687
1688// isHEXColor is the validation function for validating if the current field's value is a valid HEX color.
1689func isHEXColor(fl FieldLevel) bool {
1690return hexColorRegex.MatchString(fl.Field().String())
1691}
1692
1693// isHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal.
1694func isHexadecimal(fl FieldLevel) bool {
1695return hexadecimalRegex.MatchString(fl.Field().String())
1696}
1697
1698// isNumber is the validation function for validating if the current field's value is a valid number.
1699func isNumber(fl FieldLevel) bool {
1700switch fl.Field().Kind() {
1701case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
1702return true
1703default:
1704return numberRegex.MatchString(fl.Field().String())
1705}
1706}
1707
1708// isNumeric is the validation function for validating if the current field's value is a valid numeric value.
1709func isNumeric(fl FieldLevel) bool {
1710switch fl.Field().Kind() {
1711case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
1712return true
1713default:
1714return numericRegex.MatchString(fl.Field().String())
1715}
1716}
1717
1718// isAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value.
1719func isAlphanum(fl FieldLevel) bool {
1720return alphaNumericRegex.MatchString(fl.Field().String())
1721}
1722
1723// isAlpha is the validation function for validating if the current field's value is a valid alpha value.
1724func isAlpha(fl FieldLevel) bool {
1725return alphaRegex.MatchString(fl.Field().String())
1726}
1727
1728// isAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value.
1729func isAlphanumUnicode(fl FieldLevel) bool {
1730return alphaUnicodeNumericRegex.MatchString(fl.Field().String())
1731}
1732
1733// isAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
1734func isAlphaUnicode(fl FieldLevel) bool {
1735return alphaUnicodeRegex.MatchString(fl.Field().String())
1736}
1737
1738// isBoolean is the validation function for validating if the current field's value is a valid boolean value or can be safely converted to a boolean value.
1739func isBoolean(fl FieldLevel) bool {
1740switch fl.Field().Kind() {
1741case reflect.Bool:
1742return true
1743default:
1744_, err := strconv.ParseBool(fl.Field().String())
1745return err == nil
1746}
1747}
1748
1749// isDefault is the opposite of required aka hasValue
1750func isDefault(fl FieldLevel) bool {
1751return !hasValue(fl)
1752}
1753
1754// hasValue is the validation function for validating if the current field's value is not the default static value.
1755func hasValue(fl FieldLevel) bool {
1756field := fl.Field()
1757switch field.Kind() {
1758case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1759return !field.IsNil()
1760default:
1761if fl.(*validate).fldIsPointer && field.Interface() != nil {
1762return true
1763}
1764return field.IsValid() && !field.IsZero()
1765}
1766}
1767
1768// requireCheckFieldKind is a func for check field kind
1769func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
1770field := fl.Field()
1771kind := field.Kind()
1772var nullable, found bool
1773if len(param) > 0 {
1774field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
1775if !found {
1776return defaultNotFoundValue
1777}
1778}
1779switch kind {
1780case reflect.Invalid:
1781return defaultNotFoundValue
1782case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1783return field.IsNil()
1784default:
1785if nullable && field.Interface() != nil {
1786return false
1787}
1788return field.IsValid() && field.IsZero()
1789}
1790}
1791
1792// requireCheckFieldValue is a func for check field value
1793func requireCheckFieldValue(
1794fl FieldLevel, param string, value string, defaultNotFoundValue bool,
1795) bool {
1796field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
1797if !found {
1798return defaultNotFoundValue
1799}
1800
1801switch kind {
1802
1803case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1804return field.Int() == asInt(value)
1805
1806case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1807return field.Uint() == asUint(value)
1808
1809case reflect.Float32:
1810return field.Float() == asFloat32(value)
1811
1812case reflect.Float64:
1813return field.Float() == asFloat64(value)
1814
1815case reflect.Slice, reflect.Map, reflect.Array:
1816return int64(field.Len()) == asInt(value)
1817
1818case reflect.Bool:
1819return field.Bool() == asBool(value)
1820}
1821
1822// default reflect.String:
1823return field.String() == value
1824}
1825
1826// requiredIf is the validation function
1827// The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field.
1828func requiredIf(fl FieldLevel) bool {
1829params := parseOneOfParam2(fl.Param())
1830if len(params)%2 != 0 {
1831panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
1832}
1833for i := 0; i < len(params); i += 2 {
1834if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1835return true
1836}
1837}
1838return hasValue(fl)
1839}
1840
1841// excludedIf is the validation function
1842// The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
1843func excludedIf(fl FieldLevel) bool {
1844params := parseOneOfParam2(fl.Param())
1845if len(params)%2 != 0 {
1846panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
1847}
1848
1849for i := 0; i < len(params); i += 2 {
1850if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1851return true
1852}
1853}
1854return !hasValue(fl)
1855}
1856
1857// requiredUnless is the validation function
1858// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
1859func requiredUnless(fl FieldLevel) bool {
1860params := parseOneOfParam2(fl.Param())
1861if len(params)%2 != 0 {
1862panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName()))
1863}
1864
1865for i := 0; i < len(params); i += 2 {
1866if requireCheckFieldValue(fl, params[i], params[i+1], false) {
1867return true
1868}
1869}
1870return hasValue(fl)
1871}
1872
1873// skipUnless is the validation function
1874// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
1875func skipUnless(fl FieldLevel) bool {
1876params := parseOneOfParam2(fl.Param())
1877if len(params)%2 != 0 {
1878panic(fmt.Sprintf("Bad param number for skip_unless %s", fl.FieldName()))
1879}
1880for i := 0; i < len(params); i += 2 {
1881if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1882return true
1883}
1884}
1885return hasValue(fl)
1886}
1887
1888// excludedUnless is the validation function
1889// The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
1890func excludedUnless(fl FieldLevel) bool {
1891params := parseOneOfParam2(fl.Param())
1892if len(params)%2 != 0 {
1893panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
1894}
1895for i := 0; i < len(params); i += 2 {
1896if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1897return !hasValue(fl)
1898}
1899}
1900return true
1901}
1902
1903// excludedWith is the validation function
1904// The field under validation must not be present or is empty if any of the other specified fields are present.
1905func excludedWith(fl FieldLevel) bool {
1906params := parseOneOfParam2(fl.Param())
1907for _, param := range params {
1908if !requireCheckFieldKind(fl, param, true) {
1909return !hasValue(fl)
1910}
1911}
1912return true
1913}
1914
1915// requiredWith is the validation function
1916// The field under validation must be present and not empty only if any of the other specified fields are present.
1917func requiredWith(fl FieldLevel) bool {
1918params := parseOneOfParam2(fl.Param())
1919for _, param := range params {
1920if !requireCheckFieldKind(fl, param, true) {
1921return hasValue(fl)
1922}
1923}
1924return true
1925}
1926
1927// excludedWithAll is the validation function
1928// The field under validation must not be present or is empty if all of the other specified fields are present.
1929func excludedWithAll(fl FieldLevel) bool {
1930params := parseOneOfParam2(fl.Param())
1931for _, param := range params {
1932if requireCheckFieldKind(fl, param, true) {
1933return true
1934}
1935}
1936return !hasValue(fl)
1937}
1938
1939// requiredWithAll is the validation function
1940// The field under validation must be present and not empty only if all of the other specified fields are present.
1941func requiredWithAll(fl FieldLevel) bool {
1942params := parseOneOfParam2(fl.Param())
1943for _, param := range params {
1944if requireCheckFieldKind(fl, param, true) {
1945return true
1946}
1947}
1948return hasValue(fl)
1949}
1950
1951// excludedWithout is the validation function
1952// The field under validation must not be present or is empty when any of the other specified fields are not present.
1953func excludedWithout(fl FieldLevel) bool {
1954if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
1955return !hasValue(fl)
1956}
1957return true
1958}
1959
1960// requiredWithout is the validation function
1961// The field under validation must be present and not empty only when any of the other specified fields are not present.
1962func requiredWithout(fl FieldLevel) bool {
1963if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
1964return hasValue(fl)
1965}
1966return true
1967}
1968
1969// excludedWithoutAll is the validation function
1970// The field under validation must not be present or is empty when all of the other specified fields are not present.
1971func excludedWithoutAll(fl FieldLevel) bool {
1972params := parseOneOfParam2(fl.Param())
1973for _, param := range params {
1974if !requireCheckFieldKind(fl, param, true) {
1975return true
1976}
1977}
1978return !hasValue(fl)
1979}
1980
1981// requiredWithoutAll is the validation function
1982// The field under validation must be present and not empty only when all of the other specified fields are not present.
1983func requiredWithoutAll(fl FieldLevel) bool {
1984params := parseOneOfParam2(fl.Param())
1985for _, param := range params {
1986if !requireCheckFieldKind(fl, param, true) {
1987return true
1988}
1989}
1990return hasValue(fl)
1991}
1992
1993// isGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
1994func isGteField(fl FieldLevel) bool {
1995field := fl.Field()
1996kind := field.Kind()
1997
1998currentField, currentKind, ok := fl.GetStructFieldOK()
1999if !ok || currentKind != kind {
2000return false
2001}
2002
2003switch kind {
2004
2005case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2006
2007return field.Int() >= currentField.Int()
2008
2009case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2010
2011return field.Uint() >= currentField.Uint()
2012
2013case reflect.Float32, reflect.Float64:
2014
2015return field.Float() >= currentField.Float()
2016
2017case reflect.Struct:
2018
2019fieldType := field.Type()
2020
2021if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2022
2023t := currentField.Convert(timeType).Interface().(time.Time)
2024fieldTime := field.Convert(timeType).Interface().(time.Time)
2025
2026return fieldTime.After(t) || fieldTime.Equal(t)
2027}
2028
2029// Not Same underlying type i.e. struct and time
2030if fieldType != currentField.Type() {
2031return false
2032}
2033}
2034
2035// default reflect.String
2036return len(field.String()) >= len(currentField.String())
2037}
2038
2039// isGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value.
2040func isGtField(fl FieldLevel) bool {
2041field := fl.Field()
2042kind := field.Kind()
2043
2044currentField, currentKind, ok := fl.GetStructFieldOK()
2045if !ok || currentKind != kind {
2046return false
2047}
2048
2049switch kind {
2050
2051case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2052
2053return field.Int() > currentField.Int()
2054
2055case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2056
2057return field.Uint() > currentField.Uint()
2058
2059case reflect.Float32, reflect.Float64:
2060
2061return field.Float() > currentField.Float()
2062
2063case reflect.Struct:
2064
2065fieldType := field.Type()
2066
2067if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2068
2069t := currentField.Convert(timeType).Interface().(time.Time)
2070fieldTime := field.Convert(timeType).Interface().(time.Time)
2071
2072return fieldTime.After(t)
2073}
2074
2075// Not Same underlying type i.e. struct and time
2076if fieldType != currentField.Type() {
2077return false
2078}
2079}
2080
2081// default reflect.String
2082return len(field.String()) > len(currentField.String())
2083}
2084
2085// isGte is the validation function for validating if the current field's value is greater than or equal to the param's value.
2086func isGte(fl FieldLevel) bool {
2087field := fl.Field()
2088param := fl.Param()
2089
2090switch field.Kind() {
2091
2092case reflect.String:
2093p := asInt(param)
2094
2095return int64(utf8.RuneCountInString(field.String())) >= p
2096
2097case reflect.Slice, reflect.Map, reflect.Array:
2098p := asInt(param)
2099
2100return int64(field.Len()) >= p
2101
2102case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2103p := asIntFromType(field.Type(), param)
2104
2105return field.Int() >= p
2106
2107case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2108p := asUint(param)
2109
2110return field.Uint() >= p
2111
2112case reflect.Float32:
2113p := asFloat32(param)
2114
2115return field.Float() >= p
2116
2117case reflect.Float64:
2118p := asFloat64(param)
2119
2120return field.Float() >= p
2121
2122case reflect.Struct:
2123
2124if field.Type().ConvertibleTo(timeType) {
2125
2126now := time.Now().UTC()
2127t := field.Convert(timeType).Interface().(time.Time)
2128
2129return t.After(now) || t.Equal(now)
2130}
2131}
2132
2133panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2134}
2135
2136// isGt is the validation function for validating if the current field's value is greater than the param's value.
2137func isGt(fl FieldLevel) bool {
2138field := fl.Field()
2139param := fl.Param()
2140
2141switch field.Kind() {
2142
2143case reflect.String:
2144p := asInt(param)
2145
2146return int64(utf8.RuneCountInString(field.String())) > p
2147
2148case reflect.Slice, reflect.Map, reflect.Array:
2149p := asInt(param)
2150
2151return int64(field.Len()) > p
2152
2153case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2154p := asIntFromType(field.Type(), param)
2155
2156return field.Int() > p
2157
2158case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2159p := asUint(param)
2160
2161return field.Uint() > p
2162
2163case reflect.Float32:
2164p := asFloat32(param)
2165
2166return field.Float() > p
2167
2168case reflect.Float64:
2169p := asFloat64(param)
2170
2171return field.Float() > p
2172
2173case reflect.Struct:
2174
2175if field.Type().ConvertibleTo(timeType) {
2176
2177return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
2178}
2179}
2180
2181panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2182}
2183
2184// hasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
2185func hasLengthOf(fl FieldLevel) bool {
2186field := fl.Field()
2187param := fl.Param()
2188
2189switch field.Kind() {
2190
2191case reflect.String:
2192p := asInt(param)
2193
2194return int64(utf8.RuneCountInString(field.String())) == p
2195
2196case reflect.Slice, reflect.Map, reflect.Array:
2197p := asInt(param)
2198
2199return int64(field.Len()) == p
2200
2201case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2202p := asIntFromType(field.Type(), param)
2203
2204return field.Int() == p
2205
2206case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2207p := asUint(param)
2208
2209return field.Uint() == p
2210
2211case reflect.Float32:
2212p := asFloat32(param)
2213
2214return field.Float() == p
2215
2216case reflect.Float64:
2217p := asFloat64(param)
2218
2219return field.Float() == p
2220}
2221
2222panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2223}
2224
2225// hasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
2226func hasMinOf(fl FieldLevel) bool {
2227return isGte(fl)
2228}
2229
2230// isLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value.
2231func isLteField(fl FieldLevel) bool {
2232field := fl.Field()
2233kind := field.Kind()
2234
2235currentField, currentKind, ok := fl.GetStructFieldOK()
2236if !ok || currentKind != kind {
2237return false
2238}
2239
2240switch kind {
2241
2242case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2243
2244return field.Int() <= currentField.Int()
2245
2246case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2247
2248return field.Uint() <= currentField.Uint()
2249
2250case reflect.Float32, reflect.Float64:
2251
2252return field.Float() <= currentField.Float()
2253
2254case reflect.Struct:
2255
2256fieldType := field.Type()
2257
2258if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2259
2260t := currentField.Convert(timeType).Interface().(time.Time)
2261fieldTime := field.Convert(timeType).Interface().(time.Time)
2262
2263return fieldTime.Before(t) || fieldTime.Equal(t)
2264}
2265
2266// Not Same underlying type i.e. struct and time
2267if fieldType != currentField.Type() {
2268return false
2269}
2270}
2271
2272// default reflect.String
2273return len(field.String()) <= len(currentField.String())
2274}
2275
2276// isLtField is the validation function for validating if the current field's value is less than the field specified by the param's value.
2277func isLtField(fl FieldLevel) bool {
2278field := fl.Field()
2279kind := field.Kind()
2280
2281currentField, currentKind, ok := fl.GetStructFieldOK()
2282if !ok || currentKind != kind {
2283return false
2284}
2285
2286switch kind {
2287
2288case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2289
2290return field.Int() < currentField.Int()
2291
2292case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2293
2294return field.Uint() < currentField.Uint()
2295
2296case reflect.Float32, reflect.Float64:
2297
2298return field.Float() < currentField.Float()
2299
2300case reflect.Struct:
2301
2302fieldType := field.Type()
2303
2304if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2305
2306t := currentField.Convert(timeType).Interface().(time.Time)
2307fieldTime := field.Convert(timeType).Interface().(time.Time)
2308
2309return fieldTime.Before(t)
2310}
2311
2312// Not Same underlying type i.e. struct and time
2313if fieldType != currentField.Type() {
2314return false
2315}
2316}
2317
2318// default reflect.String
2319return len(field.String()) < len(currentField.String())
2320}
2321
2322// isLte is the validation function for validating if the current field's value is less than or equal to the param's value.
2323func isLte(fl FieldLevel) bool {
2324field := fl.Field()
2325param := fl.Param()
2326
2327switch field.Kind() {
2328
2329case reflect.String:
2330p := asInt(param)
2331
2332return int64(utf8.RuneCountInString(field.String())) <= p
2333
2334case reflect.Slice, reflect.Map, reflect.Array:
2335p := asInt(param)
2336
2337return int64(field.Len()) <= p
2338
2339case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2340p := asIntFromType(field.Type(), param)
2341
2342return field.Int() <= p
2343
2344case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2345p := asUint(param)
2346
2347return field.Uint() <= p
2348
2349case reflect.Float32:
2350p := asFloat32(param)
2351
2352return field.Float() <= p
2353
2354case reflect.Float64:
2355p := asFloat64(param)
2356
2357return field.Float() <= p
2358
2359case reflect.Struct:
2360
2361if field.Type().ConvertibleTo(timeType) {
2362
2363now := time.Now().UTC()
2364t := field.Convert(timeType).Interface().(time.Time)
2365
2366return t.Before(now) || t.Equal(now)
2367}
2368}
2369
2370panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2371}
2372
2373// isLt is the validation function for validating if the current field's value is less than the param's value.
2374func isLt(fl FieldLevel) bool {
2375field := fl.Field()
2376param := fl.Param()
2377
2378switch field.Kind() {
2379
2380case reflect.String:
2381p := asInt(param)
2382
2383return int64(utf8.RuneCountInString(field.String())) < p
2384
2385case reflect.Slice, reflect.Map, reflect.Array:
2386p := asInt(param)
2387
2388return int64(field.Len()) < p
2389
2390case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2391p := asIntFromType(field.Type(), param)
2392
2393return field.Int() < p
2394
2395case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2396p := asUint(param)
2397
2398return field.Uint() < p
2399
2400case reflect.Float32:
2401p := asFloat32(param)
2402
2403return field.Float() < p
2404
2405case reflect.Float64:
2406p := asFloat64(param)
2407
2408return field.Float() < p
2409
2410case reflect.Struct:
2411
2412if field.Type().ConvertibleTo(timeType) {
2413
2414return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
2415}
2416}
2417
2418panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2419}
2420
2421// hasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
2422func hasMaxOf(fl FieldLevel) bool {
2423return isLte(fl)
2424}
2425
2426// isTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address.
2427func isTCP4AddrResolvable(fl FieldLevel) bool {
2428if !isIP4Addr(fl) {
2429return false
2430}
2431
2432_, err := net.ResolveTCPAddr("tcp4", fl.Field().String())
2433return err == nil
2434}
2435
2436// isTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address.
2437func isTCP6AddrResolvable(fl FieldLevel) bool {
2438if !isIP6Addr(fl) {
2439return false
2440}
2441
2442_, err := net.ResolveTCPAddr("tcp6", fl.Field().String())
2443
2444return err == nil
2445}
2446
2447// isTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address.
2448func isTCPAddrResolvable(fl FieldLevel) bool {
2449if !isIP4Addr(fl) && !isIP6Addr(fl) {
2450return false
2451}
2452
2453_, err := net.ResolveTCPAddr("tcp", fl.Field().String())
2454
2455return err == nil
2456}
2457
2458// isUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address.
2459func isUDP4AddrResolvable(fl FieldLevel) bool {
2460if !isIP4Addr(fl) {
2461return false
2462}
2463
2464_, err := net.ResolveUDPAddr("udp4", fl.Field().String())
2465
2466return err == nil
2467}
2468
2469// isUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address.
2470func isUDP6AddrResolvable(fl FieldLevel) bool {
2471if !isIP6Addr(fl) {
2472return false
2473}
2474
2475_, err := net.ResolveUDPAddr("udp6", fl.Field().String())
2476
2477return err == nil
2478}
2479
2480// isUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address.
2481func isUDPAddrResolvable(fl FieldLevel) bool {
2482if !isIP4Addr(fl) && !isIP6Addr(fl) {
2483return false
2484}
2485
2486_, err := net.ResolveUDPAddr("udp", fl.Field().String())
2487
2488return err == nil
2489}
2490
2491// isIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address.
2492func isIP4AddrResolvable(fl FieldLevel) bool {
2493if !isIPv4(fl) {
2494return false
2495}
2496
2497_, err := net.ResolveIPAddr("ip4", fl.Field().String())
2498
2499return err == nil
2500}
2501
2502// isIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address.
2503func isIP6AddrResolvable(fl FieldLevel) bool {
2504if !isIPv6(fl) {
2505return false
2506}
2507
2508_, err := net.ResolveIPAddr("ip6", fl.Field().String())
2509
2510return err == nil
2511}
2512
2513// isIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address.
2514func isIPAddrResolvable(fl FieldLevel) bool {
2515if !isIP(fl) {
2516return false
2517}
2518
2519_, err := net.ResolveIPAddr("ip", fl.Field().String())
2520
2521return err == nil
2522}
2523
2524// isUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address.
2525func isUnixAddrResolvable(fl FieldLevel) bool {
2526_, err := net.ResolveUnixAddr("unix", fl.Field().String())
2527
2528return err == nil
2529}
2530
2531func isIP4Addr(fl FieldLevel) bool {
2532val := fl.Field().String()
2533
2534if idx := strings.LastIndex(val, ":"); idx != -1 {
2535val = val[0:idx]
2536}
2537
2538ip := net.ParseIP(val)
2539
2540return ip != nil && ip.To4() != nil
2541}
2542
2543func isIP6Addr(fl FieldLevel) bool {
2544val := fl.Field().String()
2545
2546if idx := strings.LastIndex(val, ":"); idx != -1 {
2547if idx != 0 && val[idx-1:idx] == "]" {
2548val = val[1 : idx-1]
2549}
2550}
2551
2552ip := net.ParseIP(val)
2553
2554return ip != nil && ip.To4() == nil
2555}
2556
2557func isHostnameRFC952(fl FieldLevel) bool {
2558return hostnameRegexRFC952.MatchString(fl.Field().String())
2559}
2560
2561func isHostnameRFC1123(fl FieldLevel) bool {
2562return hostnameRegexRFC1123.MatchString(fl.Field().String())
2563}
2564
2565func isFQDN(fl FieldLevel) bool {
2566val := fl.Field().String()
2567
2568if val == "" {
2569return false
2570}
2571
2572return fqdnRegexRFC1123.MatchString(val)
2573}
2574
2575// isDir is the validation function for validating if the current field's value is a valid existing directory.
2576func isDir(fl FieldLevel) bool {
2577field := fl.Field()
2578
2579if field.Kind() == reflect.String {
2580fileInfo, err := os.Stat(field.String())
2581if err != nil {
2582return false
2583}
2584
2585return fileInfo.IsDir()
2586}
2587
2588panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2589}
2590
2591// isDirPath is the validation function for validating if the current field's value is a valid directory.
2592func isDirPath(fl FieldLevel) bool {
2593
2594var exists bool
2595var err error
2596
2597field := fl.Field()
2598
2599// If it exists, it obviously is valid.
2600// This is done first to avoid code duplication and unnecessary additional logic.
2601if exists = isDir(fl); exists {
2602return true
2603}
2604
2605// It does not exist but may still be a valid path.
2606switch field.Kind() {
2607case reflect.String:
2608// Every OS allows for whitespace, but none
2609// let you use a dir with no name (to my knowledge).
2610// Unless you're dealing with raw inodes, but I digress.
2611if strings.TrimSpace(field.String()) == "" {
2612return false
2613}
2614if _, err = os.Stat(field.String()); err != nil {
2615switch t := err.(type) {
2616case *fs.PathError:
2617if t.Err == syscall.EINVAL {
2618// It's definitely an invalid character in the path.
2619return false
2620}
2621// It could be a permission error, a does-not-exist error, etc.
2622// Out-of-scope for this validation, though.
2623// Lastly, we make sure it is a directory.
2624if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
2625return true
2626} else {
2627return false
2628}
2629default:
2630// Something went *seriously* wrong.
2631/*
2632Per https://pkg.go.dev/os#Stat:
2633"If there is an error, it will be of type *PathError."
2634*/
2635panic(err)
2636}
2637}
2638// We repeat the check here to make sure it is an explicit directory in case the above os.Stat didn't trigger an error.
2639if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
2640return true
2641} else {
2642return false
2643}
2644}
2645
2646panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2647}
2648
2649// isJSON is the validation function for validating if the current field's value is a valid json string.
2650func isJSON(fl FieldLevel) bool {
2651field := fl.Field()
2652
2653switch field.Kind() {
2654case reflect.String:
2655val := field.String()
2656return json.Valid([]byte(val))
2657case reflect.Slice:
2658fieldType := field.Type()
2659
2660if fieldType.ConvertibleTo(byteSliceType) {
2661b := field.Convert(byteSliceType).Interface().([]byte)
2662return json.Valid(b)
2663}
2664}
2665
2666panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2667}
2668
2669// isJWT is the validation function for validating if the current field's value is a valid JWT string.
2670func isJWT(fl FieldLevel) bool {
2671return jWTRegex.MatchString(fl.Field().String())
2672}
2673
2674// isHostnamePort validates a <dns>:<port> combination for fields typically used for socket address.
2675func isHostnamePort(fl FieldLevel) bool {
2676val := fl.Field().String()
2677host, port, err := net.SplitHostPort(val)
2678if err != nil {
2679return false
2680}
2681// Port must be a iny <= 65535.
2682if portNum, err := strconv.ParseInt(
2683port, 10, 32,
2684); err != nil || portNum > 65535 || portNum < 1 {
2685return false
2686}
2687
2688// If host is specified, it should match a DNS name
2689if host != "" {
2690return hostnameRegexRFC1123.MatchString(host)
2691}
2692return true
2693}
2694
2695// isLowercase is the validation function for validating if the current field's value is a lowercase string.
2696func isLowercase(fl FieldLevel) bool {
2697field := fl.Field()
2698
2699if field.Kind() == reflect.String {
2700if field.String() == "" {
2701return false
2702}
2703return field.String() == strings.ToLower(field.String())
2704}
2705
2706panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2707}
2708
2709// isUppercase is the validation function for validating if the current field's value is an uppercase string.
2710func isUppercase(fl FieldLevel) bool {
2711field := fl.Field()
2712
2713if field.Kind() == reflect.String {
2714if field.String() == "" {
2715return false
2716}
2717return field.String() == strings.ToUpper(field.String())
2718}
2719
2720panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2721}
2722
2723// isDatetime is the validation function for validating if the current field's value is a valid datetime string.
2724func isDatetime(fl FieldLevel) bool {
2725field := fl.Field()
2726param := fl.Param()
2727
2728if field.Kind() == reflect.String {
2729_, err := time.Parse(param, field.String())
2730
2731return err == nil
2732}
2733
2734panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2735}
2736
2737// isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
2738func isTimeZone(fl FieldLevel) bool {
2739field := fl.Field()
2740
2741if field.Kind() == reflect.String {
2742// empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name
2743if field.String() == "" {
2744return false
2745}
2746
2747// Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name
2748if strings.ToLower(field.String()) == "local" {
2749return false
2750}
2751
2752_, err := time.LoadLocation(field.String())
2753return err == nil
2754}
2755
2756panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2757}
2758
2759// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code.
2760func isIso3166Alpha2(fl FieldLevel) bool {
2761val := fl.Field().String()
2762return iso3166_1_alpha2[val]
2763}
2764
2765// isIso3166Alpha3 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code.
2766func isIso3166Alpha3(fl FieldLevel) bool {
2767val := fl.Field().String()
2768return iso3166_1_alpha3[val]
2769}
2770
2771// isIso3166AlphaNumeric is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code.
2772func isIso3166AlphaNumeric(fl FieldLevel) bool {
2773field := fl.Field()
2774
2775var code int
2776switch field.Kind() {
2777case reflect.String:
2778i, err := strconv.Atoi(field.String())
2779if err != nil {
2780return false
2781}
2782code = i % 1000
2783case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2784code = int(field.Int() % 1000)
2785case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2786code = int(field.Uint() % 1000)
2787default:
2788panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2789}
2790return iso3166_1_alpha_numeric[code]
2791}
2792
2793// isIso31662 is the validation function for validating if the current field's value is a valid iso3166-2 code.
2794func isIso31662(fl FieldLevel) bool {
2795val := fl.Field().String()
2796return iso3166_2[val]
2797}
2798
2799// isIso4217 is the validation function for validating if the current field's value is a valid iso4217 currency code.
2800func isIso4217(fl FieldLevel) bool {
2801val := fl.Field().String()
2802return iso4217[val]
2803}
2804
2805// isIso4217Numeric is the validation function for validating if the current field's value is a valid iso4217 numeric currency code.
2806func isIso4217Numeric(fl FieldLevel) bool {
2807field := fl.Field()
2808
2809var code int
2810switch field.Kind() {
2811case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2812code = int(field.Int())
2813case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2814code = int(field.Uint())
2815default:
2816panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2817}
2818return iso4217_numeric[code]
2819}
2820
2821// isBCP47LanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag, as parsed by language.Parse
2822func isBCP47LanguageTag(fl FieldLevel) bool {
2823field := fl.Field()
2824
2825if field.Kind() == reflect.String {
2826_, err := language.Parse(field.String())
2827return err == nil
2828}
2829
2830panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2831}
2832
2833// isIsoBicFormat is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362
2834func isIsoBicFormat(fl FieldLevel) bool {
2835bicString := fl.Field().String()
2836
2837return bicRegex.MatchString(bicString)
2838}
2839
2840// isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0
2841func isSemverFormat(fl FieldLevel) bool {
2842semverString := fl.Field().String()
2843
2844return semverRegex.MatchString(semverString)
2845}
2846
2847// isCveFormat is the validation function for validating if the current field's value is a valid cve id, defined in CVE mitre org
2848func isCveFormat(fl FieldLevel) bool {
2849cveString := fl.Field().String()
2850
2851return cveRegex.MatchString(cveString)
2852}
2853
2854// isDnsRFC1035LabelFormat is the validation function
2855// for validating if the current field's value is
2856// a valid dns RFC 1035 label, defined in RFC 1035.
2857func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
2858val := fl.Field().String()
2859return dnsRegexRFC1035Label.MatchString(val)
2860}
2861
2862// digitsHaveLuhnChecksum returns true if and only if the last element of the given digits slice is the Luhn checksum of the previous elements
2863func digitsHaveLuhnChecksum(digits []string) bool {
2864size := len(digits)
2865sum := 0
2866for i, digit := range digits {
2867value, err := strconv.Atoi(digit)
2868if err != nil {
2869return false
2870}
2871if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
2872v := value * 2
2873if v >= 10 {
2874sum += 1 + (v % 10)
2875} else {
2876sum += v
2877}
2878} else {
2879sum += value
2880}
2881}
2882return (sum % 10) == 0
2883}
2884
2885// isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID
2886func isMongoDB(fl FieldLevel) bool {
2887val := fl.Field().String()
2888return mongodbRegex.MatchString(val)
2889}
2890
2891// isSpiceDB is the validation function for validating if the current field's value is valid for use with Authzed SpiceDB in the indicated way
2892func isSpiceDB(fl FieldLevel) bool {
2893val := fl.Field().String()
2894param := fl.Param()
2895
2896switch param {
2897case "permission":
2898return spicedbPermissionRegex.MatchString(val)
2899case "type":
2900return spicedbTypeRegex.MatchString(val)
2901case "id", "":
2902return spicedbIDRegex.MatchString(val)
2903}
2904
2905panic("Unrecognized parameter: " + param)
2906}
2907
2908// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
2909func isCreditCard(fl FieldLevel) bool {
2910val := fl.Field().String()
2911var creditCard bytes.Buffer
2912segments := strings.Split(val, " ")
2913for _, segment := range segments {
2914if len(segment) < 3 {
2915return false
2916}
2917creditCard.WriteString(segment)
2918}
2919
2920ccDigits := strings.Split(creditCard.String(), "")
2921size := len(ccDigits)
2922if size < 12 || size > 19 {
2923return false
2924}
2925
2926return digitsHaveLuhnChecksum(ccDigits)
2927}
2928
2929// hasLuhnChecksum is the validation for validating if the current field's value has a valid Luhn checksum
2930func hasLuhnChecksum(fl FieldLevel) bool {
2931field := fl.Field()
2932var str string // convert to a string which will then be split into single digits; easier and more readable than shifting/extracting single digits from a number
2933switch field.Kind() {
2934case reflect.String:
2935str = field.String()
2936case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2937str = strconv.FormatInt(field.Int(), 10)
2938case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2939str = strconv.FormatUint(field.Uint(), 10)
2940default:
2941panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2942}
2943size := len(str)
2944if size < 2 { // there has to be at least one digit that carries a meaning + the checksum
2945return false
2946}
2947digits := strings.Split(str, "")
2948return digitsHaveLuhnChecksum(digits)
2949}
2950
2951// isCron is the validation function for validating if the current field's value is a valid cron expression
2952func isCron(fl FieldLevel) bool {
2953cronString := fl.Field().String()
2954return cronRegex.MatchString(cronString)
2955}
2956