From 2c6ed18e253b33ffe423d6781d1e4b9ac80a5e83 Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Mon, 14 Oct 2024 20:16:43 +0100 Subject: [PATCH] encoding/jsonschema: disallow $schema at non-root for earlier versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before 2019-09, the specification says [1]: > The "$schema" keyword SHOULD be used in a root schema. It MUST NOT > appear in subschemas. This change makes us more spec-compliant in that respect. Also change a test case where we were using `$schema` at non-root location. [1]: https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.7 Signed-off-by: Roger Peppe Change-Id: If74b25848cc3c334eaaefb46adfe4fe89d0cbb45 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1202562 Unity-Result: CUE porcuepine Reviewed-by: Daniel Martí TryBot-Result: CUEcueckoo --- encoding/jsonschema/constraints_meta.go | 6 ++++++ encoding/jsonschema/decode.go | 6 ++++++ .../jsonschema/testdata/txtar/id_in_oneOf.txtar | 2 -- .../testdata/txtar/schema_not_at_root.txtar | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 encoding/jsonschema/testdata/txtar/schema_not_at_root.txtar diff --git a/encoding/jsonschema/constraints_meta.go b/encoding/jsonschema/constraints_meta.go index 6f371a7688a..6b47bcd8ce5 100644 --- a/encoding/jsonschema/constraints_meta.go +++ b/encoding/jsonschema/constraints_meta.go @@ -50,6 +50,12 @@ func constraintID(key string, n cue.Value, s *state) { // constraintSchema implements $schema, which // identifies this as a JSON schema and specifies its version. func constraintSchema(key string, n cue.Value, s *state) { + if !s.isRoot && !vfrom(VersionDraft2019_09).contains(s.schemaVersion) { + // Before 2019-09, the $schema keyword was not allowed + // to appear anywhere but the root. + s.errf(n, "$schema can only appear at the root in JSON Schema version %v", s.schemaVersion) + return + } str, ok := s.strValue(n) if !ok { // If there's no $schema value, use the default. diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go index 0e9fd442570..aae58f26762 100644 --- a/encoding/jsonschema/decode.go +++ b/encoding/jsonschema/decode.go @@ -115,6 +115,7 @@ func (d *decoder) schema(ref []ast.Label, v cue.Value) (a []ast.Decl) { root := state{ decoder: d, schemaVersion: d.cfg.DefaultVersion, + isRoot: true, } var name ast.Label @@ -415,6 +416,10 @@ type state struct { // to $ref. hasRefKeyword bool + // isRoot holds whether this state is at the root + // of the schema. + isRoot bool + minContains *uint64 maxContains *uint64 @@ -709,6 +714,7 @@ func (s0 *state) schemaState(n cue.Value, types cue.Kind, idRef []label) (ast.Ex knownTypes: allTypes, idRef: idRef, pos: n, + isRoot: s0.isRoot && n == s0.pos, } if n.Kind() == cue.BoolKind { if vfrom(VersionDraft6).contains(s.schemaVersion) { diff --git a/encoding/jsonschema/testdata/txtar/id_in_oneOf.txtar b/encoding/jsonschema/testdata/txtar/id_in_oneOf.txtar index 5a2848adb54..f9a9f1fa271 100644 --- a/encoding/jsonschema/testdata/txtar/id_in_oneOf.txtar +++ b/encoding/jsonschema/testdata/txtar/id_in_oneOf.txtar @@ -4,12 +4,10 @@ "$id": "https://test.example/foo", "oneOf": [ { - "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://1.test.example/string", "type": "string" }, { - "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://2.test.example/object", "type": "object" } diff --git a/encoding/jsonschema/testdata/txtar/schema_not_at_root.txtar b/encoding/jsonschema/testdata/txtar/schema_not_at_root.txtar new file mode 100644 index 00000000000..42f5587be8b --- /dev/null +++ b/encoding/jsonschema/testdata/txtar/schema_not_at_root.txtar @@ -0,0 +1,16 @@ +-- schema.json -- +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://test.example/foo", + "oneOf": [ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "string" + } + ] +} + +-- out/decode/extract -- +ERROR: +$schema can only appear at the root in JSON Schema version http://json-schema.org/draft-07/schema#: + schema.json:6:11