We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove eval fallback from EncryptedJSONField
#4359

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pulpcore/app/models/fields.py`
Content:
```
1 import json
2 import logging
3 import os
4 from functools import lru_cache
5 from gettext import gettext as _
6 
7 from cryptography.fernet import Fernet, MultiFernet
8 from django.conf import settings
9 from django.core.exceptions import ImproperlyConfigured
10 from django.db.models import FileField, JSONField, Lookup
11 from django.db.models.fields import Field, TextField
12 from django.utils.encoding import force_bytes, force_str
13 from pulpcore.app.files import TemporaryDownloadedFile
14 from pulpcore.app.loggers import deprecation_logger
15 
16 _logger = logging.getLogger(__name__)
17 
18 
19 @lru_cache(maxsize=1)
20 def _fernet():
21     # Cache the enryption keys once per application.
22     _logger.debug(f"Loading encryption key from {settings.DB_ENCRYPTION_KEY}")
23     with open(settings.DB_ENCRYPTION_KEY, "rb") as key_file:
24         return MultiFernet(
25             [
26                 Fernet(key.strip())
27                 for key in key_file.readlines()
28                 if not key.startswith(b"#") and key.strip() != b""
29             ]
30         )
31 
32 
33 class ArtifactFileField(FileField):
34     """
35     A custom FileField that always saves files to location specified by 'upload_to'.
36 
37     The field can be set as either a path to the file or File object. In both cases the file is
38     moved or copied to the location specified by 'upload_to' field parameter.
39     """
40 
41     def pre_save(self, model_instance, add):
42         """
43         Return FieldFile object which specifies path to the file to be stored in database.
44 
45         There are two ways to get artifact into Pulp: sync and upload.
46 
47         The upload case
48          - file is not stored yet, aka file._committed = False
49          - nothing to do here in addition to Django pre_save actions
50 
51         The sync case:
52          - file is already stored in a temporary location, aka file._committed = True
53          - it needs to be moved into Pulp artifact storage if it's not there
54          - TemporaryDownloadedFile takes care of correctly set storage path
55          - only then Django pre_save actions should be performed
56 
57         Args:
58             model_instance (`class::pulpcore.plugin.Artifact`): The instance this field belongs to.
59             add (bool): Whether the instance is being saved to the database for the first time.
60                         Ignored by Django pre_save method.
61 
62         Returns:
63             FieldFile object just before saving.
64 
65         """
66         file = model_instance.file
67         artifact_storage_path = self.upload_to(model_instance, "")
68 
69         already_in_place = file.name in [
70             artifact_storage_path,
71             os.path.join(settings.MEDIA_ROOT, artifact_storage_path),
72         ]
73         is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, "artifact"))
74 
75         if not already_in_place and is_in_artifact_storage:
76             raise ValueError(
77                 _(
78                     "The file referenced by the Artifact is already present in "
79                     "Artifact storage. Files must be stored outside this location "
80                     "prior to Artifact creation."
81                 )
82             )
83 
84         move = file._committed and file.name != artifact_storage_path
85         if move:
86             if not already_in_place:
87                 file._file = TemporaryDownloadedFile(open(file.name, "rb"))
88             file._committed = False
89 
90         return super().pre_save(model_instance, add)
91 
92 
93 class EncryptedTextField(TextField):
94     """A field mixin that encrypts text using settings.DB_ENCRYPTION_KEY."""
95 
96     def __init__(self, *args, **kwargs):
97         if kwargs.get("primary_key"):
98             raise ImproperlyConfigured("EncryptedTextField does not support primary_key=True.")
99         if kwargs.get("unique"):
100             raise ImproperlyConfigured("EncryptedTextField does not support unique=True.")
101         if kwargs.get("db_index"):
102             raise ImproperlyConfigured("EncryptedTextField does not support db_index=True.")
103         super().__init__(*args, **kwargs)
104 
105     def get_prep_value(self, value):
106         if value is not None:
107             assert isinstance(value, str)
108             value = force_str(_fernet().encrypt(force_bytes(value)))
109         return super().get_prep_value(value)
110 
111     def from_db_value(self, value, expression, connection):
112         if value is not None:
113             value = force_str(_fernet().decrypt(force_bytes(value)))
114         return value
115 
116 
117 class EncryptedJSONField(JSONField):
118     """A Field mixin that encrypts the JSON text using settings.DP_ENCRYPTION_KEY."""
119 
120     def __init__(self, *args, **kwargs):
121         if kwargs.get("primary_key"):
122             raise ImproperlyConfigured("EncryptedJSONField does not support primary_key=True.")
123         if kwargs.get("unique"):
124             raise ImproperlyConfigured("EncryptedJSONField does not support unique=True.")
125         if kwargs.get("db_index"):
126             raise ImproperlyConfigured("EncryptedJSONField does not support db_index=True.")
127         super().__init__(*args, **kwargs)
128 
129     def encrypt(self, value):
130         if isinstance(value, dict):
131             return {k: self.encrypt(v) for k, v in value.items()}
132         elif isinstance(value, (list, tuple, set)):
133             return [self.encrypt(v) for v in value]
134 
135         return force_str(_fernet().encrypt(force_bytes(json.dumps(value, cls=self.encoder))))
136 
137     def decrypt(self, value):
138         if isinstance(value, dict):
139             return {k: self.decrypt(v) for k, v in value.items()}
140         elif isinstance(value, (list, tuple, set)):
141             return [self.decrypt(v) for v in value]
142 
143         dec_value = force_str(_fernet().decrypt(force_bytes(value)))
144         try:
145             return json.loads(dec_value, cls=self.decoder)
146         except json.JSONDecodeError:
147             deprecation_logger.info(
148                 "Failed to decode json in an EncryptedJSONField. Falling back to eval. "
149                 "Please run pulpcore-manager rotate-db-key to repair."
150                 "This is deprecated and will be removed in pulpcore 3.40."
151             )
152             return eval(dec_value)
153 
154     def get_prep_value(self, value):
155         if value is not None:
156             if hasattr(value, "as_sql"):
157                 return value
158             value = self.encrypt(value)
159         return super().get_prep_value(value)
160 
161     def from_db_value(self, value, expression, connection):
162         if value is not None:
163             value = self.decrypt(super().from_db_value(value, expression, connection))
164         return value
165 
166 
167 @Field.register_lookup
168 class NotEqualLookup(Lookup):
169     # this is copied from https://docs.djangoproject.com/en/3.2/howto/custom-lookups/
170     lookup_name = "ne"
171 
172     def as_sql(self, compiler, connection):
173         lhs, lhs_params = self.process_lhs(compiler, connection)
174         rhs, rhs_params = self.process_rhs(compiler, connection)
175         params = lhs_params + rhs_params
176         return "%s <> %s" % (lhs, rhs), params
177
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pulpcore/app/models/fields.py b/pulpcore/app/models/fields.py
--- a/pulpcore/app/models/fields.py
+++ b/pulpcore/app/models/fields.py
@@ -11,7 +11,6 @@
 from django.db.models.fields import Field, TextField
 from django.utils.encoding import force_bytes, force_str
 from pulpcore.app.files import TemporaryDownloadedFile
-from pulpcore.app.loggers import deprecation_logger
 
 _logger = logging.getLogger(__name__)
 
@@ -141,15 +140,7 @@
             return [self.decrypt(v) for v in value]
 
         dec_value = force_str(_fernet().decrypt(force_bytes(value)))
-        try:
-            return json.loads(dec_value, cls=self.decoder)
-        except json.JSONDecodeError:
-            deprecation_logger.info(
-                "Failed to decode json in an EncryptedJSONField. Falling back to eval. "
-                "Please run pulpcore-manager rotate-db-key to repair."
-                "This is deprecated and will be removed in pulpcore 3.40."
-            )
-            return eval(dec_value)
+        return json.loads(dec_value, cls=self.decoder)
 
     def get_prep_value(self, value):
         if value is not None:

{"golden_diff": "diff --git a/pulpcore/app/models/fields.py b/pulpcore/app/models/fields.py\n--- a/pulpcore/app/models/fields.py\n+++ b/pulpcore/app/models/fields.py\n@@ -11,7 +11,6 @@\n from django.db.models.fields import Field, TextField\n from django.utils.encoding import force_bytes, force_str\n from pulpcore.app.files import TemporaryDownloadedFile\n-from pulpcore.app.loggers import deprecation_logger\n \n _logger = logging.getLogger(__name__)\n \n@@ -141,15 +140,7 @@\n             return [self.decrypt(v) for v in value]\n \n         dec_value = force_str(_fernet().decrypt(force_bytes(value)))\n-        try:\n-            return json.loads(dec_value, cls=self.decoder)\n-        except json.JSONDecodeError:\n-            deprecation_logger.info(\n-                \"Failed to decode json in an EncryptedJSONField. Falling back to eval. \"\n-                \"Please run pulpcore-manager rotate-db-key to repair.\"\n-                \"This is deprecated and will be removed in pulpcore 3.40.\"\n-            )\n-            return eval(dec_value)\n+        return json.loads(dec_value, cls=self.decoder)\n \n     def get_prep_value(self, value):\n         if value is not None:\n", "issue": "Remove eval fallback from EncryptedJSONField\n#4359\n", "before_files": [{"content": "import json\nimport logging\nimport os\nfrom functools import lru_cache\nfrom gettext import gettext as _\n\nfrom cryptography.fernet import Fernet, MultiFernet\nfrom django.conf import settings\nfrom django.core.exceptions import ImproperlyConfigured\nfrom django.db.models import FileField, JSONField, Lookup\nfrom django.db.models.fields import Field, TextField\nfrom django.utils.encoding import force_bytes, force_str\nfrom pulpcore.app.files import TemporaryDownloadedFile\nfrom pulpcore.app.loggers import deprecation_logger\n\n_logger = logging.getLogger(__name__)\n\n\n@lru_cache(maxsize=1)\ndef _fernet():\n    # Cache the enryption keys once per application.\n    _logger.debug(f\"Loading encryption key from {settings.DB_ENCRYPTION_KEY}\")\n    with open(settings.DB_ENCRYPTION_KEY, \"rb\") as key_file:\n        return MultiFernet(\n            [\n                Fernet(key.strip())\n                for key in key_file.readlines()\n                if not key.startswith(b\"#\") and key.strip() != b\"\"\n            ]\n        )\n\n\nclass ArtifactFileField(FileField):\n    \"\"\"\n    A custom FileField that always saves files to location specified by 'upload_to'.\n\n    The field can be set as either a path to the file or File object. In both cases the file is\n    moved or copied to the location specified by 'upload_to' field parameter.\n    \"\"\"\n\n    def pre_save(self, model_instance, add):\n        \"\"\"\n        Return FieldFile object which specifies path to the file to be stored in database.\n\n        There are two ways to get artifact into Pulp: sync and upload.\n\n        The upload case\n         - file is not stored yet, aka file._committed = False\n         - nothing to do here in addition to Django pre_save actions\n\n        The sync case:\n         - file is already stored in a temporary location, aka file._committed = True\n         - it needs to be moved into Pulp artifact storage if it's not there\n         - TemporaryDownloadedFile takes care of correctly set storage path\n         - only then Django pre_save actions should be performed\n\n        Args:\n            model_instance (`class::pulpcore.plugin.Artifact`): The instance this field belongs to.\n            add (bool): Whether the instance is being saved to the database for the first time.\n                        Ignored by Django pre_save method.\n\n        Returns:\n            FieldFile object just before saving.\n\n        \"\"\"\n        file = model_instance.file\n        artifact_storage_path = self.upload_to(model_instance, \"\")\n\n        already_in_place = file.name in [\n            artifact_storage_path,\n            os.path.join(settings.MEDIA_ROOT, artifact_storage_path),\n        ]\n        is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, \"artifact\"))\n\n        if not already_in_place and is_in_artifact_storage:\n            raise ValueError(\n                _(\n                    \"The file referenced by the Artifact is already present in \"\n                    \"Artifact storage. Files must be stored outside this location \"\n                    \"prior to Artifact creation.\"\n                )\n            )\n\n        move = file._committed and file.name != artifact_storage_path\n        if move:\n            if not already_in_place:\n                file._file = TemporaryDownloadedFile(open(file.name, \"rb\"))\n            file._committed = False\n\n        return super().pre_save(model_instance, add)\n\n\nclass EncryptedTextField(TextField):\n    \"\"\"A field mixin that encrypts text using settings.DB_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            assert isinstance(value, str)\n            value = force_str(_fernet().encrypt(force_bytes(value)))\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = force_str(_fernet().decrypt(force_bytes(value)))\n        return value\n\n\nclass EncryptedJSONField(JSONField):\n    \"\"\"A Field mixin that encrypts the JSON text using settings.DP_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def encrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.encrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.encrypt(v) for v in value]\n\n        return force_str(_fernet().encrypt(force_bytes(json.dumps(value, cls=self.encoder))))\n\n    def decrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.decrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.decrypt(v) for v in value]\n\n        dec_value = force_str(_fernet().decrypt(force_bytes(value)))\n        try:\n            return json.loads(dec_value, cls=self.decoder)\n        except json.JSONDecodeError:\n            deprecation_logger.info(\n                \"Failed to decode json in an EncryptedJSONField. Falling back to eval. \"\n                \"Please run pulpcore-manager rotate-db-key to repair.\"\n                \"This is deprecated and will be removed in pulpcore 3.40.\"\n            )\n            return eval(dec_value)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            if hasattr(value, \"as_sql\"):\n                return value\n            value = self.encrypt(value)\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = self.decrypt(super().from_db_value(value, expression, connection))\n        return value\n\n\n@Field.register_lookup\nclass NotEqualLookup(Lookup):\n    # this is copied from https://docs.djangoproject.com/en/3.2/howto/custom-lookups/\n    lookup_name = \"ne\"\n\n    def as_sql(self, compiler, connection):\n        lhs, lhs_params = self.process_lhs(compiler, connection)\n        rhs, rhs_params = self.process_rhs(compiler, connection)\n        params = lhs_params + rhs_params\n        return \"%s <> %s\" % (lhs, rhs), params\n", "path": "pulpcore/app/models/fields.py"}], "after_files": [{"content": "import json\nimport logging\nimport os\nfrom functools import lru_cache\nfrom gettext import gettext as _\n\nfrom cryptography.fernet import Fernet, MultiFernet\nfrom django.conf import settings\nfrom django.core.exceptions import ImproperlyConfigured\nfrom django.db.models import FileField, JSONField, Lookup\nfrom django.db.models.fields import Field, TextField\nfrom django.utils.encoding import force_bytes, force_str\nfrom pulpcore.app.files import TemporaryDownloadedFile\n\n_logger = logging.getLogger(__name__)\n\n\n@lru_cache(maxsize=1)\ndef _fernet():\n    # Cache the enryption keys once per application.\n    _logger.debug(f\"Loading encryption key from {settings.DB_ENCRYPTION_KEY}\")\n    with open(settings.DB_ENCRYPTION_KEY, \"rb\") as key_file:\n        return MultiFernet(\n            [\n                Fernet(key.strip())\n                for key in key_file.readlines()\n                if not key.startswith(b\"#\") and key.strip() != b\"\"\n            ]\n        )\n\n\nclass ArtifactFileField(FileField):\n    \"\"\"\n    A custom FileField that always saves files to location specified by 'upload_to'.\n\n    The field can be set as either a path to the file or File object. In both cases the file is\n    moved or copied to the location specified by 'upload_to' field parameter.\n    \"\"\"\n\n    def pre_save(self, model_instance, add):\n        \"\"\"\n        Return FieldFile object which specifies path to the file to be stored in database.\n\n        There are two ways to get artifact into Pulp: sync and upload.\n\n        The upload case\n         - file is not stored yet, aka file._committed = False\n         - nothing to do here in addition to Django pre_save actions\n\n        The sync case:\n         - file is already stored in a temporary location, aka file._committed = True\n         - it needs to be moved into Pulp artifact storage if it's not there\n         - TemporaryDownloadedFile takes care of correctly set storage path\n         - only then Django pre_save actions should be performed\n\n        Args:\n            model_instance (`class::pulpcore.plugin.Artifact`): The instance this field belongs to.\n            add (bool): Whether the instance is being saved to the database for the first time.\n                        Ignored by Django pre_save method.\n\n        Returns:\n            FieldFile object just before saving.\n\n        \"\"\"\n        file = model_instance.file\n        artifact_storage_path = self.upload_to(model_instance, \"\")\n\n        already_in_place = file.name in [\n            artifact_storage_path,\n            os.path.join(settings.MEDIA_ROOT, artifact_storage_path),\n        ]\n        is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, \"artifact\"))\n\n        if not already_in_place and is_in_artifact_storage:\n            raise ValueError(\n                _(\n                    \"The file referenced by the Artifact is already present in \"\n                    \"Artifact storage. Files must be stored outside this location \"\n                    \"prior to Artifact creation.\"\n                )\n            )\n\n        move = file._committed and file.name != artifact_storage_path\n        if move:\n            if not already_in_place:\n                file._file = TemporaryDownloadedFile(open(file.name, \"rb\"))\n            file._committed = False\n\n        return super().pre_save(model_instance, add)\n\n\nclass EncryptedTextField(TextField):\n    \"\"\"A field mixin that encrypts text using settings.DB_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            assert isinstance(value, str)\n            value = force_str(_fernet().encrypt(force_bytes(value)))\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = force_str(_fernet().decrypt(force_bytes(value)))\n        return value\n\n\nclass EncryptedJSONField(JSONField):\n    \"\"\"A Field mixin that encrypts the JSON text using settings.DP_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def encrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.encrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.encrypt(v) for v in value]\n\n        return force_str(_fernet().encrypt(force_bytes(json.dumps(value, cls=self.encoder))))\n\n    def decrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.decrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.decrypt(v) for v in value]\n\n        dec_value = force_str(_fernet().decrypt(force_bytes(value)))\n        return json.loads(dec_value, cls=self.decoder)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            if hasattr(value, \"as_sql\"):\n                return value\n            value = self.encrypt(value)\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = self.decrypt(super().from_db_value(value, expression, connection))\n        return value\n\n\n@Field.register_lookup\nclass NotEqualLookup(Lookup):\n    # this is copied from https://docs.djangoproject.com/en/3.2/howto/custom-lookups/\n    lookup_name = \"ne\"\n\n    def as_sql(self, compiler, connection):\n        lhs, lhs_params = self.process_lhs(compiler, connection)\n        rhs, rhs_params = self.process_rhs(compiler, connection)\n        params = lhs_params + rhs_params\n        return \"%s <> %s\" % (lhs, rhs), params\n", "path": "pulpcore/app/models/fields.py"}]}