diff --git a/cmd/csaf_provider/config.go b/cmd/csaf_provider/config.go index 1221397..a7f3e5d 100644 --- a/cmd/csaf_provider/config.go +++ b/cmd/csaf_provider/config.go @@ -27,6 +27,7 @@ type config struct { OpenPGPURL string `toml:"openpgp_url"` Domain string `toml:"domain"` NoPassphrase bool `toml:"no_passphrase"` + NoValidation bool `toml:"no_validation"` DynamicProviderMetaData bool `toml:"dynamic_provider_metadata"` Publisher *csaf.Publisher `toml:"publisher"` } diff --git a/cmd/csaf_provider/controller.go b/cmd/csaf_provider/controller.go index fea9fc1..91e4d08 100644 --- a/cmd/csaf_provider/controller.go +++ b/cmd/csaf_provider/controller.go @@ -190,23 +190,26 @@ func (c *controller) upload(rw http.ResponseWriter, r *http.Request) { return } - validationErrors, err := csaf.Validate(data) - if err != nil { - c.failed(rw, "upload.html", err) - return - } - - if len(validationErrors) > 0 { - c.multiFailed(rw, "upload.html", validationErrors) - return - } - var content interface{} if err := json.Unmarshal(data, &content); err != nil { c.failed(rw, "upload.html", err) return } + // Validate againt JSON schema. + if !c.cfg.NoValidation { + validationErrors, err := csaf.ValidateCSAF(content) + if err != nil { + c.failed(rw, "upload.html", err) + return + } + + if len(validationErrors) > 0 { + c.multiFailed(rw, "upload.html", validationErrors) + return + } + } + ex, err := newExtraction(content) if err != nil { c.failed(rw, "upload.html", err) diff --git a/cmd/csaf_provider/tmpl/upload.html b/cmd/csaf_provider/tmpl/upload.html index 8d74c6d..4520b49 100644 --- a/cmd/csaf_provider/tmpl/upload.html +++ b/cmd/csaf_provider/tmpl/upload.html @@ -9,7 +9,7 @@

CSAF-Provider - CSAF uploaded

{{ if .Error }} {{ if eq (len .Error) 1 }} - Error: {{ .Error }}. + Error: {{ index .Error 0 }}. {{ else }}

Errors: diff --git a/csaf/validation.go b/csaf/validation.go index 679baed..d2efac6 100644 --- a/csaf/validation.go +++ b/csaf/validation.go @@ -1,33 +1,50 @@ package csaf import ( + "context" _ "embed" + "encoding/json" + "sort" + "strings" - "github.com/xeipuuv/gojsonschema" + "github.com/qri-io/jsonschema" ) //go:embed schema/csaf_json_schema.json -var schema string +var schema []byte -// Validate validates the document data against the JSON schema +// ValidateCSAF validates the document data against the JSON schema // of CSAF. -func Validate(data []byte) ([]string, error) { - schemaLoader := gojsonschema.NewStringLoader(schema) - documentLoader := gojsonschema.NewStringLoader(string(data)) +func ValidateCSAF(doc interface{}) ([]string, error) { - result, err := gojsonschema.Validate(schemaLoader, documentLoader) - if err != nil { + ctx := context.Background() + + rs := &jsonschema.Schema{} + if err := json.Unmarshal(schema, rs); err != nil { return nil, err } - if result.Valid() { - return nil, nil - } + vs := rs.Validate(ctx, doc) + errs := *vs.Errs - errors := result.Errors() - res := make([]string, len(errors)) - for i, e := range errors { - res[i] = e.String() + sort.Slice(errs, func(i, j int) bool { + pi := errs[i].PropertyPath + pj := errs[j].PropertyPath + if strings.HasPrefix(pj, pi) { + return true + } + if strings.HasPrefix(pi, pj) { + return false + } + if pi != pj { + return pi < pj + } + return errs[i].Message < errs[j].Message + }) + + res := make([]string, len(errs)) + for i, e := range errs { + res[i] = e.PropertyPath + ": " + e.Message } return res, nil diff --git a/go.mod b/go.mod index 0ea2c6c..db7c792 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/PaesslerAG/gval v1.1.2 github.com/PaesslerAG/jsonpath v0.1.1 github.com/ProtonMail/gopenpgp/v2 v2.3.0 + github.com/qri-io/jsonschema v0.2.1 ) require ( @@ -14,10 +15,8 @@ require ( github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/qri-io/jsonpointer v0.1.1 // indirect github.com/sirupsen/logrus v1.4.2 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect golang.org/x/text v0.3.3 // indirect diff --git a/go.sum b/go.sum index 6366fe0..2ef2e30 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA= +github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= +github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0= +github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -30,12 +36,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=