Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use a single validator library in rekor-cli #1818

Merged
merged 2 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
123 changes: 69 additions & 54 deletions cmd/rekor-cli/app/pflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ package app

import (
"encoding/base64"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
Expand All @@ -28,7 +31,7 @@ import (

"github.com/spf13/pflag"

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

type FlagType string
Expand Down Expand Up @@ -71,31 +74,59 @@ func initializePFlagMap() {
},
operatorFlag: func() pflag.Value {
// this validates a valid operator name
return valueFactory(operatorFlag, validateString("oneof=and or"), "")
operatorFlagValidator := func(val string) error {
o := struct {
Value string `valid:"in(and|or)"`
}{val}
_, err := validator.ValidateStruct(o)
return err
}
return valueFactory(operatorFlag, operatorFlagValidator, "")
},
emailFlag: func() pflag.Value {
// this validates an email address
return valueFactory(emailFlag, validateString("required,email"), "")
emailValidator := func(val string) error {
if !validator.IsEmail(val) {
return fmt.Errorf("'%v' is not a valid email address", val)
}
return nil
}
return valueFactory(emailFlag, emailValidator, "")
},
logIndexFlag: func() pflag.Value {
// this checks for a valid integer >= 0
return valueFactory(logIndexFlag, validateLogIndex, "")
return valueFactory(logIndexFlag, validateUint, "")
},
pkiFormatFlag: func() pflag.Value {
// this ensures a PKI implementation exists for the requested format
return valueFactory(pkiFormatFlag, validateString(fmt.Sprintf("required,oneof=%v", strings.Join(pki.SupportedFormats(), " "))), "pgp")
pkiFormatValidator := func(val string) error {
if !validator.IsIn(val, pki.SupportedFormats()...) {
return fmt.Errorf("'%v' is not a valid pki format", val)
}
return nil
}
return valueFactory(pkiFormatFlag, pkiFormatValidator, "pgp")
},
typeFlag: func() pflag.Value {
// this ensures the type of the log entry matches a type supported in the CLI
return valueFactory(typeFlag, validateTypeFlag, "rekord")
},
fileFlag: func() pflag.Value {
// this validates that the file exists and can be opened by the current uid
return valueFactory(fileFlag, validateString("required,file"), "")
return valueFactory(fileFlag, validateFile, "")
},
urlFlag: func() pflag.Value {
// this validates that the string is a valid http/https URL
return valueFactory(urlFlag, validateString("required,url,startswith=http|startswith=https"), "")
httpHTTPSValidator := func(val string) error {
if !validator.IsURL(val) {
return fmt.Errorf("'%v' is not a valid url", val)
}
if !(strings.HasPrefix(val, "http") || strings.HasPrefix(val, "https")) {
return errors.New("URL must be for http or https scheme")
}
return nil
}
return valueFactory(urlFlag, httpHTTPSValidator, "")
},
fileOrURLFlag: func() pflag.Value {
// applies logic of fileFlag OR urlFlag validators from above
Expand All @@ -111,7 +142,13 @@ func initializePFlagMap() {
},
formatFlag: func() pflag.Value {
// this validates the output format requested
return valueFactory(formatFlag, validateString("required,oneof=json default tle"), "")
formatValidator := func(val string) error {
if !validator.IsIn(val, "json", "default", "tle") {
return fmt.Errorf("'%v' is not a valid output format", val)
}
return nil
}
return valueFactory(formatFlag, formatValidator, "")
},
timeoutFlag: func() pflag.Value {
// this validates the timeout is >= 0
Expand Down Expand Up @@ -257,33 +294,23 @@ func validateID(v string) error {
return fmt.Errorf("ID len error, expected %v (EntryID) or %v (UUID) but got len %v for ID %v", sharding.EntryIDHexStringLen, sharding.UUIDHexStringLen, len(v), v)
}

if err := validateString("required,hexadecimal")(v); err != nil {
if !validator.IsHexadecimal(v) {
return fmt.Errorf("invalid uuid: %v", v)
}

return nil
}

// validateLogIndex ensures that the supplied string is a valid log index (integer >= 0)
func validateLogIndex(v string) error {
i, err := strconv.Atoi(v)
if err != nil {
return err
}
l := struct {
Index int `validate:"gte=0"`
}{i}

return useValidator(logIndexFlag, l)
}

// validateOID ensures that the supplied string is a valid ASN.1 object identifier
func validateOID(v string) error {
o := struct {
Oid []string `validate:"dive,numeric"`
}{strings.Split(v, ".")}
values := strings.Split(v, ".")
for _, value := range values {
if !validator.IsNumeric(value) {
return fmt.Errorf("field '%v' is not a valid number", value)
}
}

return useValidator(oidFlag, o)
return nil
}

// validateTimeout ensures that the supplied string is a valid time.Duration value >= 0
Expand All @@ -292,10 +319,10 @@ func validateTimeout(v string) error {
if err != nil {
return err
}
d := struct {
Duration time.Duration `validate:"min=0"`
}{duration}
return useValidator(timeoutFlag, d)
if duration < 0 {
return errors.New("timeout must be a positive value")
}
return nil
}

// validateBase64 ensures that the supplied string is valid base64 encoded data
Expand All @@ -312,26 +339,6 @@ func validateTypeFlag(v string) error {
return err
}

// validateString returns a function that validates an input string against the specified tag,
// as defined in the format supported by go-playground/validator
func validateString(tag string) validationFunc {
return func(v string) error {
validator := validator.New()
return validator.Var(v, tag)
}
}

// useValidator performs struct level validation on s as defined in the struct's tags using
// the go-playground/validator library
func useValidator(flagType FlagType, s interface{}) error {
validate := validator.New()
if err := validate.Struct(s); err != nil {
return fmt.Errorf("error parsing %v flag: %w", flagType, err)
}

return nil
}

// validateUint ensures that the supplied string is a valid unsigned integer >= 0
func validateUint(v string) error {
i, err := strconv.Atoi(v)
Expand All @@ -341,9 +348,17 @@ func validateUint(v string) error {
if i < 0 {
return fmt.Errorf("invalid unsigned int: %v", v)
}
u := struct {
Uint uint `validate:"gte=0"`
}{uint(i)}
return nil
}

return useValidator(uintFlag, u)
// validateFile ensures that the supplied string is a valid path to a file that exists
func validateFile(v string) error {
fileInfo, err := os.Stat(filepath.Clean(v))
if err != nil {
return err
}
if fileInfo.IsDir() {
return errors.New("path to a directory was provided")
}
return nil
}
44 changes: 24 additions & 20 deletions cmd/rekor-cli/app/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
package app

import (
"errors"
"fmt"
"strings"

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

// validateSHA512Value ensures that the supplied string matches the
Expand All @@ -36,13 +38,14 @@ func validateSHA512Value(v string) error {
hash = split[1]
}

s := struct {
Prefix string `validate:"omitempty,oneof=sha512"`
Hash string `validate:"required,len=128,hexadecimal"`
}{prefix, hash}
if strings.TrimSpace(prefix) != "" && prefix != "sha512" {
return fmt.Errorf("invalid prefix '%v'", prefix)
}

validate := validator.New()
return validate.Struct(s)
if !validator.IsSHA512(strings.ToLower(hash)) {
return errors.New("invalid SHA512 value")
}
return nil
}

// validateSHA256Value ensures that the supplied string matches the following format:
Expand All @@ -60,13 +63,14 @@ func validateSHA256Value(v string) error {
hash = split[1]
}

s := struct {
Prefix string `validate:"omitempty,oneof=sha256"`
Hash string `validate:"required,len=64,hexadecimal"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

qq, i noticed on the other PR, we dropped required from required,email - is this behavior equivalent with this new library?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, the check fails if the criteria isn't met

}{prefix, hash}
if strings.TrimSpace(prefix) != "" && prefix != "sha256" {
return fmt.Errorf("invalid prefix '%v'", prefix)
}

validate := validator.New()
return validate.Struct(s)
if !validator.IsSHA256(strings.ToLower(hash)) {
return errors.New("invalid SHA256 value")
}
return nil
}

func validateSHA1Value(v string) error {
Expand All @@ -81,12 +85,12 @@ func validateSHA1Value(v string) error {
hash = split[1]
}

s := struct {
Prefix string `validate:"omitempty,oneof=sha1"`
Hash string `validate:"required,len=40,hexadecimal"`
}{prefix, hash}

validate := validator.New()
return validate.Struct(s)
if strings.TrimSpace(prefix) != "" && prefix != "sha1" {
return fmt.Errorf("invalid prefix '%v'", prefix)
}

if !validator.IsSHA1(strings.ToLower(hash)) {
return errors.New("invalid SHA1 value")
}
return nil
}
5 changes: 0 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ require (
github.com/go-openapi/strfmt v0.21.7
github.com/go-openapi/swag v0.22.4
github.com/go-openapi/validate v0.22.1
github.com/go-playground/validator/v10 v10.16.0
github.com/google/go-cmp v0.6.0
github.com/google/rpmpack v0.5.0
github.com/google/trillian v1.5.3
Expand Down Expand Up @@ -97,7 +96,6 @@ require (
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand Down Expand Up @@ -147,8 +145,6 @@ require (
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand All @@ -161,7 +157,6 @@ require (
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
Expand Down
13 changes: 0 additions & 13 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,6 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
Expand Down Expand Up @@ -241,14 +239,6 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU=
github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
Expand Down Expand Up @@ -458,8 +448,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf h1:ndns1qx/5dL43g16EQkPV/i8+b3l5bYQwLeoSBe7tS8=
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf/go.mod h1:aGkAgvWY/IUcVFfuly53REpfv5edu25oij+qHRFaraA=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
Expand Down Expand Up @@ -596,7 +584,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
Expand Down