Skip to content

Commit

Permalink
Use github.com/santhosh-tekuri/jsonschema/v5
Browse files Browse the repository at this point in the history
The github.com/qri-io/jsonschema seems to suffer from thread safety
issues, see[1]. This replaces that implementation with
github.com/santhosh-tekuri/jsonschema/v5.

Note that the `uniqueKeys` functionality that has been disabled in enterprise-contract#1534
has not been ported and since it is now dead code has been removed.

[1] https://github.com/enterprise-contract/ec-cli/actions/runs/8777708975/job/24083046629?pr=1544#step:6:5121
  • Loading branch information
zregvart committed Apr 25, 2024
1 parent 8ee5a00 commit 6b12ca9
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 784 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ require (
github.com/open-policy-agent/opa v0.63.0
github.com/package-url/packageurl-go v0.1.2
github.com/qri-io/jsonpointer v0.1.1
github.com/qri-io/jsonschema v0.2.1
github.com/redhat-appstudio/application-api v0.0.0-20231026192857-89515ad2504f
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/secure-systems-lab/go-securesystemslib v0.8.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1088,8 +1088,6 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf h1:014O62
github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
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/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redhat-appstudio/application-api v0.0.0-20231026192857-89515ad2504f h1:PoKf7gCV/g5blkzVlODkqeynmfIACcR7NqWF8eqnuec=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import (
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/qri-io/jsonschema"
app "github.com/redhat-appstudio/application-api/api/v1alpha1"
"github.com/santhosh-tekuri/jsonschema/v5"
"github.com/sigstore/cosign/v2/pkg/cosign"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
log "github.com/sirupsen/logrus"
Expand All @@ -51,7 +51,7 @@ import (
// equivalent to http.DefaultTransport, with a reduced timeout and keep-alive
var imageRefTransport = remote.WithTransport(remote.DefaultTransport)

var attestationSchemas = map[string]jsonschema.Schema{
var attestationSchemas = map[string]*jsonschema.Schema{
"https://slsa.dev/provenance/v0.2": schema.SLSA_Provenance_v0_2,
}

Expand Down Expand Up @@ -252,43 +252,41 @@ func (a ApplicationSnapshotImage) ValidateAttestationSyntax(ctx context.Context)
return errors.New("no attestation data")
}

allErrors := map[string][]jsonschema.KeyError{}
var validationErr error
for _, sp := range a.attestations {
pt := sp.PredicateType()
if schema, ok := attestationSchemas[pt]; ok {
// Found a validator for this predicate type so let's use it
log.Debugf("Attempting to validate an attestation with predicateType %s", pt)
if errs, err := schema.ValidateBytes(ctx, sp.Statement()); err != nil {
// Error while trying to validate

var statement any
if err := json.Unmarshal(sp.Statement(), &statement); err != nil {
return fmt.Errorf("unable to decode attestation data from attestation image: %w", err)
} else {
if len(errs) == 0 {
log.Debugf("Statement schema was validated successfully against the %s schema", pt)
} else {
log.Debugf("Validated the statement against %s schema and found the following errors: %v", pt, errs)
allErrors[pt] = errs
}

if err := schema.Validate(statement); err != nil {
if _, ok = err.(*jsonschema.ValidationError); !ok {
// Error while trying to validate
return fmt.Errorf("unable to validate attestation data from attestation image: %w", err)
}

validationErr = errors.Join(validationErr, err)
} else {
log.Debugf("Statement schema was validated successfully against the %s schema", pt)
}
} else {
log.Debugf("No schema validation found for predicateType %s", pt)
}
}

if len(allErrors) == 0 {
if validationErr == nil {
// TODO another option might be to filter out invalid statement JSONs
// and keep only the valid ones
return nil
}

log.Debug("Failed to validate statements from the attestation image against all known schemas")
msg := ""
for id, errs := range allErrors {
msg += fmt.Sprintf("\nSchema ID: %s", id)
for _, e := range errs {
msg += fmt.Sprintf("\n - %s", e.Error())
}
}
return fmt.Errorf("attestation syntax validation failed: %s", msg)
return fmt.Errorf("attestation syntax validation failed: %s", validationErr.Error())
}

// Attestations returns the value of the attestations field of the ApplicationSnapshotImage struct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func TestSyntaxValidation(t *testing.T) {
attestations: []attestation.Attestation{
invalid,
},
err: regexp.MustCompile(`EV003: Attestation syntax validation failed, .*, caused by:\nSchema ID: https://slsa.dev/provenance/v0.2\n - /predicate/builder/id: "invalid" invalid uri: uri missing scheme prefix`),
err: regexp.MustCompile(`^attestation syntax validation failed: jsonschema: '/predicate/builder/id' does not validate with https://slsa.dev/provenance/v0.2#/properties/predicate/properties/builder/properties/id/format: 'invalid' is not valid 'uri'$`),
},
{
name: "valid",
Expand All @@ -415,15 +415,15 @@ func TestSyntaxValidation(t *testing.T) {
attestations: []attestation.Attestation{
createSimpleAttestation(&in_toto.ProvenanceStatementSLSA02{}),
},
err: regexp.MustCompile(`EV002: Unable to decode attestation data from attestation image, .*, caused by: unexpected end of JSON input`),
err: regexp.MustCompile(`^attestation syntax validation failed: jsonschema: .*$`), // map order is not deterministic so each run produces a different error
},
{
name: "valid and invalid",
attestations: []attestation.Attestation{
valid,
invalid,
},
err: regexp.MustCompile(`EV003`),
err: regexp.MustCompile(`^attestation syntax validation failed: jsonschema: '/predicate/builder/id' does not validate with https://slsa.dev/provenance/v0.2#/properties/predicate/properties/builder/properties/id/format: 'invalid' is not valid 'uri'$`),
},
}

Expand All @@ -438,7 +438,7 @@ func TestSyntaxValidation(t *testing.T) {
assert.NoError(t, err)
} else {
assert.Error(t, err)
assert.Regexp(t, err, err.Error())
assert.Regexp(t, c.err, err.Error())
}
})
}
Expand Down
Loading

0 comments on commit 6b12ca9

Please sign in to comment.