diff --git a/news/7402.bugfix b/news/7402.bugfix new file mode 100644 index 000000000..91eb085f5 --- /dev/null +++ b/news/7402.bugfix @@ -0,0 +1 @@ +Reject VCS URLs with an empty revision. diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py index da53827cd..71b4650a2 100644 --- a/src/pip/_internal/vcs/versioncontrol.py +++ b/src/pip/_internal/vcs/versioncontrol.py @@ -11,7 +11,7 @@ import sys from pip._vendor import pkg_resources from pip._vendor.six.moves.urllib import parse as urllib_parse -from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.utils.compat import samefile from pip._internal.utils.misc import ( ask_path_exists, @@ -436,6 +436,12 @@ class VersionControl(object): rev = None if '@' in path: path, rev = path.rsplit('@', 1) + if not rev: + raise InstallationError( + "The URL {!r} has an empty revision (after @) " + "which is not supported. Include a revision after @ " + "or remove @ from the URL.".format(url) + ) url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) return url, rev, user_pass diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index 42fc43d68..590cb5c0b 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -5,7 +5,7 @@ import pytest from mock import patch from pip._vendor.packaging.version import parse as parse_version -from pip._internal.exceptions import BadCommand +from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.utils.misc import hide_url, hide_value from pip._internal.vcs import make_vcs_requirement_url from pip._internal.vcs.bazaar import Bazaar @@ -292,6 +292,21 @@ def test_version_control__get_url_rev_and_auth__missing_plus(url): assert 'malformed VCS url' in str(excinfo.value) +@pytest.mark.parametrize('url', [ + # Test a URL with revision part as empty. + 'git+https://github.com/MyUser/myProject.git@#egg=py_pkg', +]) +def test_version_control__get_url_rev_and_auth__no_revision(url): + """ + Test passing a URL to VersionControl.get_url_rev_and_auth() with + empty revision + """ + with pytest.raises(InstallationError) as excinfo: + VersionControl.get_url_rev_and_auth(url) + + assert 'an empty revision (after @)' in str(excinfo.value) + + @pytest.mark.parametrize('url, expected', [ # Test http. ('bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject',