<commit_before>====================================
Bulk Email HTML Content Sanitization
====================================

Status
------

Accepted

Background
----------

The Bulk Course Email tool allows course teams to author messages that can be sent to learners and supporting staff enrolled in their courses. An editor is provided as part of the tool for authoring these email messages. If desired, the editor allows advanced users to write custom raw HTML in the message.

It is considered good security practice to scan and sanitize user-provided content before storing or using the data.

Decision
--------

We will sanitize the HTML content received through the bulk course email tool before sending the messages to any recipients.

We will use the `bleach`_ Python package to sanitize the data.

We will introduce a new configuration setting called ``BULK_COURSE_EMAIL_ALLOWED_HTML_TAGS`` that acts as an *allowlist* of HTML tags permitted for use within the body of a message authored by the bulk course email tool. A default list of options is provided in the ``lms/envs/common.py`` `configuration file`_. Offending data will be escaped (converted to plaintext) over being stripped out.

Consequences
------------

A message sent through the Bulk Course Email tool that includes any restricted HTML content will not appear as intended to the recipients of the message. The restricted HTML content will be converted to plaintext and will not render.

.. _bleach: https://bleach.readthedocs.io/en/latest/
.. _configuration file: https://github.com/openedx/edx-platform/blob/e608db847c39c2e3d723ef81f7dac66f63663a28/lms/envs/common.py#L4965<commit_msg>docs: Add ADR for not sanitizing html in bulk email.<commit_after>=============================================
Bulk Email HTML Content Will Not Be Sanitized
=============================================

Status
------

Accepted

Background
----------

The Bulk Course Email tool allows course teams to author messages that can be sent to learners and supporting staff enrolled in their courses. An editor is provided as part of the tool for authoring these email messages. If desired, the editor allows advanced users to write custom raw HTML in the message.

It is considered good security practice to scan and sanitize user-provided content before storing or using the data.

Decision
--------

We will not sanitize the HTML content received through the bulk course email tool on the back end. The content that instructors create for courses also allows unfiltered html, which is arguably a larger risk than email, which will block the execution of certain types of code. In the future, if a standard is set for filtering course content, the same standard could be applied here.


Rejected solutions
------------------

Bleach with allowlist
*********************

It has been standard practice for us to use `Bleach`_ with an allowlist to sanitize user provided content within the Open edX ecosystem. Santization using blocklists is vulnerable to obfuscation attacks, and the industry standard is to use an allowlist and explicitly enumerate all supported values. Given that this tool has been live for many years with no sanitization in place, a highly restrictive allowlist would be difficult to roll out to users, resulting in broken email templates and angry instructors who are used to having free rein. A permissive allowlist would potentially address this, but presents the non-trivial problem of assembling and maintaining a comprehensive list.

.. _bleach: https://bleach.readthedocs.io/en/latest/
{'fraction_non_alphanumeric': 0.10181818181818182, 'fraction_numerical': 0.008111888111888113, 'mean_word_length': 4.393665158371041, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2500702', 'n_tokens_mistral': 857, 'n_tokens_neox': 804, 'n_words': 486}