Sanitize search text in bootstrap table (#3609)

* Sanitize search text in bootstrap table

* Clean search query on the server side before rendering search page template

- Refactor existing sanitizing code into functions

* Make ASCII and Unicode cleaning optional
This commit is contained in:
Oliver 2022-08-25 14:10:39 +10:00 committed by GitHub
parent e8621a97bc
commit 8fa67b8671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 50 deletions

View File

@ -20,7 +20,9 @@ from django.http import StreamingHttpResponse
from django.test import TestCase
from django.utils.translation import gettext_lazy as _
import regex
import requests
from bleach import clean
from djmoney.money import Money
from PIL import Image
@ -856,6 +858,55 @@ def clean_decimal(number):
return clean_number.quantize(Decimal(1)) if clean_number == clean_number.to_integral() else clean_number.normalize()
def strip_html_tags(value: str, raise_error=True, field_name=None):
"""Strip HTML tags from an input string using the bleach library.
If raise_error is True, a ValidationError will be thrown if HTML tags are detected
"""
cleaned = clean(
value,
strip=True,
tags=[],
attributes=[],
)
# Add escaped characters back in
replacements = {
'>': '>',
'&lt;': '<',
'&amp;': '&',
}
for o, r in replacements.items():
cleaned = cleaned.replace(o, r)
# If the length changed, it means that HTML tags were removed!
if len(cleaned) != len(value) and raise_error:
field = field_name or 'non_field_errors'
raise ValidationError({
field: [_("Remove HTML tags from this value")]
})
return cleaned
def remove_non_printable_characters(value: str, remove_ascii=True, remove_unicode=True):
"""Remove non-printable / control characters from the provided string"""
if remove_ascii:
# Remove ASCII control characters
cleaned = regex.sub(u'[\x01-\x1F]+', '', value)
if remove_unicode:
# Remove Unicode control characters
cleaned = regex.sub(u'[^\P{C}]+', '', value)
return cleaned
def get_objectreference(obj, type_ref: str = 'content_type', object_ref: str = 'object_id'):
"""Lookup method for the GenericForeignKey fields.

View File

@ -1,13 +1,10 @@
"""Mixins for (API) views in the whole project."""
from django.utils.translation import gettext_lazy as _
import regex
from bleach import clean
from rest_framework import generics, status
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
from InvenTree.helpers import remove_non_printable_characters, strip_html_tags
class CleanMixin():
"""Model mixin class which cleans inputs using the Mozilla bleach tools."""
@ -49,34 +46,8 @@ class CleanMixin():
Ref: https://github.com/mozilla/bleach/issues/192
"""
cleaned = clean(
data,
strip=True,
tags=[],
attributes=[],
)
# Add escaped characters back in
replacements = {
'&gt;': '>',
'&lt;': '<',
'&amp;': '&',
}
for o, r in replacements.items():
cleaned = cleaned.replace(o, r)
# If the length changed, it means that HTML tags were removed!
if len(cleaned) != len(data):
raise ValidationError({
field: [_("Remove HTML tags from this value")]
})
# Remove ASCII control characters
cleaned = regex.sub(u'[\x01-\x1F]+', '', cleaned)
# Remove Unicode control characters
cleaned = regex.sub(u'[^\P{C}]+', '', cleaned)
cleaned = strip_html_tags(data, field_name=field)
cleaned = remove_non_printable_characters(cleaned)
return cleaned

View File

@ -38,6 +38,7 @@ from part.models import PartCategory
from users.models import RuleSet, check_user_role
from .forms import EditUserForm, SetPasswordForm
from .helpers import remove_non_printable_characters, strip_html_tags
def auth_request(request):
@ -600,6 +601,9 @@ class SearchView(TemplateView):
query = request.POST.get('search', '')
query = strip_html_tags(query, raise_error=False)
query = remove_non_printable_characters(query)
context['query'] = query
return super(TemplateView, self).render_to_response(context)

View File

@ -33,6 +33,8 @@
});
}
var search_text = sanitizeInputString("{{ query|escapejs }}");
function addItem(label, title, icon, options) {
// Construct a "badge" to add to the sidebar item
@ -85,7 +87,7 @@
"{% url 'api-part-list' %}",
{
params: {
original_search: "{{ query }}",
original_search: search_text,
},
checkbox: false,
disableFilters: true,
@ -96,7 +98,7 @@
loadPartCategoryTable($("#table-category"), {
params: {
original_search: "{{ query }}",
original_search: search_text,
}
});
@ -107,7 +109,7 @@
"{% url 'api-manufacturer-part-list' %}",
{
params: {
original_search: "{{ query }}",
original_search: search_text,
part_detail: true,
supplier_detail: true,
manufacturer_detail: true
@ -122,7 +124,7 @@
"{% url 'api-supplier-part-list' %}",
{
params: {
original_search: "{{ query }}",
original_search: search_text,
part_detail: true,
supplier_detail: true,
manufacturer_detail: true
@ -141,7 +143,7 @@
loadBuildTable('#table-build-order', {
locale: '{{ request.LANGUAGE_CODE }}',
params: {
original_search: '{{ query }}',
original_search: search_text,
}
});
@ -156,7 +158,7 @@
filterKey: 'stocksearch',
url: "{% url 'api-stock-list' %}",
params: {
original_search: "{{ query }}",
original_search: search_text,
part_detail: true,
location_detail: true
}
@ -167,7 +169,7 @@
loadStockLocationTable($("#table-location"), {
filterKey: 'locationsearch',
params: {
original_search: "{{ query }}",
original_search: search_text,
},
});
@ -180,8 +182,8 @@
loadCompanyTable('#table-manufacturer', "{% url 'api-company-list' %}", {
params: {
original_search: "{{ query }}",
is_manufacturer: "true",
original_search: search_text,
is_manufacturer: true,
}
});
@ -190,8 +192,8 @@
loadCompanyTable('#table-supplier', "{% url 'api-company-list' %}", {
params: {
original_search: "{{ query }}",
is_supplier: "true",
original_search: search_text,
is_supplier: true,
}
});
@ -199,7 +201,7 @@
loadPurchaseOrderTable('#table-purchase-order', {
params: {
original_search: '{{ query }}',
original_search: search_text,
}
});
@ -210,8 +212,8 @@
loadCompanyTable('#table-customer', "{% url 'api-company-list' %}", {
params: {
original_search: "{{ query }}",
is_customer: "true",
original_search: search_text,
is_customer: true,
}
});
@ -219,7 +221,7 @@
loadSalesOrderTable('#table-sales-orders', {
params: {
original_search: '{{ query }}',
original_search: search_text,
}
});
@ -230,7 +232,7 @@
enableSidebar(
'search',
{
hide_toggle: 'true',
hide_toggle: true,
}
);

View File

@ -346,7 +346,9 @@ function convertQueryParameters(params, filters) {
if ('original_search' in params) {
var search = params['search'] || '';
params['search'] = search + ' ' + params['original_search'];
var clean_search = sanitizeInputString(search + ' ' + params['original_search']);
params['search'] = clean_search;
delete params['original_search'];
}