go-playground / validator

:100:Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
MIT License
16.05k stars 1.29k forks source link

SSN regex doesn't match the rest of the validation and is loose with formatting rules #1272

Open nderscore opened 1 month ago

nderscore commented 1 month ago

Package version eg. v9, v10:

v10

Issue, Question or Enhancement:

The ssn validation is composed of two checks:

  1. A length check on the input string, checking for 11 characters.

https://github.com/go-playground/validator/blob/e20b94842ab102ba94e73eaf66f5c0466e405882/baked_in.go#L427

  1. A regular expression to check the pattern of that string

https://github.com/go-playground/validator/blob/e20b94842ab102ba94e73eaf66f5c0466e405882/regexes.go#L52

The regular expression includes this optional (0 or 1) character class [ -]? as the separator between each segment of the social security number, meaning it supports each of the following formats:

This presents at least two issues:

  1. The length check means that social security numbers with formatting stripped (123121234) will not pass the validation, despite the regular expression matching it. Since it's pretty common to deal with unformatted values, it might be worth supporting strings with a length of 9 in addition to 11

  2. The regular expression is a bit too loose with formatting. Mixed usage of spaces and hyphens is not something that I would expect to pass the validation. The regular expression should probably enforce consistent separators.

Code sample, to showcase or reproduce:

package main

import (
    "fmt"

    "github.com/go-playground/validator/v10"
)

type Foo struct {
    Ssn string `validate:"ssn"`
}

var validate *validator.Validate

func main() {
    validate = validator.New(validator.WithRequiredStructEnabled())

    cases := []string{
        "123-12-1234",
        "123 12 1234",
        "123-12 1234",
        "123 12-1234",
        "123 121234",
        "12312 1234",
        "12312-1234",
        "123-121234",
        "123121234",
    }

    for _, s := range cases {
        ss := Foo{Ssn: s}
        err := validate.Struct(ss)
        fmt.Printf("%s = +%v\n", s, err)
    }
}