diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index bea10d036..d8793f693 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -454,6 +454,12 @@ class CursorPagination(BasePagination): ordering = '-created' template = 'rest_framework/pagination/previous_and_next.html' + # The offset in the cursor is used in situations where we have a + # nearly-unique index. (Eg millisecond precision creation timestamps) + # We guard against malicious users attempting to cause expensive database + # queries, by having a hard cap on the maximum possible size of the offset. + offset_cutoff = 1000 + def paginate_queryset(self, queryset, request, view=None): if self.page_size is None: return None @@ -675,18 +681,12 @@ class CursorPagination(BasePagination): if encoded is None: return None - # The offset in the cursor is used in situations where we have a - # nearly-unique index. (Eg millisecond precision creation timestamps) - # We guard against malicious users attempting to cause expensive database - # queries, by having a hard cap on the maximum possible size of the offset. - OFFSET_CUTOFF = 1000 - try: querystring = b64decode(encoded.encode('ascii')).decode('ascii') tokens = urlparse.parse_qs(querystring, keep_blank_values=True) offset = tokens.get('o', ['0'])[0] - offset = _positive_int(offset, cutoff=OFFSET_CUTOFF) + offset = _positive_int(offset, cutoff=self.offset_cutoff) reverse = tokens.get('r', ['0'])[0] reverse = bool(int(reverse)) diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 03c4fdf47..74f090a52 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -6,7 +6,7 @@ import pytest from rest_framework import ( exceptions, filters, generics, pagination, serializers, status ) -from rest_framework.pagination import PAGE_BREAK, PageLink +from rest_framework.pagination import PAGE_BREAK, PageLink, Cursor from rest_framework.request import Request from rest_framework.test import APIRequestFactory @@ -648,6 +648,16 @@ class TestCursorPagination: assert isinstance(self.pagination.to_html(), type('')) + def test_offset_cutoff(self): + (previous, current, next, previous_url, next_url) = self.get_pages('/') + + next_url = self.pagination.encode_cursor(Cursor(1050, False, None)) + (previous, current, next, previous_url, next_url) = self.get_pages(next_url) + + assert previous == [7, 7, 7, 8, 9] + assert current == [] + assert next is None + def test_get_displayed_page_numbers(): """