mask_url function to ease masking auth data in urls (#87256)

This commit is contained in:
Brian Coca
2026-07-17 15:20:30 -04:00
committed by GitHub
parent 35e81f866f
commit 7d7b8d905b
3 changed files with 57 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
minor_changes:
- mask_url function in module_utils to allow for masking of auth data embedded in urls.
+19
View File
@@ -45,6 +45,7 @@ import traceback
import types # pylint: disable=unused-import
import urllib.error
import urllib.request
from contextlib import contextmanager
from functools import partial
from http import cookiejar
@@ -314,6 +315,24 @@ class ParseResultDottedDict(dict):
return [self.get(k, None) for k in ('scheme', 'netloc', 'path', 'params', 'query', 'fragment')]
def mask_url(url: str) -> str:
"""
Safely display a url by masking confidential data
from a string or the result from urlparse/split
"""
if (parsed_url := urlparse(url)) and not parsed_url.username:
return url
netloc: str
mask = '****'
if parsed_url.password:
netloc = parsed_url.netloc.replace(f'{parsed_url.username}:{parsed_url.password}@', f'{mask}:{mask}@')
else:
netloc = parsed_url.netloc.replace(f'{parsed_url.username}@', f'{mask}@')
return urlunparse(parsed_url._replace(netloc=netloc))
def generic_urlparse(parts):
"""
Returns a dictionary of url parts as parsed by urlparse,
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# (c) 2026 The Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import annotations
import pytest
from ansible.module_utils.urls import mask_url
# for test data use 'secret' as part of any parameter that requires masking, avoid elsewhere
@pytest.mark.parametrize(
'url, wanted',
(
('http://nothingtoseehere.com', ('nothingtoseehere.com', 'http')),
('http://nothingtoseehere.com:80/stuff.asp?he=no', ('http://nothingtoseehere.com:80/stuff.asp?he=no',)),
('http://nothingtoseehere.com:80?password=intheclear&user=wrongbutweignore', ('wrongbut', 'intheclear', 'password')),
('https://secretuser@hideme.com/index.html', ('hideme.com', 'index.html', '*')),
('https://secretuser@hideme.com/index.html?token=nothidden&user=alsonothidden', ('token', 'nothidden', 'alsonothidden', 'user')),
('https://secretuser:secretpass@hideme.com/randomfile.html', ('randomfile.html')),
('https://secretuser:secretpass@hideme.com:443/protected.html', ('protected.html', '443')),
('ftp://secretuser:secretpass@files.insecure/subdir/intheclear.txt', ('subdir', 'intheclear.txt', 'ftp', 'files.insecure')),
('sftp://secretuser:secretpass@files.secure/subdir2/encrypted', ('encrypted', 'sftp')),
('ftps://secretuser:secretpass@file.secure/yolo.asc', ('yolo.asc', 'file.secure')),
('ftps://file.server/yolo.asc', ('yolo.asc')),
('ftps://secretuser:secretsecret@file.server/yolo.asc', ('file.server/yolo.asc')),
)
)
def test_mask_url(url, wanted):
masked = mask_url(url)
assert 'secret' not in masked
for notmasked in wanted:
assert notmasked in masked