From 96394aca7c0719af2b4154fd108837a0bd55283c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Wed, 2 Oct 2019 17:03:44 +0200
Subject: [PATCH] jsondatastore: check if request.body is a json (#17168)

---
 passerelle/apps/jsondatastore/models.py | 19 ++++++++++++++++---
 tests/test_jsondatastore.py             | 24 ++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/passerelle/apps/jsondatastore/models.py b/passerelle/apps/jsondatastore/models.py
index 62d60fed..30c8146a 100644
--- a/passerelle/apps/jsondatastore/models.py
+++ b/passerelle/apps/jsondatastore/models.py
@@ -31,6 +31,16 @@ def get_hex_uuid():
     return uuid.uuid4().get_hex()
 
 
+def clean_json_data(data):
+    try:
+        payload = json.loads(data)
+        if not isinstance(payload, dict):
+            raise APIError('payload must be a dict')
+        return payload
+    except ValueError:
+        raise APIError('could not decode body to json')
+
+
 class JsonData(models.Model):
     datastore = models.ForeignKey('JsonDataStore', null=True)
 
@@ -76,8 +86,9 @@ class JsonDataStore(BaseResource):
               example_pattern='create',
               description=_('Create'))
     def create(self, request, name_id=None, **kwargs):
+        content = clean_json_data(request.body)
         attrs = {
-            'content': json.loads(request.body),
+            'content': content,
             'datastore': self,
         }
         if name_id is not None:
@@ -106,10 +117,12 @@ class JsonDataStore(BaseResource):
     def get_or_replace(self, request, uuid, name_id=None):
         data = self.get_data_object(uuid, name_id)
         if request.method == 'POST':
-            data.content = json.loads(request.body)
+            new_content = clean_json_data(request.body)
+            data.content = new_content
             data.save()
         elif request.method == 'PATCH':
-            data.content.update(json.loads(request.body))
+            new_content = clean_json_data(request.body)
+            data.content.update(new_content)
             data.save()
         return {'id': data.uuid, 'text': data.text, 'content': data.content}
 
diff --git a/tests/test_jsondatastore.py b/tests/test_jsondatastore.py
index 2fb3e144..1ccd5762 100644
--- a/tests/test_jsondatastore.py
+++ b/tests/test_jsondatastore.py
@@ -35,6 +35,14 @@ def test_jsondatastore(app, jsondatastore, jsondatastore2):
     assert len(resp.json['data']) == 1
     assert resp.json['data'][0]['content'] == {'foo': 'bar'}
 
+    # check json payload
+    resp = app.post('/jsondatastore/foobar/data/create', params='foo=bar')
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'could not decode body to json'
+    resp = app.post_json('/jsondatastore/foobar/data/create', params='foo=bar')
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'payload must be a dict'
+
     resp = app.get('/jsondatastore/foobar/data/%s/' % uuid)
     assert resp.json['id'] == uuid
     assert resp.json['content'] == {'foo': 'bar'}
@@ -43,6 +51,14 @@ def test_jsondatastore(app, jsondatastore, jsondatastore2):
     assert resp.json['id'] == uuid
     assert resp.json['content'] == {'foo': 'bar2'}
 
+    # check json payload
+    resp = app.post('/jsondatastore/foobar/data/%s/' % uuid, params='foo=bar2')
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'could not decode body to json'
+    resp = app.post_json('/jsondatastore/foobar/data/%s/' % uuid, params='foo=bar2')
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'payload must be a dict'
+
     resp = app.get('/jsondatastore/foobar/data/%s/' % uuid)
     assert resp.json['id'] == uuid
     assert resp.json['content'] == {'foo': 'bar2'}
@@ -51,6 +67,14 @@ def test_jsondatastore(app, jsondatastore, jsondatastore2):
     assert resp.json['id'] == uuid
     assert resp.json['content'] == {'foo': 'bar2', 'foo2': 'bar2'}
 
+    # check json payload
+    resp = app.patch('/jsondatastore/foobar/data/%s/' % uuid, params='foo2=bar2')
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'could not decode body to json'
+    resp = app.patch_json('/jsondatastore/foobar/data/%s/' % uuid, params='foo2=bar2')
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'payload must be a dict'
+
     resp = app.post_json('/jsondatastore/foobar/data/%s/delete' % uuid)
     assert resp.json['err'] == 0
 
-- 
2.20.1

