Skip to content

Commit

Permalink
Version 2.4 (#1272)
Browse files Browse the repository at this point in the history
* Added MaxValueValidator to NumberFilter.
* Update docs copyright year.
* Droped using bumpversion.
  • Loading branch information
carltongibson committed Sep 27, 2020
1 parent 2ebce74 commit 340cf7a
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 33 deletions.
24 changes: 0 additions & 24 deletions .bumpversion.cfg

This file was deleted.

17 changes: 17 additions & 0 deletions CHANGES.rst
@@ -1,3 +1,20 @@
Version 2.4.0 (2020-9-27)
--------------------------

* SECURITY: Added a ``MaxValueValidator`` to the form field for
``NumberFilter``. This prevents a potential DoS attack if numbers with very
large exponents were subsequently converted to integers.

The default limit value for the validator is ``1e50``.

The new ``NumberFilter.get_max_validator()`` allows customising the used
validator, and may return ``None`` to disable the validation entirely.

* Added testing against Django 3.1 and Python 3.9.

In addition tests against Django main development branch are now required to
pass.

Version 2.3.0 (2020-6-5)
------------------------

Expand Down
2 changes: 1 addition & 1 deletion django_filters/__init__.py
Expand Up @@ -10,7 +10,7 @@
from . import rest_framework
del pkgutil

__version__ = '2.3.0'
__version__ = '2.4.0'


def parse_version(version):
Expand Down
18 changes: 18 additions & 0 deletions django_filters/filters.py
Expand Up @@ -2,6 +2,7 @@
from datetime import timedelta

from django import forms
from django.core.validators import MaxValueValidator
from django.db.models import Q
from django.db.models.constants import LOOKUP_SEP
from django.forms.utils import pretty_name
Expand Down Expand Up @@ -357,6 +358,23 @@ class ModelMultipleChoiceFilter(QuerySetRequestMixin, MultipleChoiceFilter):
class NumberFilter(Filter):
field_class = forms.DecimalField

def get_max_validator(self):
"""
Return a MaxValueValidator for the field, or None to disable.
"""
return MaxValueValidator(1e50)

@property
def field(self):
if not hasattr(self, '_field'):
field = super().field
max_validator = self.get_max_validator()
if max_validator:
field.validators.append(max_validator)

self._field = field
return self._field


class NumericRangeFilter(Filter):
field_class = RangeField
Expand Down
8 changes: 5 additions & 3 deletions docs/conf.py
Expand Up @@ -13,6 +13,8 @@

import sys, os

from django_filters import __version__

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
Expand Down Expand Up @@ -41,16 +43,16 @@

# General information about the project.
project = u'django-filter'
copyright = u'2019, Alex Gaynor, Carlton Gibson and others.'
copyright = u'2020, Alex Gaynor, Carlton Gibson and others.'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '2.3'
version = __version__
# The full version, including alpha/beta/rc tags.
release = '2.3.0'
release = __version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
6 changes: 6 additions & 0 deletions docs/ref/filters.txt
Expand Up @@ -426,6 +426,12 @@ QuerySet, which then gets used as the model's manager::
Filters based on a numerical value, used with ``IntegerField``, ``FloatField``,
and ``DecimalField`` by default.

.. method:: NumberFilter.get_max_validator()

Return a ``MaxValueValidator`` instance that will be added to
``field.validators``. By default uses a limit value of ``1e50``. Return
``None`` to disable maximum value validation.

``NumericRangeFilter``
~~~~~~~~~~~~~~~~~~~~~~

Expand Down
1 change: 0 additions & 1 deletion requirements/maintainer.txt
Expand Up @@ -2,7 +2,6 @@ alabaster==0.7.7
argh==0.26.1
Babel==2.2.0
backports.ssl-match-hostname==3.4.0.2
bumpversion==0.5.3
certifi==2015.9.6.2
docutils==0.12
funcsigs==0.4
Expand Down
11 changes: 7 additions & 4 deletions setup.py
Expand Up @@ -2,12 +2,15 @@
import sys
from setuptools import setup, find_packages

# FIXME: Main module requires django to be present, so cannot run setup.py in
# clean environment.
# from django_filters import __version__
__version__ = '2.4.0'

f = open('README.rst')
readme = f.read()
f.close()

version = '2.3.0'

if sys.argv[-1] == 'publish':
if os.system("pip freeze | grep wheel"):
print("wheel not installed.\nUse `pip install wheel`.\nExiting.")
Expand All @@ -18,13 +21,13 @@
os.system("python setup.py sdist bdist_wheel")
os.system("twine upload dist/*")
print("You probably want to also tag the version now:")
print(" git tag -a %s -m 'version %s'" % (version, version))
print(" git tag -a %s -m 'version %s'" % (__version__, __version__))
print(" git push --tags")
sys.exit()

setup(
name='django-filter',
version=version,
version=__version__,
description=('Django-filter is a reusable Django application for allowing'
' users to filter querysets dynamically.'),
long_description=readme,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_forms.py
Expand Up @@ -255,3 +255,17 @@ def test_is_bound_and_not_valid(self):
self.assertFalse(f.is_valid())
self.assertEqual(f.data, {'price': 'four dollars'})
self.assertEqual(f.errors, {'price': ['Enter a number.']})

def test_number_filter_max_value_validation(self):
class F(FilterSet):
class Meta:
model = Book
fields = ['average_rating']

f = F({'average_rating': '1E1001'})
self.assertTrue(f.is_bound)
self.assertFalse(f.is_valid())
self.assertEqual(
f.errors,
{'average_rating': ['Ensure this value is less than or equal to 1e+50.']}
)

0 comments on commit 340cf7a

Please sign in to comment.