CVE-2026-46338

MEDIUMPre-NVD 4.34.3
EchelonGraph scoreLOW confidence

This medium-severity CVE scores 4.3 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit probability: 0.0%, top 91% of all CVEs by exploit prediction. GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m, epss
4.3
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • Lower severity and no public exploit yet
CISA-KEV: Not listedEPSS: 0%CVSS: 4.3Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

Regression in pymdownx.snippets reintroduces sibling-prefix path traversal bypass despite restrict_base_path

Summary

pymdownx.snippets has a regression of the CVE-2023-32309 / GHSA-jh85-wwv9-24hv fix. With restrict_base_path: True (the default), the current filename.startswith(base) containment check does not enforce a directory boundary. As a result, a markdown snippet directive can read files from sibling paths that share the same prefix as base_path, such as docs vs docs_internal.

The regression was introduced in PR #2039 / commit 7c13bda5b7793b172efd1abb6712e156a83fe07d, which replaced the original directory-identity check with a plain string-prefix comparison.

Details

The regression was introduced in commit 7c13bda5b7793b172efd1abb6712e156a83fe07d (2023-05-15, #2039 *"Fix regression of snippets nested deeply under specified base path"*), which relaxed the original os.path.samefile(base, os.path.dirname(filename)) check to a plain startswith(base).

SnippetPreprocessor.get_snippet_path() in pymdownx/snippets.py:

if self.restrict_base_path:
    filename = os.path.abspath(os.path.join(base, path))
    # If the absolute path is no longer under the specified base path, reject the file
    if not filename.startswith(base):
        continue

base is os.path.abspath(b) and has no trailing separator. str.startswith(base) is True for any filename whose string representation begins with the same characters as base, regardless of whether those characters end at a directory boundary.

Concrete example:

* base = "/x/docs" * path = "../docs_secret/leak.txt" (inside the markdown snippet directive) * os.path.join(base, path)"/x/docs/../docs_secret/leak.txt" * os.path.abspath(...)"/x/docs_secret/leak.txt" * filename.startswith(base)True, because "/x/docs_secret/..." begins with the literal string "/x/docs".

All releases from 10.0.1 (2023-05-15) through 10.21.2 (current) are affected.

Impact

Arbitrary file read within the host the build runs on, bounded by the prefix match. With base_path = /x/docs the attacker can read files from any sibling directory whose path begins with the literal string /x/docs followed by any non-separator character — for example /x/docs_internal/, /x/docs.bak/, /x/docs2/.

The threat model is the same as the original CVE-2023-32309: markdown content processed by the snippets preprocessor in a build pipeline (typical scenario: an MkDocs documentation site built in CI from PR contributions or otherwise less-trusted markdown) can read files outside the configured base. CI builds that publish the generated HTML expose the read file to the public; CI builds with secrets on disk leak those secrets.

Reproduction

Minimal local PoC, non-destructive:

import os, shutil, tempfile, markdown

work = tempfile.mkdtemp(prefix="pmx_poc_") try: base = os.path.join(work, "docs") sibling = os.path.join(work, "docs_secret") os.makedirs(base) os.makedirs(sibling) with open(os.path.join(sibling, "leak.txt"), "w") as f: f.write("TOP_SECRET_FROM_SIBLING_DIR\n")

out = markdown.markdown( '--8<-- "../docs_secret/leak.txt"\n', extensions=["pymdownx.snippets"], extension_configs={ "pymdownx.snippets": { "base_path": [base], "restrict_base_path": True, "check_paths": True, } }, ) print(out) # -> TOP_SECRET_FROM_SIBLING_DIR finally: shutil.rmtree(work)

Default restrict_base_path: True is sufficient — no non-default option is required.

Suggested fix

Minimal change — require the separator after the base prefix:

-                        if not filename.startswith(base):
+                        # Append os.sep so a sibling directory whose name shares a prefix
+                        # (e.g. /x/docs vs /x/docs_evil) cannot satisfy the check.
+                        if not filename.startswith(base + os.sep):
                             continue

This preserves the original intent (allow snippets nested at any depth under base_path) while restoring the directory-boundary check. It does not affect the os.path.isdir(base) branch where base is a file (that branch still uses os.path.samefile).

Alternative: os.path.commonpath([base, filename]) == base is equivalent and slightly more idiomatic, though it raises ValueError on different drives on Windows and would need a try/except. The startswith(base + os.sep) fix is the smaller diff.

Note: this fix does not change behaviour for symlinks inside base_path. The existing implementation uses os.path.abspath (not os.path.realpath), so a symlink within base_path pointing outside is still followed. That is a separate concern — symlinks require write access to base_path, a much higher bar than the current bypass — and matches the behaviour the CVE-2023 fix established.

Regression test

A regression test class TestSnippetsSiblingPrefix was added in tests/test_extensions/test_snippets.py. It uses tests/test_extensions/_snippets/nested as base_path and a new fixture directory tests/test_extensions/_snippets/nested_sibling_evil/leak.txt. It asserts that the markdown directive --8<-- "../nested_sibling_evil/leak.txt" raises SnippetMissingError.

* Without fix: test fails (AssertionError: SnippetMissingError not raised, sibling file is silently read). * With fix: test passes.

Full suite: python -m pytest tests/ -q738 passed (737 baseline + 1 new regression test). No regressions.

Affected versions

>= 10.0.1, <= 10.21.2

CVSS v3
4.3
EG Score
4.3(low)
EPSS
9.2%
KEV
Not listed

Published

May 19, 2026

Last Modified

May 19, 2026

Vendor Advisories for CVE-2026-46338(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
PyPI(1)
PackageVulnerable rangeFixed inDependents
pymdown-extensions10.0.1 ... 10.9 (39 versions)10.21.3

Data Freshness Timeline

(refreshed 0× in last 7d / 12× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-06-29 20:41 UTCEG score recompute
  2. 2026-06-27 21:41 UTCEG score recompute
  3. 2026-06-26 19:43 UTCEG score recompute
  4. 2026-06-25 16:29 UTCEG score recompute
  5. 2026-06-17 13:09 UTCEG score recompute
  6. 2026-06-16 10:49 UTCEG score recompute
  7. 2026-06-15 08:13 UTCEG score recompute
  8. 2026-06-14 23:18 UTCEPSS rescore
  9. 2026-06-14 06:17 UTCEG score recompute
  10. 2026-06-13 23:00 UTCEPSS rescore
  11. 2026-06-13 03:39 UTCEG score recompute
  12. 2026-06-12 23:12 UTCEPSS rescore
  13. 2026-06-12 01:24 UTCEG score recompute
  14. 2026-06-10 23:28 UTCEG score recompute
  15. 2026-06-09 21:33 UTCEG score recompute
  16. 2026-06-08 19:31 UTCEG score recompute
  17. 2026-06-07 14:38 UTCEG score recompute
  18. 2026-06-06 12:43 UTCEG score recompute
  19. 2026-06-05 10:47 UTCEG score recompute
  20. 2026-06-04 08:52 UTCEG score recompute
  21. 2026-06-03 06:56 UTCEG score recompute
  22. 2026-06-02 05:01 UTCEG score recompute
  23. 2026-06-01 03:06 UTCEG score recompute
  24. 2026-05-24 06:13 UTCEG score recompute

Frequently asked(5)

What is CVE-2026-46338?
CVE-2026-46338 is a medium vulnerability published on May 19, 2026. Regression in pymdownx.snippets reintroduces sibling-prefix path traversal bypass despite restrictbasepath Summary pymdownx.snippets has a regression of the CVE-2023-32309 / GHSA-jh85-wwv9-24hv fix. With restrictbasepath: True (the default), the current filename.startswith(base) containment check…
When was CVE-2026-46338 disclosed?
CVE-2026-46338 was first published in the National Vulnerability Database on May 19, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
Is CVE-2026-46338 actively exploited?
CVE-2026-46338 is not currently on CISA's Known Exploited Vulnerabilities catalog. FIRST EPSS estimates a 9.2% percentile likelihood of exploitation in the next 30 days — higher percentiles indicate greater predicted risk.
What is the CVSS score of CVE-2026-46338?
CVE-2026-46338 has a CVSS v4.0 base score of 4.3 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-46338?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-46338, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-46338

Explore →

Is Your Infrastructure Affected by CVE-2026-46338?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.