diff --git a/baked_in.go b/baked_in.go index c968ad4a..1b75dfcc 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1872,6 +1872,15 @@ func requiredIf(fl FieldLevel) bool { if len(params)%2 != 0 { panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName())) } + + seen := make(map[string]struct{}) + for i := 0; i < len(params); i += 2 { + if _, ok := seen[params[i]]; ok { + panic(fmt.Sprintf("Duplicate param %s for required_if %s", params[i], fl.FieldName())) + } + seen[params[i]] = struct{}{} + } + for i := 0; i < len(params); i += 2 { if !requireCheckFieldValue(fl, params[i], params[i+1], false) { return true diff --git a/doc.go b/doc.go index 23cce991..2998c616 100644 --- a/doc.go +++ b/doc.go @@ -264,6 +264,7 @@ The field under validation must be present and not empty only if all the other specified fields are equal to the value following the specified field. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value. +Using the same field name multiple times in the parameters will result in a panic at runtime. Usage: required_if diff --git a/validator_test.go b/validator_test.go index 2fcb3ffc..95fcdf73 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11099,6 +11099,49 @@ func TestRequiredIf(t *testing.T) { _ = validate.Struct(test3) } +func TestRequiredIfDuplicateParams(t *testing.T) { + validate := New() + + PanicMatches(t, func() { + type TestStruct struct { + Field1 string `validate:"required_if=Field2 value1 Field2 value2"` + Field2 string + } + test := TestStruct{ + Field1: "", + Field2: "value1", + } + _ = validate.Struct(test) + }, "Duplicate param Field2 for required_if Field1") + + PanicMatches(t, func() { + type TestStruct struct { + Field1 string `validate:"required_if=Field2 val1 Field3 val2 Field2 val3"` + Field2 string + Field3 string + } + test := TestStruct{ + Field1: "", + Field2: "val1", + Field3: "val2", + } + _ = validate.Struct(test) + }, "Duplicate param Field2 for required_if Field1") + + type TestStruct struct { + Field1 string `validate:"required_if=Field2 val1 Field3 val2"` + Field2 string + Field3 string + } + test := TestStruct{ + Field1: "", + Field2: "val1", + Field3: "val2", + } + errs := validate.Struct(test) + NotEqual(t, errs, nil) +} + func TestRequiredUnless(t *testing.T) { type Inner struct { Field *string @@ -11169,19 +11212,40 @@ func TestRequiredUnless(t *testing.T) { AssertError(t, errs, "Field10", "Field10", "Field10", "Field10", "required_unless") AssertError(t, errs, "Field11", "Field11", "Field11", "Field11", "required_unless") - defer func() { - if r := recover(); r == nil { - t.Errorf("test3 should have panicked!") + PanicMatches(t, func() { + test3 := struct { + Inner *Inner + Field1 string `validate:"required_unless=Inner.Field" json:"field_1"` + }{ + Inner: &Inner{Field: &fieldVal}, } - }() + _ = validate.Struct(test3) + }, "Bad param number for required_unless Field1") - test3 := struct { - Inner *Inner - Field1 string `validate:"required_unless=Inner.Field" json:"field_1"` - }{ - Inner: &Inner{Field: &fieldVal}, + type DuplicateStruct struct { + Field1 string `validate:"required_unless=Field2 value1 Field2 value2"` + Field2 string } - _ = validate.Struct(test3) + test4 := DuplicateStruct{ + Field1: "", + Field2: "value1", + } + errs = validate.Struct(test4) + Equal(t, errs, nil) + + test5 := DuplicateStruct{ + Field1: "", + Field2: "value2", + } + errs = validate.Struct(test5) + Equal(t, errs, nil) + + test6 := DuplicateStruct{ + Field1: "", + Field2: "value3", + } + errs = validate.Struct(test6) + NotEqual(t, errs, nil) } func TestSkipUnless(t *testing.T) {