Skip to content

Commit

Permalink
MongoDB validator improved (#1196)
Browse files Browse the repository at this point in the history
## Enhances

Added MongoDB connection string validation


**Make sure that you've checked the boxes below before you submit PR:**
- [x] Tests exist or have been written that cover this particular
change.

@go-playground/validator-maintainers
  • Loading branch information
pixel365 authored Jun 1, 2024
1 parent e20b948 commit ab370b6
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| mongodb | MongoDB ObjectID |
| mongodb_connection_string | MongoDB Connection String |
| cron | Cron |
| spicedb | SpiceDb ObjectID/Permission/Type |
| datetime | Datetime |
Expand Down
15 changes: 11 additions & 4 deletions baked_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ var (
"credit_card": isCreditCard,
"cve": isCveFormat,
"luhn_checksum": hasLuhnChecksum,
"mongodb": isMongoDB,
"mongodb": isMongoDBObjectId,
"mongodb_connection_string": isMongoDBConnectionString,
"cron": isCron,
"spicedb": isSpiceDB,
}
Expand Down Expand Up @@ -2926,10 +2927,16 @@ func digitsHaveLuhnChecksum(digits []string) bool {
return (sum % 10) == 0
}

// isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID
func isMongoDB(fl FieldLevel) bool {
// isMongoDBObjectId is the validation function for validating if the current field's value is valid MongoDB ObjectID
func isMongoDBObjectId(fl FieldLevel) bool {
val := fl.Field().String()
return mongodbRegex.MatchString(val)
return mongodbIdRegex.MatchString(val)
}

// isMongoDBConnectionString is the validation function for validating if the current field's value is valid MongoDB Connection String
func isMongoDBConnectionString(fl FieldLevel) bool {
val := fl.Field().String()
return mongodbConnectionRegex.MatchString(val)
}

// isSpiceDB is the validation function for validating if the current field's value is valid for use with Authzed SpiceDB in the indicated way
Expand Down
12 changes: 10 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1386,11 +1386,19 @@ This validates that a string value contains a valid credit card number using Luh
This validates that a string or (u)int value contains a valid checksum using the Luhn algorithm.
# MongoDb ObjectID
# MongoDB
This validates that a string is a valid 24 character hexadecimal string.
This validates that a string is a valid 24 character hexadecimal string or valid connection string.
Usage: mongodb
mongodb_connection_string
Example:
type Test struct {
ObjectIdField string `validate:"mongodb"`
ConnectionStringField string `validate:"mongodb_connection_string"`
}
# Cron
Expand Down
6 changes: 4 additions & 2 deletions regexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ const (
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
mongodbRegexString = "^[a-f\\d]{24}$"
mongodbIdRegexString = "^[a-f\\d]{24}$"
mongodbConnStringRegexString = "^mongodb(\\+srv)?:\\/\\/(([a-zA-Z\\d]+):([a-zA-Z\\d$:\\/?#\\[\\]@]+)@)?(([a-z\\d.-]+)(:[\\d]+)?)((,(([a-z\\d.-]+)(:(\\d+))?))*)?(\\/[a-zA-Z-_]{1,64})?(\\?(([a-zA-Z]+)=([a-zA-Z\\d]+))(&(([a-zA-Z\\d]+)=([a-zA-Z\\d]+))?)*)?$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
Expand Down Expand Up @@ -139,7 +140,8 @@ var (
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
cveRegex = regexp.MustCompile(cveRegexString)
mongodbRegex = regexp.MustCompile(mongodbRegexString)
mongodbIdRegex = regexp.MustCompile(mongodbIdRegexString)
mongodbConnectionRegex = regexp.MustCompile(mongodbConnStringRegexString)
cronRegex = regexp.MustCompile(cronRegexString)
spicedbIDRegex = regexp.MustCompile(spicedbIDRegexString)
spicedbPermissionRegex = regexp.MustCompile(spicedbPermissionRegexString)
Expand Down
52 changes: 52 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13386,6 +13386,58 @@ func TestMongoDBObjectIDFormatValidation(t *testing.T) {
}
}
}
func TestMongoDBConnectionStringFormatValidation(t *testing.T) {
tests := []struct {
value string `validate:"mongodb_connection_string"`
tag string
expected bool
}{
{"mongodb://username:password@server.example.com:20017/database?replicaSet=test&connectTimeoutMS=300000&ssl=true", "mongodb_connection_string", true},
{"mongodb+srv://username:password@server.example.com:20017/database?replicaSet=test&connectTimeoutMS=300000&ssl=true", "mongodb_connection_string", true},
{"mongodb+srv://username:password@server.example.com:20017,server.example.com,server.example.com:20017/database?replicaSet=test&connectTimeoutMS=300000&ssl=true", "mongodb_connection_string", true},
{"mongodb+srv://username:password@server.example.com:20017,server.example.com,server.example.com:20017?replicaSet=test&connectTimeoutMS=300000&ssl=true", "mongodb_connection_string", true},
{"mongodb://username:password@server.example.com:20017,server.example.com,server.example.com:20017/database?replicaSet=test&connectTimeoutMS=300000&ssl=true", "mongodb_connection_string", true},
{"mongodb://localhost", "mongodb_connection_string", true},
{"mongodb://localhost:27017", "mongodb_connection_string", true},
{"localhost", "mongodb_connection_string", false},
{"mongodb://", "mongodb_connection_string", false},
{"mongodb+srv://", "mongodb_connection_string", false},
{"mongodbsrv://localhost", "mongodb_connection_string", false},
{"mongodb+srv://localhost", "mongodb_connection_string", true},
{"mongodb+srv://localhost:27017", "mongodb_connection_string", true},
{"mongodb+srv://localhost?replicaSet=test", "mongodb_connection_string", true},
{"mongodb+srv://localhost:27017?replicaSet=test", "mongodb_connection_string", true},
{"mongodb+srv://localhost:27017?", "mongodb_connection_string", false},
{"mongodb+srv://localhost:27017?replicaSet", "mongodb_connection_string", false},
{"mongodb+srv://localhost/database", "mongodb_connection_string", true},
{"mongodb+srv://localhost:27017/database", "mongodb_connection_string", true},
{"mongodb+srv://username@localhost", "mongodb_connection_string", false},
{"mongodb+srv://username:password@localhost", "mongodb_connection_string", true},
{"mongodb+srv://username:password@localhost:27017", "mongodb_connection_string", true},
{"mongodb+srv://username:password@localhost:27017,192.0.0.7,192.0.0.9:27018,server.example.com", "mongodb_connection_string", true},
}

validate := New()

for i, test := range tests {
errs := validate.Var(test.value, test.tag)

if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d mongodb_connection_string failed Error: %s", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d mongodb_connection_string failed Error: %s", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "mongodb_connection_string" {
t.Fatalf("Index: %d mongodb_connection_string failed Error: %s", i, errs)
}
}
}
}
}

func TestSpiceDBValueFormatValidation(t *testing.T) {
tests := []struct {
Expand Down

0 comments on commit ab370b6

Please sign in to comment.