diff --git a/lib/json/json_test.go b/lib/json/json_test.go index 73d35aa..472c3c3 100644 --- a/lib/json/json_test.go +++ b/lib/json/json_test.go @@ -1027,6 +1027,33 @@ func TestJSONValidate(t *testing.T) { assert.true(err3 != None) `), }, + { + // only the first JSON document was validated; trailing content + // (a second document or garbage) silently passed, so a script + // believed the whole text conformed. The data must be a single + // JSON document. + name: `validate: trailing content after the data document is rejected`, + script: itn.HereDoc(` + load('json', 'validate') + validate('{"a":1} {"b":2}', '{"type":"object"}') + `), + wantErr: `trailing content`, + }, + { + name: `try_validate: a trailing document cannot sneak past as cannot-run`, + script: itn.HereDoc(` + load('json', 'try_validate') + # the trailing "x" would fail {"type":"number"} but was never + # checked; now the whole input is rejected as cannot-run. + res, err = try_validate('5 "x"', '{"type":"number"}') + assert.eq(res, None) + assert.true('trailing content' in err) + # trailing whitespace is still fine + ok, e2 = try_validate('5\n', '{"type":"number"}') + assert.eq(ok, True) + assert.eq(e2, None) + `), + }, { name: `validate: draft-7 schema via $schema`, script: itn.HereDoc(` diff --git a/lib/json/validate.go b/lib/json/validate.go index a803664..67c4f0b 100644 --- a/lib/json/validate.go +++ b/lib/json/validate.go @@ -136,6 +136,13 @@ func prepareValidation(data, schema starlark.Value) (*jsonschema.Schema, interfa if err := dec.Decode(&doc); err != nil { return nil, nil, fmt.Errorf("invalid data: %w", err) } + // Validate a single JSON document. Trailing content (a second document + // or garbage) means the whole input was never checked, so reject it + // rather than silently validating only the first value. dec.More() + // stays false for trailing whitespace. + if dec.More() { + return nil, nil, fmt.Errorf("invalid data: unexpected trailing content after JSON document") + } return compiled, doc, nil }