mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
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:
parent
e8621a97bc
commit
8fa67b8671
@ -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 = {
|
||||
'>': '>',
|
||||
'<': '<',
|
||||
'&': '&',
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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 = {
|
||||
'>': '>',
|
||||
'<': '<',
|
||||
'&': '&',
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -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'];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user