fix: Reject nul characters in ipr search parameters ()

* 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.
This commit is contained in:
Jennifer Richards 2023-09-12 10:32:16 -03:00 committed by GitHub
parent febdeff85f
commit 18a1af22f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 8 deletions

View file

@ -209,6 +209,24 @@ class IprTests(TestCase):
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/")

View file

@ -10,7 +10,7 @@ from django.contrib import messages
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
@ -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
@ -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
@ -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]