TALOS-2026-2414
CVE-2026-41958
A path traversal vulnerability exists in the unzip_http RemoteZipFile extract functionality of VisiData (version(s): dev (commit 38b21f78)). A specially crafted .zip file can lead to arbitrary file write. An attacker can provide a crafted URL 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
6.5 - CVSS:3.1/AV:N/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 zip archives served over HTTP. When a user points VisiData at a URL ending in .zip, the application presents the archive contents as a navigable sheet where each row corresponds to an entry in the zip. The user can then press x to extract the currently selected entry, or select multiple entries and press gx to extract them all in bulk to the current working directory. To enable this without downloading the entire archive upfront, VisiData ships its own HTTP range-request zip reader implemented in visidata/loaders/unzip_http.py, rather than delegating to Python’s standard zipfile module. As a consequence, none of the path sanitisation protections present in the stdlib apply here.
The affected flow begins when a remote URL is opened in VisiData. VisiData issues an HTTP HEAD request to read the Content-Length, then fetches the last 65536 bytes of the remote file to locate the zip central directory. It parses all central directory entries and builds a list of RemoteZipInfo objects, each carrying the raw filename string exactly as stored in the zip [1]:
[1] rzi = RemoteZipInfo(filename.decode(), date_time, local_header_ofs, method, complen, uncomplen)
These RemoteZipInfo objects are presented to the user as rows in a VisiData sheet. The filename field is the raw value from the central directory and has not been inspected or sanitised in any way at this point. When the user selects entries and triggers extraction, VisiData calls RemoteZipFile.extract() for each selected member, passing the raw filename directly as the member argument:
def extract(self, member, path=None, pwd=None):
if pwd:
raise NotImplementedError('Passwords not supported yet')
path = path or pathlib.Path('.')
[2] outpath = path/member
[3] os.makedirs(outpath.parent, exist_ok=True)
with self.open(member) as fpin:
[4] with open(path/member, mode='wb') as fpout:
while True:
r = fpin.read(65536)
if not r:
break
fpout.write(r)
At [2], Python’s pathlib / operator joins the user-supplied destination path with the raw member name. Unlike os.path.join, pathlib’s / operator does not resolve or strip path traversal sequences; a member named ../../.ssh/authorized_keys produces a Path object whose string representation is path/../../.ssh/authorized_keys. At [3], os.makedirs is called on the parent of this unresolved path. The OS resolves the .. components at this point, causing any required intermediate directories to be created outside the intended extraction directory. At [4], the file is opened for writing using the same unresolved path, and the decompressed content is written there, landing at the fully traversed location on disk.
Exploitation requires user interaction in that the victim must open the remote URL and trigger extraction. This is a realistic scenario: the typical workflow for inspecting and saving a remote archive is to open the URL in VisiData, briefly review the listed entries, select all with gs, and extract with gx. An attacker can exploit this pattern by crafting an archive that contains a large number of plausible-looking filenames alongside one or more traversal entries, making individual inspection unlikely. The traversal entries are visible as rows in the VisiData sheet, but are easy to overlook in a large archive. An attacker who can serve a malicious .zip at a reachable URL and persuade a VisiData user to open and extract it can therefore write arbitrary content to any path on the filesystem writable by the victim’s user, including shell initialisation files, ~/.ssh/authorized_keys, or cron entries.
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