fix: sanitize email HTML templates (#5108)

## Which issue(s) this PR closes

Closes https://github.com/grafana/oncall-private/issues/2760

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.
This commit is contained in:
Joey Orlando 2024-10-02 11:55:24 -04:00 committed by GitHub
parent 70b7273078
commit 97096c6982
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 1 deletions

View file

@ -16,6 +16,7 @@ from apps.alerts.tasks.notify_user import notify_user_task
from apps.schedules.ical_utils import get_cached_oncall_users_for_multiple_schedules
from apps.schedules.models import OnCallSchedule
from apps.user_management.models import Organization, Team, User
from common.utils import escape_html
UserNotifications = list[tuple[User, bool]]
@ -145,6 +146,10 @@ def direct_paging(
if alert_group and alert_group.resolved:
raise DirectPagingAlertGroupResolvedError
# https://github.com/grafana/oncall-private/issues/2760
title = escape_html(title)
message = escape_html(message)
if title is None:
title = _construct_title(from_user, team, users)

View file

@ -312,3 +312,25 @@ def test_construct_title(make_organization, make_team, make_user_for_organizatio
assert _construct_title(from_user, team, multiple_users) == _title(
f"{team.name}, {user1.username}, {user2.username} and {user3.username}"
)
@pytest.mark.django_db
def test_direct_paging_title_and_message_are_html_escaped(make_organization, make_user_for_organization):
dirty_input = "<script>alert('hacked');</script>"
clean_input = "&lt;script&gt;alert('hacked');&lt;/script&gt;"
organization = make_organization()
from_user = make_user_for_organization(organization)
other_user = make_user_for_organization(organization)
direct_paging(organization, from_user, dirty_input, dirty_input, users=[(other_user, False)])
# alert group created
alert_groups = AlertGroup.objects.all()
assert alert_groups.count() == 1
ag = alert_groups.get()
alert = ag.alerts.get()
assert ag.web_title_cache == clean_input
assert alert.title == clean_input
assert alert.message == clean_input

View file

@ -249,7 +249,7 @@ def clean_markup(text):
def escape_html(text):
return html.escape(text, quote=False)
return html.escape(text, quote=False) if text else text
def urlize_with_respect_to_a(html):