We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Protect against Git-repo jobs clobbering sys.modules
### As ...

Patti - Platform Admin

### I want ...

To add Jobs via Git repositories without worrying about them impacting the overall stability of the platform.

After #3840 , there is a risk that a misnamed (or maliciously-named) Git repository could potentially clobber existing Python code. For example, creating a repository and assigning it the slug `nautobot` causes all sorts of havoc as it results in Nautobot unloading itself and then attempting to reimport all of Nautobot's code from the Git repository by that name.

### So that ...

The application is resilient to user error as well as mischievous/malicious actions by authenticated users.

### I know this is done when...

- Creating a GitRepository whose `slug` matches any currently loaded Python module in the Nautobot environment is rejected with an appropriate error message.

### Optional - Feature groups this request pertains to.

- [X] Automation
- [ ] Circuits
- [ ] DCIM
- [ ] IPAM
- [ ] Misc (including Data Sources)
- [ ] Organization
- [ ] Plugins (and other Extensibility)
- [X] Security (Secrets, etc)
- [ ] Image Management
- [ ] UI/UX
- [ ] Documentation
- [ ] Other (not directly a platform feature)

### Database Changes

None

### External Dependencies

None

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nautobot/extras/models/datasources.py`
Content:
```
1 """Models for representing external data sources."""
2 import os
3 
4 from django.conf import settings
5 from django.core.exceptions import ValidationError
6 from django.core.serializers.json import DjangoJSONEncoder
7 from django.core.validators import URLValidator
8 from django.db import models
9 
10 from nautobot.core.models.fields import AutoSlugField, slugify_dashes_to_underscores
11 from nautobot.core.models.generics import PrimaryModel
12 from nautobot.extras.utils import extras_features, check_if_key_is_graphql_safe
13 
14 
15 @extras_features(
16     "config_context_owners",
17     "export_template_owners",
18     "job_results",
19     "webhooks",
20 )
21 class GitRepository(PrimaryModel):
22     """Representation of a Git repository used as an external data source."""
23 
24     name = models.CharField(
25         max_length=100,
26         unique=True,
27     )
28     slug = AutoSlugField(
29         populate_from="name",
30         help_text="Internal field name. Please use underscores rather than dashes in this key.",
31         slugify_function=slugify_dashes_to_underscores,
32     )
33 
34     remote_url = models.URLField(
35         max_length=255,
36         # For the moment we don't support ssh:// and git:// URLs
37         help_text="Only HTTP and HTTPS URLs are presently supported",
38         validators=[URLValidator(schemes=["http", "https"])],
39     )
40     branch = models.CharField(
41         max_length=64,
42         default="main",
43     )
44 
45     current_head = models.CharField(
46         help_text="Commit hash of the most recent fetch from the selected branch. Used for syncing between workers.",
47         max_length=48,
48         default="",
49         blank=True,
50     )
51 
52     secrets_group = models.ForeignKey(
53         to="extras.SecretsGroup",
54         on_delete=models.SET_NULL,
55         default=None,
56         blank=True,
57         null=True,
58         related_name="git_repositories",
59     )
60 
61     # Data content types that this repo is a source of. Valid options are dynamically generated based on
62     # the data types registered in registry['datasource_contents'].
63     provided_contents = models.JSONField(encoder=DjangoJSONEncoder, default=list, blank=True)
64 
65     clone_fields = ["remote_url", "secrets_group", "provided_contents"]
66 
67     class Meta:
68         ordering = ["name"]
69         verbose_name = "Git repository"
70         verbose_name_plural = "Git repositories"
71 
72     def __init__(self, *args, **kwargs):
73         super().__init__(*args, **kwargs)
74 
75         # Store the initial repo slug so we can check for changes on save().
76         self.__initial_slug = self.slug
77 
78     def __str__(self):
79         return self.name
80 
81     def clean(self):
82         super().clean()
83 
84         if self.slug != "":
85             check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, "slug")
86 
87             if self.present_in_database and self.slug != self.__initial_slug:
88                 raise ValidationError(
89                     f"Slug cannot be changed once set. Current slug is {self.__initial_slug}, "
90                     f"requested slug is {self.slug}"
91                 )
92 
93     def get_latest_sync(self):
94         """
95         Return a `JobResult` for the latest sync operation.
96 
97         Returns:
98             JobResult
99         """
100         from nautobot.extras.models import JobResult
101 
102         # This will match all "GitRepository" jobs (pull/refresh, dry-run, etc.)
103         prefix = "nautobot.core.jobs.GitRepository"
104         return JobResult.objects.filter(task_name__startswith=prefix, task_kwargs__repository=self.pk).latest()
105 
106     def to_csv(self):
107         return (
108             self.name,
109             self.slug,
110             self.remote_url,
111             self.branch,
112             self.secrets_group.name if self.secrets_group else None,
113             self.provided_contents,
114         )
115 
116     @property
117     def filesystem_path(self):
118         return os.path.join(settings.GIT_ROOT, self.slug)
119 
120     def sync(self, user, dry_run=False):
121         """
122         Enqueue a Job to pull the Git repository from the remote and return the sync result.
123 
124         Args:
125             user (User): The User that will perform the sync.
126             dry_run (bool): If set, dry-run the Git sync.
127 
128         Returns:
129             JobResult
130         """
131         from nautobot.extras.datasources import (
132             enqueue_pull_git_repository_and_refresh_data,
133             enqueue_git_repository_diff_origin_and_local,
134         )
135 
136         if dry_run:
137             return enqueue_git_repository_diff_origin_and_local(self, user)
138         return enqueue_pull_git_repository_and_refresh_data(self, user)
139
```
--- 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/nautobot/extras/models/datasources.py b/nautobot/extras/models/datasources.py
--- a/nautobot/extras/models/datasources.py
+++ b/nautobot/extras/models/datasources.py
@@ -1,4 +1,5 @@
 """Models for representing external data sources."""
+from importlib.util import find_spec
 import os
 
 from django.conf import settings
@@ -81,13 +82,24 @@
     def clean(self):
         super().clean()
 
-        if self.slug != "":
-            check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, "slug")
+        # Autogenerate slug now, rather than in pre_save(), if not set already, as we need to check it below.
+        if self.slug == "":
+            self._meta.get_field("slug").create_slug(self, add=(not self.present_in_database))
+
+        if self.present_in_database and self.slug != self.__initial_slug:
+            raise ValidationError(
+                f"Slug cannot be changed once set. Current slug is {self.__initial_slug}, "
+                f"requested slug is {self.slug}"
+            )
 
-            if self.present_in_database and self.slug != self.__initial_slug:
+        if not self.present_in_database:
+            check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, "slug")
+            # Check on create whether the proposed slug conflicts with a module name already in the Python environment.
+            # Because we add GIT_ROOT to the end of sys.path, trying to import this repository will instead
+            # import the earlier-found Python module in its place, which would be undesirable.
+            if find_spec(self.slug) is not None:
                 raise ValidationError(
-                    f"Slug cannot be changed once set. Current slug is {self.__initial_slug}, "
-                    f"requested slug is {self.slug}"
+                    f'Please choose a different slug, as "{self.slug}" is an installed Python package or module.'
                 )
 
     def get_latest_sync(self):

{"golden_diff": "diff --git a/nautobot/extras/models/datasources.py b/nautobot/extras/models/datasources.py\n--- a/nautobot/extras/models/datasources.py\n+++ b/nautobot/extras/models/datasources.py\n@@ -1,4 +1,5 @@\n \"\"\"Models for representing external data sources.\"\"\"\n+from importlib.util import find_spec\n import os\n \n from django.conf import settings\n@@ -81,13 +82,24 @@\n     def clean(self):\n         super().clean()\n \n-        if self.slug != \"\":\n-            check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, \"slug\")\n+        # Autogenerate slug now, rather than in pre_save(), if not set already, as we need to check it below.\n+        if self.slug == \"\":\n+            self._meta.get_field(\"slug\").create_slug(self, add=(not self.present_in_database))\n+\n+        if self.present_in_database and self.slug != self.__initial_slug:\n+            raise ValidationError(\n+                f\"Slug cannot be changed once set. Current slug is {self.__initial_slug}, \"\n+                f\"requested slug is {self.slug}\"\n+            )\n \n-            if self.present_in_database and self.slug != self.__initial_slug:\n+        if not self.present_in_database:\n+            check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, \"slug\")\n+            # Check on create whether the proposed slug conflicts with a module name already in the Python environment.\n+            # Because we add GIT_ROOT to the end of sys.path, trying to import this repository will instead\n+            # import the earlier-found Python module in its place, which would be undesirable.\n+            if find_spec(self.slug) is not None:\n                 raise ValidationError(\n-                    f\"Slug cannot be changed once set. Current slug is {self.__initial_slug}, \"\n-                    f\"requested slug is {self.slug}\"\n+                    f'Please choose a different slug, as \"{self.slug}\" is an installed Python package or module.'\n                 )\n \n     def get_latest_sync(self):\n", "issue": "Protect against Git-repo jobs clobbering sys.modules\n### As ...\n\nPatti - Platform Admin\n\n### I want ...\n\nTo add Jobs via Git repositories without worrying about them impacting the overall stability of the platform.\r\n\r\nAfter #3840 , there is a risk that a misnamed (or maliciously-named) Git repository could potentially clobber existing Python code. For example, creating a repository and assigning it the slug `nautobot` causes all sorts of havoc as it results in Nautobot unloading itself and then attempting to reimport all of Nautobot's code from the Git repository by that name.\n\n### So that ...\n\nThe application is resilient to user error as well as mischievous/malicious actions by authenticated users.\n\n### I know this is done when...\n\n- Creating a GitRepository whose `slug` matches any currently loaded Python module in the Nautobot environment is rejected with an appropriate error message.\n\n### Optional - Feature groups this request pertains to.\n\n- [X] Automation\n- [ ] Circuits\n- [ ] DCIM\n- [ ] IPAM\n- [ ] Misc (including Data Sources)\n- [ ] Organization\n- [ ] Plugins (and other Extensibility)\n- [X] Security (Secrets, etc)\n- [ ] Image Management\n- [ ] UI/UX\n- [ ] Documentation\n- [ ] Other (not directly a platform feature)\n\n### Database Changes\n\nNone\n\n### External Dependencies\n\nNone\n", "before_files": [{"content": "\"\"\"Models for representing external data sources.\"\"\"\nimport os\n\nfrom django.conf import settings\nfrom django.core.exceptions import ValidationError\nfrom django.core.serializers.json import DjangoJSONEncoder\nfrom django.core.validators import URLValidator\nfrom django.db import models\n\nfrom nautobot.core.models.fields import AutoSlugField, slugify_dashes_to_underscores\nfrom nautobot.core.models.generics import PrimaryModel\nfrom nautobot.extras.utils import extras_features, check_if_key_is_graphql_safe\n\n\n@extras_features(\n    \"config_context_owners\",\n    \"export_template_owners\",\n    \"job_results\",\n    \"webhooks\",\n)\nclass GitRepository(PrimaryModel):\n    \"\"\"Representation of a Git repository used as an external data source.\"\"\"\n\n    name = models.CharField(\n        max_length=100,\n        unique=True,\n    )\n    slug = AutoSlugField(\n        populate_from=\"name\",\n        help_text=\"Internal field name. Please use underscores rather than dashes in this key.\",\n        slugify_function=slugify_dashes_to_underscores,\n    )\n\n    remote_url = models.URLField(\n        max_length=255,\n        # For the moment we don't support ssh:// and git:// URLs\n        help_text=\"Only HTTP and HTTPS URLs are presently supported\",\n        validators=[URLValidator(schemes=[\"http\", \"https\"])],\n    )\n    branch = models.CharField(\n        max_length=64,\n        default=\"main\",\n    )\n\n    current_head = models.CharField(\n        help_text=\"Commit hash of the most recent fetch from the selected branch. Used for syncing between workers.\",\n        max_length=48,\n        default=\"\",\n        blank=True,\n    )\n\n    secrets_group = models.ForeignKey(\n        to=\"extras.SecretsGroup\",\n        on_delete=models.SET_NULL,\n        default=None,\n        blank=True,\n        null=True,\n        related_name=\"git_repositories\",\n    )\n\n    # Data content types that this repo is a source of. Valid options are dynamically generated based on\n    # the data types registered in registry['datasource_contents'].\n    provided_contents = models.JSONField(encoder=DjangoJSONEncoder, default=list, blank=True)\n\n    clone_fields = [\"remote_url\", \"secrets_group\", \"provided_contents\"]\n\n    class Meta:\n        ordering = [\"name\"]\n        verbose_name = \"Git repository\"\n        verbose_name_plural = \"Git repositories\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        # Store the initial repo slug so we can check for changes on save().\n        self.__initial_slug = self.slug\n\n    def __str__(self):\n        return self.name\n\n    def clean(self):\n        super().clean()\n\n        if self.slug != \"\":\n            check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, \"slug\")\n\n            if self.present_in_database and self.slug != self.__initial_slug:\n                raise ValidationError(\n                    f\"Slug cannot be changed once set. Current slug is {self.__initial_slug}, \"\n                    f\"requested slug is {self.slug}\"\n                )\n\n    def get_latest_sync(self):\n        \"\"\"\n        Return a `JobResult` for the latest sync operation.\n\n        Returns:\n            JobResult\n        \"\"\"\n        from nautobot.extras.models import JobResult\n\n        # This will match all \"GitRepository\" jobs (pull/refresh, dry-run, etc.)\n        prefix = \"nautobot.core.jobs.GitRepository\"\n        return JobResult.objects.filter(task_name__startswith=prefix, task_kwargs__repository=self.pk).latest()\n\n    def to_csv(self):\n        return (\n            self.name,\n            self.slug,\n            self.remote_url,\n            self.branch,\n            self.secrets_group.name if self.secrets_group else None,\n            self.provided_contents,\n        )\n\n    @property\n    def filesystem_path(self):\n        return os.path.join(settings.GIT_ROOT, self.slug)\n\n    def sync(self, user, dry_run=False):\n        \"\"\"\n        Enqueue a Job to pull the Git repository from the remote and return the sync result.\n\n        Args:\n            user (User): The User that will perform the sync.\n            dry_run (bool): If set, dry-run the Git sync.\n\n        Returns:\n            JobResult\n        \"\"\"\n        from nautobot.extras.datasources import (\n            enqueue_pull_git_repository_and_refresh_data,\n            enqueue_git_repository_diff_origin_and_local,\n        )\n\n        if dry_run:\n            return enqueue_git_repository_diff_origin_and_local(self, user)\n        return enqueue_pull_git_repository_and_refresh_data(self, user)\n", "path": "nautobot/extras/models/datasources.py"}], "after_files": [{"content": "\"\"\"Models for representing external data sources.\"\"\"\nfrom importlib.util import find_spec\nimport os\n\nfrom django.conf import settings\nfrom django.core.exceptions import ValidationError\nfrom django.core.serializers.json import DjangoJSONEncoder\nfrom django.core.validators import URLValidator\nfrom django.db import models\n\nfrom nautobot.core.models.fields import AutoSlugField, slugify_dashes_to_underscores\nfrom nautobot.core.models.generics import PrimaryModel\nfrom nautobot.extras.utils import extras_features, check_if_key_is_graphql_safe\n\n\n@extras_features(\n    \"config_context_owners\",\n    \"export_template_owners\",\n    \"job_results\",\n    \"webhooks\",\n)\nclass GitRepository(PrimaryModel):\n    \"\"\"Representation of a Git repository used as an external data source.\"\"\"\n\n    name = models.CharField(\n        max_length=100,\n        unique=True,\n    )\n    slug = AutoSlugField(\n        populate_from=\"name\",\n        help_text=\"Internal field name. Please use underscores rather than dashes in this key.\",\n        slugify_function=slugify_dashes_to_underscores,\n    )\n\n    remote_url = models.URLField(\n        max_length=255,\n        # For the moment we don't support ssh:// and git:// URLs\n        help_text=\"Only HTTP and HTTPS URLs are presently supported\",\n        validators=[URLValidator(schemes=[\"http\", \"https\"])],\n    )\n    branch = models.CharField(\n        max_length=64,\n        default=\"main\",\n    )\n\n    current_head = models.CharField(\n        help_text=\"Commit hash of the most recent fetch from the selected branch. Used for syncing between workers.\",\n        max_length=48,\n        default=\"\",\n        blank=True,\n    )\n\n    secrets_group = models.ForeignKey(\n        to=\"extras.SecretsGroup\",\n        on_delete=models.SET_NULL,\n        default=None,\n        blank=True,\n        null=True,\n        related_name=\"git_repositories\",\n    )\n\n    # Data content types that this repo is a source of. Valid options are dynamically generated based on\n    # the data types registered in registry['datasource_contents'].\n    provided_contents = models.JSONField(encoder=DjangoJSONEncoder, default=list, blank=True)\n\n    clone_fields = [\"remote_url\", \"secrets_group\", \"provided_contents\"]\n\n    class Meta:\n        ordering = [\"name\"]\n        verbose_name = \"Git repository\"\n        verbose_name_plural = \"Git repositories\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        # Store the initial repo slug so we can check for changes on save().\n        self.__initial_slug = self.slug\n\n    def __str__(self):\n        return self.name\n\n    def clean(self):\n        super().clean()\n\n        # Autogenerate slug now, rather than in pre_save(), if not set already, as we need to check it below.\n        if self.slug == \"\":\n            self._meta.get_field(\"slug\").create_slug(self, add=(not self.present_in_database))\n\n        if self.present_in_database and self.slug != self.__initial_slug:\n            raise ValidationError(\n                f\"Slug cannot be changed once set. Current slug is {self.__initial_slug}, \"\n                f\"requested slug is {self.slug}\"\n            )\n\n        if not self.present_in_database:\n            check_if_key_is_graphql_safe(self.__class__.__name__, self.slug, \"slug\")\n            # Check on create whether the proposed slug conflicts with a module name already in the Python environment.\n            # Because we add GIT_ROOT to the end of sys.path, trying to import this repository will instead\n            # import the earlier-found Python module in its place, which would be undesirable.\n            if find_spec(self.slug) is not None:\n                raise ValidationError(\n                    f'Please choose a different slug, as \"{self.slug}\" is an installed Python package or module.'\n                )\n\n    def get_latest_sync(self):\n        \"\"\"\n        Return a `JobResult` for the latest sync operation.\n\n        Returns:\n            JobResult\n        \"\"\"\n        from nautobot.extras.models import JobResult\n\n        # This will match all \"GitRepository\" jobs (pull/refresh, dry-run, etc.)\n        prefix = \"nautobot.core.jobs.GitRepository\"\n        return JobResult.objects.filter(task_name__startswith=prefix, task_kwargs__repository=self.pk).latest()\n\n    def to_csv(self):\n        return (\n            self.name,\n            self.slug,\n            self.remote_url,\n            self.branch,\n            self.secrets_group.name if self.secrets_group else None,\n            self.provided_contents,\n        )\n\n    @property\n    def filesystem_path(self):\n        return os.path.join(settings.GIT_ROOT, self.slug)\n\n    def sync(self, user, dry_run=False):\n        \"\"\"\n        Enqueue a Job to pull the Git repository from the remote and return the sync result.\n\n        Args:\n            user (User): The User that will perform the sync.\n            dry_run (bool): If set, dry-run the Git sync.\n\n        Returns:\n            JobResult\n        \"\"\"\n        from nautobot.extras.datasources import (\n            enqueue_pull_git_repository_and_refresh_data,\n            enqueue_git_repository_diff_origin_and_local,\n        )\n\n        if dry_run:\n            return enqueue_git_repository_diff_origin_and_local(self, user)\n        return enqueue_pull_git_repository_and_refresh_data(self, user)\n", "path": "nautobot/extras/models/datasources.py"}]}