Skip to content

Commit cf59bf7

Browse files
gh-146121: Clarify security model of pkgutil.getdata; revert checks (GH-148197)
This reverts commit bcdf231, and clarifies get_data's security model. Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent a4d9d64 commit cf59bf7

File tree

4 files changed

+26
-27
lines changed

4 files changed

+26
-27
lines changed

Doc/library/pkgutil.rst

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,24 +151,48 @@ support.
151151
:meth:`get_data <importlib.abc.ResourceLoader.get_data>` API. The
152152
*package* argument should be the name of a package, in standard module format
153153
(``foo.bar``). The *resource* argument should be in the form of a relative
154-
filename, using ``/`` as the path separator. The parent directory name
155-
``..`` is not allowed, and nor is a rooted name (starting with a ``/``).
154+
filename, using ``/`` as the path separator.
156155

157156
The function returns a binary string that is the contents of the specified
158157
resource.
159158

159+
This function uses the :term:`loader` method
160+
:func:`~importlib.abc.FileLoader.get_data`
161+
to support modules installed in the filesystem, but also in zip files,
162+
databases, or elsewhere.
163+
160164
For packages located in the filesystem, which have already been imported,
161165
this is the rough equivalent of::
162166

163167
d = os.path.dirname(sys.modules[package].__file__)
164168
data = open(os.path.join(d, resource), 'rb').read()
165169

170+
Like the :func:`open` function, :func:`!get_data` can follow parent
171+
directories (``../``) and absolute paths (starting with ``/`` or ``C:/``,
172+
for example).
173+
It can open compilation/installation artifacts like ``.py`` and ``.pyc``
174+
files or files with :func:`reserved filenames <os.path.isreserved>`.
175+
To be compatible with non-filesystem loaders, avoid using these features.
176+
177+
.. warning::
178+
179+
This function is intended for trusted input.
180+
It does not verify that *resource* "belongs" to *package*.
181+
182+
If you use a user-provided *resource* path, consider verifying it.
183+
For example, require an alphanumeric filename with a known extension, or
184+
install and check a list of known resources.
185+
166186
If the package cannot be located or loaded, or it uses a :term:`loader`
167187
which does not support :meth:`get_data <importlib.abc.ResourceLoader.get_data>`,
168188
then ``None`` is returned. In particular, the :term:`loader` for
169189
:term:`namespace packages <namespace package>` does not support
170190
:meth:`get_data <importlib.abc.ResourceLoader.get_data>`.
171191

192+
.. seealso::
193+
194+
The :mod:`importlib.resources` module provides structured access to
195+
module resources.
172196

173197
.. function:: resolve_name(name)
174198

Lib/pkgutil.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,6 @@ def get_data(package, resource):
393393
# signature - an os.path format "filename" starting with the dirname of
394394
# the package's __file__
395395
parts = resource.split('/')
396-
if os.path.isabs(resource) or '..' in parts:
397-
raise ValueError("resource must be a relative path with no "
398-
"parent directory components")
399396
parts.insert(0, os.path.dirname(mod.__file__))
400397
resource_name = os.path.join(*parts)
401398
return loader.get_data(resource_name)

Lib/test/test_pkgutil.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,6 @@ def test_getdata_filesys(self):
6161

6262
del sys.modules[pkg]
6363

64-
def test_getdata_path_traversal(self):
65-
pkg = 'test_getdata_traversal'
66-
67-
# Make a package with some resources
68-
package_dir = os.path.join(self.dirname, pkg)
69-
os.mkdir(package_dir)
70-
# Empty init.py
71-
f = open(os.path.join(package_dir, '__init__.py'), "wb")
72-
f.close()
73-
74-
with self.assertRaises(ValueError):
75-
pkgutil.get_data(pkg, '../../../etc/passwd')
76-
with self.assertRaises(ValueError):
77-
pkgutil.get_data(pkg, 'sub/../../../etc/passwd')
78-
with self.assertRaises(ValueError):
79-
pkgutil.get_data(pkg, os.path.abspath('/etc/passwd'))
80-
81-
del sys.modules[pkg]
82-
8364
def test_getdata_zipfile(self):
8465
zip = 'test_getdata_zipfile.zip'
8566
pkg = 'test_getdata_zipfile'

Misc/NEWS.d/next/Security/2026-03-16-18-07-00.gh-issue-146121.vRbdro.rst

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)