Skip to content

Commit

Permalink
fix: Reject nul characters in ipr search parameters (#6292)
Browse files Browse the repository at this point in the history
* fix: Reject nul characters in ipr search parameters

Really ought to rework this as a form, but in the meantime
this should prevent 500s. Could probably reduce the number
of places we check the value.

* fix: Guard against absent parameters

* fix: Remove stray junk

* fix: Use correct response code (400, not 405)

* test: Test handling of null chars in IPR search

* refactor: Simplify branch statements

This helps my code validator see that "start" is
always set.
  • Loading branch information
jennifer-richards authored Sep 12, 2023
1 parent febdeff commit 18a1af2
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
18 changes: 18 additions & 0 deletions ietf/ipr/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,24 @@ def test_search(self):
r = self.client.get(url + "?submit=iprtitle&iprtitle=%s" % quote(ipr.title))
self.assertContains(r, ipr.title)

def test_search_null_characters(self):
"""IPR search gracefully rejects null characters in parameters"""
# Not a combinatorially exhaustive set, but tries to exercise all the parameters
bad_params = [
"option=document_search&document_search=draft-\x00stuff"
"submit=dra\x00ft",
"submit=draft&id=some\x00id",
"submit=draft&id_document_tag=some\x00id",
"submit=draft&id=someid&state=re\x00moved",
"submit=draft&id=someid&state=posted&state=re\x00moved",
"submit=draft&id=someid&state=removed&draft=draft-no\x00tvalid",
"submit=rfc&rfc=rfc\x00123",
]
url = urlreverse("ietf.ipr.views.search")
for query_params in bad_params:
r = self.client.get(f"{url}?{query_params}")
self.assertEqual(r.status_code, 400, f"querystring '{query_params}' should be rejected")

def test_feed(self):
ipr = HolderIprDisclosureFactory()
r = self.client.get("/feed/ipr/")
Expand Down
26 changes: 18 additions & 8 deletions ietf/ipr/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from django.db.models import Q
from django.forms.models import inlineformset_factory, model_to_dict
from django.forms.formsets import formset_factory
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseBadRequest
from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string
from django.urls import reverse as urlreverse
Expand Down Expand Up @@ -629,11 +629,16 @@ def post(request, id):

def search(request):
search_type = request.GET.get("submit")
if search_type and "\x00" in search_type:
return HttpResponseBadRequest("Null characters are not allowed")

# query field
q = ''
# legacy support
if not search_type and request.GET.get("option", None) == "document_search":
docname = request.GET.get("document_search", "")
if docname and "\x00" in docname:
return HttpResponseBadRequest("Null characters are not allowed")
if docname.startswith("draft-"):
search_type = "draft"
q = docname
Expand All @@ -643,18 +648,24 @@ def search(request):
if search_type:
form = SearchForm(request.GET)
docid = request.GET.get("id") or request.GET.get("id_document_tag") or ""
if docid and "\x00" in docid:
return HttpResponseBadRequest("Null characters are not allowed")
docs = doc = None
iprs = []
related_iprs = []

# set states
states = request.GET.getlist('state',settings.PUBLISH_IPR_STATES)
if any("\x00" in state for state in states if state):
return HttpResponseBadRequest("Null characters are not allowed")
if states == ['all']:
states = IprDisclosureStateName.objects.values_list('slug',flat=True)

# get query field
if request.GET.get(search_type):
q = request.GET.get(search_type)
if q and "\x00" in q:
return HttpResponseBadRequest("Null characters are not allowed")

if q or docid:
# Search by RFC number or draft-identifier
Expand All @@ -664,13 +675,12 @@ def search(request):

if docid:
start = DocAlias.objects.filter(name__iexact=docid)
else:
if search_type == "draft":
q = normalize_draftname(q)
start = DocAlias.objects.filter(name__icontains=q, name__startswith="draft")
elif search_type == "rfc":
start = DocAlias.objects.filter(name="rfc%s" % q.lstrip("0"))

elif search_type == "draft":
q = normalize_draftname(q)
start = DocAlias.objects.filter(name__icontains=q, name__startswith="draft")
else: # search_type == "rfc"
start = DocAlias.objects.filter(name="rfc%s" % q.lstrip("0"))

# one match
if len(start) == 1:
first = start[0]
Expand Down

0 comments on commit 18a1af2

Please sign in to comment.