From: Chris Lamb <lamby@debian.org>
Date: Thu, 8 Aug 2019 10:35:56 +0100
Subject: CVE-2019-14234

Backported from
<https://github.com/django/django/commit/ed682a24fca774818542757651bfba576c3fc3ef>
---
 django/contrib/postgres/fields/hstore.py |  2 +-
 django/contrib/postgres/fields/jsonb.py  |  8 +++-----
 tests/postgres_tests/test_hstore.py      | 14 ++++++++++++++
 tests/postgres_tests/test_json.py        | 15 ++++++++++++++-
 4 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/django/contrib/postgres/fields/hstore.py b/django/contrib/postgres/fields/hstore.py
index 8322d81..6d6f542 100644
--- a/django/contrib/postgres/fields/hstore.py
+++ b/django/contrib/postgres/fields/hstore.py
@@ -85,7 +85,7 @@ class KeyTransform(Transform):
 
     def as_sql(self, compiler, connection):
         lhs, params = compiler.compile(self.lhs)
-        return "(%s -> '%s')" % (lhs, self.key_name), params
+        return '(%s -> %%s)' % lhs, [self.key_name] + params
 
 
 class KeyTransformFactory(object):
diff --git a/django/contrib/postgres/fields/jsonb.py b/django/contrib/postgres/fields/jsonb.py
index ae83d9e..6d30b7a 100644
--- a/django/contrib/postgres/fields/jsonb.py
+++ b/django/contrib/postgres/fields/jsonb.py
@@ -75,12 +75,10 @@ class KeyTransform(Transform):
         if len(key_transforms) > 1:
             return "{} #> %s".format(lhs), [key_transforms] + params
         try:
-            int(self.key_name)
+            lookup = int(self.key_name)
         except ValueError:
-            lookup = "'%s'" % self.key_name
-        else:
-            lookup = "%s" % self.key_name
-        return "%s -> %s" % (lhs, lookup), params
+            lookup = self.key_name
+        return '(%s %s %%s)' % (lhs, self.operator), [lookup] + params
 
 
 class KeyTransformFactory(object):
diff --git a/tests/postgres_tests/test_hstore.py b/tests/postgres_tests/test_hstore.py
index 0afa630..fbcd32c 100644
--- a/tests/postgres_tests/test_hstore.py
+++ b/tests/postgres_tests/test_hstore.py
@@ -3,8 +3,10 @@ from __future__ import unicode_literals
 
 import json
 
+from django.db import connection
 from django.core import exceptions, serializers
 from django.forms import Form
+from django.test.utils import CaptureQueriesContext
 
 from . import PostgreSQLTestCase
 from .models import HStoreModel
@@ -163,6 +165,18 @@ class TestQuerying(PostgreSQLTestCase):
             self.objs[:2]
         )
 
+    def test_key_sql_injection(self):
+        with CaptureQueriesContext(connection) as queries:
+            self.assertFalse(
+                HStoreModel.objects.filter(**{
+                    "field__test' = 'a') OR 1 = 1 OR ('d": 'x',
+                }).exists()
+            )
+        self.assertIn(
+            """."field" -> 'test'' = ''a'') OR 1 = 1 OR (''d') = 'x' """,
+            queries[0]['sql'],
+        )
+
 
 class TestSerialization(PostgreSQLTestCase):
     test_data = ('[{"fields": {"field": "{\\"a\\": \\"b\\"}"}, '
diff --git a/tests/postgres_tests/test_json.py b/tests/postgres_tests/test_json.py
index 1978552..216fe77 100644
--- a/tests/postgres_tests/test_json.py
+++ b/tests/postgres_tests/test_json.py
@@ -1,10 +1,11 @@
 import datetime
 import unittest
 
-from django.core import exceptions, serializers
 from django.db import connection
+from django.core import exceptions, serializers
 from django.forms import CharField, Form
 from django.test import TestCase
+from django.test.utils import CaptureQueriesContext
 from django.utils.html import escape
 
 from . import PostgreSQLTestCase
@@ -236,6 +237,18 @@ class TestValidation(PostgreSQLTestCase):
         self.assertEqual(cm.exception.code, 'invalid')
         self.assertEqual(cm.exception.message % cm.exception.params, "Value must be valid JSON.")
 
+    def test_key_sql_injection(self):
+        with CaptureQueriesContext(connection) as queries:
+            self.assertFalse(
+                JSONModel.objects.filter(**{
+                    """field__test' = '"a"') OR 1 = 1 OR ('d""": 'x',
+                }).exists()
+            )
+        self.assertIn(
+            """."field" -> 'test'' = ''"a"'') OR 1 = 1 OR (''d') = '"x"' """,
+            queries[0]['sql'],
+        )
+
 
 class TestFormField(PostgreSQLTestCase):
 
