|
From: Günter M. <mi...@us...> - 2026-08-21 13:57:41
|
- **status**: open --> open-fixed - **private**: Yes --> No - **Comment**: The issue is fixed in [r10394]. Thank you again for reporting. --- **[bugs:#519] XSS via unescaped HTML in PEP email masking transform** **Status:** open-fixed **Created:** Mon Jul 27, 2026 10:09 AM UTC by Florian Weimer **Last Updated:** Sat Aug 15, 2026 07:10 PM UTC **Owner:** nobody **Attachments:** - [RHEL-214726.txt](https://sourceforge.net/p/docutils/bugs/519/attachment/RHEL-214726.txt) (5.9 kB; text/plain) I have been instructed to forward the attached security report to you privately. As far as I can tell, it is accurate, and the vulnerability is still present in the current sources. My understanding is that docutils offers a processing mode that is robust in the presence of malicious inputs, which is why a trust boundary is crossed. Please let me know how you want to handle this. An automatically generated proper reproducer looks like this: ``` #!/usr/bin/env python3 """Reproducer for XSS in PEP email masking transform (mask_email).""" import sys sys.path.insert(0, 'docutils') from docutils.core import publish_parts src = """\ PEP: 9999 Title: Test Author: `"><img src=x onerror=alert(1)> <ev...@ex...>`_ Status: Draft Type: Standards Track Created: 01-Jan-2026 Abstract ======== Test. """ parts = publish_parts(source=src, reader_name='pep', writer_name='html') body = parts['html_body'] if '<img src=x onerror=alert(1)>' in body: print('VULNERABLE: unescaped HTML found in output') print() for line in body.splitlines(): if 'onerror' in line: print(' ', line.strip()) sys.exit(1) else: print('OK: no unescaped HTML in output') sys.exit(0) ``` The reconstructed patch is: ``` Index: docutils/docutils/transforms/peps.py =================================================================== --- docutils/docutils/transforms/peps.py (revision 10391) +++ docutils/docutils/transforms/peps.py (working copy) @@ -303,8 +303,8 @@ if ref['refuri'][8:] in non_masked_addresses: replacement = ref[0] else: - replacement_text = ref.astext().replace('@', ' at ') - replacement = nodes.raw('', replacement_text, format='html') + replacement_text = ref.astext().replace('@', ' at ') + replacement = nodes.Text(replacement_text) if pepno is None: return replacement else: ``` --- Sent from sourceforge.net because doc...@li... is subscribed to https://sourceforge.net/p/docutils/bugs/ To unsubscribe from further messages, a project admin can change settings at https://sourceforge.net/p/docutils/admin/bugs/options. Or, if this is a mailing list, you can unsubscribe from the mailing list. |