VYPR
High severityNVD Advisory· Published Jul 23, 2024· Updated Aug 13, 2024

Sentry vulnerable to stored Cross-Site Scripting (XSS)

CVE-2024-41656

Description

Sentry is an error tracking and performance monitoring platform. Starting in version 10.0.0 and prior to version 24.7.1, an unsanitized payload sent by an Integration platform integration allows storing arbitrary HTML tags on the Sentry side with the subsequent rendering them on the Issues page. Self-hosted Sentry users may be impacted in case of untrustworthy Integration platform integrations sending external issues from their side to Sentry. A patch has been released in Sentry 24.7.1. For Sentry SaaS customers, no action is needed. This has been patched on July 23, and even prior to the fix, the exploitation was not possible due to the strict Content Security Policy deployed on sentry.io site. For self-hosted users, the maintainers of Sentry strongly recommend upgrading Sentry to the latest version. If it is not possible, one could enable CSP on one's self-hosted installation with CSP_REPORT_ONLY = False (enforcing mode). This will mitigate the risk of cross-site scripting.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
sentryPyPI
>= 10.0.0, < 24.7.124.7.1

Affected products

1

Patches

1
5c679521f153

fix(issues): structured issue annotations (#74648)

https://github.com/getsentry/sentryAlexander TarasovJul 22, 2024via ghsa
7 files changed · +22 19
  • src/sentry/api/serializers/models/group.py+6 1 modified
    @@ -72,6 +72,11 @@ def merge_list_dictionaries(
             dict1.setdefault(key, []).extend(val)
     
     
    +class GroupAnnotation(TypedDict):
    +    displayName: str
    +    url: str
    +
    +
     class GroupStatusDetailsResponseOptional(TypedDict, total=False):
         autoResolved: bool
         ignoreCount: int
    @@ -145,7 +150,7 @@ class BaseGroupSerializerResponse(BaseGroupResponseOptional):
         isSubscribed: bool
         subscriptionDetails: GroupSubscriptionResponseOptional | None
         hasSeen: bool
    -    annotations: Sequence[str]
    +    annotations: Sequence[GroupAnnotation]
     
     
     class SeenStats(TypedDict):
    
  • src/sentry/integrations/mixins/issues.py+1 1 modified
    @@ -348,7 +348,7 @@ def map_external_issues_to_annotations(self, external_issues):
             for ei in external_issues:
                 link = self.get_issue_url(ei.key)
                 label = self.get_issue_display_name(ei) or ei.key
    -            annotations.append(f'<a href="{link}">{label}</a>')
    +            annotations.append({"url": link, "displayName": label})
     
             return annotations
     
    
  • src/sentry/models/platformexternalissue.py+1 1 modified
    @@ -35,7 +35,7 @@ def get_annotations_for_group_list(cls, group_list):
             # group annotations by group id
             annotations_by_group_id = defaultdict(list)
             for ei in external_issues:
    -            annotation = f'<a href="{ei.web_url}">{ei.display_name}</a>'
    +            annotation = {"url": ei.web_url, "displayName": ei.display_name}
                 annotations_by_group_id[ei.group_id].append(annotation)
     
             return annotations_by_group_id
    
  • src/sentry/plugins/bases/issue2.py+4 6 modified
    @@ -2,7 +2,6 @@
     
     from django.conf import settings
     from django.urls import re_path, reverse
    -from django.utils.html import format_html
     from rest_framework.request import Request
     from rest_framework.response import Response
     
    @@ -431,11 +430,10 @@ def tags(self, request: Request, group, tag_list, **kwargs):
                 return tag_list
     
             tag_list.append(
    -            format_html(
    -                '<a href="{}">{}</a>',
    -                self._get_issue_url_compat(group, issue),
    -                self._get_issue_label_compat(group, issue),
    -            )
    +            {
    +                "url": self._get_issue_url_compat(group, issue),
    +                "displayName": self._get_issue_label_compat(group, issue),
    +            }
             )
     
             return tag_list
    
  • src/sentry/plugins/bases/issue.py+4 6 modified
    @@ -2,7 +2,6 @@
     
     from django import forms
     from django.conf import settings
    -from django.utils.html import format_html
     from rest_framework.request import Request
     
     from sentry.models.activity import Activity
    @@ -312,11 +311,10 @@ def tags(self, request: Request, group, tag_list, **kwargs):
                 return tag_list
     
             tag_list.append(
    -            format_html(
    -                '<a href="{}" rel="noreferrer">{}</a>',
    -                self.get_issue_url(group=group, issue_id=issue_id),
    -                self.get_issue_label(group=group, issue_id=issue_id),
    -            )
    +            {
    +                "url": self.get_issue_url(group=group, issue_id=issue_id),
    +                "displayName": self.get_issue_label(group=group, issue_id=issue_id),
    +            }
             )
     
             return tag_list
    
  • tests/sentry/api/endpoints/test_group_details.py+5 3 modified
    @@ -156,7 +156,7 @@ def test_platform_external_issue_annotation(self):
             response = self.client.get(url, format="json")
     
             assert response.data["annotations"] == [
    -            '<a href="https://example.com/issues/2">Issue#2</a>'
    +            {"url": "https://example.com/issues/2", "displayName": "Issue#2"}
             ]
     
         def test_plugin_external_issue_annotation(self):
    @@ -172,7 +172,9 @@ def test_plugin_external_issue_annotation(self):
             url = f"/api/0/issues/{group.id}/"
             response = self.client.get(url, format="json")
     
    -        assert response.data["annotations"] == ['<a href="https://trello.com/c/134">Trello-134</a>']
    +        assert response.data["annotations"] == [
    +            {"url": "https://trello.com/c/134", "displayName": "Trello-134"}
    +        ]
     
         def test_integration_external_issue_annotation(self):
             group = self.create_group()
    @@ -191,7 +193,7 @@ def test_integration_external_issue_annotation(self):
             response = self.client.get(url, format="json")
     
             assert response.data["annotations"] == [
    -            '<a href="https://example.com/browse/api-123">api-123</a>'
    +            {"url": "https://example.com/browse/api-123", "displayName": "api-123"}
             ]
     
         def test_permalink_superuser(self):
    
  • tests/sentry/integrations/test_issues.py+1 1 modified
    @@ -505,7 +505,7 @@ def test_annotations(self):
             link = self.installation.get_issue_url(self.external_issue.key)
     
             assert self.installation.get_annotations_for_group_list([self.group]) == {
    -            self.group.id: [f'<a href="{link}">{label}</a>']
    +            self.group.id: [{"url": link, "displayName": label}]
             }
     
             with assume_test_silo_mode(SiloMode.CONTROL):
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.