diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py
index 2803c6d45c27bf..ee0e24f6e86ae3 100644
--- a/Lib/test/test_xmlrpc.py
+++ b/Lib/test/test_xmlrpc.py
@@ -208,6 +208,17 @@ def test_dump_encoding(self):
self.assertEqual(xmlrpclib.loads(strg)[0][0], value)
self.assertEqual(xmlrpclib.loads(strg)[1], methodname)
+ def test_dump_escape_methodname(self):
+ payload = 'foobar'
+ s = xmlrpclib.dumps((), methodname=payload)
+ self.assertIn(
+ 'foo</methodName><injected attr="evil"/>'
+ '<methodName>bar', s
+ )
+ self.assertNotIn('', s)
+ load, m = xmlrpclib.loads(s)
+ self.assertEqual(m, payload)
+
def test_dump_bytes(self):
sample = b"my dog has fleas"
self.assertEqual(sample, xmlrpclib.Binary(sample))
diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py
index f441376d09c4aa..84e4e4d11a7319 100644
--- a/Lib/xmlrpc/client.py
+++ b/Lib/xmlrpc/client.py
@@ -965,7 +965,7 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None,
data = (
xmlheader,
"\n"
- "", methodname, "\n",
+ "", escape(methodname), "\n",
data,
"\n"
)
diff --git a/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst b/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst
new file mode 100644
index 00000000000000..6245af7e362e92
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst
@@ -0,0 +1 @@
+Fix XML injection vulnerability in :func:`xmlrpc.client.dumps` where the ``methodname`` was not being escaped before interpolation into the XML body.