mark decision notes as safe so jinja doesn't escape them (#22454)

* mark decision notes as safe so jinja doesn't escape them

* add test to prove unsafe content is included unescaped

* rm pointless no_jinja_autoescape (it's not jinja); update comments
This commit is contained in:
Andrew Williamson 2024-07-09 17:17:10 +01:00 коммит произвёл GitHub
Родитель 8d4b227677
Коммит c928b187a3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
2 изменённых файлов: 35 добавлений и 16 удалений

Просмотреть файл

@ -43,7 +43,7 @@ class BaseTestCinderAction:
self.decision = CinderDecision.objects.create(
cinder_id='ab89',
action=DECISION_ACTIONS.AMO_APPROVE,
notes='extra notes',
notes="extra note's",
addon=addon,
)
self.cinder_job = CinderJob.objects.create(
@ -167,6 +167,7 @@ class BaseTestCinderAction:
assert f'[ref:ab89/{self.abuse_report_auth.id}]' in mail.outbox[0].body
assert '"' not in mail.outbox[0].body
assert '<b>' not in mail.outbox[0].body
assert ''' not in mail.outbox[0].body
assert self.decision.notes in mail.outbox[0].body
def _check_owner_email(self, mail_item, subject, snippet):
@ -177,6 +178,7 @@ class BaseTestCinderAction:
assert '[ref:ab89]' in mail_item.body
assert '"' not in mail_item.body
assert '<b>' not in mail_item.body
assert ''' not in mail_item.body
assert self.decision.notes in mail_item.body
def _test_owner_takedown_email(self, subject, snippet):
@ -198,6 +200,7 @@ class BaseTestCinderAction:
)
assert '"' not in mail_item.body
assert '<b>' not in mail_item.body
assert ''' not in mail_item.body
assert self.decision.notes in mail_item.body
def _test_owner_affirmation_email(self, subject):
@ -206,6 +209,7 @@ class BaseTestCinderAction:
assert 'right to appeal' not in mail_item.body
notes = f'{self.decision.notes}. ' if self.decision.notes else ''
assert f' was correct. {notes}Based on that determination' in (mail_item.body)
assert ''' not in mail_item.body
if isinstance(self.decision.target, Addon):
# Verify we used activity mail for Addon related target emails
log_token = ActivityLogToken.objects.get()
@ -216,6 +220,7 @@ class BaseTestCinderAction:
assert len(mail.outbox) == 1
self._check_owner_email(mail_item, subject, 'we have restored')
assert 'right to appeal' not in mail_item.body
assert ''' not in mail_item.body
assert self.decision.notes in mail_item.body
def _test_approve_appeal_or_override(CinderActionClass):
@ -307,6 +312,20 @@ class BaseTestCinderAction:
assert 'Bad policy' not in mail.outbox[idx].body # policy name
assert 'Parent' not in mail.outbox[idx].body # parent policy text
def test_email_content_not_escaped(self):
unsafe_str = '<script>jar=window.triggerExploit();"</script>'
self.decision.update(notes=unsafe_str)
action = self.ActionClass(self.decision)
action.notify_owners()
assert unsafe_str in mail.outbox[0].body
action = CinderActionApproveNoAction(self.decision)
mail.outbox.clear()
action.notify_reporters(
reporter_abuse_reports=[self.abuse_report_auth], is_appeal=True
)
assert unsafe_str in mail.outbox[0].body
class TestCinderActionUser(BaseTestCinderAction, TestCase):
ActionClass = CinderActionBanUser

Просмотреть файл

@ -15,7 +15,7 @@ from olympia import activity, amo
from olympia.activity import log_create
from olympia.addons.models import Addon
from olympia.amo.templatetags.jinja_helpers import absolutify
from olympia.amo.utils import no_jinja_autoescape, send_mail
from olympia.amo.utils import send_mail
from olympia.bandwagon.models import Collection
from olympia.ratings.models import Rating
from olympia.users.models import UserProfile
@ -90,16 +90,15 @@ class CinderAction:
owners = self.get_owners()
if not owners:
return
with no_jinja_autoescape():
template = loader.get_template(self.owner_template_path)
template = loader.get_template(self.owner_template_path)
target_name = self.get_target_name()
reference_id = f'ref:{self.decision.get_reference_id()}'
context_dict = {
'is_third_party_initiated': self.decision.is_third_party_initiated,
'manual_reasoning_text': self.decision.notes or '',
# Auto-escaping is already disabled above as we're dealing with an
# email but the target name could have triggered lazy escaping when
# it was generated so it needs special treatment to avoid it.
# It's a plain-text email so we're safe to include comments without escaping
# them - we don't want ', etc, rendered as html entities.
'manual_reasoning_text': mark_safe(self.decision.notes or ''),
# It's a plain-text email so we're safe to include the name without escaping
'name': mark_safe(target_name),
'policy_document_url': POLICY_DOCUMENT_URL,
'reference_id': reference_id,
@ -148,8 +147,7 @@ class CinderAction:
)
if not template or not reporter_abuse_reports:
return
with no_jinja_autoescape():
template = loader.get_template(template)
template = loader.get_template(template)
for abuse_report in reporter_abuse_reports:
email_address = (
abuse_report.reporter.email
@ -169,10 +167,8 @@ class CinderAction:
target_name, reference_id
)
context_dict = {
# Auto-escaping is already disabled above as we're dealing
# with an email but the target name could have triggered
# lazy escaping when it was generated so it needs special
# treatment to avoid it.
# It's a plain-text email so we're safe to include the name without
# escaping it.
'name': mark_safe(target_name),
'policies': self.decision.policies.all(),
'policy_document_url': POLICY_DOCUMENT_URL,
@ -181,8 +177,12 @@ class CinderAction:
'type': self.get_target_type(),
'SITE_URL': settings.SITE_URL,
}
if self.decision.cinder_job.is_appeal:
context_dict['manual_reasoning_text'] = self.decision.notes or ''
if is_appeal:
# It's a plain-text email so we're safe to include comments without
# escaping them - we don't want ', etc, rendered as html entities.
context_dict['manual_reasoning_text'] = mark_safe(
self.decision.notes or ''
)
if self.decision.can_be_appealed(
is_reporter=True, abuse_report=abuse_report
):