VYPR
Moderate severityNVD Advisory· Published Aug 17, 2013· Updated Apr 29, 2026

CVE-2013-1888

CVE-2013-1888

Description

pip before 1.3 allows local users to overwrite arbitrary files via a symlink attack on a file in the /tmp/pip-build temporary directory.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
pipPyPI
< 1.31.3

Affected products

4
  • cpe:2.3:a:pypa:pip:*:*:*:*:*:*:*:*
    Range: <1.3
  • cpe:2.3:o:fedoraproject:fedora:17:*:*:*:*:*:*:*+ 2 more
    • cpe:2.3:o:fedoraproject:fedora:17:*:*:*:*:*:*:*
    • cpe:2.3:o:fedoraproject:fedora:18:*:*:*:*:*:*:*
    • cpe:2.3:o:fedoraproject:fedora:19:*:*:*:*:*:*:*

Patches

2
c039e47df471

Merge 1af3f2b7140dd240d0c5836f59b5d589e94c7bb6 into c96548f57909b0acc33321ce9021f9a86ece61ee

https://github.com/pypa/pipMarcus SmithJan 26, 2013via nvd-ref
7 files changed · +137 4
  • AUTHORS.txt+1 0 modified
    @@ -14,6 +14,7 @@ Clay McClure
     Cody Soyland
     Daniel Holth
     Dave Abrahams
    +David (d1b)
     Dmitry Gladkov
     Donald Stufft
     Francesco
    
  • CHANGES.txt+3 0 modified
    @@ -7,6 +7,9 @@ develop (unreleased)
     * Added "pip list" for listing installed packages and the latest version
       available. Thanks Rafael Caricio, Miguel Araujo, Dmitry Gladkov (Pull #752)
     
    +* Fixed security issues with pip's use of temp build directories.
    +  Thanks David (d1b) and Thomas Güttler. (Pull #780)
    +
     * Improvements to sphinx docs and cli help. (Pull #773)
     
     * Fixed issue #707, dealing with OS X temp dir handling, which was causing
    
  • docs/cookbook.txt+1 1 modified
    @@ -72,7 +72,7 @@ pip allows you to *just* unpack archives to a build directory without installing
     
     $ pip install --no-install SomePackage
     
    -If you're in a virtualenv, the build dir is ``<virtualenv path>/build``.  Otherwise, it's ``<OS temp dir>/pip-build``
    +If you're in a virtualenv, the build dir is ``<virtualenv path>/build``.  Otherwise, it's ``<OS temp dir>/pip-build-<username>``
     
     Afterwards, to finish the job of installing unpacked archives, run::
     
    
  • pip/commands/install.py+1 1 modified
    @@ -70,7 +70,7 @@ def __init__(self, *args, **kw):
                 default=build_prefix,
                 help='Directory to unpack packages into and build in. '
                 'The default in a virtualenv is "<venv path>/build". '
    -            'The default for global installs is "<OS temp dir>/pip-build".')
    +            'The default for global installs is "<OS temp dir>/pip-build-<username>".')
     
             cmd_opts.add_option(
                 '-t', '--target',
    
  • pip/locations.py+28 1 modified
    @@ -4,7 +4,9 @@
     import site
     import os
     import tempfile
    +import getpass
     from pip.backwardcompat import get_python_lib
    +import pip.exceptions
     
     
     def running_under_virtualenv():
    @@ -25,6 +27,31 @@ def virtualenv_no_global():
         if running_under_virtualenv() and os.path.isfile(no_global_file):
             return True
     
    +def _get_build_prefix():
    +    """ Returns a safe build_prefix """
    +    path = os.path.join(tempfile.gettempdir(), 'pip-build-%s' % \
    +        getpass.getuser())
    +    if sys.platform == 'win32':
    +        """ on windows(tested on 7) temp dirs are isolated """
    +        return path
    +    try:
    +        os.mkdir(path)
    +    except OSError:
    +        file_uid = None
    +        try:
    +            fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
    +            file_uid = os.fstat(fd).st_uid
    +            os.close(fd)
    +        except OSError:
    +            file_uid = None
    +        if file_uid != os.getuid():
    +            msg = "The temporary folder for building (%s) is not owned by your user!" \
    +                % path
    +            print (msg)
    +            print("pip will not work until the temporary folder is " + \
    +                 "either deleted or owned by your user account.")
    +            raise pip.exceptions.InstallationError(msg)
    +    return path
     
     if running_under_virtualenv():
         build_prefix = os.path.join(sys.prefix, 'build')
    @@ -33,7 +60,7 @@ def virtualenv_no_global():
         # Use tempfile to create a temporary folder for build
         # Note: we are NOT using mkdtemp so we can have a consistent build dir
         # Note: using realpath due to tmp dirs on OSX being symlinks
    -    build_prefix = os.path.realpath(os.path.join(tempfile.gettempdir(), 'pip-build'))
    +    build_prefix = os.path.realpath(_get_build_prefix())
     
         ## FIXME: keep src in cwd for now (it is not a temporary folder)
         try:
    
  • setup.py+1 1 modified
    @@ -8,7 +8,7 @@
     here = os.path.abspath(os.path.dirname(__file__))
     
     def read(*parts):
    -    return codecs.open(os.path.join(here, *parts), 'r', 'utf8').read()
    +    return codecs.open(os.path.join(here, *parts), 'r').read()
     
     def find_version(*file_paths):
         version_file = read(*file_paths)
    
  • tests/test_locations.py+102 0 added
    @@ -0,0 +1,102 @@
    +"""
    +locations.py tests
    +
    +"""
    +import os
    +import sys
    +import shutil
    +import tempfile
    +import getpass
    +from mock import Mock
    +from nose import SkipTest
    +from nose.tools import assert_raises
    +import pip
    +
    +class TestLocations:
    +    def setup(self):
    +        self.tempdir = tempfile.mkdtemp()
    +        self.st_uid = 9999
    +        self.username = "example"
    +        self.patch()
    +
    +    def tearDown(self):
    +        self.revert_patch()
    +        shutil.rmtree(self.tempdir, ignore_errors=True)
    +
    +    def patch(self):
    +        """ first store and then patch python methods pythons """
    +        self.tempfile_gettempdir = tempfile.gettempdir
    +        self.old_os_fstat = os.fstat
    +        if sys.platform != 'win32':
    +            # os.getuid not implemented on windows
    +            self.old_os_getuid = os.getuid
    +        self.old_getpass_getuser = getpass.getuser
    +
    +        # now patch
    +        tempfile.gettempdir = lambda : self.tempdir
    +        getpass.getuser = lambda : self.username
    +        os.getuid = lambda : self.st_uid
    +        os.fstat = lambda fd : self.get_mock_fstat(fd)
    +
    +    def revert_patch(self):
    +        """ revert the patches to python methods """
    +        tempfile.gettempdir = self.tempfile_gettempdir
    +        getpass.getuser = self.old_getpass_getuser
    +        if sys.platform != 'win32':
    +            # os.getuid not implemented on windows
    +            os.getuid = self.old_os_getuid
    +        os.fstat = self.old_os_fstat
    +
    +    def get_mock_fstat(self, fd):
    +        """ returns a basic mock fstat call result.
    +            Currently only the st_uid attribute has been set.
    +        """
    +        result = Mock()
    +        result.st_uid = self.st_uid
    +        return result
    +
    +    def get_build_dir_location(self):
    +        """ returns a string pointing to the
    +            current build_prefix.
    +        """
    +        return os.path.join(self.tempdir, 'pip-build-%s' % self.username)
    +
    +    def test_dir_path(self):
    +        """ test the path name for the build_prefix
    +        """
    +        from pip import locations
    +        assert locations._get_build_prefix() == self.get_build_dir_location()
    +
    +    def test_dir_created(self):
    +        """ test that the build_prefix directory is generated when
    +            _get_build_prefix is called.
    +        """
    +        #skip on windows, build dir is not created
    +        if sys.platform == 'win32':
    +            raise SkipTest()
    +        assert not os.path.exists(self.get_build_dir_location() ), \
    +            "the build_prefix directory should not exist yet!"
    +        from pip import locations
    +        locations._get_build_prefix()
    +        assert os.path.exists(self.get_build_dir_location() ), \
    +            "the build_prefix directory should now exist!"
    +
    +    def test_error_raised_when_owned_by_another(self):
    +        """ test calling _get_build_prefix when there is a temporary
    +            directory owned by another user raises an InstallationError.
    +        """
    +        #skip on windows; this exception logic only runs on linux
    +        if sys.platform == 'win32':
    +            raise SkipTest()
    +        from pip import locations
    +        os.getuid = lambda : 1111
    +        os.mkdir(self.get_build_dir_location() )
    +        assert_raises(pip.exceptions.InstallationError, locations._get_build_prefix)
    +
    +    def test_no_error_raised_when_owned_by_you(self):
    +        """ test calling _get_build_prefix when there is a temporary
    +            directory owned by you raise no InstallationError.
    +        """
    +        from pip import locations
    +        os.mkdir(self.get_build_dir_location())
    +        locations._get_build_prefix()
    
dd8b41e9e730

Merge 5c10fc5bbd7088311375b592c7874523836ac037 into c96548f57909b0acc33321ce9021f9a86ece61ee

https://github.com/pypa/pipDavidJan 25, 2013via nvd-ref
2 files changed · +109 1
  • pip/locations.py+28 1 modified
    @@ -4,7 +4,9 @@
     import site
     import os
     import tempfile
    +import getpass
     from pip.backwardcompat import get_python_lib
    +import pip.exceptions
     
     
     def running_under_virtualenv():
    @@ -25,6 +27,31 @@ def virtualenv_no_global():
         if running_under_virtualenv() and os.path.isfile(no_global_file):
             return True
     
    +def _get_build_prefix():
    +    """ Returns a safe build_prefix """
    +    path = os.path.join(tempfile.gettempdir(), 'pip-build-%s' % \
    +        getpass.getuser())
    +    if sys.platform == 'win32':
    +        """ on windows(tested on 7) temp dirs are isolated """
    +        return path
    +    try:
    +        os.mkdir(path)
    +    except OSError:
    +        file_uid = None
    +        try:
    +            fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
    +            file_uid = os.fstat(fd).st_uid
    +            os.close(fd)
    +        except OSError:
    +            file_uid = None
    +        if file_uid != os.getuid():
    +            msg = "The temporary folder for building (%s) is not owned by your user!" \
    +                % path
    +            print (msg)
    +            print("pip will not work until the temporary folder is " + \
    +                 "either deleted or owned by your user account.")
    +            raise pip.exceptions.InstallationError(msg)
    +    return path
     
     if running_under_virtualenv():
         build_prefix = os.path.join(sys.prefix, 'build')
    @@ -33,7 +60,7 @@ def virtualenv_no_global():
         # Use tempfile to create a temporary folder for build
         # Note: we are NOT using mkdtemp so we can have a consistent build dir
         # Note: using realpath due to tmp dirs on OSX being symlinks
    -    build_prefix = os.path.realpath(os.path.join(tempfile.gettempdir(), 'pip-build'))
    +    build_prefix = os.path.realpath(_get_build_prefix())
     
         ## FIXME: keep src in cwd for now (it is not a temporary folder)
         try:
    
  • tests/test_locations.py+81 0 added
    @@ -0,0 +1,81 @@
    +"""
    +locations.py tests
    +
    +"""
    +import os
    +import sys
    +import shutil
    +import tempfile
    +import getpass
    +from mock import Mock
    +import pip
    +
    +class TestLocations:
    +    def setup(self):
    +        self.tempdir = tempfile.mkdtemp()
    +        self.st_uid = 9999
    +        self.username = "example"
    +        self.patch()
    +
    +    def tearDown(self):
    +        self.revert_patch()
    +        shutil.rmtree(self.tempdir, ignore_errors=True)
    +
    +    def patch(self):
    +        """ first store and then patch python methods pythons """
    +        self.tempfile_gettempdir = tempfile.gettempdir
    +        self.old_os_fstat = os.fstat
    +        self.old_os_getuid = os.getuid
    +        self.old_getpass_getuser = getpass.getuser
    +
    +        # now patch
    +        tempfile.gettempdir = lambda : self.tempdir
    +        getpass.getuser = lambda : self.username
    +        os.getuid = lambda : self.st_uid
    +        os.fstat = lambda fd : self.get_mock_fstat(fd)
    +
    +    def revert_patch(self):
    +        """ revert the patches to python methods """
    +        tempfile.gettempdir = self.tempfile_gettempdir
    +        getpass.getuser = self.old_getpass_getuser
    +        os.getuid = self.old_os_getuid
    +        os.fstat = self.old_os_fstat
    +
    +    def get_mock_fstat(self, fd):
    +        """ returns a basic mock fstat call result.
    +            Currently only the st_uid attribute has been set.
    +        """
    +        result = Mock()
    +        result.st_uid = self.st_uid
    +        return result
    +
    +    def get_build_dir_location(self):
    +        """ returns a string pointing to the
    +            current build_prefix.
    +        """
    +        return os.path.join(self.tempdir, 'pip-build-%s' % self.username)
    +
    +    def test_dir_created(self):
    +        """ test that the build_prefix directory is generated when
    +            _get_build_prefix is called.
    +        """
    +
    +        assert not os.path.exists(self.get_build_dir_location() ), \
    +            "the build_prefix directory should not exist yet!"
    +        from pip import locations
    +        locations._get_build_prefix()
    +        assert os.path.exists(self.get_build_dir_location() ), \
    +            "the build_prefix directory should now exist!"
    +
    +    def test_error_raised_when_owned_by_another(self):
    +        """ test calling _get_build_prefix when there is a temporary
    +            directory owned by another user raises an InstallationError.
    +        """
    +        from pip import locations
    +        os.getuid = lambda : 1111
    +        os.mkdir(self.get_build_dir_location() )
    +        try:
    +            locations._get_build_prefix()
    +            raise AssertionError("An InstallationError should have been raised!")
    +        except pip.exceptions.InstallationError:
    +            pass
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.