Skip to content

Commit

Permalink
Fix schema validation logic (#87)
Browse files Browse the repository at this point in the history
* fix schema validation logic

* build tags

* build tags

* build flags

* move goflags

* update jwx
  • Loading branch information
decentralgabe committed Apr 19, 2022
1 parent d775876 commit 6292424
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 48 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
actions: read
contents: read
security-events: write
env:
GOFLAGS: "-tags=jwx_es256k"

strategy:
fail-fast: false
Expand All @@ -50,10 +52,8 @@ jobs:
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main


- run: |
mage clean
mage build
- name: Autobuild
uses: github/codeql-action/autobuild@v2

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
5 changes: 5 additions & 0 deletions credential/schema/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package schema

import "fmt"

const (
// VCJSONSchemaType https://w3c-ccg.github.io/vc-json-schemas/v2/index.html#credential_schema_definition_metadata
VCJSONSchemaType string = "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json"
)

type JSONSchema map[string]interface{}

// VCJSONSchema is the model representing the
Expand Down
29 changes: 20 additions & 9 deletions credential/schema/vcjsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package schema
import (
"github.com/TBD54566975/ssi-sdk/schema"
"github.com/goccy/go-json"
"github.com/pkg/errors"

"github.com/TBD54566975/ssi-sdk/credential"
"github.com/gobuffalo/packr/v2"
Expand All @@ -19,29 +20,39 @@ var (

// StringToVCJSONCredentialSchema marshals a string into a credential json credential schema
func StringToVCJSONCredentialSchema(maybeVCJSONCredentialSchema string) (*VCJSONSchema, error) {
if err := schema.IsValidJSONSchema(maybeVCJSONCredentialSchema); err != nil {
return nil, err
}
var vcs VCJSONSchema
if err := json.Unmarshal([]byte(maybeVCJSONCredentialSchema), &vcs); err != nil {
return nil, err
}

schemaBytes, err := json.Marshal(vcs.Schema)
if err != nil {
return nil, errors.Wrap(err, "could not marshal vc json schema's schema property")
}
maybeSchema := string(schemaBytes)
if err := schema.IsValidJSONSchema(maybeSchema); err != nil {
return nil, errors.Wrap(err, "VC JSON Schema did not contain a valid JSON Schema")
}
return &vcs, nil
}

// IsValidCredentialSchema determines if a given credential schema is compliant with the specification's
// JSON Schema https://w3c-ccg.github.io/vc-json-schemas/v2/index.html#credential_schema_definition
func IsValidCredentialSchema(maybeCredentialSchema string) error {
if err := schema.IsValidJSONSchema(maybeCredentialSchema); err != nil {
return err
}

vcJSONSchemaSchema, err := getKnownSchema(verifiableCredentialJSONSchemaSchema)
if err != nil {
return err
return errors.Wrap(err, "could not get known schema for VC JSON Schema")
}

if err := schema.IsJSONValidAgainstSchema(maybeCredentialSchema, vcJSONSchemaSchema); err != nil {
return errors.Wrap(err, "credential schema did not validate")
}

if _, err := StringToVCJSONCredentialSchema(maybeCredentialSchema); err != nil {
return errors.Wrap(err, "credential schema not valid")
}

return schema.IsJSONValidAgainstSchema(maybeCredentialSchema, vcJSONSchemaSchema)
return nil
}

func IsCredentialValidForVCJSONSchema(credential credential.VerifiableCredential, vcJSONSchema VCJSONSchema) error {
Expand Down
1 change: 1 addition & 0 deletions credential/schema/vcjsonschema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func TestIsCredentialValidForSchema(t *testing.T) {

vcJSONSchema, err := StringToVCJSONCredentialSchema(vcJSONSchemaString)
assert.NoError(t, err)
assert.NotEmpty(t, vcJSONSchema)

// Validate credential against vcJSONSchema
err = IsCredentialValidForVCJSONSchema(cred, *vcJSONSchema)
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.1 // indirect
github.com/lestrrat-go/jwx v1.2.22
github.com/lestrrat-go/jwx v1.2.23
github.com/lestrrat-go/option v1.0.0 // indirect
github.com/magefile/mage v1.13.0
github.com/markbates/errx v1.1.0 // indirect
Expand All @@ -45,9 +45,9 @@ require (
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
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
Expand Down
15 changes: 8 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZ
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v1.2.22 h1:bCxMokwHNuJHVxgANP4OBddXGtQ9Oy+6cqp4O2rW7DU=
github.com/lestrrat-go/jwx v1.2.22/go.mod h1:sAXjRwzSvCN6soO4RLoWWm1bVPpb8iOuv0IYfH8OWd8=
github.com/lestrrat-go/jwx v1.2.23 h1:8oP5fY1yzCRraUNNyfAVdOkLCqY7xMZz11lVcvHqC1Y=
github.com/lestrrat-go/jwx v1.2.23/go.mod h1:sAXjRwzSvCN6soO4RLoWWm1bVPpb8iOuv0IYfH8OWd8=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/magefile/mage v1.13.0 h1:XtLJl8bcCM7EFoO8FyH8XK3t7G5hQAeK+i4tq+veT9M=
Expand Down Expand Up @@ -368,8 +368,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -516,11 +516,12 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
8 changes: 6 additions & 2 deletions schema/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ func IsValidJSONSchema(maybeSchema string) error {
if !IsValidJSON(maybeSchema) {
return errors.New("input is not valid json")
}
loader := gojsonschema.NewStringLoader(maybeSchema)
return gojsonschema.NewSchemaLoader().AddSchemas(loader)
stringLoader := gojsonschema.NewStringLoader(maybeSchema)
schemaLoader := gojsonschema.NewSchemaLoader()
schemaLoader.Validate = true
schemaLoader.Draft = gojsonschema.Draft7
_, err := schemaLoader.Compile(stringLoader)
return err
}

// IsValidJSON checks if a string is valid json https://stackoverflow.com/a/36922225
Expand Down
71 changes: 49 additions & 22 deletions schema/jsonschema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,36 @@ func TestJSONSchemaVectors(t *testing.T) {
}

func TestJSONSchemaValidation(t *testing.T) {
t.Run("Test Valid Address JSON Schema", func(t *testing.T) {
t.Run("Test Invalid JSON Schema", func(tt *testing.T) {
err := IsValidJSONSchema("bad")
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "input is not valid json")

badSchema := `{
"$id": "https://example.com/person.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "The person's first name."
},
"lastName": {
"type": "string",
"description": "The person's last name."
},
"required": ["middleName"]
},
"additionalProperties": false
}`
err = IsValidJSONSchema(badSchema)
assert.Error(tt, err)
})

t.Run("Test Valid Address JSON Schema", func(tt *testing.T) {
addressJSONSchema, err := getTestVector(JSONSchemaTestVector1)
assert.NoError(t, err)
assert.NoError(tt, err)

addressData := map[string]interface{}{
"street-address": "1455 Market St.",
Expand All @@ -43,17 +70,17 @@ func TestJSONSchemaValidation(t *testing.T) {
}

addressDataBytes, err := json.Marshal(addressData)
assert.NoError(t, err)
assert.NoError(tt, err)

addressDataJSON := string(addressDataBytes)
assert.True(t, IsValidJSON(addressDataJSON))
assert.True(tt, IsValidJSON(addressDataJSON))

assert.NoError(t, IsJSONValidAgainstSchema(addressDataJSON, addressJSONSchema))
assert.NoError(tt, IsJSONValidAgainstSchema(addressDataJSON, addressJSONSchema))
})

t.Run("Test Invalid Address JSON Schema", func(t *testing.T) {
t.Run("Test Invalid Address JSON Schema", func(tt *testing.T) {
addressJSONSchema, err := getTestVector(JSONSchemaTestVector1)
assert.NoError(t, err)
assert.NoError(tt, err)

// Missing required field
addressData := map[string]interface{}{
Expand All @@ -63,19 +90,19 @@ func TestJSONSchemaValidation(t *testing.T) {
}

addressDataBytes, err := json.Marshal(addressData)
assert.NoError(t, err)
assert.NoError(tt, err)

addressDataJSON := string(addressDataBytes)
assert.True(t, IsValidJSON(addressDataJSON))
assert.True(tt, IsValidJSON(addressDataJSON))

err = IsJSONValidAgainstSchema(addressDataJSON, addressJSONSchema)
assert.Error(t, err)
assert.Contains(t, err.Error(), "postal-code is required")
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "postal-code is required")
})

t.Run("Test Valid Person JSON Schema", func(t *testing.T) {
t.Run("Test Valid Person JSON Schema", func(tt *testing.T) {
personJSONSchema, err := getTestVector(JSONSchemaTestVector2)
assert.NoError(t, err)
assert.NoError(tt, err)

// Additional field
personData := map[string]interface{}{
Expand All @@ -84,17 +111,17 @@ func TestJSONSchemaValidation(t *testing.T) {
}

personDataBytes, err := json.Marshal(personData)
assert.NoError(t, err)
assert.NoError(tt, err)

personDataJSON := string(personDataBytes)
assert.True(t, IsValidJSON(personDataJSON))
assert.True(tt, IsValidJSON(personDataJSON))

assert.NoError(t, IsJSONValidAgainstSchema(personDataJSON, personJSONSchema))
assert.NoError(tt, IsJSONValidAgainstSchema(personDataJSON, personJSONSchema))
})

t.Run("Test Invalid Person JSON Schema", func(t *testing.T) {
t.Run("Test Invalid Person JSON Schema", func(tt *testing.T) {
personJSONSchema, err := getTestVector(JSONSchemaTestVector2)
assert.NoError(t, err)
assert.NoError(tt, err)

// Additional field
personData := map[string]interface{}{
Expand All @@ -105,14 +132,14 @@ func TestJSONSchemaValidation(t *testing.T) {
}

personDataBytes, err := json.Marshal(personData)
assert.NoError(t, err)
assert.NoError(tt, err)

personDataJSON := string(personDataBytes)
assert.True(t, IsValidJSON(personDataJSON))
assert.True(tt, IsValidJSON(personDataJSON))

err = IsJSONValidAgainstSchema(personDataJSON, personJSONSchema)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Additional property middleName is not allowed")
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "Additional property middleName is not allowed")
})
}

Expand Down

0 comments on commit 6292424

Please sign in to comment.