From 8d434d98ab3f9cd17c2d041607a715b5e29bcede Mon Sep 17 00:00:00 2001 From: Gabe Cohen Date: Mon, 18 Apr 2022 20:05:01 -0700 Subject: [PATCH 1/6] fix schema validation logic --- credential/schema/model.go | 5 ++ credential/schema/vcjsonschema.go | 29 +++++++---- credential/schema/vcjsonschema_test.go | 1 + schema/jsonschema.go | 8 ++- schema/jsonschema_test.go | 71 ++++++++++++++++++-------- 5 files changed, 81 insertions(+), 33 deletions(-) diff --git a/credential/schema/model.go b/credential/schema/model.go index 8afadd16..565ebca1 100644 --- a/credential/schema/model.go +++ b/credential/schema/model.go @@ -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 diff --git a/credential/schema/vcjsonschema.go b/credential/schema/vcjsonschema.go index 4d6d9975..fda8a75c 100644 --- a/credential/schema/vcjsonschema.go +++ b/credential/schema/vcjsonschema.go @@ -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" @@ -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 { diff --git a/credential/schema/vcjsonschema_test.go b/credential/schema/vcjsonschema_test.go index 836a62bb..31c6b19d 100644 --- a/credential/schema/vcjsonschema_test.go +++ b/credential/schema/vcjsonschema_test.go @@ -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) diff --git a/schema/jsonschema.go b/schema/jsonschema.go index ff1e31a7..8c57e816 100644 --- a/schema/jsonschema.go +++ b/schema/jsonschema.go @@ -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 diff --git a/schema/jsonschema_test.go b/schema/jsonschema_test.go index c7ecae2a..7cb6eebc 100644 --- a/schema/jsonschema_test.go +++ b/schema/jsonschema_test.go @@ -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.", @@ -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{}{ @@ -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{}{ @@ -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{}{ @@ -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") }) } From 58072e8b29d962185997d7b90d1cc58cc4b00797 Mon Sep 17 00:00:00 2001 From: Gabe Cohen Date: Mon, 18 Apr 2022 20:09:22 -0700 Subject: [PATCH 2/6] build tags --- .github/workflows/codeql-analysis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9b4962b2..373bb389 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,8 +52,7 @@ jobs: - run: | - mage clean - mage build + go build -tags jwx_es256k - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 From 9912032e12816117b412b1ed8a6db2c07ac662ec Mon Sep 17 00:00:00 2001 From: Gabe Cohen Date: Mon, 18 Apr 2022 20:11:10 -0700 Subject: [PATCH 3/6] build tags --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 373bb389..edd25b36 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -52,7 +52,7 @@ jobs: - run: | - go build -tags jwx_es256k + go build -tags jwx_es256k ./... - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 From b493c3ad1cfedd5827db750c32537090a9f46607 Mon Sep 17 00:00:00 2001 From: Gabe Cohen Date: Mon, 18 Apr 2022 20:14:49 -0700 Subject: [PATCH 4/6] build flags --- .github/workflows/codeql-analysis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index edd25b36..45224a45 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -43,6 +43,8 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 + env: + GOFLAGS: "-tags=jwx_es256k" with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,9 +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: | - go build -tags jwx_es256k ./... + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 From b9d662b35981aaeeca230bfb0152c93e9c8d7340 Mon Sep 17 00:00:00 2001 From: Gabe Cohen Date: Mon, 18 Apr 2022 20:18:55 -0700 Subject: [PATCH 5/6] move goflags --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 45224a45..5cb3d9f2 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,6 +28,8 @@ jobs: actions: read contents: read security-events: write + env: + GOFLAGS: "-tags=jwx_es256k" strategy: fail-fast: false @@ -43,8 +45,6 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 - env: - GOFLAGS: "-tags=jwx_es256k" with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. From 518f3403da3da2131d5767a8dcdad3c436b74316 Mon Sep 17 00:00:00 2001 From: Gabe Cohen Date: Mon, 18 Apr 2022 20:23:43 -0700 Subject: [PATCH 6/6] update jwx --- go.mod | 8 ++++---- go.sum | 15 ++++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 04d6c4c7..62467aec 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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 ) diff --git a/go.sum b/go.sum index 89bc5b41..78355420 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= @@ -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=