Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions lib/jsonschema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -595,9 +595,15 @@ local reg_map = {

generate_validator = function(ctx, schema)
-- get type informations as they will be necessary anyway
local datatype = ctx:localvartab(sformat('%s(%s)',
-- These must be real `local`s (not shared table fields) because they hold
-- per-call state. Recursive calls into nested schema validators (e.g. via
-- `items` or `properties`) would otherwise mutate the outer function's
-- `datatype`, leading to wrong-branch execution after the recursion returns
-- (e.g. running a string-only check like `maxLength` on a table value and
-- crashing inside `utf8_len`).
local datatype = ctx:localvar(sformat('%s(%s)',
ctx:libfunc('type'), ctx:param(1)))
local datakind = ctx:localvartab(sformat('%s == "table" and %s(%s)',
local datakind = ctx:localvar(sformat('%s == "table" and %s(%s)',
datatype, ctx:libfunc('lib.tablekind'), ctx:param(1)))

if type(schema) == "table" and schema._org_val ~= nil then
Expand Down
17 changes: 17 additions & 0 deletions t/default.lua
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,20 @@ local t = {
local ok, err = validator(t)
assert(ok~=nil, ("pattern: failed to check pattern with `%%`: %s"):format(err))
ngx.say("passed: pass check pattern with `%`")

----------------------------------------------------- test case 9
-- regression: per-call `datatype` must not be shared across recursive calls.
-- Schemas like `{type=array, maxLength=N, items={type=string}}` used to crash
-- because the items recursion mutated the outer function's datatype variable,
-- causing the outer string-only `maxLength` check to run on the array value
-- and call `utf8_len` on a table.
local rule = {
type = "array",
maxLength = 10,
items = { type = "string" },
}
local validator = jsonschema.generate_validator(rule)
local pcall_ok, valid, val_err = pcall(validator, { "a", "b", "c" })
assert(pcall_ok, "fail: validator threw an error: " .. tostring(valid))
assert(valid, "fail: validator returned false: " .. tostring(val_err))
ngx.say("passed: recursive datatype is not shared across calls")
Loading