Windows, ACL, and building wheels

On Windows, there appears to be a difference in Access Control Lists of wheels built with different versions of Python.

After py -3.11 -m build --wheel, then Get-Acl .\dist\*3.1.4* | Format-List produces:

Path   : Microsoft.PowerShell.Core\FileSystem::C:\git-projects\Automation\common\dist\mhi_common-3.1.4a0-py3-none-any.whl
Owner  : MHI\aneufeld
Group  : MHI\Domain Users
Access : BUILTIN\Administrators Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize
Audit  :
Sddl   : O:S-1-5-21-1055587669-2364366017-3853703429-2293G:DUD:AI(A;ID;FA;;;BA)(A;ID;FA;;;SY)(A;ID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)

After py -3.13 -m build --wheel, then Get-Acl .\dist\*3.1.4* | Format-List produces:

Path   : Microsoft.PowerShell.Core\FileSystem::C:\git-projects\Automation\common\dist\mhi_common-3.1.4a0-py3-none-any.whl
Owner  : MHI\aneufeld
Group  : MHI\Domain Users
Access : OWNER RIGHTS Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
																	
Audit  :
Sddl   : O:S-1-5-21-1055587669-2364366017-3853703429-2293G:DUD:P(A;;FA;;;OW)(A;;FA;;;SY)(A;;FA;;;BA)

That difference again…

Built with Python 3.11 or earlier …

Access : BUILTIN\Administrators            Allow  FullControl
         NT AUTHORITY\SYSTEM               Allow  FullControl
         BUILTIN\Users                     Allow  ReadAndExecute, Synchronize
         NT AUTHORITY\Authenticated Users  Allow  Modify, Synchronize

… verse built with Python 3.12 or later …

Access : OWNER RIGHTS            Allow  FullControl
         NT AUTHORITY\SYSTEM     Allow  FullControl
         BUILTIN\Administrators  Allow  FullControl

This change in ACL is a speed-bump in our build process. When triggered automatically with a Scheduled Task, the owner becomes Administrators, which is fine until there is a need to start a build manually, in which case it fails because the dist directory cannot be removed by mere Authenticated Users

Only the .whl files seem to be affected; the build directory and contents does not have restricted ACLs.

What is the reasoning behind the ACL difference? Was it intentional or is this a bug? Can it be prevented by command-line arguments other than specifying an older Python interpreter?

Someone else seems to be affected by this, and created a question on Stack Overflow about this issue.

Can you double-check that you’re using the same version of all dependencies, especially setuptools or whatever other build backend you’re using?

Can you reproduce this with just a simple file (open('some_path', 'w')) or with some flavor of temporary file (like a NamedTemporaryFile with delete=False, or a file created in a TemporaryDirectory and subsequently moved out, or a file created in a TemporaryDirectory with delete=False (3.12+ only)?

From pyproject.toml:

[build-system]
requires = [
    "setuptools>=42",
    "wheel",
]
build-backend = "setuptools.build_meta"

Same dependencies in 3.11 & 3.13:

PS C:\git-projects\Automation\common> py -3.13 -m pip list | findstr "wheel setuptools build packaging"
build                         1.2.2.post1
packaging                     25.0
setuptools                    80.9.0
wheel                         0.45.1
PS C:\git-projects\Automation\common> py -3.11 -m pip list | findstr "wheel setuptools build packaging"
build                         1.2.2.post1
packaging                     25.0
setuptools                    80.9.0
wheel                         0.45.1

temp-test.py:

from tempfile import NamedTemporaryFile

with NamedTemporaryFile('w', dir='.', suffix='.tmp', delete=False) as tf:
    tf.write("Hello world")
PS C:\git-projects\Automation\common> py -3.13 .\temp-test.py
PS C:\git-projects\Automation\common> py -3.11 .\temp-test.py
PS C:\git-projects\Automation\common> Get-Acl *.tmp | Format-List


Path   : Microsoft.PowerShell.Core\FileSystem::C:\git-projects\Automation\common\tmp2y1wdqxl.tmp
Owner  : MHI\aneufeld
Group  : MHI\Domain Users
Access : BUILTIN\Administrators Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize
Audit  :
Sddl   : O:S-1-5-21-1055587669-2364366017-3853703429-2293G:DUD:AI(A;ID;FA;;;BA)(A;ID;FA;;;SY)(A;ID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)


Path   : Microsoft.PowerShell.Core\FileSystem::C:\git-projects\Automation\common\tmphos8wgfo.tmp
Owner  : MHI\aneufeld
Group  : MHI\Domain Users
Access : BUILTIN\Administrators Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize
Audit  :
Sddl   : O:S-1-5-21-1055587669-2364366017-3853703429-2293G:DUD:AI(A;ID;FA;;;BA)(A;ID;FA;;;SY)(A;ID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)

Permissions of the NamedTemporaryFile in 3.11 & 3.13 are identical.

Building the wheel a few different ways …

del dist\*3.1.4*; py -m build --wheel; Get-Acl dist\*3.1.4* | Format-List

[... output deleted for brevity ...]

Access : OWNER RIGHTS Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
del dist\*3.1.4*; py -m build --no-isolation --wheel; Get-Acl dist\*3.1.4* | Format-List

[...]

Access : OWNER RIGHTS Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
del dist\*3.1.4*; py setup.py bdist_wheel; Get-Acl dist\*3.1.4* | Format-List

[...]
        Please avoid running ``setup.py`` directly.
[...]

Access : BUILTIN\Administrators Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
         NT AUTHORITY\Authenticated Users Allow  Modify, Synchronize

Running setup.py directly results in normal ACL privileges. Using py -m build is results in the restricted ACL privileges.