CVE-2026-34203
Description
Nautobot is a Network Source of Truth and Network Automation Platform. Prior to versions 2.4.30 and 3.0.10, user creation and editing via the REST API fails to apply the password validation rules defined by Django's AUTH_PASSWORD_VALIDATORS setting (which defaults to an empty list, i.e., no specific rules, but can be configured in Nautobot's nautobot_config.py to apply various rules if desired). This can potentially allow for the creation or modification of users to have passwords that are weak or otherwise do not comply with configured standards. This issue has been patched in versions 2.4.30 and 3.0.10.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
nautobotPyPI | < 2.4.30 | 2.4.30 |
nautobotPyPI | >= 3.0.0, < 3.0.10 | 3.0.10 |
Affected products
1Patches
2d1ef3135aa02[3.0] Add call to validate_password when managing users via REST API (#8778)
5 files changed · +89 −0
changes/8778.security+1 −0 added@@ -0,0 +1 @@ +Added missing enforcement of any configured Django password validators when managing users via the REST API (CVE-2026-34203).
nautobot/core/settings.yaml+14 −0 modified@@ -113,6 +113,20 @@ properties: items: type: "string" type: "array" + AUTH_PASSWORD_VALIDATORS: + default: [] + description: >- + A list of dictionaries, describing password validator classes (as strings) and any associated configuration, to + manage validation of passwords for local user accounts. By default, this is an empty list, meaning any password + is permitted. + + A future release of Nautobot may change the default value of this setting to increase security. + items: + type: "object" + see_also: + "Django documentation for `AUTH_PASSWORD_VALIDATORS`": "https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS" + "Django documentation for password validators": "https://docs.djangoproject.com/en/stable/topics/auth/passwords/#module-django.contrib.auth.password_validation" + type: "array" AUTHENTICATION_BACKENDS: default: - "nautobot.core.authentication.ObjectPermissionBackend"
nautobot/docs/user-guide/administration/security/notices.md+36 −0 modified@@ -4,6 +4,42 @@ As a part of the Nautobot development team's commitment to security, we maintain <!-- pyml disable-num-lines 500 proper-names --> +## CVE-2026-34203 + +<!-- pyml disable-next-line no-inline-html --> +<table> + <tr> + <th>Disclosure Date</th> + <td>March 30, 2026</td> + </tr> + <tr> + <th>Summary</th> + <td>User creation and editing via the REST API failed to apply the password validation rules defined by Django's <code>AUTH_PASSWORD_VALIDATORS</code> setting (which defaults to an empty list, i.e., no specific rules, but can be configured in Nautobot's <code>nautobot_config.py</code> to apply various rules if desired). This could potentially allow for the creation or modification of users to have passwords that are weak or otherwise do not comply with configured standards.</td> + </tr> + <tr> + <th>Full Description</th> + <td><a href="https://github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873">GHSA-xmpv-j7p2-j873</a></td> + </tr> + <tr> + <th>Affected Versions</th> + <td> + <ul> + <li><2.4.30</li> + <li>≥3.0.0, <3.0.10</li> + </ul> + </td> + </tr> + <tr> + <th>Patched Versions</th> + <td> + <ul> + <li>2.4.30 (<a href="https://github.com/nautobot/nautobot/pull/8779">patch</a>)</li> + <li>3.0.10 (<a href="https://github.com/nautobot/nautobot/pull/8778">patch</a>)</li> + </ul> + </td> + </tr> +</table> + ## CVE-2025-49142 <!-- pyml disable-next-line no-inline-html -->
nautobot/users/api/serializers.py+3 −0 modified@@ -1,6 +1,7 @@ from django.contrib.auth import authenticate, get_user_model from django.contrib.auth.hashers import make_password from django.contrib.auth.models import Group +from django.contrib.auth.password_validation import validate_password from django.contrib.contenttypes.models import ContentType from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -27,6 +28,8 @@ def validate(self, attrs): validated_data = super().validate(attrs) if mock_password: validated_data["password"] = None + elif "password" in validated_data: + validate_password(validated_data["password"], user=self.instance) return validated_data def create(self, validated_data):
nautobot/users/tests/test_api.py+35 −0 modified@@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType +from django.test import override_settings from django.urls import reverse from django.utils.timezone import now from rest_framework import HTTP_HEADER_ENCODING, status @@ -99,6 +100,20 @@ def test_create_object(self): else: self.assertFalse(user.has_usable_password()) + @override_settings( + AUTH_PASSWORD_VALIDATORS=[ + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": {"min_length": 9}}, + ], + ) + def test_create_object_password_validation(self): + """Check for https://github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873 on user creation.""" + self.add_permissions("users.add_user") + response = self.client.post( + self._get_list_url(), {"username": "weakuser", "password": "weak"}, format="json", **self.header + ) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertFalse(User.objects.filter(username="weakuser").exists()) + def test_recreate_object_csv(self): """Add validation that the recreated user has no password.""" super().test_recreate_object_csv() @@ -118,6 +133,26 @@ def test_update_object(self): user.refresh_from_db() self.assertTrue(user.check_password(self.update_data["password"])) + @override_settings( + AUTH_PASSWORD_VALIDATORS=[ + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": {"min_length": 9}}, + ], + ) + def test_update_object_password_validation(self): + """Check for https://github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873 on user update.""" + self.add_permissions("users.change_user") + user = self.get_deletable_object() + user.set_password("sufficiently_strong_for_this_test") + user.save() + response = self.client.patch( + self._get_detail_url(user), {"username": "newusername", "password": "weak"}, format="json", **self.header + ) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + user.refresh_from_db() + self.assertNotEqual(user.username, "newusername") + self.assertTrue(user.check_password("sufficiently_strong_for_this_test")) + self.assertFalse(user.check_password("weak")) + def test_get_put_round_trip(self): """Add validation that the password is cleared by a PUT with no specified password.""" super().test_get_put_round_trip()
589f7caf5412[2.4] Add call to validate_password when managing users via REST API (#8779)
5 files changed · +89 −0
changes/8779.security+1 −0 added@@ -0,0 +1 @@ +Added missing enforcement of any configured Django password validators when managing users via the REST API (CVE-2026-34203).
nautobot/core/settings.yaml+14 −0 modified@@ -113,6 +113,20 @@ properties: items: type: "string" type: "array" + AUTH_PASSWORD_VALIDATORS: + default: [] + description: >- + A list of dictionaries, describing password validator classes (as strings) and any associated configuration, to + manage validation of passwords for local user accounts. By default, this is an empty list, meaning any password + is permitted. + + A future release of Nautobot may change the default value of this setting to increase security. + items: + type: "object" + see_also: + "Django documentation for `AUTH_PASSWORD_VALIDATORS`": "https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS" + "Django documentation for password validators": "https://docs.djangoproject.com/en/stable/topics/auth/passwords/#module-django.contrib.auth.password_validation" + type: "array" AUTHENTICATION_BACKENDS: default: - "nautobot.core.authentication.ObjectPermissionBackend"
nautobot/docs/user-guide/administration/security/notices.md+36 −0 modified@@ -4,6 +4,42 @@ As a part of the Nautobot development team's commitment to security, we maintain <!-- pyml disable-num-lines 500 proper-names --> +## CVE-2026-34203 + +<!-- pyml disable-next-line no-inline-html --> +<table> + <tr> + <th>Disclosure Date</th> + <td>March 30, 2026</td> + </tr> + <tr> + <th>Summary</th> + <td>User creation and editing via the REST API failed to apply the password validation rules defined by Django's <code>AUTH_PASSWORD_VALIDATORS</code> setting (which defaults to an empty list, i.e., no specific rules, but can be configured in Nautobot's <code>nautobot_config.py</code> to apply various rules if desired). This could potentially allow for the creation or modification of users to have passwords that are weak or otherwise do not comply with configured standards.</td> + </tr> + <tr> + <th>Full Description</th> + <td><a href="https://github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873">GHSA-xmpv-j7p2-j873</a></td> + </tr> + <tr> + <th>Affected Versions</th> + <td> + <ul> + <li><2.4.30</li> + <li>≥3.0.0, <3.0.10</li> + </ul> + </td> + </tr> + <tr> + <th>Patched Versions</th> + <td> + <ul> + <li>2.4.30 (<a href="https://github.com/nautobot/nautobot/pull/8779">patch</a>)</li> + <li>3.0.10 (<a href="https://github.com/nautobot/nautobot/pull/8778">patch</a>)</li> + </ul> + </td> + </tr> +</table> + ## CVE-2025-49142 <!-- pyml disable-next-line no-inline-html -->
nautobot/users/api/serializers.py+3 −0 modified@@ -1,6 +1,7 @@ from django.contrib.auth import authenticate, get_user_model from django.contrib.auth.hashers import make_password from django.contrib.auth.models import Group +from django.contrib.auth.password_validation import validate_password from django.contrib.contenttypes.models import ContentType from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -27,6 +28,8 @@ def validate(self, attrs): validated_data = super().validate(attrs) if mock_password: validated_data["password"] = None + elif "password" in validated_data: + validate_password(validated_data["password"], user=self.instance) return validated_data def create(self, validated_data):
nautobot/users/tests/test_api.py+35 −0 modified@@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType +from django.test import override_settings from django.urls import reverse from django.utils.timezone import now from rest_framework import HTTP_HEADER_ENCODING, status @@ -99,6 +100,20 @@ def test_create_object(self): else: self.assertFalse(user.has_usable_password()) + @override_settings( + AUTH_PASSWORD_VALIDATORS=[ + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": {"min_length": 9}}, + ], + ) + def test_create_object_password_validation(self): + """Check for https://github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873 on user creation.""" + self.add_permissions("users.add_user") + response = self.client.post( + self._get_list_url(), {"username": "weakuser", "password": "weak"}, format="json", **self.header + ) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertFalse(User.objects.filter(username="weakuser").exists()) + def test_recreate_object_csv(self): """Add validation that the recreated user has no password.""" super().test_recreate_object_csv() @@ -118,6 +133,26 @@ def test_update_object(self): user.refresh_from_db() self.assertTrue(user.check_password(self.update_data["password"])) + @override_settings( + AUTH_PASSWORD_VALIDATORS=[ + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": {"min_length": 9}}, + ], + ) + def test_update_object_password_validation(self): + """Check for https://github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873 on user update.""" + self.add_permissions("users.change_user") + user = self.get_deletable_object() + user.set_password("sufficiently_strong_for_this_test") + user.save() + response = self.client.patch( + self._get_detail_url(user), {"username": "newusername", "password": "weak"}, format="json", **self.header + ) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + user.refresh_from_db() + self.assertNotEqual(user.username, "newusername") + self.assertTrue(user.check_password("sufficiently_strong_for_this_test")) + self.assertFalse(user.check_password("weak")) + def test_get_put_round_trip(self): """Add validation that the password is cleared by a PUT with no specified password.""" super().test_get_put_round_trip()
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/nautobot/nautobot/commit/589f7caf54124ad76bc9fcbb7bdcaa25627cd598nvdPatchWEB
- github.com/nautobot/nautobot/commit/d1ef3135aa02fa07de061e8c085f8cce425fe8c9nvdPatchWEB
- github.com/nautobot/nautobot/pull/8778nvdIssue TrackingPatchWEB
- github.com/nautobot/nautobot/pull/8779nvdIssue TrackingPatchWEB
- github.com/nautobot/nautobot/security/advisories/GHSA-xmpv-j7p2-j873nvdMitigationPatchVendor AdvisoryWEB
- github.com/advisories/GHSA-xmpv-j7p2-j873ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-34203ghsaADVISORY
News mentions
0No linked articles in our index yet.