From 318e86339f81121c5ce4cbf83f43bd74d58af8b0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 17 Mar 2026 21:56:25 +0000
Subject: [PATCH 1/2] Initial plan
From ef54ae04ce5f31cc43f989308fa55934f57bffee Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 17 Mar 2026 22:00:24 +0000
Subject: [PATCH 2/2] Fix reflected XSS in composer mygraph view (issue #2794)
Co-authored-by: deniszh <1227222+deniszh@users.noreply.github.com>
---
webapp/graphite/composer/views.py | 7 ++++---
webapp/tests/test_xss.py | 24 ++++++++++++++++++++++++
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/webapp/graphite/composer/views.py b/webapp/graphite/composer/views.py
index f60c37bf6..09f4f50c6 100644
--- a/webapp/graphite/composer/views.py
+++ b/webapp/graphite/composer/views.py
@@ -21,6 +21,7 @@
from django.http import HttpResponse
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
+from django.utils.html import escape
def composer(request):
@@ -65,7 +66,7 @@ def mygraph(request):
newGraph.save()
except Exception:
log.exception("Failed to create new MyGraph in /composer/mygraph/, graphName=%s" % graphName)
- return HttpResponse("Failed to save graph %s" % graphName)
+ return HttpResponse("Failed to save graph %s" % escape(graphName))
return HttpResponse("SAVED")
@@ -75,9 +76,9 @@ def mygraph(request):
existingGraph.delete()
except ObjectDoesNotExist:
- return HttpResponse("No such graph '%s'" % graphName)
+ return HttpResponse("No such graph '%s'" % escape(graphName))
return HttpResponse("DELETED")
else:
- return HttpResponse("Invalid operation '%s'" % action)
+ return HttpResponse("Invalid operation '%s'" % escape(action))
diff --git a/webapp/tests/test_xss.py b/webapp/tests/test_xss.py
index da9d17eed..4b6cf8d54 100644
--- a/webapp/tests/test_xss.py
+++ b/webapp/tests/test_xss.py
@@ -6,6 +6,8 @@
except ImportError: # Django < 1.10
from django.urls import reverse
+from django.contrib.auth.models import User
+
from .base import TestCase
# Silence logging during tests
@@ -49,3 +51,25 @@ def test_find_xss_script_tag(self):
for param in ('from', 'until'):
response = self.client.get(url, {'query': 'test', param: xssStr})
self.assertXSS(response, status_code=400, msg_prefix='XSS detected in %s: ' % param)
+
+
+class ComposerMyGraphXSSTest(TestCase):
+ def setUp(self):
+ self.user = User.objects.create_user('testxss', 'testxss@example.com', 'pass')
+ self.client.login(username='testxss', password='pass')
+
+ def test_mygraph_xss_action(self):
+ """Test that XSS in the action parameter is properly escaped (issue #2794)"""
+ url = reverse('composer_mygraph')
+ xssStr = '">'
+
+ response = self.client.get(url, {'action': xssStr, 'graphName': 'test'})
+ self.assertXSS(response, msg_prefix='XSS detected in action: ')
+
+ def test_mygraph_xss_graphname(self):
+ """Test that XSS in the graphName parameter is properly escaped (issue #2794)"""
+ url = reverse('composer_mygraph')
+ xssStr = '">'
+
+ response = self.client.get(url, {'action': 'delete', 'graphName': xssStr})
+ self.assertXSS(response, msg_prefix='XSS detected in graphName: ')