weaseljson_schemagen.py generates C++ type names (enum classes and structs) that start with a digit when a JSON Schema property key, $defs name, or array hint begins with a digit. Such names are not valid C++ identifiers, so the generated header fails to compile. This happens for perfectly valid JSON Schemas — JSON object keys may be any string, including "3", "2c", "2024", etc.
Root cause
Type names are produced by camel() and unique_name() in contrib/schemagen/weaseljson_schemagen.py:
defcamel(name):# line 153parts=[pforpinname.replace("-"," ").replace("_"," ").split(" ")ifp]ifnotparts:return"T"return"".join(p[:1].upper()+p[1:]forpinparts)
camel("3") returns "3" and camel("2c") returns "2c". The member-name helper sanitize() (line 111) explicitly fixes this case:
ifs[0].isdigit():s="_"+s
but camel() and unique_name() (line 251) never apply that check, so the digit-leading name is emitted verbatim as an enum/struct type name.
Expected: schemagen should produce a compilable header for any valid JSON Schema whose property/$defs names start with a digit (e.g. by prefixing the generated type name with _ when it begins with a digit, the way sanitize() already does for member names).
Actual: schemagen exits 0 but emits type names beginning with a digit, and the generated header does not compile.
Impact
Any schema using a digit-leading property key with an enum value, or a digit-leading key wrapping arrays-of-arrays-of-objects (and similar hint derivations), yields non-compiling generated code. Digit-leading keys are common and valid (e.g. "123", "2024", "2c"), so this is reachable with ordinary schemas, not only adversarial ones.
contrib/schemagen/weaseljson_schemagen.py:251 — unique_name() does not sanitize a leading digit.
contrib/schemagen/weaseljson_schemagen.py:111-119 — sanitize() shows the existing correct handling for member names (if s[0].isdigit(): s = "_" + s), which is not applied to generated type names.
## Summary
`weaseljson_schemagen.py` generates C++ type names (enum classes and structs) that **start with a digit** when a JSON Schema property key, `$defs` name, or array `hint` begins with a digit. Such names are not valid C++ identifiers, so the generated header fails to compile. This happens for perfectly valid JSON Schemas — JSON object keys may be any string, including `"3"`, `"2c"`, `"2024"`, etc.
## Root cause
Type names are produced by `camel()` and `unique_name()` in `contrib/schemagen/weaseljson_schemagen.py`:
```python
def camel(name): # line 153
parts = [p for p in name.replace("-", " ").replace("_", " ").split(" ") if p]
if not parts:
return "T"
return "".join(p[:1].upper() + p[1:] for p in parts)
```
`camel("3")` returns `"3"` and `camel("2c")` returns `"2c"`. The member-name helper `sanitize()` (line 111) explicitly fixes this case:
```python
if s[0].isdigit():
s = "_" + s
```
but `camel()` and `unique_name()` (line 251) never apply that check, so the digit-leading name is emitted verbatim as an enum/struct type name.
## Reproduction
Minimal schema (`schema.json`):
```json
{"type":"object","additionalProperties":false,"properties":{"3":{"enum":["a","b"]}}}
```
```sh
python3 contrib/schemagen/weaseljson_schemagen.py schema.json -o gen.h --namespace ts
```
`schemagen` exits 0 and emits (note `enum class 3`, `std::optional<3>`, `3_names`):
```cpp
enum class 3 : int { a, b };
struct Root1;
struct Root1 {
std::optional<3> _3; // "3"
};
...
static constexpr const char *3_names[] = { "a", "b" };
```
Compiling any TU that includes `gen.h` fails:
```
gen.h:17:12: error: expected identifier before numeric constant
17 | enum class 3 : int { a, b };
| ^
```
The same defect affects generated **struct** names. For example:
```json
{"type":"object","additionalProperties":false,"properties":{"2c":{"type":"array","items":{"type":"array","items":{"type":"object","additionalProperties":false,"properties":{"x":{"type":"string"}}}}}}}
```
emits:
```cpp
struct 2cItemItem; // invalid: starts with a digit
struct 2cItemItem { ... };
enum class Kind : uint8_t { Root1, 2cItemItem, Arr0, Arr1 }; // invalid enumerator
std::optional<std::vector<std::vector<2cItemItem>>> _2c; // invalid
```
which also fails to compile:
```
gen.h:18:8: error: expected identifier before numeric constant
18 | struct 2cItemItem;
```
## Expected vs actual
- **Expected:** schemagen should produce a compilable header for any valid JSON Schema whose property/`$defs` names start with a digit (e.g. by prefixing the generated type name with `_` when it begins with a digit, the way `sanitize()` already does for member names).
- **Actual:** schemagen exits 0 but emits type names beginning with a digit, and the generated header does not compile.
## Impact
Any schema using a digit-leading property key with an enum value, or a digit-leading key wrapping arrays-of-arrays-of-objects (and similar hint derivations), yields non-compiling generated code. Digit-leading keys are common and valid (e.g. `"123"`, `"2024"`, `"2c"`), so this is reachable with ordinary schemas, not only adversarial ones.
## Files / lines
- `contrib/schemagen/weaseljson_schemagen.py:153` — `camel()` returns digit-leading names.
- `contrib/schemagen/weaseljson_schemagen.py:251` — `unique_name()` does not sanitize a leading digit.
- `contrib/schemagen/weaseljson_schemagen.py:111-119` — `sanitize()` shows the existing correct handling for member names (`if s[0].isdigit(): s = "_" + s`), which is not applied to generated type names.
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.pygenerates C++ type names (enum classes and structs) that start with a digit when a JSON Schema property key,$defsname, or arrayhintbegins with a digit. Such names are not valid C++ identifiers, so the generated header fails to compile. This happens for perfectly valid JSON Schemas — JSON object keys may be any string, including"3","2c","2024", etc.Root cause
Type names are produced by
camel()andunique_name()incontrib/schemagen/weaseljson_schemagen.py:camel("3")returns"3"andcamel("2c")returns"2c". The member-name helpersanitize()(line 111) explicitly fixes this case:but
camel()andunique_name()(line 251) never apply that check, so the digit-leading name is emitted verbatim as an enum/struct type name.Reproduction
Minimal schema (
schema.json):schemagenexits 0 and emits (noteenum class 3,std::optional<3>,3_names):Compiling any TU that includes
gen.hfails:The same defect affects generated struct names. For example:
emits:
which also fails to compile:
Expected vs actual
$defsnames start with a digit (e.g. by prefixing the generated type name with_when it begins with a digit, the waysanitize()already does for member names).Impact
Any schema using a digit-leading property key with an enum value, or a digit-leading key wrapping arrays-of-arrays-of-objects (and similar hint derivations), yields non-compiling generated code. Digit-leading keys are common and valid (e.g.
"123","2024","2c"), so this is reachable with ordinary schemas, not only adversarial ones.Files / lines
contrib/schemagen/weaseljson_schemagen.py:153—camel()returns digit-leading names.contrib/schemagen/weaseljson_schemagen.py:251—unique_name()does not sanitize a leading digit.contrib/schemagen/weaseljson_schemagen.py:111-119—sanitize()shows the existing correct handling for member names (if s[0].isdigit(): s = "_" + s), which is not applied to generated type names.