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