Reject bool where a plain int is expected in _is_valid#2088
Reject bool where a plain int is expected in _is_valid#2088NishchayMahor wants to merge 1 commit into
Conversation
bool is a subclass of int, so isinstance(True, int) is True and _is_valid accepted a bool wherever a bare int was expected (e.g. query limit/offset/ autocut), silently coercing True/False to 1/0 instead of raising WeaviateInvalidInputError. Guard the int case, mirroring the bool/int fix in config.py from weaviate#2077. Adds a validator unit-test case.
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Infrastructure as Code | View in Orca | ||
| SAST | View in Orca | ||
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
|
To avoid any confusion in the future about your contribution to Weaviate, we work with a Contributor License Agreement. If you agree, you can simply add a comment to this PR that you agree with the CLA so that we can merge. |
|
I agree with the CLA. |
eeshsaxena
left a comment
There was a problem hiding this comment.
This looks correct to me. I traced the call path: _validate_input does any(_is_valid(exp, value) for exp in validate.expected), so for expected=[int] it calls _is_valid(int, value) with a bare int. None of the earlier branches in _is_valid match a plain int (not None, not _ExtraTypes, get_origin(int) is None), so it falls through to return isinstance(value, expected) - and since bool is a subclass of int, isinstance(True, int) is True. So a bool did slip through wherever a bare int is expected, exactly as described.
Two things I checked that make the fix feel well-scoped:
- It guards on
expected is intspecifically, so the existingexpected=[bool]call sites (validating genuine bool params) are untouched - a realboolstill validates against[bool]. Good that it doesn't over-reach. - I grepped the call sites for a reachable
Union[int, ...](theUnionbranch has the sameisinstance(True, int)behaviour), and didn't find one - the int-bearing validations are all list-form like[int, None], which route through this bare-int path. So the narrower fix here is sufficient for the actual call sites.
The added (True, [int], True) parametrize case pins it (expects WeaviateInvalidInputError).
Deferring to the maintainers for the final call.
What
_is_validinweaviate/validator.pyends withreturn isinstance(value, expected). Becauseboolis a subclass ofint,isinstance(True, int)isTrue, so aboolpasses validation wherever a bareintis expected:Real call sites hit this — e.g.
query.pyvalidateslimit/offset/autocutas[int, None]. So:silently passes validation and sends
True(coerced to1) instead of raisingWeaviateInvalidInputError. This is the same class of bug that #2076 reported and #2077 fixed inconfig.py/util.py(... and not isinstance(x, bool)); this is the unguarded sibling site in the validator.Fix
Guard the
intcase at the terminal return, so aboolis rejected where a plainintis expected:bool-typed expectations (_is_valid(bool, True)) and all other types are unaffected.Testing
Added a case to the existing parametrized
test_validatorintest/collection/test_validator.py:It fails on
mainand passes with the fix (verified). The suite is fully offline (no DB/Docker), and the existing(1.0, [int], True)case establishes the same "reject the wrong numeric type" pattern.