TALOS-2026-2415
CVE-2026-42532
A path traversal vulnerability exists in the EmailSheet extract_parts functionality of VisiData (version(s): dev (commit 38b21f78)). A specially crafted .eml file can lead to arbitrary file write. An attacker can provide a malicious file to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
VisiData (version(s): dev (commit 38b21f78))
VisiData - https://www.visidata.org
5.5 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CWE-22 - Improper Limitation of a Pathname to a Restricted Directory (‘Path Traversal’)
VisiData is a popular open-source terminal interface for exploring and arranging tabular data. It supports a wide range of file formats and data sources, and can open files both locally and remotely via URL, presenting the contents as interactive navigable sheets.
VisiData supports opening RFC 2822 email files (.eml, .mhtml), presenting each MIME part as a row in a sheet. The loader is implemented in visidata/loaders/eml.py. Users can extract attachments individually with x or in bulk with gx, which prompts for a destination directory and extracts all selected parts into it.
The affected flow begins when a .eml file is opened in VisiData. EmailSheet.iterload() parses the message using Python’s email.parser module and yields each MIME part via Message.walk() [1]:
def iterload(self):
import email.parser
parser = email.parser.Parser()
with self.source.open(encoding='utf-8') as fp:
[1] yield from parser.parse(fp).walk()
Each yielded object is an email.message.Message instance, presented as a selectable row in the sheet. When the user selects parts and presses gx, VisiData calls extract_parts() with the selected Message objects and the user-supplied destination path. Inside extract_parts(), each part’s filename is obtained and used unsanitised to construct the output path [2] [3]:
for i, part in enumerate(parts):
[2] fn = part.get_filename() or f'part{i}'
[3] vd.execAsync(sheet.extract_part, givenpath / fn, part)
At [2], fn is obtained by calling Message.get_filename(), a Python stdlib method in email/message.py that reads the filename parameter from the part’s Content-Disposition header and returns it verbatim without any path sanitisation. At [3], Python’s pathlib / operator joins the user-supplied destination givenpath with fn. As pathlib does not resolve or strip path traversal sequences, a Content-Disposition filename value of ../traversal.txt produces a path of givenpath/../traversal.txt. The OS resolves this when extract_part() opens the path for writing, causing the file to land outside the intended destination directory.
Exploitation requires user interaction: the victim must open the .eml and trigger extraction. While filenames are shown during extraction, a user is not expected to scrutinise every filename for path traversal sequences, particularly when the email contains many attachments. A realistic attack delivers an email that appears to carry several ordinary attachments alongside one entry whose Content-Disposition filename parameter is set to a path traversal string. When the victim opens the .eml in VisiData and bulk extracts all attachments, the malicious entry is written to the traversed path, which can include shell initialisation files, ~/.ssh/authorized_keys, or any other file writable by the running user.
2026-05-26 - Vendor Disclosure
2026-06-08 - Vendor Disclosure Resent
2026-06-15 - Request for Vendor Receipt
2026-06-16 - Vendor Acknowledged
2026-09-18 - Follow-up sent
Claudio Bozzato of Cisco Talos