Skip to content

Commit

Permalink
Redact single-part login credentials from URLs (#6921)
Browse files Browse the repository at this point in the history
  • Loading branch information
atugushev authored and Patrik Kopkan committed Sep 4, 2019
1 parent f93d5d3 commit 89f5adc
Show file tree
Hide file tree
Showing 8 changed files with 33 additions and 25 deletions.
1 change: 1 addition & 0 deletions news/6891.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Redact single-part login credentials from URLs in log messages.
4 changes: 2 additions & 2 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from pip._internal.models.search_scope import SearchScope
from pip._internal.models.target_python import TargetPython
from pip._internal.utils.hashes import STRONG_HASHES
from pip._internal.utils.misc import redact_password_from_url
from pip._internal.utils.misc import redact_auth_from_url
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
from pip._internal.utils.ui import BAR_TYPES

Expand Down Expand Up @@ -369,7 +369,7 @@ def make_search_scope(options, suppress_no_index=False):
if options.no_index and not suppress_no_index:
logger.debug(
'Ignoring indexes: %s',
','.join(redact_password_from_url(url) for url in index_urls),
','.join(redact_auth_from_url(url) for url in index_urls),
)
index_urls = []

Expand Down
6 changes: 3 additions & 3 deletions src/pip/_internal/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
WHEEL_EXTENSION,
build_netloc,
path_to_url,
redact_password_from_url,
redact_auth_from_url,
)
from pip._internal.utils.packaging import check_requires_python
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
Expand Down Expand Up @@ -154,7 +154,7 @@ def _get_html_response(url, session):
if _is_url_like_archive(url):
_ensure_html_response(url, session=session)

logger.debug('Getting page %s', redact_password_from_url(url))
logger.debug('Getting page %s', redact_auth_from_url(url))

resp = session.get(
url,
Expand Down Expand Up @@ -1381,7 +1381,7 @@ def __init__(self, content, url, headers=None):
self.headers = headers

def __str__(self):
return redact_password_from_url(self.url)
return redact_auth_from_url(self.url)

def iter_links(self):
# type: () -> Iterable[Link]
Expand Down
6 changes: 3 additions & 3 deletions src/pip/_internal/models/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pip._internal.utils.misc import (
WHEEL_EXTENSION,
path_to_url,
redact_password_from_url,
redact_auth_from_url,
split_auth_from_netloc,
splitext,
)
Expand Down Expand Up @@ -68,10 +68,10 @@ def __str__(self):
else:
rp = ''
if self.comes_from:
return '%s (from %s)%s' % (redact_password_from_url(self._url),
return '%s (from %s)%s' % (redact_auth_from_url(self._url),
self.comes_from, rp)
else:
return redact_password_from_url(str(self._url))
return redact_auth_from_url(str(self._url))

def __repr__(self):
return '<Link %s>' % self
Expand Down
6 changes: 3 additions & 3 deletions src/pip/_internal/models/search_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from pip._internal.models.index import PyPI
from pip._internal.utils.compat import HAS_TLS
from pip._internal.utils.misc import normalize_path, redact_password_from_url
from pip._internal.utils.misc import normalize_path, redact_auth_from_url
from pip._internal.utils.typing import MYPY_CHECK_RUNNING

if MYPY_CHECK_RUNNING:
Expand Down Expand Up @@ -80,12 +80,12 @@ def get_formatted_locations(self):
if self.index_urls and self.index_urls != [PyPI.simple_url]:
lines.append(
'Looking in indexes: {}'.format(', '.join(
redact_password_from_url(url) for url in self.index_urls))
redact_auth_from_url(url) for url in self.index_urls))
)
if self.find_links:
lines.append(
'Looking in links: {}'.format(', '.join(
redact_password_from_url(url) for url in self.find_links))
redact_auth_from_url(url) for url in self.find_links))
)
return '\n'.join(lines)

Expand Down
6 changes: 3 additions & 3 deletions src/pip/_internal/req/req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
ensure_dir,
get_installed_version,
hide_url,
redact_password_from_url,
redact_auth_from_url,
rmtree,
)
from pip._internal.utils.packaging import get_metadata
Expand Down Expand Up @@ -170,9 +170,9 @@ def __str__(self):
if self.req:
s = str(self.req)
if self.link:
s += ' from %s' % redact_password_from_url(self.link.url)
s += ' from %s' % redact_auth_from_url(self.link.url)
elif self.link:
s = redact_password_from_url(self.link.url)
s = redact_auth_from_url(self.link.url)
else:
s = '<InstallRequirement>'
if self.satisfied_by is not None:
Expand Down
19 changes: 13 additions & 6 deletions src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,15 +1194,22 @@ def split_auth_from_netloc(netloc):
def redact_netloc(netloc):
# type: (str) -> str
"""
Replace the password in a netloc with "****", if it exists.
Replace the sensitive data in a netloc with "****", if it exists.
For example, "user:pass@example.com" returns "user:****@example.com".
For example:
- "user:pass@example.com" returns "user:****@example.com"
- "accesstoken@example.com" returns "****@example.com"
"""
netloc, (user, password) = split_auth_from_netloc(netloc)
if user is None:
return netloc
password = '' if password is None else ':****'
return '{user}{password}@{netloc}'.format(user=urllib_parse.quote(user),
if password is None:
user = '****'
password = ''
else:
user = urllib_parse.quote(user)
password = ':****'
return '{user}{password}@{netloc}'.format(user=user,
password=password,
netloc=netloc)

Expand Down Expand Up @@ -1254,7 +1261,7 @@ def remove_auth_from_url(url):
return _transform_url(url, _get_netloc)[0]


def redact_password_from_url(url):
def redact_auth_from_url(url):
# type: (str) -> str
"""Replace the password in a given url with ****."""
return _transform_url(url, _redact_netloc)[0]
Expand Down Expand Up @@ -1302,7 +1309,7 @@ def hide_value(value):

def hide_url(url):
# type: (str) -> HiddenText
redacted = redact_password_from_url(url)
redacted = redact_auth_from_url(url)
return HiddenText(url, redacted=redacted)


Expand Down
10 changes: 5 additions & 5 deletions tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
parse_netloc,
path_to_display,
path_to_url,
redact_auth_from_url,
redact_netloc,
redact_password_from_url,
remove_auth_from_url,
rmtree,
split_auth_from_netloc,
Expand Down Expand Up @@ -1332,7 +1332,7 @@ def test_split_auth_netloc_from_url(url, expected):
# Test a basic case.
('example.com', 'example.com'),
# Test with username and no password.
('user@example.com', 'user@example.com'),
('accesstoken@example.com', '****@example.com'),
# Test with username and password.
('user:pass@example.com', 'user:****@example.com'),
# Test with username and empty password.
Expand Down Expand Up @@ -1371,16 +1371,16 @@ def test_remove_auth_from_url(auth_url, expected_url):


@pytest.mark.parametrize('auth_url, expected_url', [
('https://user@example.com/abc', 'https://user@example.com/abc'),
('https://accesstoken@example.com/abc', 'https://****@example.com/abc'),
('https://user:password@example.com', 'https://user:****@example.com'),
('https://user:@example.com', 'https://user:****@example.com'),
('https://example.com', 'https://example.com'),
# Test URL-encoded reserved characters.
('https://user%3Aname:%23%40%5E@example.com',
'https://user%3Aname:****@example.com'),
])
def test_redact_password_from_url(auth_url, expected_url):
url = redact_password_from_url(auth_url)
def test_redact_auth_from_url(auth_url, expected_url):
url = redact_auth_from_url(auth_url)
assert url == expected_url


Expand Down

0 comments on commit 89f5adc

Please sign in to comment.