From c037639bc0cecc421720eb804a48f21ceb1805f7 Mon Sep 17 00:00:00 2001 From: Peter Lyons Date: Thu, 25 Sep 2025 22:40:51 -0600 Subject: [PATCH] fix: rule `struct-tag` missing go-validator tags (#1526) (#1533) --- rule/struct_tag.go | 68 ++++++++++++++++++++++++++++++++++++++---- testdata/struct_tag.go | 24 ++++++++------- 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/rule/struct_tag.go b/rule/struct_tag.go index 02110bf..0830a25 100644 --- a/rule/struct_tag.go +++ b/rule/struct_tag.go @@ -844,10 +844,14 @@ var validateSingleOptions = map[string]struct{}{ "cidr": {}, "cidrv4": {}, "cidrv6": {}, + "contains": {}, + "containsany": {}, + "containsrune": {}, "credit_card": {}, "cron": {}, "cve": {}, "datauri": {}, + "datetime": {}, "dir": {}, "dirpath": {}, "dive": {}, @@ -855,11 +859,34 @@ var validateSingleOptions = map[string]struct{}{ "e164": {}, "ein": {}, "email": {}, + "endsnotwith": {}, + "endswith": {}, + "eq": {}, + "eq_ignore_case": {}, + "eqcsfield": {}, + "eqfield": {}, "eth_addr": {}, "eth_addr_checksum": {}, + "excluded_if": {}, + "excluded_unless": {}, + "excluded_with": {}, + "excluded_with_all": {}, + "excluded_without": {}, + "excluded_without_all": {}, + "excludes": {}, + "excludesall": {}, + "excludesrune": {}, + "fieldcontains": {}, + "fieldexcludes": {}, "file": {}, "filepath": {}, "fqdn": {}, + "gt": {}, + "gtcsfield": {}, + "gte": {}, + "gtecsfield": {}, + "gtefield": {}, + "gtfield": {}, "hexadecimal": {}, "hexcolor": {}, "hostname": {}, @@ -872,21 +899,21 @@ var validateSingleOptions = map[string]struct{}{ "http_url": {}, "image": {}, "ip": {}, - "ip_addr": {}, "ip4_addr": {}, "ip6_addr": {}, + "ip_addr": {}, "ipv4": {}, "ipv6": {}, "isbn": {}, "isbn10": {}, "isbn13": {}, "isdefault": {}, - "iso3166_1_alpha_numeric": {}, - "iso3166_1_alpha_numeric_eu": {}, "iso3166_1_alpha2": {}, "iso3166_1_alpha2_eu": {}, "iso3166_1_alpha3": {}, "iso3166_1_alpha3_eu": {}, + "iso3166_1_alpha_numeric": {}, + "iso3166_1_alpha_numeric_eu": {}, "iso3166_2": {}, "iso4217": {}, "iso4217_numeric": {}, @@ -894,23 +921,46 @@ var validateSingleOptions = map[string]struct{}{ "json": {}, "jwt": {}, "latitude": {}, + "len": {}, "longitude": {}, "lowercase": {}, + "lt": {}, + "ltcsfield": {}, + "lte": {}, + "ltecsfield": {}, + "ltefield": {}, + "ltfield": {}, "luhn_checksum": {}, "mac": {}, + "max": {}, "md4": {}, "md5": {}, + "min": {}, "mongodb": {}, "mongodb_connection_string": {}, "multibyte": {}, + "ne": {}, + "ne_ignore_case": {}, + "necsfield": {}, + "nefield": {}, "number": {}, "numeric": {}, "omitempty": {}, + "omitnil": {}, + "omitzero": {}, + "oneof": {}, + "oneofci": {}, "port": {}, "postcode_iso3166_alpha2": {}, "postcode_iso3166_alpha2_field": {}, "printascii": {}, "required": {}, + "required_if": {}, + "required_unless": {}, + "required_with": {}, + "required_with_all": {}, + "required_without": {}, + "required_without_all": {}, "rgb": {}, "rgba": {}, "ripemd128": {}, @@ -919,18 +969,23 @@ var validateSingleOptions = map[string]struct{}{ "sha256": {}, "sha384": {}, "sha512": {}, + "skip_unless": {}, + "spicedb": {}, "ssn": {}, - "tcp_addr": {}, + "startsnotwith": {}, + "startswith": {}, "tcp4_addr": {}, "tcp6_addr": {}, + "tcp_addr": {}, "tiger128": {}, "tiger160": {}, "tiger192": {}, "timezone": {}, - "udp_addr": {}, "udp4_addr": {}, "udp6_addr": {}, + "udp_addr": {}, "ulid": {}, + "unique": {}, "unix_addr": {}, "uppercase": {}, "uri": {}, @@ -938,13 +993,14 @@ var validateSingleOptions = map[string]struct{}{ "url_encoded": {}, "urn_rfc2141": {}, "uuid": {}, - "uuid_rfc4122": {}, "uuid3": {}, "uuid3_rfc4122": {}, "uuid4": {}, "uuid4_rfc4122": {}, "uuid5": {}, "uuid5_rfc4122": {}, + "uuid_rfc4122": {}, + "validateFn": {}, } // These are options that are used in expressions of the form: diff --git a/testdata/struct_tag.go b/testdata/struct_tag.go index f6b70f8..3561db0 100644 --- a/testdata/struct_tag.go +++ b/testdata/struct_tag.go @@ -156,17 +156,19 @@ type MapStruct struct { } type ValidateUser struct { - Id string `validate:"omitempty,min=3,max=32"` - Username string `validate:"required,min=3,max=32"` - Email string `validate:"required,email"` - Password string `validate:"required,min=8,max=32"` - Biography string `validate:"min=0,max=1000"` - DisplayName string `validate:"displayName,min=3,max=32"` // MATCH /unknown option "displayName" in validate tag/ - Complex string `validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,required"` - BadComplex string `validate:"gt=0,keys,eq=1|eq=2,endkeys,required"` // MATCH /option "keys" must follow a "dive" option in validate tag/ - BadComplex2 string `validate:"gt=0,dive,eq=1|eq=2,endkeys,required"` // MATCH /option "endkeys" without a previous "keys" option in validate tag/ - BadComplex3 string `validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,endkeys,required"` // MATCH /option "endkeys" without a previous "keys" option in validate tag/ - Issue1367 string `validate:"required_without=ExternalValue,excluded_with=ExternalValue"` + Id string `validate:"omitempty,min=3,max=32"` + Username string `validate:"required,min=3,max=32"` + Email string `validate:"required,email"` + Password string `validate:"required,min=8,max=32"` + Biography string `validate:"min=0,max=1000"` + DisplayName string `validate:"displayName,min=3,max=32"` // MATCH /unknown option "displayName" in validate tag/ + Complex string `validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,required"` + BadComplex string `validate:"gt=0,keys,eq=1|eq=2,endkeys,required"` // MATCH /option "keys" must follow a "dive" option in validate tag/ + BadComplex2 string `validate:"gt=0,dive,eq=1|eq=2,endkeys,required"` // MATCH /option "endkeys" without a previous "keys" option in validate tag/ + BadComplex3 string `validate:"gt=0,dive,keys,eq=1|eq=2,endkeys,endkeys,required"` // MATCH /option "endkeys" without a previous "keys" option in validate tag/ + Issue1367 string `validate:"required_without=ExternalValue,excluded_with=ExternalValue"` + Handle *string `validate:"omitnil,omitempty"` + Score int `validate:"omitzero"` } type TomlUser struct {