Apr 22, 2026·6 min read·3 visits
A path traversal vulnerability in Jupyter nbconvert allows arbitrary local file exfiltration. Exploitation requires user interaction to convert a malicious notebook to HTML with the HTMLExporter.embed_images setting enabled.
Jupyter nbconvert versions 6.5 through 7.17.0 contain a path traversal vulnerability. When the HTMLExporter.embed_images configuration is enabled, the markdown renderer improperly resolves local image paths. This flaw allows an attacker to read arbitrary files from the host system by providing a maliciously crafted Jupyter Notebook containing path traversal sequences in image references.
The nbconvert library is a core component of the Jupyter ecosystem responsible for translating Jupyter Notebook files (.ipynb) into various secondary formats such as HTML, PDF, and LaTeX. The vulnerability, classified as CWE-22 (Path Traversal), resides in the post-processing engine of the HTMLExporter class. Specifically, the affected logic handles the conversion and inline embedding of image resources when the HTMLExporter.embed_images configuration variable is set to True.
The vulnerability manifests when the conversion utility attempts to resolve image paths defined within the Markdown cells of a provided notebook. The implementation extracts the src attribute from image tags and translates it to a local filesystem path to read the binary data. Because the application fails to adequately sanitize the provided path or enforce boundary limitations, it processes directory traversal sequences such as ../.
Exploitation requires a specific sequence of actions. An attacker must construct a malicious Jupyter Notebook containing an image reference pointing to a target system file. The vulnerability is triggered when a victim or an automated processing pipeline executes nbconvert against the malicious notebook with the image embedding feature enabled.
Upon successful execution, the tool reads the contents of the target file from the underlying filesystem. The application then base64-encodes the file's raw binary data and embeds it directly into the generated HTML output as a Data URI. The attacker can then extract and decode the base64 string to access the file contents, resulting in a high-severity breach of confidentiality.
The root cause of CVE-2026-39378 is the improper use of the os.path.join() function without subsequent boundary validation within nbconvert/filters/markdown_mistune.py. The HTMLExporter class utilizes a custom filter to locate image references in the compiled HTML or parsed Markdown and transform them into embedded base64 strings.
When extracting the source path of an image, the vulnerable implementation combined the notebook's base directory with the user-controlled image path using os.path.join(self.path, src). The Python os.path.join() function resolves relative paths mathematically. If the user-supplied src variable contains multiple ../ sequences, the resulting string traverses upwards from the base directory.
The application proceeds to read the file located at the resolved path without verifying that the final destination resides within the self.path directory tree. This absence of canonicalization and boundary checking allows the application to read any file accessible by the system user executing the nbconvert process.
The remediation for CVE-2026-39378 involves structural changes across multiple files to enforce strict path boundaries. The primary fix addresses the image embedding logic in nbconvert/filters/markdown_mistune.py via commit 0e6b8ccabf2aca6c18fac8c574f22b7155f441fb.
The patched implementation introduces absolute path resolution and a boundary enforcement check. By utilizing os.path.abspath(), the code forces the operating system to resolve all directory traversal sequences. The resulting canonical path is then checked against the allowed base directory.
# nbconvert/filters/markdown_mistune.py (Patched)
def _src_to_base64(self, src: str) -> Optional[str]:
src_path = os.path.join(self.path, src)
# Resolve the absolute path of the requested file
resolved = os.path.abspath(src_path)
# Resolve the absolute path of the permitted directory
allowed_base = os.path.abspath(self.path)
# Restrict execution if the resolved path is outside the allowed base
if not resolved.startswith(allowed_base + os.sep) and resolved != allowed_base:
return NoneA secondary patch, implemented in commit ba5e5cdd737704388251fa55fa9e58f5752fa39d, hardens the ExtractAttachmentsPreprocessor within nbconvert/preprocessors/extractattachments.py. This fix prevents path traversal vectors during the extraction of embedded notebook attachments by utilizing os.path.basename().
# nbconvert/preprocessors/extractattachments.py (Patched)
# Flatten the filename by stripping any directory components
safe_fname = os.path.basename(fname)
if safe_fname != fname:
self.log.warning(
"Attachment filename '%s' contained path components, using basename '%s'",
fname,
safe_fname,
)
# Safe concatenation guarantees the path remains within path_name
new_filename = os.path.join(self.path_name, safe_fname)Together, these patches eliminate the external control of file paths. The system now strictly isolates file reading and extraction operations to the directory containing the source notebook file.
The exploitation of CVE-2026-39378 requires the attacker to construct a valid Jupyter Notebook file (.ipynb) containing an embedded payload. The payload leverages standard Markdown image syntax referencing absolute paths or deep relative traversal paths.
The attacker injects a string such as  or standard HTML <img> tags into a Markdown cell within the notebook structure. The depth of the traversal sequence (../) must be sufficient to reach the root filesystem directory from the directory where the victim will process the notebook.
The attack completes when a victim processes the malicious notebook. The execution parameter requires --HTMLExporter.embed_images=True if run via the command line interface, or the equivalent configuration setting in jupyter_nbconvert_config.py.
The output HTML file generated by the victim contains the exfiltrated file data. The attacker extracts the src attribute of the targeted <img> tag and decodes the base64 string to read the contents of the target file in plaintext. No specific system privileges are required by the attacker beyond the ability to supply the notebook file to the conversion process.
The vulnerability produces a high confidentiality impact. Successful exploitation permits an attacker to read any file on the local filesystem that the nbconvert process has permission to access.
In standard desktop environments, this exposes user directories, local configuration files, and SSH keys. In automated environments such as CI/CD pipelines, containerized notebook converters, or shared JupyterHub deployments, the vulnerability exposes environmental variables, database connection strings, and application source code.
The impact is strictly limited to confidentiality. The vulnerability provides no mechanism for file writing, modification, or arbitrary code execution. The integrity and availability of the system remain entirely unaffected by this path traversal flaw.
The requirement for user interaction and the non-default configuration state (HTMLExporter.embed_images=False) mitigate the overall severity. The vulnerability carries a CVSS v3.1 score of 6.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N). The current EPSS score is 0.00036, indicating a low probability of broad exploitation in the wild.
The definitive remediation for CVE-2026-39378 is upgrading the nbconvert package to version 7.17.1 or higher. Administrators should utilize standard Python package managers to deploy the update, executing pip install --upgrade nbconvert across all environments.
Organizations managing automated notebook processing pipelines must audit their dependencies to ensure the updated version is reflected in container images and build manifests. Validation of the deployed version ensures the path normalization logic processes incoming files correctly.
If immediate patching is not technically feasible, administrators must apply a configuration workaround. The vulnerability requires the embed_images setting to be active. Explicitly setting HTMLExporter.embed_images=False in the application configuration entirely neutralizes the attack vector.
Security teams should monitor execution arguments and configuration files for instances where embed_images is explicitly enabled. Prohibiting this configuration flag in automated environments prevents the vulnerable code path from executing until the patch is applied.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
nbconvert Jupyter | >= 6.5, < 7.17.1 | 7.17.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 / CWE-73 |
| Attack Vector | Network |
| CVSS Score | 6.5 |
| EPSS Score | 0.00036 (10.88%) |
| Impact | High Confidentiality |
| Exploit Status | Proof of Concept available |
| KEV Status | Not Listed |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.