Skip to content

Zipfile.extractall does not preserve file permissions #59999

Open

Description

@uruz
mannequin
BPO 15795
Nosy @ronaldoussoren, @abalkin, @gustaebel, @pitrou, @kmtracey, @merwok, @bitdancer, @PCManticore, @serhiy-storchaka, @mikofski, @nanjekyejoannah, @woodruffw, @Makishima, @selimb, @dignissimus
PRs
  • bpo-15795: Preserve permissions on UNIX (based/like) systems #17790
  • gh-59999: Add option to preserve permissions in ZipFile.extract #32289
  • Files
  • issue15795.patch
  • issue15795_updated.patch
  • issue15795_cleaned.patch
  • issue15795_test_and_doc_fixes.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/serhiy-storchaka'
    closed_at = None
    created_at = <Date 2012-08-27.22:36:03.648>
    labels = ['type-feature', 'library', '3.11']
    title = 'Zipfile.extractall does not preserve file permissions'
    updated_at = <Date 2022-04-04.14:55:21.787>
    user = 'https://bugs.python.org/uruz'

    bugs.python.org fields:

    activity = <Date 2022-04-04.14:55:21.787>
    actor = 'eric.araujo'
    assignee = 'serhiy.storchaka'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2012-08-27.22:36:03.648>
    creator = 'uruz'
    dependencies = []
    files = ['27086', '34852', '34873', '34893']
    hgrepos = []
    issue_num = 15795
    keywords = ['patch']
    message_count = 24.0
    messages = ['169233', '169242', '169629', '174356', '174359', '191425', '191459', '216228', '216267', '216298', '216304', '216376', '225526', '233445', '237139', '261145', '283918', '311328', '378712', '394819', '404877', '416661', '416672', '416674']
    nosy_count = 24.0
    nosy_names = ['ronaldoussoren', 'belopolsky', 'lars.gustaebel', 'pitrou', 'techtonik', 'swarren', 'kmtracey', 'eric.araujo', 'cbrannon', 'r.david.murray', 'Claudiu.Popa', 'serhiy.storchaka', 'uruz', 'Aaron.Train', 'desaintmartin', 'bwanamarko', 'Orborde', 'Glenn.Jones', '\xc3\x89tienne Dupuis', 'nanjekyejoannah', 'yossarian', 'kulakov-n', 'selimb', 'sam_ezeh']
    pr_nums = ['17790', '32289']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue15795'
    versions = ['Python 3.11']

    Linked PRs

    Activity

    1. uruz commented on Aug 27, 2012

      uruzmannequin
      MannequinAuthor

      Zipfile._extract_member does not preserve file permissions while extracting it. As may be seen at link[1], raw open() is used and no os.chmod() applied after that, therefore any permission data stored in zipfile is dropped and file is created with default permission depending on current user's umask.

      [1] http://hg.python.org/cpython/file/52159aa5d401/Lib/zipfile.py#l1251

    2. added
      type-bugAn unexpected behavior, bug, or error
      stdlibStandard Library Python modules in the Lib/ directory
      on Aug 27, 2012
    3. bitdancer commented on Aug 28, 2012

      @bitdancer
      Member

      Does this have any relationship to bpo-3394? From the discussion on that issue it sounds like zipfile is doing things with external_attributes if it is set. But I don't know much about zipfile internals.

    4. uruz commented on Sep 1, 2012

      uruzmannequin
      MannequinAuthor

      I'm attaching a patch, which solves the issue. Patch intoduces new argument "preserve_permissions" for extract and extractall methods. That argument may accept one of the three values: do not preserve permissions, preserve a safe subset of them or preserve all permissions. Three constants introduced for these options. Patch also contains test and docs. Tests pass for me on OS X 10.5, but I'm not sure if they'll pass on other operating systems.

    5. AaronTrain commented on Oct 31, 2012

      AaronTrainmannequin
      Mannequin

      Thanks for the patch. Is this going to be resolved soon?

    6. bitdancer commented on Oct 31, 2012

      @bitdancer
      Member

      OK, so this is an enhancement to specifically allow preservation of "unsafe" permissions?

      Adding the nosy list from bpo-3394.

    7. added
      type-featureA feature request or enhancement
      and removed
      type-bugAn unexpected behavior, bug, or error
      on Oct 31, 2012
    8. techtonik commented on Jun 18, 2013

      techtonikmannequin
      Mannequin

      There should be an easy way to restore file attributes.

    9. ronaldoussoren commented on Jun 19, 2013

      @ronaldoussoren
      Contributor

      On first glance the patch looks good. I haven't tested it with the current trunk though.

    10. GlennJones commented on Apr 14, 2014

      GlennJonesmannequin
      Mannequin

      Here is an updated patch that applies cleanly to head. Tests pass against head of repo.

    11. bitdancer commented on Apr 15, 2014

      @bitdancer
      Member

      Thanks.

      The patch contains a number of lines that are not wrapped to <80, which is one of our requirements. It would be great to get that fixed. (In the documentation, you can use \ to wrap the prototype line.)

      There is non-ascii in one place in the documentation...probably an em-dash, which is written in sphinx as '---'.

      Also, please remove the news entry from the patch, it will just make it harder to apply (and is in the wrong place anyway...we add entries at the top of the appropriate section, not the bottom).

    12. GlennJones commented on Apr 15, 2014

      GlennJonesmannequin
      Mannequin

      Patch cleaned up based on previous comments.

    13. PCManticore commented on Apr 15, 2014

      PCManticoremannequin
      Mannequin

      Hello. I added a couple of comments to your latest patch.

    14. 14 remaining items

    15. dignissimus commented on Apr 4, 2022

      dignissimusmannequin
      Mannequin

      I don't know what the best course of action would be but if preserving permissions needs to be back-ported, could the default permission preservation flag in 3.11+ be the one to preserve safe permissions and then make it so that the previous versions (<3.11, without the constants) always take this course of action? Maintaining the different options for preserving permissions while still allowing for this functionality to be back-ported.

      I don't have a strong opinion on backporting permission preservation but to me, it seems like it would be a change in behaviour instead of a bug fix. The current default in the PR is to not preserve any permissions but if necessary, I'll change it to whatever is agreed upon.

      I'll move the constants into an enum, but right now I'm not sure how I'd name the class.

      As an aside, while writing this comment I realised that the reason tests aren't passing on my end might very well be due to the fact I do CPython work on an NTFS partition instead of on my main EXT4 partition.

    16. merwok commented on Apr 4, 2022

      @merwok
      Member

      On second thought, maybe no fix should be backported.
      Changing behaviour silently might break things, changing with a new option puts the change in a grey fix/enhancement area, so maybe better to let current versions as they are, with the bug/limitation noted in the documentation, and give control in 3.11 only.

    17. transferred this issue fromon Apr 10, 2022
    18. dignissimus commented on Apr 12, 2022

      @dignissimus
      Contributor

      After scratching my head for a while, I was able to get tests to pass. There's this small quirk where it's not possible to create a file with mode 0 using ZipFile.writestr if you don't apply any other masks. It's due to these lines.

      cpython/Lib/zipfile.py

      Lines 1628 to 1629 in ac6c3de

      if not zinfo.external_attr:
      zinfo.external_attr = 0o600 << 16 # permissions: ?rw-------

      This happens even though it's possible to create such a file (although the file will have the 0o100000 regular file mask).

      Python 3.11.0a6+ (heads/bpo-15795:8a056b4fc2, Apr 13 2022, 00:11:18) [GCC 11.1.0] on linux
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import pathlib
      >>> import os
      >>> pathlib.Path("file").touch(mode=0)
      >>> os.stat("file").st_mode >> 16
      0
      >>> 

      Since I was setting external_attr, I should have included the mask for the file type. So I don't think this behaviour is a problem.

    19. added a commit that references this issue on May 24, 2023
    20. thatch commented on May 20, 2025

      @thatch
      Contributor

      I'm reluctant to populate all the permission bits even with an opt-in arg, but it looks like tarfile already does.

      @emmatyping One potential middle-of-the-road approach would be just preserving the X bit (perhaps obeying umask); all of the linked issues I've looked at are about missing X bit, and this is close to what the uv wheel installer does.

    21. danny0838 commented on Jul 3, 2026

      @danny0838
      Contributor

      Is there any progress on this issue?

      The current behavior is that only the filename (with some tidying) and file content are restored by ZipFile.{extract,extractall}. This directly contradicts the documentation, which states: "Its file information is extracted as accurately as possible."

      For a long time, users have been forced to manually handle the restoration of metadata themselves, either by subclassing ZipFile or writing custom post-processing logic in their apps.

      To address this without introducing breaking changes, I think we should first update the documentation to accurately reflect the current behavior.

      And consider introducing an optional parameter, such as preserve_metadata, to allow users to opt-in to restoring metadata like modification times (which may optionally include extended timestamps) and Unix modes.

      Since extracting raw Unix permissions poses security risks (e.g., malicious archives injecting SUID/SGID bits or unexpected 0o777 permissions), we could design this parameter to accept predefined safety policies or permission masks, similar to the filter parameter in TarFile.{extract,extractall}.

      I would be happy to help draft a PR for the documentation fix first if the maintainers agree with this direction.

    22. danny0838 commented on Jul 26, 2026

      @danny0838
      Contributor

      Issued gh-154704 as a class-based approach for better flexibility, extensibility, and backward compatibility about any file attribute restoration logic.

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

    Metadata

    Metadata

    Labels

    3.11only security fixesstdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions