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'}):
