diff --git a/hypha/apply/activity/templates/activity/include/listing_base.html b/hypha/apply/activity/templates/activity/include/listing_base.html index 2336b81eb69304326aa066c8f728db74aec8c805..af43a912d0212e159ebffcd6c9d461f3e7abf788 100644 --- a/hypha/apply/activity/templates/activity/include/listing_base.html +++ b/hypha/apply/activity/templates/activity/include/listing_base.html @@ -1,4 +1,4 @@ -{% load i18n activity_tags bleach_tags markdown_tags submission_tags apply_tags heroicons %} +{% load i18n activity_tags nh3_tags markdown_tags submission_tags apply_tags heroicons %} <div class="feed__item feed__item--{{ activity.type }} border shadow-sm rounded-sm pb-2 " id="communications#{{ activity.id }}"> <div class="feed__pre-content hidden lg:block"> @@ -48,7 +48,7 @@ data-visibility="{{activity.visibility}}" data-edit-url="{% url 'api:v1:comments-edit' pk=activity.pk %}" > - {{ activity|display_for:request.user|submission_links|markdown|bleach }} + {{ activity|display_for:request.user|submission_links|markdown|nh3 }} </div> <style> @media only screen and (min-width: 1024px){ @@ -60,7 +60,7 @@ <div class="js-edit-block pe-3" aria-live="polite"></div> {% else %} <div class="px-3 prose"> - {{ activity|display_for:request.user|submission_links|markdown|bleach }} + {{ activity|display_for:request.user|submission_links|markdown|nh3 }} </div> {% endif %} diff --git a/hypha/apply/activity/templates/activity/include/notifications_dropdown.html b/hypha/apply/activity/templates/activity/include/notifications_dropdown.html index 65d7830fbac72b004565e03d1ad0bf8d40cc7b3c..0866fcae53ba8bbf9217d138922d0fdbb1950636 100644 --- a/hypha/apply/activity/templates/activity/include/notifications_dropdown.html +++ b/hypha/apply/activity/templates/activity/include/notifications_dropdown.html @@ -1,4 +1,4 @@ -{% load i18n activity_tags bleach_tags markdown_tags submission_tags apply_tags %} +{% load i18n activity_tags nh3_tags markdown_tags submission_tags apply_tags %} <div class="notifications notifications--dropdown"> <div class="notifications__content zeta" role="activity"> diff --git a/hypha/apply/activity/templates/activity/notifications.html b/hypha/apply/activity/templates/activity/notifications.html index 647b09ca3f3ccc91220da782b478133b0d37b8cd..130b90d030cd9bf6b10e7a76b32d3c71f00d63fd 100644 --- a/hypha/apply/activity/templates/activity/notifications.html +++ b/hypha/apply/activity/templates/activity/notifications.html @@ -1,6 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n static activity_tags apply_tags bleach_tags markdown_tags submission_tags heroicons %} - +{% load i18n static activity_tags apply_tags nh3_tags markdown_tags submission_tags heroicons %} {% block content %} <div class="admin-bar"> <div class="admin-bar__inner"> diff --git a/hypha/apply/activity/templates/messages/email/determination.html b/hypha/apply/activity/templates/messages/email/determination.html index 99f53547de21d00620e0a0f17d9a68ca8fa82808..325d559b140eba377d7b33d41ef745cccecf9f95 100644 --- a/hypha/apply/activity/templates/messages/email/determination.html +++ b/hypha/apply/activity/templates/messages/email/determination.html @@ -1,8 +1,8 @@ {% extends "messages/email/applicant_base.html" %} -{% load bleach_tags i18n %} +{% load nh3_tags i18n %} {% block content %}{% trans "Your application has been reviewed and the outcome is" %}: {{ determination.clean_outcome }} - {{ determination.message|bleach|striptags }} + {{ determination.message|nh3|striptags }} {% trans "Read the full determination here" %}: {{ request.scheme }}://{{ request.get_host }}{{ determination.get_absolute_url }}{% endblock %} diff --git a/hypha/apply/api/v1/serializers.py b/hypha/apply/api/v1/serializers.py index 01288cc79fa8b5cecbf5d42f0f4a3ef00e964982..d80715f24634bbb2488ec8853292f5c568240d86 100644 --- a/hypha/apply/api/v1/serializers.py +++ b/hypha/apply/api/v1/serializers.py @@ -1,5 +1,5 @@ from django.contrib.auth import get_user_model -from django_bleach.templatetags.bleach_tags import bleach_value +from django_nh3.templatetags.nh3_tags import nh3_value from rest_framework import serializers from hypha.apply.activity.models import Activity @@ -416,7 +416,7 @@ class CommentSerializer(serializers.ModelSerializer): ) def get_message(self, obj): - return bleach_value(markdown_to_html(obj.message)) + return nh3_value(markdown_to_html(obj.message)) def get_editable(self, obj): return self.context["request"].user == obj.user diff --git a/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html b/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html index 6d53d17f8e0a63b77db1f20fe27ae4bc04f9ba82..992c709d4aa131ef2608595492ca4d1f6e67e3b9 100644 --- a/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html +++ b/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html @@ -1,6 +1,6 @@ {% extends "base-apply.html" %} {% load render_table from django_tables2 %} -{% load i18n static wagtailcore_tags workflow_tags statusbar_tags heroicons dashboard_statusbar_tags apply_tags invoice_tools markdown_tags bleach_tags %} +{% load i18n static wagtailcore_tags workflow_tags statusbar_tags heroicons dashboard_statusbar_tags apply_tags invoice_tools markdown_tags nh3_tags %} {% block body_class %}bg-light-grey{% endblock %} {% block title %}{% trans "Dashboard" %}{% endblock %} @@ -31,7 +31,7 @@ {% for task in my_tasks.data %} <div class="bg-white p-1 flex mb-1 items-center"> <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg> - <div class="flex-1">{{ task.text|markdown|bleach }}</div> + <div class="flex-1">{{ task.text|markdown|nh3 }}</div> <a class="button button-primary m-2" href="{{ task.url }}">View</a> </div> {% endfor %} diff --git a/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html b/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html index 3048ee17059d9a7d4b9ca7c26bf513475c610ed1..2054d55a80e8f99dc2aaf6ce2175210baea26a1c 100644 --- a/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html +++ b/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html @@ -1,6 +1,6 @@ {% extends "base-apply.html" %} {% load render_table from django_tables2 %} -{% load i18n static markdown_tags bleach_tags %} +{% load i18n static markdown_tags nh3_tags %} {% block title %}{% trans "Dashboard" %}{% endblock %} @@ -21,7 +21,7 @@ {% for task in my_tasks.data %} <div class="bg-white p-1 flex mb-1 items-center"> <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg> - <div class="flex-1">{{ task.text|markdown|bleach }}</div> + <div class="flex-1">{{ task.text|markdown|nh3 }}</div> <a class="button button-primary m-2" href="{{ task.url }}">View</a> </div> {% endfor %} diff --git a/hypha/apply/dashboard/templates/dashboard/dashboard.html b/hypha/apply/dashboard/templates/dashboard/dashboard.html index 6252c72f68ec129baf4e18e2416a10716dc72c20..41667459240e08c359bc9263bb73b683d73639e9 100644 --- a/hypha/apply/dashboard/templates/dashboard/dashboard.html +++ b/hypha/apply/dashboard/templates/dashboard/dashboard.html @@ -1,6 +1,6 @@ {% extends "base-apply.html" %} {% load render_table from django_tables2 %} -{% load i18n static bleach_tags markdown_tags %} +{% load i18n static nh3_tags markdown_tags %} {% block extra_css %} {{ my_reviewed.filterset.form.media.css }} @@ -30,7 +30,7 @@ {% for task in my_tasks.data %} <div class="bg-white p-1 flex mb-1 items-center"> <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg> - <div class="flex-1">{{ task.text|markdown|bleach }}</div> + <div class="flex-1">{{ task.text|markdown|nh3 }}</div> <a class="button button-primary m-2" href="{{ task.url }}">View</a> </div> {% endfor %} diff --git a/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html b/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html index 34f6b69628650062f007eb116c9c6fa1c9109a37..6320843ea76d50ecca0db23a4636d96b72bd3d66 100644 --- a/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html +++ b/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html @@ -1,6 +1,6 @@ {% extends "base-apply.html" %} {% load render_table from django_tables2 %} -{% load i18n static markdown_tags bleach_tags %} +{% load i18n static markdown_tags nh3_tags %} {% block title %}{% trans "Dashboard" %}{% endblock %} @@ -21,7 +21,7 @@ {% for task in my_tasks.data %} <div class="bg-white p-1 flex mb-1 items-center"> <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg> - <div class="flex-1">{{ task.text|markdown|bleach }}</div> + <div class="flex-1">{{ task.text|markdown|nh3 }}</div> <a class="button button-primary m-2" href="{{ task.url }}">View</a> </div> {% endfor %} diff --git a/hypha/apply/determinations/models.py b/hypha/apply/determinations/models.py index 0707ba0cf68ac3e9a42d1b1a8de4a48361fce84b..09bbfb22cf1df34d0282749a4e529b3d7d851a0a 100644 --- a/hypha/apply/determinations/models.py +++ b/hypha/apply/determinations/models.py @@ -1,4 +1,4 @@ -import bleach +import nh3 from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models @@ -130,7 +130,7 @@ class Determination(DeterminationFormFieldsMixin, AccessFormData, models.Model): @property def stripped_message(self): - return bleach.clean(self.message, tags=[], strip=True) + return nh3.clean(self.message, tags=set()) @property def clean_outcome(self): diff --git a/hypha/apply/determinations/templates/determinations/base_determination_form.html b/hypha/apply/determinations/templates/determinations/base_determination_form.html index ec982f879a425d470be29f2eb27d7964e57a81d6..153c84bc66fe3b4e3c46ebc64bd4ba36ef393c72 100644 --- a/hypha/apply/determinations/templates/determinations/base_determination_form.html +++ b/hypha/apply/determinations/templates/determinations/base_determination_form.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n static bleach_tags %} +{% load i18n static nh3_tags %} {% block title %}{% if object %}{% trans "Edit a Determination" %} {% if object.is_draft %}{% trans "draft" %}{% endif %}{% else %}{% trans "Create a Determination" %}{% endif %}{% endblock %} {% block content %} diff --git a/hypha/apply/determinations/templates/determinations/determination_detail.html b/hypha/apply/determinations/templates/determinations/determination_detail.html index c160ddfe28a0e77fffedece25cf76e7599fb6fea..0ca28fead0722bd89681540f6a2599d2c926b731 100644 --- a/hypha/apply/determinations/templates/determinations/determination_detail.html +++ b/hypha/apply/determinations/templates/determinations/determination_detail.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n bleach_tags heroicons %} +{% load i18n nh3_tags heroicons %} {% block title %}{% trans "Determination for" %} {{ determination.submission.title }}{% endblock %} @@ -32,14 +32,14 @@ <div class="rich-text rich-text--answers prose"> <h4>{% trans "Determination message" %}</h4> - {{ determination.message|bleach }} + {{ determination.message|nh3 }} {% for group in determination.detailed_data.values %} {% if group.title %} - <h4>{{ group.title|bleach }}</h4> + <h4>{{ group.title|nh3 }}</h4> {% endif %} {% for question, answer in group.questions %} <h5>{{ question }}</h5> - {% if answer %}{% if answer == True %}{{ answer|yesno:"Agree,Disagree" }}{% else %}{{ answer|bleach }}{% endif %}{% else %}-{% endif %} + {% if answer %}{% if answer == True %}{{ answer|yesno:"Agree,Disagree" }}{% else %}{{ answer|nh3 }}{% endif %}{% else %}-{% endif %} {% endfor %} {% endfor %} </div> diff --git a/hypha/apply/funds/differ.py b/hypha/apply/funds/differ.py index 66ccf9ffbd9db78fc104f2b7dad267ee1f4b42ca..0c289eb98df37b3fafaddaebac7c88478d24b60f 100644 --- a/hypha/apply/funds/differ.py +++ b/hypha/apply/funds/differ.py @@ -1,7 +1,8 @@ import re from difflib import SequenceMatcher +from typing import Tuple -from bleach.sanitizer import Cleaner +import nh3 from django.utils.html import format_html from django.utils.safestring import mark_safe @@ -16,13 +17,26 @@ def wrap_added(text): return format_html('<span class="bg-green-200">{}</span>', mark_safe(text)) -def compare(answer_a, answer_b, should_bleach=True): - if should_bleach: - cleaner = Cleaner(tags=["h4"], attributes={}, strip=True) +def compare(answer_a: str, answer_b: str, should_clean: bool = True) -> Tuple[str, str]: + """Compare two strings, populate diff HTML and insert it, and return a tuple of the given strings. + + Args: + answer_a: + The original string + answer_b: + The string to compare to the original + should_clean: + Optional boolean to determine if the string should be sanitized with NH3 (default=True) + + Returns: + A tuple of the original strings with diff HTML inserted. + """ + + if should_clean: answer_a = re.sub("(<li[^>]*>)", r"\1â—¦ ", answer_a) answer_b = re.sub("(<li[^>]*>)", r"\1â—¦ ", answer_b) - answer_a = cleaner.clean(answer_a) - answer_b = cleaner.clean(answer_b) + answer_a = nh3.clean(answer_a, tags={"h4"}, attributes={}) + answer_b = nh3.clean(answer_b, tags={"h4"}, attributes={}) diff = SequenceMatcher(None, answer_a, answer_b) from_diff = [] diff --git a/hypha/apply/funds/forms.py b/hypha/apply/funds/forms.py index d1face9a80302aee16653d5cb4e9a3a148303d14..3921248b033559b27dd7a65cb38b4db2d69dcb82 100644 --- a/hypha/apply/funds/forms.py +++ b/hypha/apply/funds/forms.py @@ -3,7 +3,7 @@ from functools import partial from itertools import groupby from operator import methodcaller -import bleach +import nh3 from django import forms from django.db.models import Q from django.utils.safestring import mark_safe @@ -448,7 +448,7 @@ def make_role_reviewer_fields(): staff_reviewers = User.objects.staff().only("full_name", "pk") for role in ReviewerRole.objects.all().order_by("order"): - role_name = bleach.clean(role.name, strip=True) + role_name = nh3.clean(role.name, tags=set()) field_name = f"role_reviewer_{role.id}" field = forms.ModelChoiceField( queryset=staff_reviewers, diff --git a/hypha/apply/projects/templates/application_projects/report_detail.html b/hypha/apply/projects/templates/application_projects/report_detail.html index a2cf266eff7f733abf0b256505ba269b8b37348e..b0209e7be5fb6dc6b8cd5d92b1e631037eb025a0 100644 --- a/hypha/apply/projects/templates/application_projects/report_detail.html +++ b/hypha/apply/projects/templates/application_projects/report_detail.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n static bleach_tags heroicons %} +{% load i18n static nh3_tags heroicons %} {% block title %}{% trans "Report" %} | {{ object.project.title }}{% endblock %} {% block body_class %}{% endblock %} @@ -28,12 +28,12 @@ {% else %} <h4>{% trans "Public Report" %}</h4> <div class="rich-text"> - {{ object.current.public_content|bleach|safe }} + {{ object.current.public_content|nh3|safe }} </div> <h4>{% trans "Private Report" %}</h4> <div class="rich-text"> - {{ object.current.private_content|bleach|safe }} + {{ object.current.private_content|nh3|safe }} </div> {% for file in object.current.files.all %} {% if forloop.first %} diff --git a/hypha/apply/projects/templates/application_projects/vendor_detail.html b/hypha/apply/projects/templates/application_projects/vendor_detail.html index c45bf7c16926eaf367c3a83619368fb2bc2b5047..9fb4d2f0b31a4dda8ef3fa47a5c0052fd5763c1e 100644 --- a/hypha/apply/projects/templates/application_projects/vendor_detail.html +++ b/hypha/apply/projects/templates/application_projects/vendor_detail.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load bleach_tags i18n approval_tools heroicons %} +{% load nh3_tags i18n approval_tools heroicons %} {% user_can_edit_project object request.user as editable %} {% block title %}{% trans "Contracting Information for" %} {{ project.title }} {% endblock %} @@ -31,7 +31,7 @@ <div class="rich-text rich-text--answers"> {% for group in vendor_detailed_response.values %} {% if group.title %} - <h1>{{ group.title|bleach }}</h4> + <h1>{{ group.title|nh3 }}</h4> {% endif %} {% for question, answer in group.questions %} <h5>{{ question }}</h5> @@ -44,7 +44,7 @@ </div> </div> {% else %} - <p>{% if answer == True or answer == False %}{{ answer|yesno:"Yes,No" }}{% else %}{% if answer %}{{ answer|bleach }}{% else %}-{% endif %}{% endif %}</p> + <p>{% if answer == True or answer == False %}{{ answer|yesno:"Yes,No" }}{% else %}{% if answer %}{{ answer|nh3 }}{% else %}-{% endif %}{% endif %}</p> {% endif %} {% endfor %} {% endfor %} diff --git a/hypha/apply/review/templates/review/render_scored_answer_field.html b/hypha/apply/review/templates/review/render_scored_answer_field.html index a3ac5718d0616c3b8d8d3dd110ddfa9db5d0e42a..6b94842c47316f47441da8dadcbabd087c1a3dbf 100644 --- a/hypha/apply/review/templates/review/render_scored_answer_field.html +++ b/hypha/apply/review/templates/review/render_scored_answer_field.html @@ -1,8 +1,8 @@ -{% load bleach_tags %} +{% load nh3_tags %} {% block data_display %} <div> <h5>{{ value.field_label }}</h5> <div>{{ score }}</div> - <div>{{ comment|bleach }}</div> + <div>{{ comment|nh3 }}</div> </div> {% endblock %} diff --git a/hypha/apply/review/templates/review/review_detail.html b/hypha/apply/review/templates/review/review_detail.html index 706828fd41c20049471d3f0bb651c5d74a366f6f..535f6ff36305251af0386f2eff2aa7dfe6baed67 100644 --- a/hypha/apply/review/templates/review/review_detail.html +++ b/hypha/apply/review/templates/review/review_detail.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n bleach_tags submission_tags heroicons %} +{% load i18n nh3_tags submission_tags heroicons %} {% block title %}{% trans "Review for" %} {{ review.submission.title }}{% endblock %} {% block content %} diff --git a/hypha/apply/review/templates/review/review_list.html b/hypha/apply/review/templates/review/review_list.html index 3cfdd3298db1beb7a8b7f82ce29d8d86fde41c59..272f0b560a86e18b67bf0e183997e0af911ca169 100644 --- a/hypha/apply/review/templates/review/review_list.html +++ b/hypha/apply/review/templates/review/review_list.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n bleach_tags review_tags workflow_tags %} +{% load i18n nh3_tags review_tags workflow_tags %} {% block title %}{% trans "Reviews" %}{% endblock %} @@ -28,7 +28,7 @@ {% elif answers.question == "Opinions"%} <td class="reviews-list__td">{{ answer }}</td> {% else %} - <td class="reviews-list__td">{{ answer|bleach }}</td> + <td class="reviews-list__td">{{ answer|nh3 }}</td> {% endif %} {% endfor %} </tr> diff --git a/hypha/apply/stream_forms/blocks.py b/hypha/apply/stream_forms/blocks.py index 70d264dd58cc49614085e6357ffd758c776ea60c..13dc30015fd6d3d92436198cffb4e00925c711ba 100644 --- a/hypha/apply/stream_forms/blocks.py +++ b/hypha/apply/stream_forms/blocks.py @@ -1,5 +1,5 @@ # Credit to https://github.com/BertrandBordage for initial implementation -import bleach +import nh3 from anyascii import anyascii from dateutil.parser import isoparse, parse from django import forms @@ -11,7 +11,7 @@ from django.utils.encoding import force_str from django.utils.html import conditional_escape from django.utils.text import slugify from django.utils.translation import gettext_lazy as _ -from django_bleach.templatetags.bleach_tags import bleach_value +from django_nh3.templatetags.nh3_tags import nh3_value from wagtail.blocks import ( BooleanBlock, CharBlock, @@ -89,7 +89,7 @@ class FormFieldBlock(StructBlock): } def prepare_data(self, value, data, serialize=False): - return bleach_value(str(data)) + return nh3_value(str(data)) def render(self, value, context): data = context.get("data") @@ -141,7 +141,7 @@ class CharFieldBlock(OptionalFormFieldBlock): def get_searchable_content(self, value, data): # CharField acts as a fallback. Force data to string data = str(data) - return bleach.clean(data or "", tags=[], strip=True) + return nh3.clean(data or "", tags=set()) class MultiInputCharFieldBlock(CharFieldBlock): @@ -165,7 +165,7 @@ class TextFieldBlock(OptionalFormFieldBlock): template = "stream_forms/render_unsafe_field.html" def get_searchable_content(self, value, data): - return bleach.clean(data or "", tags=[], strip=True) + return nh3.clean(data or "", tags=set()) class NumberFieldBlock(OptionalFormFieldBlock): diff --git a/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html b/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html index f9b58617702719f094a6549eb5aa0aaf303b021c..4ef393bd465e29ef720db84d0907e2102ab38d29 100644 --- a/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html +++ b/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html @@ -1,8 +1,8 @@ {% extends "stream_forms/render_field.html" %} -{% load bleach_tags markdown_tags %} +{% load nh3_tags markdown_tags %} {% block data_display %} {% if data %} - {{ data|markdown|bleach }} + {{ data|markdown|nh3 }} {% else %} {{ block.super }} {% endif %} diff --git a/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html b/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html index 9725f800f6d0f4aa3b84d1e06bc2aa481c649c36..3138a4d25213df1027775c460dca877e3d2b5283 100644 --- a/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html +++ b/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html @@ -1,13 +1,13 @@ {% extends "stream_forms/render_field.html" %} -{% load bleach_tags %} +{% load nh3_tags %} {% block data_display %} {% if data %} {% if value.format == 'url' %} - <a class="link" href="{{ data|bleach }}" target="_blank" rel="noopener noreferrer">{{ data|bleach }}</a> + <a class="link" href="{{ data|nh3 }}" target="_blank" rel="noopener noreferrer">{{ data|nh3 }}</a> {% elif value.format == 'email' %} - <a class="u-email" href="mailto:{{ data|bleach }}">{{ data|bleach }}</a> + <a class="u-email" href="mailto:{{ data|nh3 }}">{{ data|nh3 }}</a> {% else %} - <p>{{ data|bleach }}</p> + <p>{{ data|nh3 }}</p> {% endif %} {% else %} {{ block.super }} diff --git a/hypha/apply/templates/forms/includes/field.html b/hypha/apply/templates/forms/includes/field.html index b9dec1d834e61c38dd9768460b3220fe25e4b8ad..f4c2802c155974e7b35edb97f895e2202385857d 100644 --- a/hypha/apply/templates/forms/includes/field.html +++ b/hypha/apply/templates/forms/includes/field.html @@ -1,5 +1,5 @@ {% load i18n util_tags %} -{% load bleach_tags markdown_tags heroicons %} +{% load nh3_tags markdown_tags heroicons %} {% with widget_type=field|widget_type field_type=field|field_type %} <div class="form__group {{ field.id_for_label }} form__group--{{ widget_type }} {% if widget_type == 'checkbox_input' %} form__group--checkbox{% endif %}{% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' or widget_type == 'single_file_field_widget' or widget_type == 'multi_file_field_widget' %} form__group--file{% endif %}{% if field.help_text %} form__group--wrap{% endif %}{% if field.errors %} form__error{% endif %}{% if is_application and field.field.group_number > 1 %} field-group field-group-{{ field.field.group_number }}{% endif %}{% if is_application and field.field.grouper_for %} form-fields-grouper{% endif %}"{% if is_application and field.field.grouper_for %}data-grouper-for="{{ field.field.grouper_for }}" data-toggle-on="{{ field.field.choices.0.0 }}" data-toggle-off="{{ field.field.choices.1.0 }}"{% endif %}{% if is_application and field.field.group_number > 1 %} data-hidden="{% if not show_all_group_fields and not field.field.visible %}true{% else %}false{% endif %}" data-required="{{ field.field.required_when_visible }}"{% endif %}{% if field.field.word_limit %} data-word-limit="{{ field.field.word_limit }}"{% endif %}> @@ -34,7 +34,7 @@ {% endif %} {% if field.help_text %} - <div class="form__help prose prose-sm">{{ field.help_text|markdown|bleach }}</div> + <div class="form__help prose prose-sm">{{ field.help_text|markdown|nh3 }}</div> {% endif %} {% if field.field.help_link %} diff --git a/hypha/apply/utils/blocks.py b/hypha/apply/utils/blocks.py index 7f74b0c5086c4090aa3f46d0329af44032581553..baee0b40e16d78a2451e58fc456ad1f0b300bd26 100644 --- a/hypha/apply/utils/blocks.py +++ b/hypha/apply/utils/blocks.py @@ -1,6 +1,6 @@ from collections import Counter -import bleach +import nh3 from django.core.exceptions import ValidationError from django.forms.utils import ErrorList from django.utils.safestring import mark_safe @@ -48,7 +48,7 @@ class RichTextFieldBlock(TextFieldBlock): icon = "form" def get_searchable_content(self, value, data): - return bleach.clean(data or "", tags=[], strip=True) + return nh3.clean(data or "", tags=set()) def no_response(self): return "<p>-</p>" @@ -64,7 +64,7 @@ class MarkdownTextFieldBlock(TextFieldBlock): template = "stream_forms/render_markdown_field.html" def get_searchable_content(self, value, data): - return bleach.clean(data or "", tags=[], strip=True) + return nh3.clean(data or "", tags=set()) def no_response(self): return "<p>-</p>" diff --git a/hypha/home/templates/apply_home/includes/apply_listing.html b/hypha/home/templates/apply_home/includes/apply_listing.html index 83d8cbf39aea07dc71780a3a71367b1143dc467f..d4b4ac9e4918bfab147aa4f121f30150cee135c6 100644 --- a/hypha/home/templates/apply_home/includes/apply_listing.html +++ b/hypha/home/templates/apply_home/includes/apply_listing.html @@ -1,4 +1,4 @@ -{% load i18n bleach_tags wagtailcore_tags markdown_tags heroicons %} +{% load i18n nh3_tags wagtailcore_tags markdown_tags heroicons %} {% if page.open_round and page.list_on_front_page %} <div class="flex justify-between items-center px-4 py-8 gap-4 hover:bg-slate-50 transition-colors"> @@ -17,7 +17,7 @@ {% if page.description %} <p> - {{ page.description|markdown|bleach }} + {{ page.description|markdown|nh3 }} </p> {% endif %} </div> diff --git a/hypha/public/news/templates/news/news_index.html b/hypha/public/news/templates/news/news_index.html new file mode 100644 index 0000000000000000000000000000000000000000..1cdeb6220791654bc5cc98b63cc49d78b6abc027 --- /dev/null +++ b/hypha/public/news/templates/news/news_index.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} +{% load wagtailcore_tags wagtailimages_tags static markdown_tags nh3_tags %} +{% block feedlinks %}<link rel="alternate" type="application/rss+xml" title="{{ page.title }}" href="{% url "news_feed" %}">{% endblock %} +{% block body_class %}light-grey-bg{% endblock %} + +{% block content %} + <div class="wrapper wrapper--small wrapper--inner-space-medium"> + + {% if page.introduction %} + <h4 class="heading heading--listings-introduction">{{ page.introduction|markdown|nh3 }}</h4> + {% endif %} + + <form class="form" method="GET"> + <div class="form__select form__select--narrow form__select--inline"> + <select name="news_type"> + <option value="">All</option> + {% for news_type in news_types %} + <option value="{{ news_type.0 }}" {% if request.GET.news_type == news_type.0|slugify %}selected="selected"{% endif %}>{{ news_type.1 }}</option> + {% endfor %} + </select> + </div> + <button class="link link--button link--button__stretch" type="submit">Filter</button> + </form> + + {% if news %} + <div class="wrapper wrapper--listings wrapper--top-space"> + {% for n in news %} + <a class="listing" href="{% pageurl n %}"> + {% if n.listing_image %} + {% image n.listing_image fill-450x300 %} + {% endif %} + <h4 class="listing__title" role="listitem"> + {{ n.listing_title|default:n.title }} + </h4> + {% if n.listing_summary or n.introduction %} + <h6 class="listing__teaser">{{ n.listing_summary|default:n.introduction }}</h6> + {% endif %} + <span class="listing__meta"> + {{ n.display_date|date:"SHORT_DATE_FORMAT" }} + {% if n.authors.all %} + | By: + {% for author in n.authors.all %} + {{ author.author }} + {% endfor %} + {% endif %} + </span> + </a> + {% endfor %} + </div> + {% include "includes/pagination.html" with paginator_page=news %} + {% else %} + {# no items #} + {% endif %} + + </div> +{% endblock %} diff --git a/hypha/settings/base.py b/hypha/settings/base.py index 6c4eb4854d6341eecfe016cabb3caf472d40b850..e5fb607357c61b36dde7fe2fef3adf6cab60a6eb 100644 --- a/hypha/settings/base.py +++ b/hypha/settings/base.py @@ -383,9 +383,9 @@ SOCIAL_AUTH_PIPELINE = ( "hypha.apply.users.pipeline.make_otf_staff", ) -# Bleach Settings +# NH3 Settings -BLEACH_ALLOWED_TAGS = [ +NH3_ALLOWED_TAGS = [ "a", "b", "big", @@ -426,18 +426,20 @@ BLEACH_ALLOWED_TAGS = [ "tr", "ul", ] -BLEACH_ALLOWED_ATTRIBUTES = [ - "class", - "colspan", - "href", - "rowspan", - "target", - "title", - "width", -] -BLEACH_ALLOWED_STYLES = [] -BLEACH_STRIP_TAGS = True -BLEACH_STRIP_COMMENTS = True + +NH3_ALLOWED_ATTRIBUTES = { + "*": [ + "class", + "colspan", + "href", + "rowspan", + "target", + "title", + "width", + ] +} + +NH3_STRIP_COMMENTS = True # Hijack Settings diff --git a/hypha/settings/django.py b/hypha/settings/django.py index 40e092d9fb353a9fbce21207bfe3b55aa4a6af49..3f3cac9bff4657631ec89834eaaf6f5a986e8981 100644 --- a/hypha/settings/django.py +++ b/hypha/settings/django.py @@ -57,7 +57,7 @@ INSTALLED_APPS = [ "django_filters", "django_select2", "addressfield", - "django_bleach", + "django_nh3", "django_fsm", "django_pwned_passwords", "django_slack", diff --git a/requirements.txt b/requirements.txt index 7ce4429913666f734e4c483e574f6f6c4ec0c1f6..1ab5b86ba21da4361635d5ff006df92b0396e979 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ click==8.1.7 dj-database-url==2.1.0 django-anymail==10.2 django-basic-auth-ip-whitelist==0.5 -django-bleach==3.1.0 +django-nh3==0.1.1 django-countries==7.5.1 django-elevate==2.0.3 django-extensions==3.2.3