Skip to content

Escape username parameter#901

Merged
DharshanBJ merged 2 commits intodevfrom
shared/escapeUsername
Apr 10, 2026
Merged

Escape username parameter#901
DharshanBJ merged 2 commits intodevfrom
shared/escapeUsername

Conversation

@DharshanBJ
Copy link
Copy Markdown
Contributor

No description provided.

@DharshanBJ DharshanBJ requested a review from a team as a code owner April 8, 2026 22:32
Copilot AI review requested due to automatic review settings April 8, 2026 22:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR addresses an XML injection risk in WS-Trust RST construction by escaping untrusted values placed into the SOAP XML payload.

Changes:

  • Introduces escape_xml() and uses it when inserting username and password into the WS-Trust RST XML.
  • Adds unit tests to validate XML escaping and prevent username-based XML injection.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
msal/wstrust_request.py Adds a general XML escaping helper and applies it to username/password in the RST builder.
tests/test_wstrust.py Adds tests for escaping and a regression test for username-based XML injection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 116 to 121
endpoint_address=endpoint_address,
time_now=wsu_time_format(now),
time_expire=wsu_time_format(now + timedelta(minutes=10)),
username=username, password=escape_password(password),
username=escape_xml(username), password=escape_xml(password),
wst=Mex.NS["wst"] if soap_action == Mex.ACTION_13 else Mex.NS["wst2005"],
applies_to=cloud_audience_urn,
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_build_rst() now escapes username/password, but still interpolates other RST fields (e.g., endpoint_address and cloud_audience_urn via applies_to) into XML without escaping. This can still allow XML injection or malformed XML if those values contain <, &, etc. Apply escape_xml() to these interpolated text-node values too (or switch to building the XML via an XML library like ElementTree to avoid manual escaping entirely).

Copilot uses AI. Check for mistakes.
return (password.replace('&', '&amp;').replace('"', '&quot;')
def escape_xml(s):
return (s.replace('&', '&amp;').replace('"', '&quot;')
.replace("'", '&apos;') # the only one not provided by cgi.escape(s, True)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline comment references cgi.escape, which has been deprecated/removed in modern Python and is misleading here. Update the comment to reference an appropriate XML escaping utility (e.g., xml.sax.saxutils.escape) or remove the historical note to avoid confusion.

Suggested change
.replace("'", '&apos;') # the only one not provided by cgi.escape(s, True)
.replace("'", '&apos;') # Include apostrophes explicitly for XML attribute/content safety

Copilot uses AI. Check for mistakes.
self.assertIn(b"<saml:Assertion", result.get("token", ""))


class Test_WsTrustRequest(unittest.TestCase):
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test class naming is inconsistent with common unittest conventions (CamelCase without underscores). Consider renaming to TestWsTrustRequest for consistency and discoverability.

Suggested change
class Test_WsTrustRequest(unittest.TestCase):
class TestWsTrustRequest(unittest.TestCase):

Copilot uses AI. Check for mistakes.
from xml.etree import ElementTree as ET
import os

from msal.wstrust_request import _build_rst, escape_xml
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests import and call _build_rst, a private (underscore-prefixed) helper. This makes the test more brittle to internal refactors. If this behavior is intended to be stable, consider making it a supported helper (public name), or alternatively test via a public API boundary with mocking around I/O.

Copilot uses AI. Check for mistakes.

def test_username_xml_injection_is_prevented(self):
malicious = 'admin</wsse:Username><x>INJECTED'
rst = _build_rst(malicious, 'pw', 'urn:x', 'https://x',
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests import and call _build_rst, a private (underscore-prefixed) helper. This makes the test more brittle to internal refactors. If this behavior is intended to be stable, consider making it a supported helper (public name), or alternatively test via a public API boundary with mocking around I/O.

Copilot uses AI. Check for mistakes.
@DharshanBJ DharshanBJ changed the title Fix XML injection vulnerability in WS-Trust username parameter (CWE-91) Escape username parameter Apr 9, 2026
@DharshanBJ DharshanBJ merged commit 67136fe into dev Apr 10, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants