Skip to content

Commit 92b1ea2

Browse files
authored
fix: don't share datatype across recursive validator calls (#100)
1 parent cf8fbad commit 92b1ea2

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

lib/jsonschema.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,9 +595,15 @@ local reg_map = {
595595

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

603609
if type(schema) == "table" and schema._org_val ~= nil then

t/default.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,20 @@ local t = {
233233
local ok, err = validator(t)
234234
assert(ok~=nil, ("pattern: failed to check pattern with `%%`: %s"):format(err))
235235
ngx.say("passed: pass check pattern with `%`")
236+
237+
----------------------------------------------------- test case 9
238+
-- regression: per-call `datatype` must not be shared across recursive calls.
239+
-- Schemas like `{type=array, maxLength=N, items={type=string}}` used to crash
240+
-- because the items recursion mutated the outer function's datatype variable,
241+
-- causing the outer string-only `maxLength` check to run on the array value
242+
-- and call `utf8_len` on a table.
243+
local rule = {
244+
type = "array",
245+
maxLength = 10,
246+
items = { type = "string" },
247+
}
248+
local validator = jsonschema.generate_validator(rule)
249+
local pcall_ok, valid, val_err = pcall(validator, { "a", "b", "c" })
250+
assert(pcall_ok, "fail: validator threw an error: " .. tostring(valid))
251+
assert(valid, "fail: validator returned false: " .. tostring(val_err))
252+
ngx.say("passed: recursive datatype is not shared across calls")

0 commit comments

Comments
 (0)