weaseljson_schemagen.py silently drops any required entry that does not have a matching key in properties. Because the only supported object mode is additionalProperties: false (which rejects all keys not listed in properties), such a schema is unsatisfiable, yet schemagen accepts it at generation time and the generated parser happily accepts documents that the schema invalidates.
This contradicts the README contract:
contrib/schemagen/README.md lines 50-52 ("Schema violations (rejected at parse time)"): "missing required property (top-level or nested)"
contrib/schemagen/README.md header of the parser: "WeaselJson_OK means the document is both valid JSON and schema-valid"
and silently produces a permissive parser where a JSON Schema validator would reject every document.
Where
contrib/schemagen/weaseljson_schemagen.py:
Line 433: required = set(node.get("required", []))
Lines 435-443: required is consulted only for keys appearing in properties (obj.fields.append(Field(key, cpp, ty, key in required, nullable))); entries of required with no matching property simply vanish.
requiredMask() (lines ~836-845) emits mask words/bits only for obj.fields, so there is never a bit for a key that is not a field.
The check in generated cbEndObject() (if ((f.seen[i] & req[i]) != req[i]) missing = true) therefore never fires for such keys.
This schema is valid input for the generator (nothing in the "not supported" list), but under JSON Schema semantics no document can satisfy it: "b" is required, yet whenever "b" appears it violates additionalProperties: false (it has no property entry).
Generation succeeds with no error, emitting requiredMask{ 0x0u } for Kind::Root1 even though key "b" is required.
RootBuilderb;charbuf[]="{\"a\": 1}";// missing required key "b"
WeaselJsonStatuss=b.feed(buf,sizeof(buf)-1);if(s==WeaselJson_AGAIN)s=b.finish();// s == WeaselJson_OK <-- document is accepted
Observed:
missing required key 'b' -> status 0 (ACCEPTED - should be REJECT)
accepted result: a=1
Conversely, {"a": 1, "b": 2} is rejected (unknown key), which is the only part that happens to be correct; everything else is accepted.
The same gap applies to any nested object schema, not just the root.
Expected behavior
Following the established pattern from issue #55 / PR #58 (absent additionalProperties is now rejected at generation time), the generator should reject schemas where set(required) is not a subset of properties.keys() at generation time, e.g. GenError: "required key 'b' has no matching properties entry". There is no correct parse-time fallback: since additionalProperties: false is mandatory and is the only supported mode, the schema can never match any document, so a generation-time GenError (like the other unsupported constructs) is the appropriate behavior.
Impact
A schema with a typo/dead required entry (e.g. key renamed in properties but not in required, or a required key simply forgotten during properties refactoring) silently produces a parser that accepts documents missing that required key, i.e. invalid data passes validation instead of being rejected at generation or parse time.
## Summary
`weaseljson_schemagen.py` silently drops any `required` entry that does not have a matching key in `properties`. Because the only supported object mode is `additionalProperties: false` (which rejects all keys not listed in `properties`), such a schema is unsatisfiable, yet schemagen accepts it at generation time and the generated parser happily accepts documents that the schema invalidates.
This contradicts the README contract:
- `contrib/schemagen/README.md` lines 50-52 ("Schema violations (rejected at parse time)"): *"missing required property (top-level or nested)"*
- `contrib/schemagen/README.md` header of the parser: *"`WeaselJson_OK` means the document is both valid JSON and schema-valid"*
and silently produces a permissive parser where a JSON Schema validator would reject **every** document.
## Where
`contrib/schemagen/weaseljson_schemagen.py`:
- Line 433: `required = set(node.get("required", []))`
- Lines 435-443: `required` is consulted only for keys appearing in `properties` (`obj.fields.append(Field(key, cpp, ty, key in required, nullable))`); entries of `required` with no matching property simply vanish.
- `requiredMask()` (lines ~836-845) emits mask words/bits only for `obj.fields`, so there is never a bit for a key that is not a field.
The check in generated `cbEndObject()` (`if ((f.seen[i] & req[i]) != req[i]) missing = true`) therefore never fires for such keys.
## Reproduction
`schema.json`:
```json
{
"type": "object",
"additionalProperties": false,
"properties": { "a": { "type": "integer" } },
"required": ["b"]
}
```
This schema is valid input for the generator (nothing in the "not supported" list), but under JSON Schema semantics no document can satisfy it: `"b"` is required, yet whenever `"b"` appears it violates `additionalProperties: false` (it has no property entry).
Generate and accept:
```sh
python3 contrib/schemagen/weaseljson_schemagen.py schema.json -o gen.h
```
Generation succeeds with no error, emitting `requiredMask` `{ 0x0u }` for `Kind::Root1` even though key `"b"` is required.
```cpp
RootBuilder b;
char buf[] = "{\"a\": 1}"; // missing required key "b"
WeaselJsonStatus s = b.feed(buf, sizeof(buf) - 1);
if (s == WeaselJson_AGAIN) s = b.finish();
// s == WeaselJson_OK <-- document is accepted
```
Observed:
```
missing required key 'b' -> status 0 (ACCEPTED - should be REJECT)
accepted result: a=1
```
Conversely, `{"a": 1, "b": 2}` is rejected (unknown key), which is the only part that happens to be correct; everything else is accepted.
The same gap applies to any nested object schema, not just the root.
## Expected behavior
Following the established pattern from issue #55 / PR #58 (absent `additionalProperties` is now rejected at generation time), the generator should reject schemas where `set(required)` is not a subset of `properties.keys()` at generation time, e.g. `GenError: "required key 'b' has no matching properties entry"`. There is no correct parse-time fallback: since `additionalProperties: false` is mandatory and is the only supported mode, the schema can never match any document, so a generation-time `GenError` (like the other unsupported constructs) is the appropriate behavior.
## Impact
A schema with a typo/dead `required` entry (e.g. key renamed in `properties` but not in `required`, or a required key simply forgotten during `properties` refactoring) silently produces a parser that accepts documents missing that required key, i.e. invalid data passes validation instead of being rejected at generation or parse time.
weaselbot
was assigned by andrew2026-08-30 21:25:58 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
weaseljson_schemagen.pysilently drops anyrequiredentry that does not have a matching key inproperties. Because the only supported object mode isadditionalProperties: false(which rejects all keys not listed inproperties), such a schema is unsatisfiable, yet schemagen accepts it at generation time and the generated parser happily accepts documents that the schema invalidates.This contradicts the README contract:
contrib/schemagen/README.mdlines 50-52 ("Schema violations (rejected at parse time)"): "missing required property (top-level or nested)"contrib/schemagen/README.mdheader of the parser: "WeaselJson_OKmeans the document is both valid JSON and schema-valid"and silently produces a permissive parser where a JSON Schema validator would reject every document.
Where
contrib/schemagen/weaseljson_schemagen.py:required = set(node.get("required", []))requiredis consulted only for keys appearing inproperties(obj.fields.append(Field(key, cpp, ty, key in required, nullable))); entries ofrequiredwith no matching property simply vanish.requiredMask()(lines ~836-845) emits mask words/bits only forobj.fields, so there is never a bit for a key that is not a field.The check in generated
cbEndObject()(if ((f.seen[i] & req[i]) != req[i]) missing = true) therefore never fires for such keys.Reproduction
schema.json:This schema is valid input for the generator (nothing in the "not supported" list), but under JSON Schema semantics no document can satisfy it:
"b"is required, yet whenever"b"appears it violatesadditionalProperties: false(it has no property entry).Generate and accept:
Generation succeeds with no error, emitting
requiredMask{ 0x0u }forKind::Root1even though key"b"is required.Observed:
Conversely,
{"a": 1, "b": 2}is rejected (unknown key), which is the only part that happens to be correct; everything else is accepted.The same gap applies to any nested object schema, not just the root.
Expected behavior
Following the established pattern from issue #55 / PR #58 (absent
additionalPropertiesis now rejected at generation time), the generator should reject schemas whereset(required)is not a subset ofproperties.keys()at generation time, e.g.GenError: "required key 'b' has no matching properties entry". There is no correct parse-time fallback: sinceadditionalProperties: falseis mandatory and is the only supported mode, the schema can never match any document, so a generation-timeGenError(like the other unsupported constructs) is the appropriate behavior.Impact
A schema with a typo/dead
requiredentry (e.g. key renamed inpropertiesbut not inrequired, or a required key simply forgotten duringpropertiesrefactoring) silently produces a parser that accepts documents missing that required key, i.e. invalid data passes validation instead of being rejected at generation or parse time.