From 0f29cb04d9f89009e84d94bd1521cf5e1a74f463 Mon Sep 17 00:00:00 2001 From: James Atkin Date: Tue, 16 Apr 2024 15:07:05 +0100 Subject: [PATCH 1/4] add base32tag --- baked_in.go | 6 ++++++ doc.go | 9 +++++++++ regexes.go | 2 ++ validator_test.go | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/baked_in.go b/baked_in.go index 95f56e008..459aa7fbe 100644 --- a/baked_in.go +++ b/baked_in.go @@ -133,6 +133,7 @@ var ( "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, "filepath": isFilePath, + "base32": isBase32, "base64": isBase64, "base64url": isBase64URL, "base64rawurl": isBase64RawURL, @@ -1399,6 +1400,11 @@ func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool { return reg.MatchString(field.String()) } +// isBase32 is the validation function for validating if the current field's value is a valid base 32. +func isBase32(fl FieldLevel) bool { + return base32Regex.MatchString(fl.Field().String()) +} + // isBase64 is the validation function for validating if the current field's value is a valid base 64. func isBase64(fl FieldLevel) bool { return base64Regex.MatchString(fl.Field().String()) diff --git a/doc.go b/doc.go index b47409188..2e8092a90 100644 --- a/doc.go +++ b/doc.go @@ -916,6 +916,15 @@ according to the RFC 2141 spec. Usage: urn_rfc2141 +# Base32 String + +This validates that a string value contains a valid bas324 value. +Although an empty string is valid base32 this will report an empty string +as an error, if you wish to accept an empty string as valid you can use +this with the omitempty tag. + + Usage: base32 + # Base64 String This validates that a string value contains a valid base64 value. diff --git a/regexes.go b/regexes.go index af98d8daa..bd51bc48c 100644 --- a/regexes.go +++ b/regexes.go @@ -17,6 +17,7 @@ const ( hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" e164RegexString = "^\\+[1-9]?[0-9]{7,14}$" + base32RegexString = "^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=|[A-Z2-7]{8})$" base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" base64RawURLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$" @@ -89,6 +90,7 @@ var ( hslaRegex = regexp.MustCompile(hslaRegexString) e164Regex = regexp.MustCompile(e164RegexString) emailRegex = regexp.MustCompile(emailRegexString) + base32Regex = regexp.MustCompile(base32RegexString) base64Regex = regexp.MustCompile(base64RegexString) base64URLRegex = regexp.MustCompile(base64URLRegexString) base64RawURLRegex = regexp.MustCompile(base64RawURLRegexString) diff --git a/validator_test.go b/validator_test.go index 3b6d26348..6f3948b3a 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5680,6 +5680,40 @@ func TestOneOfValidation(t *testing.T) { }, "Bad field type float64") } +func TestBase32Validation(t *testing.T) { + validate := New() + + s := "ABCD2345" + errs := validate.Var(s, "base32") + Equal(t, errs, nil) + + s = "AB======" + errs = validate.Var(s, "base32") + Equal(t, errs, nil) + + s = "ABCD2===" + errs = validate.Var(s, "base32") + Equal(t, errs, nil) + + s = "ABCD====" + errs = validate.Var(s, "base32") + Equal(t, errs, nil) + + s = "ABCD234=" + errs = validate.Var(s, "base32") + Equal(t, errs, nil) + + s = "" + errs = validate.Var(s, "base32") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "", "", "base32") + + s = "ABCabc1890== foo bar" + errs = validate.Var(s, "base32") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "", "", "base32") +} + func TestBase64Validation(t *testing.T) { validate := New() From 1a51f5b41766eb63e187d2ba96f344e013a929f1 Mon Sep 17 00:00:00 2001 From: James Atkin Date: Wed, 1 May 2024 11:00:08 +0100 Subject: [PATCH 2/4] correct base32 typo in docs and add full stops --- doc.go | 82 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/doc.go b/doc.go index 2e8092a90..c4bf8b31d 100644 --- a/doc.go +++ b/doc.go @@ -143,7 +143,7 @@ so the above will become excludesall=0x2C. Pipe ("|") is the 'or' validation tags deparator. If you wish to have a pipe included within the parameter i.e. excludesall=| you will need to use the UTF-8 hex representation 0x7C, which is replaced in the code as a pipe, -so the above will become excludesall=0x7C +so the above will become excludesall=0x7C. type Test struct { Field `validate:"excludesall=|"` // BAD! Do not include a pipe! @@ -678,7 +678,7 @@ to the top level struct. Only valid for Numbers, time.Duration and time.Time types, this will validate the field value against another fields value either within a struct or passed in -field. usage examples are for validation of a Start and End date: +field. Usage examples are for validation of a Start and End date: Example #1: @@ -751,25 +751,25 @@ in a field of the struct specified via a parameter. # Alpha Only -This validates that a string value contains ASCII alpha characters only +This validates that a string value contains ASCII alpha characters only. Usage: alpha # Alphanumeric -This validates that a string value contains ASCII alphanumeric characters only +This validates that a string value contains ASCII alphanumeric characters only. Usage: alphanum # Alpha Unicode -This validates that a string value contains unicode alpha characters only +This validates that a string value contains unicode alpha characters only. Usage: alphaunicode # Alphanumeric Unicode -This validates that a string value contains unicode alphanumeric characters only +This validates that a string value contains unicode alphanumeric characters only. Usage: alphanumunicode @@ -803,7 +803,7 @@ This validates that a string value contains a valid hexadecimal. # Hexcolor String This validates that a string value contains a valid hex color including -hashtag (#) +hashtag (#). Usage: hexcolor @@ -821,52 +821,52 @@ This validates that a string value contains only uppercase characters. An empty # RGB String -This validates that a string value contains a valid rgb color +This validates that a string value contains a valid RGB color. Usage: rgb # RGBA String -This validates that a string value contains a valid rgba color +This validates that a string value contains a valid RGBA color. Usage: rgba # HSL String -This validates that a string value contains a valid hsl color +This validates that a string value contains a valid HSL color. Usage: hsl # HSLA String -This validates that a string value contains a valid hsla color +This validates that a string value contains a valid HSLA color Usage: hsla # E.164 Phone Number String This validates that a string value contains a valid E.164 Phone number -https://en.wikipedia.org/wiki/E.164 (ex. +1123456789) +https://en.wikipedia.org/wiki/E.164 (ex. +1123456789). Usage: e164 # E-mail String This validates that a string value contains a valid email -This may not conform to all possibilities of any rfc standard, but neither +This may not conform to all possibilities of any RFC standard, but neither does any email provider accept all possibilities. Usage: email # JSON String -This validates that a string value is valid JSON +This validates that a string value is valid JSON. Usage: json # JWT String -This validates that a string value is a valid JWT +This validates that a string value is a valid JWT. Usage: jwt @@ -882,7 +882,7 @@ This is done using os.Stat, which is a platform independent function. This validates that a string value contains a valid file path and that the file exists on the machine and is an image. -This is done using os.Stat and github.com/gabriel-vasile/mimetype +This is done using os.Stat and github.com/gabriel-vasile/mimetype. Usage: image @@ -898,27 +898,27 @@ This is done using os.Stat, which is a platform independent function. This validates that a string value contains a valid url This will accept any url the golang request uri accepts but must contain -a schema for example http:// or rtmp:// +a schema for example http:// or rtmp://. Usage: url # URI String -This validates that a string value contains a valid uri -This will accept any uri the golang request uri accepts +This validates that a string value contains a valid URI. +This will accept any uri the Golang request URI accepts. Usage: uri # Urn RFC 2141 String -This validataes that a string value contains a valid URN +This validates that a string value contains a valid URN according to the RFC 2141 spec. Usage: urn_rfc2141 # Base32 String -This validates that a string value contains a valid bas324 value. +This validates that a string value contains a valid base32 value. Although an empty string is valid base32 this will report an empty string as an error, if you wish to accept an empty string as valid you can use this with the omitempty tag. @@ -966,7 +966,7 @@ Bitcoin Bech32 Address (segwit) This validates that a string value contains a valid bitcoin Bech32 address as defined by bip-0173 (https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) -Special thanks to Pieter Wuille for providng reference implementations. +Special thanks to Pieter Wuille for providing reference implementations. Usage: btc_addr_bech32 @@ -1017,25 +1017,25 @@ This validates that a string value does not contain the supplied rune value. # Starts With -This validates that a string value starts with the supplied string value +This validates that a string value starts with the supplied string value. Usage: startswith=hello # Ends With -This validates that a string value ends with the supplied string value +This validates that a string value ends with the supplied string value. Usage: endswith=goodbye # Does Not Start With -This validates that a string value does not start with the supplied string value +This validates that a string value does not start with the supplied string value. Usage: startsnotwith=hello # Does Not End With -This validates that a string value does not end with the supplied string value +This validates that a string value does not end with the supplied string value. Usage: endsnotwith=goodbye @@ -1241,13 +1241,13 @@ Note: See Go's ParseMAC for accepted formats and types: # Hostname RFC 952 -This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952 +This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952. Usage: hostname # Hostname RFC 1123 -This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123 +This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123. Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias. @@ -1260,21 +1260,21 @@ This validates that a string value contains a valid FQDN. # HTML Tags This validates that a string value appears to be an HTML element tag -including those described at https://developer.mozilla.org/en-US/docs/Web/HTML/Element +including those described at https://developer.mozilla.org/en-US/docs/Web/HTML/Element. Usage: html # HTML Encoded This validates that a string value is a proper character reference in decimal -or hexadecimal format +or hexadecimal format. Usage: html_encoded # URL Encoded This validates that a string value is percent-encoded (URL encoded) according -to https://tools.ietf.org/html/rfc3986#section-2.1 +to https://tools.ietf.org/html/rfc3986#section-2.1. Usage: url_encoded @@ -1306,49 +1306,49 @@ can be used to valiate fields typically passed to sockets and connections. # Datetime This validates that a string value is a valid datetime based on the supplied datetime format. -Supplied format must match the official Go time format layout as documented in https://golang.org/pkg/time/ +Supplied format must match the official Go time format layout as documented in https://golang.org/pkg/time/. Usage: datetime=2006-01-02 # Iso3166-1 alpha-2 This validates that a string value is a valid country code based on iso3166-1 alpha-2 standard. -see: https://www.iso.org/iso-3166-country-codes.html +see: https://www.iso.org/iso-3166-country-codes.html. Usage: iso3166_1_alpha2 # Iso3166-1 alpha-3 This validates that a string value is a valid country code based on iso3166-1 alpha-3 standard. -see: https://www.iso.org/iso-3166-country-codes.html +see: https://www.iso.org/iso-3166-country-codes.html. Usage: iso3166_1_alpha3 # Iso3166-1 alpha-numeric This validates that a string value is a valid country code based on iso3166-1 alpha-numeric standard. -see: https://www.iso.org/iso-3166-country-codes.html +see: https://www.iso.org/iso-3166-country-codes.html. Usage: iso3166_1_alpha3 # BCP 47 Language Tag This validates that a string value is a valid BCP 47 language tag, as parsed by language.Parse. -More information on https://pkg.go.dev/golang.org/x/text/language +More information on https://pkg.go.dev/golang.org/x/text/language. Usage: bcp47_language_tag BIC (SWIFT code) This validates that a string value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362. -More information on https://www.iso.org/standard/60390.html +More information on https://www.iso.org/standard/60390.html. Usage: bic # RFC 1035 label This validates that a string value is a valid dns RFC 1035 label, defined in RFC 1035. -More information on https://datatracker.ietf.org/doc/html/rfc1035 +More information on https://datatracker.ietf.org/doc/html/rfc1035. Usage: dns_rfc1035_label @@ -1356,21 +1356,21 @@ More information on https://datatracker.ietf.org/doc/html/rfc1035 This validates that a string value is a valid time zone based on the time zone database present on the system. Although empty value and Local value are allowed by time.LoadLocation golang function, they are not allowed by this validator. -More information on https://golang.org/pkg/time/#LoadLocation +More information on https://golang.org/pkg/time/#LoadLocation. Usage: timezone # Semantic Version This validates that a string value is a valid semver version, defined in Semantic Versioning 2.0.0. -More information on https://semver.org/ +More information on https://semver.org/. Usage: semver # CVE Identifier This validates that a string value is a valid cve id, defined in cve mitre. -More information on https://cve.mitre.org/ +More information on https://cve.mitre.org/. Usage: cve From 14dc59551c424e2046da88f29db79ce88519fec7 Mon Sep 17 00:00:00 2001 From: James Atkin Date: Wed, 26 Jun 2024 16:01:17 +0100 Subject: [PATCH 3/4] read base32 using go encoding --- baked_in.go | 12 +++++++++++- regexes.go | 2 -- validator_test.go | 1 - 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/baked_in.go b/baked_in.go index b6fbaafad..94e4a6d4c 100644 --- a/baked_in.go +++ b/baked_in.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/sha256" + "encoding/base32" "encoding/hex" "encoding/json" "fmt" @@ -1408,7 +1409,16 @@ func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool { // isBase32 is the validation function for validating if the current field's value is a valid base 32. func isBase32(fl FieldLevel) bool { - return base32Regex().MatchString(fl.Field().String()) + if fl.Field().String() == "" { + return false + } + + _, err := base32.StdEncoding.DecodeString(fl.Field().String()) + if err != nil { + return false + } + + return true } // isBase64 is the validation function for validating if the current field's value is a valid base 64. diff --git a/regexes.go b/regexes.go index 7e1dd5a03..0040aa77e 100644 --- a/regexes.go +++ b/regexes.go @@ -20,7 +20,6 @@ const ( hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" e164RegexString = "^\\+[1-9]?[0-9]{7,14}$" - base32RegexString = "^(?:[A-Z2-7]{8})*(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}=|[A-Z2-7]{8})$" base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" base64URLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2}==|[A-Za-z0-9-_]{3}=|[A-Za-z0-9-_]{4})$" base64RawURLRegexString = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$" @@ -105,7 +104,6 @@ var ( hslaRegex = lazyRegexCompile(hslaRegexString) e164Regex = lazyRegexCompile(e164RegexString) emailRegex = lazyRegexCompile(emailRegexString) - base32Regex = lazyRegexCompile(base32RegexString) base64Regex = lazyRegexCompile(base64RegexString) base64URLRegex = lazyRegexCompile(base64URLRegexString) base64RawURLRegex = lazyRegexCompile(base64RawURLRegexString) diff --git a/validator_test.go b/validator_test.go index 0f96b7775..6ed3790de 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5718,7 +5718,6 @@ func TestBase64Validation(t *testing.T) { validate := New() s := "dW5pY29ybg==" - errs := validate.Var(s, "base64") Equal(t, errs, nil) From c17835f6d05e54d845e3c0803a7c74be18a2ca41 Mon Sep 17 00:00:00 2001 From: James Atkin Date: Wed, 26 Jun 2024 16:06:02 +0100 Subject: [PATCH 4/4] fix linting --- baked_in.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/baked_in.go b/baked_in.go index 94e4a6d4c..704996ab7 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1414,11 +1414,8 @@ func isBase32(fl FieldLevel) bool { } _, err := base32.StdEncoding.DecodeString(fl.Field().String()) - if err != nil { - return false - } - return true + return err == nil } // isBase64 is the validation function for validating if the current field's value is a valid base 64.