api7 / jsonschema

Pure Lua JSON schema validator for Lua/LuaJIT
https://www.apiseven.com/
Apache License 2.0
119 stars 28 forks source link

Validation applies invalid default value when using `$ref` and `anyOf` #85

Open piotrp opened 11 months ago

piotrp commented 11 months ago

I'm using a validator to check my schemas against JSON Schema Draft-07 schema (http://json-schema.org/draft-07/schema), and found that validation method incorrectly applies default to validated table.

Reproduction:

local cjson = require "cjson"
local jsonschema = require "jsonschema"

-- simplified metaschema, stripped so that it contains only elements that generate invalid valdiator code
local metaschema = [[
{
    "definitions": {
        "schemaArray": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#" }
        }
    },
    "properties": {
        "items": {
            "anyOf": [
                { "$ref": "#" },
                { "$ref": "#/definitions/schemaArray" }
            ],
            "default": true
        }
    }
}
]]

local metaschema_validator = jsonschema.generate_validator(cjson.decode(metaschema))

local string_array_schema = {
  type = "array",
  items = { type = "string", minLength = 1 }
}

print("BEFORE> ", cjson.encode(string_array_schema))
metaschema_validator(string_array_schema)
print("AFTER>  ", cjson.encode(string_array_schema))

local string_array_validator = jsonschema.generate_validator(string_array_schema)
print(string_array_validator({ "" }))

Result:

BEFORE> {"items":{"minLength":1,"type":"string"},"type":"array"}
AFTER>  {"items":{"items":true,"minLength":1,"type":"string"},"type":"array"}
true

Adding "items":true is invalid here and breaks source schema - it makes it accept invalid inputs (the last output line should say falsefailed to validate item 1: string too short, expected at least 1, got 0).

This error is caused by handing of default values added in https://github.com/api7/jsonschema/commit/66242c398399dc1663dc6874826e61c1d0cac194. Generated code looks like this:

-- we're validating "items" object, i.e. {"minLength":1,"type":"string"}
local propvalue = p_1["items"]
if propvalue ~= nil then
  -- nested validation is skipped because there is no nested "items" key
  -- ...
end
if propvalue == nil then
  -- and here validator breaks valdiated object
  p_1["items"] = true
end