diff options
-rw-r--r-- | news/6891.feature | 1 | ||||
-rw-r--r-- | src/pip/_internal/cli/cmdoptions.py | 4 | ||||
-rw-r--r-- | src/pip/_internal/index.py | 6 | ||||
-rw-r--r-- | src/pip/_internal/models/link.py | 6 | ||||
-rw-r--r-- | src/pip/_internal/models/search_scope.py | 6 | ||||
-rw-r--r-- | src/pip/_internal/req/req_install.py | 6 | ||||
-rw-r--r-- | src/pip/_internal/utils/misc.py | 19 | ||||
-rw-r--r-- | tests/unit/test_utils.py | 10 |
8 files changed, 33 insertions, 25 deletions
diff --git a/news/6891.feature b/news/6891.feature new file mode 100644 index 000000000..4d08eedfb --- /dev/null +++ b/news/6891.feature @@ -0,0 +1 @@ +Redact single-part login credentials from URLs in log messages. diff --git a/src/pip/_internal/cli/cmdoptions.py b/src/pip/_internal/cli/cmdoptions.py index 1bfecef54..155211903 100644 --- a/src/pip/_internal/cli/cmdoptions.py +++ b/src/pip/_internal/cli/cmdoptions.py @@ -27,7 +27,7 @@ from pip._internal.models.index import PyPI 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 @@ -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 = [] diff --git a/src/pip/_internal/index.py b/src/pip/_internal/index.py index 93c198433..ba44e7bb4 100644 --- a/src/pip/_internal/index.py +++ b/src/pip/_internal/index.py @@ -40,7 +40,7 @@ from pip._internal.utils.misc import ( 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 @@ -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, @@ -1381,7 +1381,7 @@ class HTMLPage(object): 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] diff --git a/src/pip/_internal/models/link.py b/src/pip/_internal/models/link.py index 56ad2a5be..2a3c605e5 100644 --- a/src/pip/_internal/models/link.py +++ b/src/pip/_internal/models/link.py @@ -6,7 +6,7 @@ from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._internal.utils.misc import ( WHEEL_EXTENSION, path_to_url, - redact_password_from_url, + redact_auth_from_url, split_auth_from_netloc, splitext, ) @@ -68,10 +68,10 @@ class Link(KeyBasedCompareMixin): 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 diff --git a/src/pip/_internal/models/search_scope.py b/src/pip/_internal/models/search_scope.py index 621524495..5d667deef 100644 --- a/src/pip/_internal/models/search_scope.py +++ b/src/pip/_internal/models/search_scope.py @@ -8,7 +8,7 @@ from pip._vendor.six.moves.urllib import parse as urllib_parse 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: @@ -80,12 +80,12 @@ class SearchScope(object): 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) diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 264fade4c..a72d4dd70 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -39,7 +39,7 @@ from pip._internal.utils.misc import ( ensure_dir, get_installed_version, hide_url, - redact_password_from_url, + redact_auth_from_url, rmtree, ) from pip._internal.utils.packaging import get_metadata @@ -170,9 +170,9 @@ class InstallRequirement(object): 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: diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index d19df9459..f6e6e291c 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -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) @@ -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] @@ -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) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 51cf2a23a..6c1ad16f8 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -55,8 +55,8 @@ from pip._internal.utils.misc import ( 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, @@ -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. @@ -1371,7 +1371,7 @@ 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'), @@ -1379,8 +1379,8 @@ def test_remove_auth_from_url(auth_url, expected_url): ('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 |