Describe the bug
reported via email on 9 June 2026 - no acknowledgement or response:
Package: datasets (PyPI). Affected file: src/datasets/packaged_modules/folder_based_builder/folder_based_builder.py
Affected Versions: confirmed on 5.0.0 (PyPI) and HEAD 63e103e.
CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N (~7.1 High when the exfil/re-embed chain applies; Medium for local read only)
CWE: CWE-22 Path Traversal
Summary
The built-in imagefolder / audiofolder / videofolder / pdffolder (*folder) builders read a file_name value verbatim from a dataset's metadata.jsonl/.csv/.parquet and join it to the dataset directory without neutralizing .. or absolute paths. A malicious dataset can therefore make load_dataset(...) open and read files outside the dataset directory. No trust_remote_code is required (these are packaged builders).
Details
src/datasets/packaged_modules/folder_based_builder/folder_based_builder.py (~L404-405):
file_relpath = os.path.normpath(item).replace("\\", "/") # item = attacker-controlled file_name
item = os.path.join(downloaded_metadata_dir, file_relpath) # join escapes the dataset dir
os.path.normpath canonicalizes separators but does not strip a leading ../, so ../../../../etc/passwd survives, and an absolute value escapes entirely. No commonpath/relpath/realpath containment check exists. The resolved path is stored in the Image/Audio/Pdf feature and then read:
features/image.py:175 PIL.Image.open(path) (lazy read on row access), and
features/image.py:311 xopen(path,"rb").read() in embed_storage — the unconditional raw-byte read used by save_to_disk / push_to_hub.
Steps to reproduce the bug
PoC
metadata.jsonl in an imagefolder dataset:
{"file_name": "../victim/secret.png", "text": "x"}
from datasets import load_dataset
d = load_dataset("imagefolder", data_dir=malicious_dir, split="train")
d[0]["image"] # the image OUTSIDE the dataset dir
Observed: OUT-OF-DATASET READ: True (loaded the out-of-directory image). With file_name: "../../../../../../etc/passwd", the path resolves to /etc/passwd; the embed_storage path (invoked by save_to_disk/push_to_hub) pulled the bytes of /etc/passwd into the saved output (EXFIL CONFIRMED).
Impact
Loading a malicious dataset reads arbitrary local files; when the victim re-saves/re-publishes (save_to_disk/push_to_hub) the dataset, arbitrary local file contents are silently embedded into the output and exfiltrated. Triggered by the ordinary load_dataset("imagefolder", data_dir=...) and identically for a remote Hub dataset load_dataset("user/dataset").
Net-new
No matching CVE/GHSA. Distinct from the tarfile (CVE-2007-4559) mitigations in utils/extract.py, which were tested separately and correctly block ../symlink escapes — the flaw here is the unsanitized file_name metadata join, not archive extraction.
Remediation
Reject file_name values that are absolute or contain .. (require the resolved path to remain under the dataset/metadata directory via os.path.realpath + os.path.commonpath, or restrict to os.path.basename-relative paths within the tree).
Expected behavior
Environment info
Describe the bug
reported via email on 9 June 2026 - no acknowledgement or response:
Package: datasets (PyPI). Affected file: src/datasets/packaged_modules/folder_based_builder/folder_based_builder.py
Affected Versions: confirmed on 5.0.0 (PyPI) and HEAD 63e103e.
CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N (~7.1 High when the exfil/re-embed chain applies; Medium for local read only)
CWE: CWE-22 Path Traversal
Summary
The built-in
imagefolder/audiofolder/videofolder/pdffolder(*folder) builders read afile_namevalue verbatim from a dataset'smetadata.jsonl/.csv/.parquetand join it to the dataset directory without neutralizing..or absolute paths. A malicious dataset can therefore makeload_dataset(...)open and read files outside the dataset directory. Notrust_remote_codeis required (these are packaged builders).Details
src/datasets/packaged_modules/folder_based_builder/folder_based_builder.py(~L404-405):os.path.normpathcanonicalizes separators but does not strip a leading../, so../../../../etc/passwdsurvives, and an absolute value escapes entirely. Nocommonpath/relpath/realpathcontainment check exists. The resolved path is stored in theImage/Audio/Pdffeature and then read:features/image.py:175PIL.Image.open(path)(lazy read on row access), andfeatures/image.py:311xopen(path,"rb").read()inembed_storage— the unconditional raw-byte read used bysave_to_disk/push_to_hub.Steps to reproduce the bug
PoC
metadata.jsonlin animagefolderdataset:{"file_name": "../victim/secret.png", "text": "x"}Observed:
OUT-OF-DATASET READ: True(loaded the out-of-directory image). Withfile_name: "../../../../../../etc/passwd", the path resolves to/etc/passwd; theembed_storagepath (invoked bysave_to_disk/push_to_hub) pulled the bytes of/etc/passwdinto the saved output (EXFIL CONFIRMED).Impact
Loading a malicious dataset reads arbitrary local files; when the victim re-saves/re-publishes (
save_to_disk/push_to_hub) the dataset, arbitrary local file contents are silently embedded into the output and exfiltrated. Triggered by the ordinaryload_dataset("imagefolder", data_dir=...)and identically for a remote Hub datasetload_dataset("user/dataset").Net-new
No matching CVE/GHSA. Distinct from the tarfile (CVE-2007-4559) mitigations in
utils/extract.py, which were tested separately and correctly block../symlink escapes — the flaw here is the unsanitizedfile_namemetadata join, not archive extraction.Remediation
Reject
file_namevalues that are absolute or contain..(require the resolved path to remain under the dataset/metadata directory viaos.path.realpath+os.path.commonpath, or restrict toos.path.basename-relative paths within the tree).Expected behavior
Environment info