This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 5f8dca2b79215cd169b99ff7e622f32bb5a5966c
Author: Dave Brondsema <[email protected]>
AuthorDate: Thu May 21 18:09:57 2026 -0400

    [#8607] be extra safe with display names
---
 Allura/allura/controllers/auth.py           |  5 +++++
 Allura/allura/lib/validators.py             |  9 +++++++++
 Allura/allura/lib/widgets/forms.py          |  2 +-
 Allura/allura/tests/functional/test_auth.py | 22 ++++++++++++++++++++++
 4 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/Allura/allura/controllers/auth.py 
b/Allura/allura/controllers/auth.py
index 0917f19df..2db61175c 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -806,6 +806,11 @@ def update(self, preferences=None, **kw):
             if not preferences.get('display_name'):
                 flash("Display Name cannot be empty.", 'error')
                 redirect('.')
+            try:
+                preferences['display_name'] = 
V.DisplayName(not_empty=True).to_python(preferences['display_name'])
+            except fe.Invalid as e:
+                flash(str(e), 'error')
+                redirect('.')
             old = c.user.get_pref('display_name')
             c.user.set_pref('display_name', preferences['display_name'])
             if old != preferences['display_name']:
diff --git a/Allura/allura/lib/validators.py b/Allura/allura/lib/validators.py
index 814fa920b..e9d81fa91 100644
--- a/Allura/allura/lib/validators.py
+++ b/Allura/allura/lib/validators.py
@@ -133,6 +133,15 @@ class UnicodeString(fev.UnicodeString):
 String = UnicodeString if str is str else fev.ByteString
 
 
+class DisplayName(UnicodeString):
+    def _convert_to_python(self, value, state):
+        # prevent < and line breaks just to be sure HTML and email outputs are 
safe
+        value = super()._convert_to_python(value, state)
+        if value and re.search(r'[<\r\n]', value):
+            raise fev.Invalid('Display Name cannot contain < or line breaks', 
value, state)
+        return value
+
+
 class Ming(fev.FancyValidator):
 
     def __init__(self, cls, **kw):
diff --git a/Allura/allura/lib/widgets/forms.py 
b/Allura/allura/lib/widgets/forms.py
index 59cd1c0f0..2d0e38391 100644
--- a/Allura/allura/lib/widgets/forms.py
+++ b/Allura/allura/lib/widgets/forms.py
@@ -752,7 +752,7 @@ def fields(self):
             ew.TextField(
                 name='display_name',
                 label='Displayed Name',
-                validator=V.UnicodeString(not_empty=True)),
+                validator=V.DisplayName(not_empty=True)),
             username,
         ]
         if asbool(config.get('auth.require_email_addr', False)):
diff --git a/Allura/allura/tests/functional/test_auth.py 
b/Allura/allura/tests/functional/test_auth.py
index 8338a2ecc..d323137bf 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -806,6 +806,19 @@ def test_prefs(self, gen_message_id, sendsimplemail):
                                       },
                               extra_environ=dict(username='test-admin'))
 
+    @td.with_user_project('test-admin')
+    def test_prefs_display_name_rejects_unsafe_chars(self):
+        self.app.get('/').follow()
+        for bad in ['Admin <script>', 'Admin\nBcc: [email protected]', 
'Admin\r\nfoo']:
+            r = self.app.post('/auth/preferences/update',
+                              params={'preferences.display_name': bad,
+                                      '_csrf_token': 
self.app.cookies['_csrf_token'],
+                                      },
+                              extra_environ=dict(username='test-admin'))
+            assert 'cannot contain' in self.webflash(r)
+        user = M.User.query.get(username='test-admin')
+        assert user.get_pref('display_name') == 'Test Admin'
+
     @td.with_user_project('test-admin')
     @patch('allura.tasks.mail_tasks.sendsimplemail')
     @patch('allura.lib.helpers.gen_message_id')
@@ -1096,6 +1109,15 @@ def test_create_account(self):
                         _csrf_token=self.app.cookies['_csrf_token']), 
antispam=True,
             status=302)
 
+    def test_create_account_display_name_rejects_unsafe_chars(self):
+        self.app.get('/').follow()
+        r = self.app.post('/auth/save_new',
+                          params=dict(username='aaa', pw='12345678', 
pw2='12345678',
+                                      display_name='Bob <[email protected]>',
+                                      
_csrf_token=self.app.cookies['_csrf_token']))
+        assert 'cannot contain' in r
+        assert M.User.query.get(username='aaa') is None
+
     def test_create_account_require_email(self):
         self.app.get('/').follow()  # establish session
         with h.push_config(config, **{'auth.require_email_addr': 'false'}):

Reply via email to