Skip to content

User site packages are being added when site module is enabled in _pth file #120037

Description

@tanzim

Bug report

Bug description:

See #119920 (comment).

I've managed to reproduce this with the latest official Windows embedded Python releases for 3.11 and 3.12.

CPython versions tested on:

3.11, 3.12

Operating systems tested on:

Windows

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Jun 4, 2024
  2. added
    3.11only security fixes
    3.12only security fixes
    3.13only security fixes
    3.14bugs and security fixes
    on Jun 4, 2024
  3. eryksun commented on Jun 4, 2024

    @eryksun
    Contributor

    This is a straight-forward fix. Add the statement config['user_site_directory'] = 0 in the following section:

    cpython/Modules/getpath.py

    Lines 760 to 764 in 69b3e8e

    if pth:
    config['isolated'] = 1
    config['use_environment'] = 0
    config['site_import'] = 0
    config['safe_path'] = 1

  4. tanzim commented on Jun 5, 2024

    @tanzim
    Author

    While reproducing this, I noted something else on macOS - this is assuming Python 3.11 adds support for ._pth file on macOS:

    1. Placing ._pth file with interpreter executable name in the interpreter dir, e.g. bin works
    2. Enabling site has the same issue as above
    3. However, no matter how much I tried I couldn't get a .pth file to work, doesn't matter where I placed it.
  5. changed the title [-][Windows] User site packages are being added when site module is enabled in _pth file[/-] [+]User site packages are being added when site module is enabled in _pth file[/+] on Jun 5, 2024
  6. eryksun commented on Jun 5, 2024

    @eryksun
    Contributor

    However, no matter how much I tried I couldn't get a .pth file to work, doesn't matter where I placed it.

    On Windows, the site module executes .pth files found in the prefix directory; the site-packages directory "{prefix}\Lib\site-packages"; and, if enabled, the user site-packages directory. On POSIX, it checks the site-packages directory and, if enabled, the user site-packages directory, but not the prefix directory. The path of the site-packages directory on POSIX is usually of the form "{prefix}/lib/pythonX.Y/site-packages", unless a distribution customizes it.

    After adding the site-packages and user site-packages directories, and executing their .pth files, the site module tries to import a sitecustomize module. This may be simpler for you to manage since it doesn't matter where sitecustomize is located.

  7. tanzim commented on Jun 5, 2024

    @tanzim
    Author

    On POSIX, it checks the site-packages directory and, if enabled, the user site-packages directory, but not the prefix directory.

    That explains why the .pth file didn't work for me same way as Windows even though I tried placing it in prefix, prefix/bin and site packages as above. I can confirm sitecustomize works. I have to say though, the various initialization logic is pretty convoluted!

    Thanks again for the help.

  8. tanzim commented on Jun 5, 2024

    @tanzim
    Author

    Could be another esoteric piece of information that I lack however I noted that when using sitecustomize on macOS, even though import site in defined in the ._pth file, site-packages directory, e.g. /lib/python3.11/site-packages is not added to sys.path by default. On Windows it is. I could of course manually add it in the ._pth file but that feels hacky.

  9. eryksun commented on Jun 5, 2024

    @eryksun
    Contributor

    Could be another esoteric piece of information that I lack however I noted that when using sitecustomize on macOS, even though import site in defined in the ._pth file, site-packages directory, e.g. /lib/python3.11/site-packages is not added to sys.path by default. On Windows it is. I could of course manually add it in the ._pth file but that feels hacky.

    Maybe the value of sys.prefix is wrong. I don't use macOS, so I can't test this.

  10. tanzim commented on Jun 5, 2024

    @tanzim
    Author

    You guessed right. It's pointing to python/bin/lib/python3.11/site-packages and sys.prefix is set to python/bin when it should be one level up.

    I'm pretty sure this is a side effect of colocating the ._pth file with the python executable in the bin folder. If I remove the _.pth sys.prefix works as expected and site.getsitepackages() returns the correct value.

  11. eryksun commented on Jun 5, 2024

    @eryksun
    Contributor

    I'm pretty sure this is a side effect of colocating the ._pth file with the python executable in the bin folder.

    If a ._pth file is found, its parent directory gets set as home in "Modules/getpath.py". This sets sys.prefix1. If you need it to be a level up, can you run from a "python" symlink that's created a level up? A ._pth file can be located beside a symlink to the executable or beside the real executable.

    Footnotes

    1. Except when running from the build directory on POSIX, the default prefix is used. ↩

  12. zooba commented on Jun 6, 2024

    @zooba
    Member

    Yeah, having ._pth directly dictate the prefix isn't ideal, but it's also a difficult one to generalise the rules for.

    Fundamentally, the idea is that your ._pth file should prevent/limit the Python runtime from being exploitable through path hijacks, but as soon as we start adding rules like "Sometimes sys.prefix is the directory above the file" or deriving it from user-provided/environment data like normal, we break the security rule.

    I think the right fix here is to add the site-packages directory to the ._pth file if you want it. The main scenario is for embedding, when you probably aren't using (and certainly don't need) the site-packages convention at all. Still, it's a bit of a shame that it is deriving from sys.prefix rather than the stdlib path (maybe we don't even calculate this though...?).

  13. 7 remaining items

  14. tanzim commented on Jun 7, 2024

    @tanzim
    Author
    • Getting isolated mode, safe-path mode, and (once fixed) disabling the user site-packages, is contingent on the ._pth file being non-empty, in which case its contents determine the initial sys.path.

    Yeah, this is the intended use. An empty ._pth ought to just fail - not entirely obvious why it doesn't right now - because it should result in an empty sys.path. I guess there are some additional checks in getpath.py that look for empty lists...

    Because https://github.com/python/cpython/blob/main/Modules/getpath.py#L669 and https://github.com/python/cpython/blob/main/Modules/getpath.py#L723

    It's baked in Windows behavior.

  15. eryksun commented on Jun 7, 2024

    @eryksun
    Contributor

    Yeah, this is the intended use. An empty ._pth ought to just fail - not entirely obvious why it doesn't right now - because it should result in an empty sys.path. I guess there are some additional checks in getpath.py that look for empty lists...

    An empty ._pth file sets prefix, but it doesn't preclude the normal code that sets up the initial module search path in pythonpath, except to skip anything from the environment/registry because use_environment is 01. The way it's designed is that if the pth variable is non-empty from the ._pth file, then its contents replaces the calculated value of pythonpath, in addition to setting config values that isolate the application.

    Footnotes

    1. But not config['use_environment'], so other Python environment variables still affect the application. ↩

  16. zooba commented on Jun 11, 2024

    @zooba
    Member

    By "intended" I meant what I intended when I originally added it, not when it got translated into getpath.py 😄

    But it does seem reasonable to allow it to generate a relatively default path if it's empty. It's very easy to avoid that by just not making it empty, which is going to be necessary most of the time anyway.

  17. boimart1 commented on Jul 31, 2025

    @boimart1
    Contributor

    Bumping this year-old issue, I have an interest in seeing this issue resolved 😅

    It looks like the CLA is the only thing actually blocking the PR right now?

  18. zooba commented on Jul 31, 2025

    @zooba
    Member

    I have an interest in seeing this issue resolved

    I assume you mean the original issue, and not any of the others discussed in the thread.

    Could you help me understand why you need to use import site in your ._pth file? The intent of the ._pth is to avoid arbitrary paths being added in, and while there is an escape hatch available, it would be better to not need it.

  19. boimart1 commented on Aug 4, 2025

    @boimart1
    Contributor

    I assume you mean the original issue, and not any of the others discussed in the thread.

    Yes, only the original issue.

    Could you help me understand why you need to use import site in your ._pth file? The intent of the ._pth is to avoid arbitrary paths being added in, and while there is an escape hatch available, it would be better to not need it.

    I've had some more time to think of how to deal with my situation since 4 days ago, and I know there are ways to get what I want without import site in the ._pth file, so I'm not going to say I need this ;) But it would make what I'm trying to do a fair bit easier.

    The gist of it is that I'm upgrading a C++ application that embeds Python from Python 2 to 3 (finally!!!). I'm taking the opportunity to remove our dependency on a system-wide Python installation, by shipping the embeddable python package with a ._pth file. We also have several Python packages that need to be included and available to the embedded Python interpreter, but those packages cannot be baked into the embeddable package, for organisational reasons basically.

    I can fairly easily add those packages from the C++ application with site.addsitedir() right after the Python initialization. (Some of the packages have .pth files associated, e.g. pywin32, so just adding the path to sys.path from the ._pth file is insufficient.) But there are also situations where we need to invoke python.exe from outside the application, and it's more convenient if those packages are immediately available in that context as well. The first solution I reached for was import site in the ._pth file with a sitecustomize.py if needed, but this bug causes user-site packages to be added as well, thus de-isolating the environment with unpredictable side-effects.

  20. zooba commented on Aug 4, 2025

    @zooba
    Member

    Thanks, that's useful context, and it's definitely the kind of thing we want to enable.

    We have a PR that would fix it, I think it just got held up on improving the description of the issue?

  21. boimart1 commented on Aug 5, 2025

    @boimart1
    Contributor

    We have a PR that would fix it, I think it just got held up on improving the description of the issue?

    Seems like it's that + accepting the Contributor's License Agreement. Though it looks like the author @tanzim hasn't shown any recent activity. Would it be appropriate for me to open a new PR then?

  22. added a commit that references this issue on Aug 5, 2025
  23. added a commit that references this issue on Aug 19, 2025
  24. boimart1 commented on Aug 21, 2025

    @boimart1
    Contributor

    @zooba Thanks for getting this through!

    For info, will this also be backported to 3.13 and/or other released versions?

  25. zooba commented on Aug 21, 2025

    @zooba
    Member

    No, I think it's too big a behaviour change, and not justified as a security fix (if you can write to the user profile you're already far enough in - no escalation of privilege here). Embedding apps that use python.exe are already more open to misuse than this change would allow, and those that load the DLL directly can probably set the flag themselves more easily than updating the runtime. It'll be in 3.15.

  26. boimart1 commented on Aug 22, 2025

    @boimart1
    Contributor

    Undersood. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.11only security fixes3.12only security fixes3.13only security fixes3.14bugs and security fixesextension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions