Skip to content

Commit

Permalink
use a single validator library in rekor-cli (#1818)
Browse files Browse the repository at this point in the history
* use a single validator library in rekor-cli

Signed-off-by: Bob Callaway <bcallaway@google.com>

* fix lint issues

Signed-off-by: Bob Callaway <bcallaway@google.com>

---------

Signed-off-by: Bob Callaway <bcallaway@google.com>
  • Loading branch information
bobcallaway authored Nov 8, 2023
1 parent b681a14 commit c3ffda6
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 92 deletions.
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"`
}{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

0 comments on commit c3ffda6

Please sign in to comment.