From ba3f222f37ae0e8d2d847e71e1e1fdf9b61c4913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Wed, 20 Oct 2021 23:06:21 +0300 Subject: [PATCH] Fix a crash in hc.api.views.pause with an int in request body The jsonify decorator parses request payload as JSON and puts it in request.json. The payload would normally be a complex object, but if a client sends, let's say, a single integer, then request.json is a python int. The authorize decorator looks for API key first in request headers, then in request body. It expects the request body to be a complex object. This commit changes adds the following validation rule in the jsonify decorator: if request body is not empty, it *must* parse as JSON, and the root element of the parsed document *must* be a dict. --- CHANGELOG.md | 1 + hc/api/decorators.py | 15 +++++++-------- hc/api/tests/test_pause.py | 7 +++++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8088a54..f237c46b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Bug Fixes - Fix hc.api.views.ping to handle non-utf8 data in request body (#574) +- Fix a crash when hc.api.views.pause receives a single integer in request body ## v1.23.1 - 2021-10-13 diff --git a/hc/api/decorators.py b/hc/api/decorators.py index 09885067..c3a41034 100644 --- a/hc/api/decorators.py +++ b/hc/api/decorators.py @@ -61,12 +61,12 @@ def authorize_read(f): return wrapper -def validate_json(schema=None): +def validate_json(schema={"type": "object"}): """ Parse request json and validate it against `schema`. Put the parsed result in `request.json`. - If schema is None then only parse and don't validate. - Supports a limited subset of JSON schema spec. + If schema is None then only parse and check if the root + element is a dict. Supports a limited subset of JSON schema spec. """ @@ -81,11 +81,10 @@ def validate_json(schema=None): else: request.json = {} - if schema: - try: - validate(request.json, schema) - except ValidationError as e: - return error("json validation error: %s" % e) + try: + validate(request.json, schema) + except ValidationError as e: + return error("json validation error: %s" % e) return f(request, *args, **kwds) diff --git a/hc/api/tests/test_pause.py b/hc/api/tests/test_pause.py index 85e980e8..ffe0d112 100644 --- a/hc/api/tests/test_pause.py +++ b/hc/api/tests/test_pause.py @@ -94,3 +94,10 @@ class PauseTestCase(BaseTestCase): self.profile.refresh_from_db() self.assertIsNone(self.profile.next_nag_date) + + def test_it_rejects_non_dict_post_body(self): + r = self.csrf_client.post(self.url, "123", content_type="application/json") + self.assertEqual(r.status_code, 400) + self.assertEqual( + r.json()["error"], "json validation error: value is not an object" + )