From bcdf231946b1da8bdfbab4c05539bb0cc964a1c7 Mon Sep 17 00:00:00 2001
From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
Date: Wed, 18 Mar 2026 17:31:01 +0000
Subject: [PATCH] gh-146121: `pkgutil.get_data()` reject invalid resource
arguments (#146122)
Lib/pkgutil.py | 3 +++
Lib/test/test_pkgutil.py | 19 +++++++++++++++++++
...-03-16-18-07-00.gh-issue-146121.vRbdro.rst | 3 +++
3 files changed, 25 insertions(+)
create mode 100644 Misc/NEWS.d/next/Security/2026-03-16-18-07-00.gh-issue-146121.vRbdro.rst
@@ -393,6 +393,9 @@ def get_data(package, resource):
# signature - an os.path format "filename" starting with the dirname of
# the package's __file__
parts = resource.split('/')
+ if os.path.isabs(resource) or '..' in parts:
+ raise ValueError("resource must be a relative path with no "
+ "parent directory components")
parts.insert(0, os.path.dirname(mod.__file__))
resource_name = os.path.join(*parts)
return loader.get_data(resource_name)
@@ -61,6 +61,25 @@ def test_getdata_filesys(self):
del sys.modules[pkg]
+ def test_getdata_path_traversal(self):
+ pkg = 'test_getdata_traversal'
+
+ # Make a package with some resources
+ package_dir = os.path.join(self.dirname, pkg)
+ os.mkdir(package_dir)
+ # Empty init.py
+ f = open(os.path.join(package_dir, '__init__.py'), "wb")
+ f.close()
+
+ with self.assertRaises(ValueError):
+ pkgutil.get_data(pkg, '../../../etc/passwd')
+ with self.assertRaises(ValueError):
+ pkgutil.get_data(pkg, 'sub/../../../etc/passwd')
+ with self.assertRaises(ValueError):
+ pkgutil.get_data(pkg, os.path.abspath('/etc/passwd'))
+
+ del sys.modules[pkg]
+
def test_getdata_zipfile(self):
zip = 'test_getdata_zipfile.zip'
pkg = 'test_getdata_zipfile'
new file mode 100644
@@ -0,0 +1,3 @@
+:func:`pkgutil.get_data` now raises rejects *resource* arguments containing the
+parent directory components or that is an absolute path.
+This addresses `CVE-2026-3479 <https://www.cve.org/CVERecord?id=CVE-2026-3479>`_.
--
2.43.0