What happens
A request body which is valid JSON but not a JSON object raises an uncaught AttributeError.
validate_json only checks that the body parses:
try:
json.loads(s=request_body.decode())
exceptJSONDecodeErrorasexc:
validate_keys then assumes the result is a dict:
request_json=json.loads(s=request_text)
given_keys=set(request_json.keys())
src/mock_vws/_services_validators/key_validators.py:167.
Both backends, both endpoints which take a body:
in-process POST /targets body=[1, 2] -> UNCAUGHT AttributeError: 'list' object has no attribute 'keys'
in-process POST /targets body="hello" -> UNCAUGHT AttributeError: 'str' object has no attribute 'keys'
in-process POST /targets body=5 -> UNCAUGHT AttributeError: 'int' object has no attribute 'keys'
in-process POST /targets body=null -> UNCAUGHT AttributeError: 'NoneType' object has no attribute 'keys'
in-process POST /targets body=true -> UNCAUGHT AttributeError: 'bool' object has no attribute 'keys'
in-process PUT /targets/{id} body=[1, 2] -> UNCAUGHT AttributeError: 'list' object has no attribute 'keys'
flask POST /targets body=[1, 2] -> UNCAUGHT AttributeError: 'list' object has no attribute 'keys'
flask POST /targets body=null -> UNCAUGHT AttributeError: 'NoneType' object has no attribute 'keys'
{} is handled correctly and returns 400. PUT to a nonexistent target returns 404, because validate_target_id_exists runs first — the failure needs an existing target.
Through requests and httpx the AttributeError propagates out of the caller's request call. Through Flask and Docker it is a 500.
Several validators downstream of validate_keys would fail the same way if reached. validate_metadata_type, validate_active_flag, validate_width and the name validators all do "key" not in json.loads(...), which raises TypeError for a JSON number or boolean body.
Why it matters
json.dumps of a list is an easy thing for a client to send by accident, and a fuzzing or property-based test suite will produce these bodies immediately. The mock's job is to return whatever Vuforia returns; instead the caller gets an exception from their HTTP client.
There is a precedent in this codebase for handling exactly this input. docs/source/differences-to-vws.rst says of the Model Target Web API:
Dataset creation request bodies which are valid JSON but not JSON objects are reported as missing every required top-level field.
So the case was considered and handled for one API and not the other.
Suggested resolution
Reject non-object bodies in validate_json, which is the validator whose job this already is, so that everything downstream can rely on having a dict.
What to return needs checking against a real database. Fail with a 400 is the natural guess given that is what {} produces for POST /targets, but the Model Target API chose "missing every required field" for the same input, so Vuforia is not necessarily consistent here and a verified fake test is worth more than a guess.
tests/mock_vws/test_invalid_json.py and test_unexpected_json.py are the natural homes for the test. Neither currently sends valid JSON of a non-object type.
What happens
A request body which is valid JSON but not a JSON object raises an uncaught
AttributeError.validate_jsononly checks that the body parses:validate_keysthen assumes the result is a dict:src/mock_vws/_services_validators/key_validators.py:167.Both backends, both endpoints which take a body:
{}is handled correctly and returns 400.PUTto a nonexistent target returns 404, becausevalidate_target_id_existsruns first — the failure needs an existing target.Through
requestsandhttpxtheAttributeErrorpropagates out of the caller's request call. Through Flask and Docker it is a 500.Several validators downstream of
validate_keyswould fail the same way if reached.validate_metadata_type,validate_active_flag,validate_widthand the name validators all do"key" not in json.loads(...), which raisesTypeErrorfor a JSON number or boolean body.Why it matters
json.dumpsof a list is an easy thing for a client to send by accident, and a fuzzing or property-based test suite will produce these bodies immediately. The mock's job is to return whatever Vuforia returns; instead the caller gets an exception from their HTTP client.There is a precedent in this codebase for handling exactly this input.
docs/source/differences-to-vws.rstsays of the Model Target Web API:So the case was considered and handled for one API and not the other.
Suggested resolution
Reject non-object bodies in
validate_json, which is the validator whose job this already is, so that everything downstream can rely on having a dict.What to return needs checking against a real database.
Failwith a 400 is the natural guess given that is what{}produces forPOST /targets, but the Model Target API chose "missing every required field" for the same input, so Vuforia is not necessarily consistent here and a verified fake test is worth more than a guess.tests/mock_vws/test_invalid_json.pyandtest_unexpected_json.pyare the natural homes for the test. Neither currently sends valid JSON of a non-object type.