You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by gc...@apache.org on 2026/02/09 20:38:18 UTC

(allura) branch gc/8595 created (now d6b36b91a)

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

gcruz pushed a change to branch gc/8595
in repository https://gitbox.apache.org/repos/asf/allura.git


      at d6b36b91a [#8595] add new function to hide/redact email addresses and also shorten the acces tokens that appear printed in the logs

This branch includes the following new commits:

     new d6b36b91a [#8595] add new function to hide/redact email addresses and also shorten the acces tokens that appear printed in the logs

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(allura) 01/01: [#8595] add new function to hide/redact email addresses and also shorten the acces tokens that appear printed in the logs

Posted by gc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch gc/8595
in repository https://gitbox.apache.org/repos/asf/allura.git

commit d6b36b91aac2a7bdd0d1b3f9cb7d7578b18004a0
Author: Guillermo Cruz <gu...@slashdotmedia.com>
AuthorDate: Mon Feb 9 13:37:57 2026 -0700

    [#8595] add new function to hide/redact email addresses and also shorten the acces tokens that appear printed in the logs
---
 Allura/allura/controllers/rest.py |  4 +--
 Allura/allura/lib/utils.py        | 54 +++++++++++++++++++++++++++++++++++++++
 Allura/allura/model/auth.py       |  8 +++---
 3 files changed, 60 insertions(+), 6 deletions(-)

diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 267594f9b..15a109c40 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -158,7 +158,7 @@ def save_request_token(self, token: dict, request: oauthlib.common.Request) -> N
             callback=request.oauth_params.get('oauth_callback', 'oob'),
         )
         session(req_token).flush()
-        log.info('Saving new request token with key: %s', req_token.api_key)
+        log.info('Saving new request token with key: %s', req_token.api_key[:(len(req_token.api_key) // 2)])
 
     def verify_request_token(self, token: str, request: oauthlib.common.Request) -> bool:
         return M.OAuthRequestToken.query.get(api_key=token) is not None
@@ -477,7 +477,7 @@ def authorize(self, **kwargs):
 
         rtok = M.OAuthRequestToken.query.get(api_key=oauth_token)
         if rtok is None:
-            log.error('Invalid token %s', oauth_token)
+            log.error('Invalid token %s', oauth_token[:(len(oauth_token) // 2)])
             raise exc.HTTPUnauthorized
         # store what user this is, so later use of the token can act as them
         rtok.user_id = c.user._id
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index ed8c82b2f..0c4f204ab 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -882,3 +882,57 @@ def join_paths_no_traversal(base: str | Path, *paths: str | Path) -> str:
 
 def pkg_file(pkg_name: str, *paths: str) -> str:
     return join_paths_no_traversal(importlib.resources.files(pkg_name), *paths)
+
+
+def hide_email(email_or_match: str | re.Match | Iterable[str] | None) -> str:
+    hide_name_re = re.compile(r'(\w{2,}.\s+)')
+    hide_email_re = re.compile(r'<?([_a-zA-Z0-9+.=-]+)@([a-zA-Z0-9-]{2}).*>?')
+    hide_email_cleanup_re = re.compile(r'"|=\?.+\?=')
+    """
+    Emails passed to this method may or may not include brackets, but it
+    can't be inferred from just that whether or not they *should* have brackets
+    in the place where it'll be displayed.  So, use the optional kwarg to make that explicit.
+    Possible places include the mailman heading table and the body of individual messages.
+    """
+    def erepl(m):
+        uname = m.group(1)
+        truncate = 2 if len(uname) <= 7 else 3
+        res = '{}...@{}...'.format(m.group(1)[:truncate], m.group(2))
+        if ensure_brackets and not res.startswith('<'):
+            res = f"<{res}>"
+        return res
+
+    def hide(email_address):
+        email = hide_email_re.sub(erepl, email_address)
+        email = hide_email_cleanup_re.sub('', email)
+        return email
+
+    def name_tpl(m):
+        return f'{m.group(1)[:1]}. '
+
+    def hide_name(email_address):
+        str_pieces = email_address.split(' ')
+        if len(str_pieces) == 1:
+            return email_address
+        first_name = str_pieces[0]
+        email_address = email_address.replace(first_name, '', 1)
+        result = hide_name_re.sub(name_tpl, email_address)
+        return f'{first_name}{result}'
+
+    if not email_or_match:
+        return ''
+
+    ensure_brackets = False
+    if isinstance(email_or_match, re.Match):
+        email = email_or_match.group(0)
+    elif isinstance(email_or_match, str):
+        ensure_brackets = True
+        email = email_or_match
+    else:
+        emails = []
+        for address in email_or_match:
+            email = hide(address)
+            emails.append(email)
+        return ', '.join(emails)
+    email = hide_name(email)
+    email = hide(email)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 28ee44c5d..0b331ed3a 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -171,7 +171,7 @@ def set_nonce_hash(self):
 
     def send_verification_link(self):
         self.set_nonce_hash()
-        log.info('Sending verification link to %s', self.email)
+        log.info('Sending verification link to %s', utils.hide_email(self.email))
         text = '''
 To verify the email address {} belongs to the user {},
 please visit the following URL:
@@ -181,7 +181,7 @@ def send_verification_link(self):
             self.claimed_by_user(include_pending=True).username,
             h.absurl(f'/auth/verify_addr?a={h.urlquote(self.nonce)}'),
            )
-        log.info('Verification email:\n%s', text)
+        log.info('Verification email:\n%s', utils.hide_email(self.email))
         allura.tasks.mail_tasks.sendsimplemail.post(
             fromaddr=g.noreply,
             reply_to=g.noreply,
@@ -452,7 +452,7 @@ def send_password_reset_email(self, email_address=None, subject_tmpl='{site_name
             email_address = self.get_pref('email_address')
         reset_url = self.make_password_reset_url()
 
-        log.info('Sending password recovery link to %s', email_address)
+        log.info('Sending password recovery link to %s', utils.hide_email(email_address))
         subject = subject_tmpl.format(site_name=config['site_name'])
         text = g.jinja2_env.get_template('allura:templates/mail/forgot_password.txt').render(dict(
             user=self,
@@ -733,7 +733,7 @@ def by_email_address(cls, addr, only_confirmed=True):
         users = [u for u in users if u is not None]
         if len(users) > 1:
             log.warning('Multiple active users matching confirmed email: %s %s. '
-                        'Using first one', [u.username for u in users], addr)
+                        'Using first one', [u.username for u in users], utils.hide_email(addr))
         return users[0] if len(users) > 0 else None
 
     @classmethod