Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[Bug 795049] Use smart_datetime for date ranges.

In particular this adds safety for the `TypeErrors` thrown by `strptime`
when the string contains null bytes, but it is also a generally good
thing.
  • Loading branch information...
commit 40edfee61379d17b49369a9fe94f0f9683cd1a90 1 parent b8a4f73
@mythmon authored
View
9 fjord/analytics/tests/test_views.py
@@ -178,6 +178,15 @@ def test_invalid_search(self):
eq_(r.status_code, 200)
pq = PyQuery(r.content)
eq_(len(pq('li.opinion')), 7)
+ # A broken date range search shouldn't affect anything
+ # Why this? Because this is the thing the fuzzer found.
+ r = self.client.get(url, {
+ 'date_end': '/etc/shadow\x00',
+ 'date_start': '/etc/passwd\x00'
+ })
+ eq_(r.status_code, 200)
+ pq = PyQuery(r.content)
+ eq_(len(pq('li.opinion')), 7)
def test_frontpage_index_missing(self):
"""If index is missing, show es_down template."""
View
33 fjord/analytics/views.py
@@ -8,7 +8,7 @@
from tower import ugettext as _
from fjord.base.helpers import locale_name
-from fjord.base.util import smart_int
+from fjord.base.util import smart_int, smart_datetime
from fjord.feedback.models import SimpleIndex
@@ -85,8 +85,8 @@ def dashboard(request, template):
search_platform = request.GET.get('platform', None)
search_locale = request.GET.get('locale', None)
search_query = request.GET.get('q', None)
- search_date_start = request.GET.get('date_start', None)
- search_date_end = request.GET.get('date_end', None)
+ search_date_start = smart_datetime(request.GET.get('date_start', None), fallback=None)
+ search_date_end = smart_datetime(request.GET.get('date_end', None), fallback=None)
selected = request.GET.get('selected', None)
current_search = {'page': page}
@@ -106,26 +106,15 @@ def dashboard(request, template):
current_search['locale'] = search_locale
if search_date_start:
- try:
- start = datetime.strptime(search_date_start, '%Y-%m-%d')
- # This isn't a mistake. The line below *should* say `search_date_start`
- current_search['date_start'] = search_date_start
- f &= F(created__gte=start)
- except ValueError:
- # Should something happen here?
- pass
+ current_search['date_start'] = search_date_start.strftime('%Y-%m-%d')
+ f &= F(created__gte=search_date_start)
+
if search_date_end:
- try:
- end = datetime.strptime(search_date_end, '%Y-%m-%d')
- # Add one day, so that the search range includes the entire day.
- end += timedelta(days=1)
- # This isn't a mistake. The line below *should* say `search_date_end`
- current_search['date_end'] = search_date_end
- # Note 'less than', not 'less than or equal', because of the above.
- f &= F(created__lt=end)
- except ValueError:
- # Should something happen here?
- pass
+ current_search['date_end'] = search_date_end.strftime('%Y-%m-%d')
+ # Add one day, so that the search range includes the entire day.
+ end = search_date_end + timedelta(days=1)
+ # Note 'less than', not 'less than or equal', because of the added day above.
+ f &= F(created__lt=end)
if search_query:
fields = ['text', 'text_phrase', 'fuzzy']
View
23 fjord/base/tests/test__util.py
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
+from datetime import datetime
+
from nose.tools import eq_
from fjord.base.tests import TestCase
-from fjord.base.util import smart_truncate, smart_int
+from fjord.base.util import smart_truncate, smart_int, smart_datetime
def test_smart_truncate():
@@ -30,3 +32,22 @@ def test_empty_string(self):
def test_wrong_type(self):
eq_(0, smart_int(None))
eq_(10, smart_int([], 10))
+
+
+class SmartDateTest(TestCase):
+ def test_sanity(self):
+ eq_(datetime(2012, 1, 1), smart_datetime('2012-01-01'))
+ eq_(datetime(1742, 11, 5), smart_datetime('1742-11-05'))
+
+ def test_empty_string(self):
+ eq_(None, smart_datetime(''))
+
+ def test_fallback(self):
+ eq_('Hullaballo', smart_datetime('', fallback='Hullaballo'))
+
+ def test_format(self):
+ eq_(datetime(2012, 9, 28), smart_datetime('9/28/2012', format='%m/%d/%Y'))
+
+ def test_null_bytes(self):
+ # strptime likes to barf on null bytes in strings, so test it.
+ eq_(None, smart_datetime('/etc/passwd\x00'))
View
25 fjord/base/util.py
@@ -1,3 +1,6 @@
+from datetime import datetime
+
+
def smart_truncate(content, length=100, suffix='...'):
"""Truncate text at space before length bound.
@@ -21,9 +24,27 @@ def smart_truncate(content, length=100, suffix='...'):
return content[:length].rsplit(' ', 1)[0] + suffix
-def smart_int(string, fallback=0):
+def smart_int(s, fallback=0):
"""Convert a string to int, with fallback for invalid strings or types."""
try:
- return int(float(string))
+ return int(float(s))
+ except (ValueError, TypeError):
+ return fallback
+
+
+def smart_datetime(s, format='%Y-%m-%d', fallback=None):
+ """Convert a string to a datetime, with a fallback for invalid input.
+
+ Note that this won't take ``datetime``s as input, only strings. It is
+ different than ``smart_int`` in this way.
+
+ :arg s: The string to convert to a date.
+ :arg format: Format to use to parse the string into a date. Default: ``'%Y-%m-%d'``.
+ :arg fallback: Value to use in case of an error. Default: ``None``.
+
+ """
+ try:
+ return datetime.strptime(s, format)
except (ValueError, TypeError):
return fallback
+
Please sign in to comment.
Something went wrong with that request. Please try again.