diff --git a/go.mod b/go.mod index a87f23a9..5000ceb8 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/openvex/go-vex v0.2.5 github.com/package-url/packageurl-go v0.1.2 github.com/samber/lo v1.39.0 + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/savioxavier/termlink v1.3.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cobra v1.8.0 diff --git a/go.sum b/go.sum index 0669b8ac..362756f3 100644 --- a/go.sum +++ b/go.sum @@ -988,6 +988,8 @@ github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sassoftware/go-rpmutils v0.3.0 h1:tE4TZ8KcOXay5iIP64P291s6Qxd9MQCYhI7DU+f3gFA= github.com/sassoftware/go-rpmutils v0.3.0/go.mod h1:hM9wdxFsjUFR/tJ6SMsLrJuChcucCa0DsCzE9RMfwMo= github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4= diff --git a/pkg/advisory/export.go b/pkg/advisory/export.go index 4b814289..88eead4e 100644 --- a/pkg/advisory/export.go +++ b/pkg/advisory/export.go @@ -3,6 +3,7 @@ package advisory import ( "bytes" "encoding/csv" + "encoding/json" "fmt" "io" "log" @@ -12,6 +13,9 @@ import ( "strings" "time" + jsonschema "github.com/santhosh-tekuri/jsonschema/v5" + _ "github.com/santhosh-tekuri/jsonschema/v5/httploader" // to be able to download the schema from the URL + "github.com/google/osv-scanner/pkg/models" "github.com/samber/lo" "gopkg.in/yaml.v3" @@ -20,6 +24,8 @@ import ( v2 "github.com/wolfi-dev/wolfictl/pkg/configs/advisory/v2" ) +const OSVSchema = "https://raw.githubusercontent.com/ossf/osv-schema/main/validation/schema.json" + type ExportOptions struct { AdvisoryDocIndices []*configs.Index[v2.Document] Ecosystem models.Ecosystem @@ -220,12 +226,31 @@ func ExportOSV(opts ExportOptions, output string) error { } sort.Strings(keys) + // get the OSV schema to validate + compiler := jsonschema.NewCompiler() + compiler.Draft = jsonschema.Draft2020 + schema, err := compiler.Compile(OSVSchema) + if err != nil { + log.Fatal(err) + } + for _, k := range keys { e, err := osvExport[k].MarshalJSON() if err != nil { log.Fatal(err) } + // to run the validate schema + var result any + err = json.Unmarshal(e, &result) + if err != nil { + log.Fatalf("failed to unmarshall:%v", err) + } + err = schema.Validate(result) + if err != nil { + log.Fatalf("failed to validate OSV JSON Schema for %s: %v", k, err) + } + filepath := path.Join(output, fmt.Sprintf("%s-%s.json", strings.ToUpper(string(opts.Ecosystem)), k)) err = os.WriteFile(filepath, e, 0o644) //nolint: gosec if err != nil {