Skip to content

Path traversal / arbitrary file read via the file_name metadata field in *folder dataset builders (no trust_remote_code) #8324

Description

@geo-chen

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions