diff --git a/openapi3/schema_formats.go b/openapi3/schema_formats.go index 1eb41509e..095bb2228 100644 --- a/openapi3/schema_formats.go +++ b/openapi3/schema_formats.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "regexp" + "strings" ) const ( @@ -37,24 +38,23 @@ func DefineStringFormatCallback(name string, callback FormatCallback) { SchemaStringFormats[name] = Format{callback: callback} } -func validateIP(ip string) (*net.IP, error) { +func validateIP(ip string) error { parsed := net.ParseIP(ip) if parsed == nil { - return nil, &SchemaError{ + return &SchemaError{ Value: ip, Reason: "Not an IP address", } } - return &parsed, nil + return nil } func validateIPv4(ip string) error { - parsed, err := validateIP(ip) - if err != nil { + if err := validateIP(ip); err != nil { return err } - if parsed.To4() == nil { + if !(strings.Count(ip, ":") < 2) { return &SchemaError{ Value: ip, Reason: "Not an IPv4 address (it's IPv6)", @@ -62,13 +62,13 @@ func validateIPv4(ip string) error { } return nil } + func validateIPv6(ip string) error { - parsed, err := validateIP(ip) - if err != nil { + if err := validateIP(ip); err != nil { return err } - if parsed.To4() != nil { + if !(strings.Count(ip, ":") >= 2) { return &SchemaError{ Value: ip, Reason: "Not an IPv6 address (it's IPv4)", diff --git a/openapi3/schema_formats_test.go b/openapi3/schema_formats_test.go new file mode 100644 index 000000000..14733c8a1 --- /dev/null +++ b/openapi3/schema_formats_test.go @@ -0,0 +1,57 @@ +package openapi3 + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIssue430(t *testing.T) { + schema := NewOneOfSchema( + NewStringSchema().WithFormat("ipv4"), + NewStringSchema().WithFormat("ipv6"), + ) + + err := schema.Validate(context.Background()) + require.NoError(t, err) + + data := map[string]bool{ + "127.0.1.1": true, + + // https://stackoverflow.com/a/48519490/1418165 + + // v4 + "192.168.0.1": true, + // "192.168.0.1:80" doesn't parse per net.ParseIP() + + // v6 + "::FFFF:C0A8:1": false, + "::FFFF:C0A8:0001": false, + "0000:0000:0000:0000:0000:FFFF:C0A8:1": false, + // "::FFFF:C0A8:1%1" doesn't parse per net.ParseIP() + "::FFFF:192.168.0.1": false, + // "[::FFFF:C0A8:1]:80" doesn't parse per net.ParseIP() + // "[::FFFF:C0A8:1%1]:80" doesn't parse per net.ParseIP() + } + + for datum := range data { + err = schema.VisitJSON(datum) + require.Error(t, err, ErrOneOfConflict.Error()) + } + + DefineIPv4Format() + DefineIPv6Format() + + for datum, isV4 := range data { + err = schema.VisitJSON(datum) + require.NoError(t, err) + if isV4 { + require.Nil(t, validateIPv4(datum), "%q should be IPv4", datum) + require.NotNil(t, validateIPv6(datum), "%q should not be IPv6", datum) + } else { + require.NotNil(t, validateIPv4(datum), "%q should not be IPv4", datum) + require.Nil(t, validateIPv6(datum), "%q should be IPv6", datum) + } + } +}