CVE-2026-59974 Overview
CVE-2026-59974 is a path traversal vulnerability [CWE-22] in Stanza, the Stanford NLP Python library used for tokenization, sentence segmentation, named entity recognition (NER), and multilingual parsing. Versions prior to 1.14.0 contain a unzip function in stanza/resources/common.py that passes downloaded archives to zipfile.ZipFile.extractall without validating member paths. The vulnerable code is reachable via the public stanza.download and stanza.install_corenlp APIs. A malicious archive containing parent-directory traversal entries can write files outside the intended model directory. The issue is fixed in Stanza 1.14.0.
Critical Impact
A crafted model archive can overwrite arbitrary files writable by the Stanza process, potentially leading to code execution through modified shell configurations, SSH authorized_keys files, Python packages, or executable scripts.
Affected Products
- Stanford NLP Stanza Python library, all versions prior to 1.14.0
- Applications invoking stanza.download to fetch language models
- Applications invoking stanza.install_corenlp to install CoreNLP resources
Discovery Timeline
- 2026-09-16 - CVE-2026-59974 published to NVD
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-59974
Vulnerability Analysis
The vulnerability is a classic Zip Slip issue in the archive extraction routine used to unpack downloaded model resources. The unzip helper in stanza/resources/common.py calls zipfile.ZipFile.extractall(path) directly on archives fetched during model installation. Because Python's extractall does not sanitize member names, an archive containing entries like ../../../../home/user/.ssh/authorized_keys writes to arbitrary locations outside the target directory.
Exploitation requires the victim to trigger a Stanza download that resolves to an attacker-controlled archive. Any file writable by the Stanza process becomes a candidate for overwrite, including shell startup files (.bashrc, .zshrc), SSH authorization data, Python site-packages, and cron scripts. Modification of these files converts a file-write primitive into local code execution.
Root Cause
The root cause is missing validation of ZIP member paths before extraction. The library trusted archive contents implicitly because the download URLs were assumed to point to Stanford-controlled resources. However, the vulnerable code path is reachable through any Stanza download flow, and a network attacker able to influence the archive contents (through DNS hijacking, MITM on unencrypted mirrors, or a compromised mirror) can weaponize the extraction.
Attack Vector
An attacker crafts a ZIP archive containing entries with ../ sequences in their names. When Stanza extracts the archive, those entries are resolved relative to the extraction root and written outside the intended model directory. Delivery vectors include compromised mirrors, adversary-in-the-middle on the download connection, or tricking a user into calling stanza.download or stanza.install_corenlp against an attacker-controlled URL or resource file.
raise
return hashlib.md5(data).hexdigest()
+def _is_within_directory(directory, target):
+ """
+ Check that `target` resolves to a path inside `directory`.
+ """
+ directory = os.path.realpath(directory)
+ target = os.path.realpath(target)
+ return os.path.commonpath([directory]) == os.path.commonpath([directory, target])
+
def unzip(path, filename):
"""
Fully unzip a file `filename` that's in a directory `dir`.
+
+ Before unzipping, paths are checked so that a 'zip slip' error cannot happen.
+ See https://github.com/stanfordnlp/stanza/security/advisories/GHSA-2fwf-f686-7p34
"""
logger.debug(f'Unzip: {path}/{filename}...')
with zipfile.ZipFile(os.path.join(path, filename)) as f:
+ for member in f.namelist():
+ member_path = os.path.join(path, member)
+ if not _is_within_directory(path, member_path):
+ raise ValueError(
+ f"Zip file {filename} contains an entry that would extract "
+ f"outside of the target directory: {member}"
+ )
f.extractall(path)
def get_root_from_zipfile(filename):
Source: Stanza patch commit a7085e75. The patch introduces _is_within_directory and iterates every archive member before extraction, rejecting archives whose resolved member paths escape the target directory.
Detection Methods for CVE-2026-59974
Indicators of Compromise
- Unexpected modifications to shell startup files such as .bashrc, .zshrc, or .profile on hosts running Stanza
- New or modified entries in ~/.ssh/authorized_keys on user accounts that execute Stanza workloads
- Files written outside the configured STANZA_RESOURCES_DIR shortly after a stanza.download or stanza.install_corenlp call
- Presence of ZIP archives in the Stanza cache containing .. sequences in member names
Detection Strategies
- Inventory Python environments for stanza package versions below 1.14.0 using pip list or software composition analysis tools
- Monitor process execution for python processes invoking Stanza that subsequently write files outside the model cache directory
- Static analysis of Stanza archive caches to enumerate ZIP entries and flag any containing parent-directory traversal segments
Monitoring Recommendations
- Enable file integrity monitoring on user home directories, ~/.ssh/, and Python site-packages on hosts that run Stanza
- Log outbound HTTP/HTTPS requests from Python processes to identify redirects to non-Stanford download endpoints
- Alert on writes to executable paths by Python interpreters outside expected build or deployment windows
How to Mitigate CVE-2026-59974
Immediate Actions Required
- Upgrade Stanza to version 1.14.0 or later in all environments where the library is installed
- Audit hosts that have previously executed stanza.download or stanza.install_corenlp for unexpected file modifications
- Rotate SSH keys and review shell startup files on any host suspected of processing untrusted archives
Patch Information
The fix is included in Stanza 1.14.0. See the GitHub Release v1.14.0 and the GitHub Security Advisory GHSA-2fwf-f686-7p34 for full details. The patch adds a _is_within_directory check that validates every ZIP member's resolved path against the extraction root before calling extractall.
Workarounds
- Run Stanza under a dedicated low-privilege user account with no write access to sensitive files such as SSH keys or shell configuration
- Restrict Stanza downloads to a trusted, pre-vetted mirror served over HTTPS with certificate pinning
- Pre-download and validate model archives out of band, then load them via a local path rather than the network download flow
# Upgrade Stanza to the patched release
pip install --upgrade 'stanza>=1.14.0'
# Verify installed version
python -c "import stanza; print(stanza.__version__)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

