Parcourir la source

Fixes #22568: Fix ValueError exception when receiving an invalid filter_id value

Jeremy Stretch il y a 2 jours
Parent
commit
1dc6dbdb53
2 fichiers modifiés avec 53 ajouts et 2 suppressions
  1. 10 1
      netbox/netbox/filtersets.py
  2. 43 1
      netbox/utilities/tests/test_filters.py

+ 10 - 1
netbox/netbox/filtersets.py

@@ -108,9 +108,18 @@ class BaseFilterSet(django_filters.FilterSet):
         # Apply any referenced SavedFilters
         if data and ('filter' in data or 'filter_id' in data):
             data = data.copy()  # Get a mutable copy
+
+            # Coerce filter_id values to integers, ignoring any which are not valid (see #22568)
+            filter_ids = []
+            for f_id in data.pop('filter_id', []):
+                try:
+                    filter_ids.append(int(f_id))
+                except (ValueError, TypeError):
+                    pass
+
             saved_filters = SavedFilter.objects.filter(
                 Q(slug__in=data.pop('filter', [])) |
-                Q(pk__in=data.pop('filter_id', []))
+                Q(pk__in=filter_ids)
             )
             for sf in saved_filters:
                 for key, value in sf.parameters.items():

+ 43 - 1
netbox/utilities/tests/test_filters.py

@@ -1,10 +1,12 @@
 import django_filters
 from django.conf import settings
 from django.db import models
+from django.http import QueryDict
 from django.test import TestCase
 from mptt.fields import TreeForeignKey
 from taggit.managers import TaggableManager
 
+from core.models import ObjectType
 from dcim.choices import *
 from dcim.fields import MACAddressField
 from dcim.filtersets import DeviceFilterSet, InterfaceFilterSet, SiteFilterSet
@@ -21,7 +23,7 @@ from dcim.models import (
     Site,
 )
 from extras.filters import TagFilter
-from extras.models import TaggedItem
+from extras.models import SavedFilter, TaggedItem
 from ipam.filtersets import ASNFilterSet
 from ipam.models import ASN, RIR
 from netbox.filtersets import BaseFilterSet
@@ -670,3 +672,43 @@ class DynamicFilterLookupExpressionTestCase(TestCase):
         self.assertEqual(InterfaceFilterSet(params, Interface.objects.all()).qs.count(), 5)
         params = {'rf_role__empty': 'false'}
         self.assertEqual(InterfaceFilterSet(params, Interface.objects.all()).qs.count(), 1)
+
+
+class SavedFilterApplicationTestCase(TestCase):
+    """
+    Validate the application of referenced SavedFilters to a FilterSet's data.
+    """
+    @classmethod
+    def setUpTestData(cls):
+        sites = (
+            Site(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE),
+            Site(name='Site 2', slug='site-2', status=SiteStatusChoices.STATUS_PLANNED),
+        )
+        Site.objects.bulk_create(sites)
+
+        cls.saved_filter = SavedFilter.objects.create(
+            name='Active Sites',
+            slug='active-sites',
+            parameters={'status': [SiteStatusChoices.STATUS_ACTIVE]},
+        )
+        cls.saved_filter.object_types.set([ObjectType.objects.get_for_model(Site)])
+
+    def test_filter_id_valid(self):
+        # A referenced SavedFilter's parameters are applied to the queryset
+        data = QueryDict(f'filter_id={self.saved_filter.pk}')
+        self.assertEqual(SiteFilterSet(data, Site.objects.all()).qs.count(), 1)
+
+    def test_filter_id_nonexistent(self):
+        # A non-existent (but valid integer) filter_id is ignored
+        data = QueryDict('filter_id=999999')
+        self.assertEqual(SiteFilterSet(data, Site.objects.all()).qs.count(), 2)
+
+    def test_filter_id_non_integer(self):
+        # A non-integer filter_id is ignored rather than raising a ValueError (#22568)
+        data = QueryDict('filter_id=abc')
+        self.assertEqual(SiteFilterSet(data, Site.objects.all()).qs.count(), 2)
+
+    def test_filter_slug(self):
+        # A referenced SavedFilter may also be applied by slug
+        data = QueryDict('filter=active-sites')
+        self.assertEqual(SiteFilterSet(data, Site.objects.all()).qs.count(), 1)