Opened 3 weeks ago

Closed 4 days ago

#37401 closed Cleanup/optimization (fixed)

Made `FILE_UPLOAD_DIRECTORY_PERMISSIONS` respect the process umask to remove vendored `makedirs`

Reported by: Natalia Bidart Owned by: Natalia Bidart
Component: File uploads/storage Version: dev
Severity: Normal Keywords:
Cc: Shai Berger Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Following the ​forum discussion and agreement from the Steering Council and the Security Team, directories created via django.utils._os.safe_makedirs() should be subject to the process umask, matching the semantics of os.mkdir() and os.makedirs(). This affects FileSystemStorage when FILE_UPLOAD_DIRECTORY_PERMISSIONS (or directory_permissions_mode) is set, and FileBasedCache (which uses a fixed 0o700).

Currently, the vendored makedirs() added in the fix for ​CVE-2026-25674 calls os.chmod() after each os.mkdir() to force the exact mode, ignoring the umask (a behavior dating back to #13518). Since the umask can only remove bits, resulting permissions may be more restrictive than configured, never more permissive. Common combinations such as 0o755 with a 0o022 umask are unchanged, and existing directories remain untouched.

Python 3.15 added parent_mode to os.makedirs() (​python/cpython#86533), so safe_makedirs() can use it directly on 3.15+, keeping the vendored implementation (without the extra chmod()) for older Python versions until they are dropped.

The change applies on Django 6.2+ for all supported Python versions and will be documented as a backwards incompatible change in the 6.2 release notes, with updates to the FILE_UPLOAD_DIRECTORY_PERMISSIONS and FileSystemStorage.directory_permissions_mode docs. FILE_UPLOAD_PERMISSIONS is out of scope: it is applied via os.chmod() after writing and remains exact.

Change History (8)

comment:1 by Carlton Gibson, 3 weeks ago

Triage Stage: Unreviewed → Accepted

comment:2 by Shai Berger, 3 weeks ago

Cc: Shai Berger added

comment:3 by Natalia Bidart, 3 weeks ago

Has patch: set

comment:4 by Natalia Bidart, 3 weeks ago

comment:5 by Jacob Walls, 3 weeks ago

Patch needs improvement: set

comment:6 by Natalia Bidart, 6 days ago

Patch needs improvement: unset

comment:7 by Jacob Walls, 4 days ago

Triage Stage: Accepted → Ready for checkin

comment:8 by nessita <124304+nessita@…>, 4 days ago

Resolution: → fixed
Status: assigned → closed

In 4b09d8c:

Fixed #37401 -- Aligned safe_makedirs() with os.makedirs() semantics.

Python 3.15 added the parent_mode argument to os.makedirs(), the
upstream fix for the issue that led Django to vendor its own
makedirs() in the fix for CVE-2026-25674. The vendored copy differed
from CPython only by an extra chmod() call that ignored the process
umask. Dropping that call allows using os.makedirs() directly on
Python 3.15+, and removing the vendored copy once Python 3.14 support
ends.

As a trade-off, directories created by FileSystemStorage when
FILE_UPLOAD_DIRECTORY_PERMISSIONS (or directory_permissions_mode) is
set, and by FileBasedCache, now have the requested permissions mode
combined with the process umask, and the set-group-ID bit and other
bits beyond the last 9 are no longer guaranteed to be set.
FILE_UPLOAD_PERMISSIONS is unaffected.

Thanks Shai Berger for the proposal and review, Ken Whitesell and
Carlton Gibson for the discussion, and Tim Schilling, Jacob Walls, and
Lily for reviews.

Refs #36664.

Note: See TracTickets for help on using tickets.
Back to Top