Regression in pymdownx.snippets reintroduces sibling-prefix path traversal bypass despite restrict_base_path
Regression in pymdownx.snippets reintroduces sibling-prefix path traversal bypass despite restrict_base_path
Prioriteit & onderbouwing
Prioriteit: Laag
Monitoren
Laag (45/100): monitoren. Zwaarst wegend: gemeentelijke relevantie en technische ernst.
midden
- Technische ernst (severity): Genormaliseerde ernst 'medium'; geen CVSS-score beschikbaar.
laag
- Geen exploit bekend: Er is geen exploit of actief misbruik bekend.
verhoogd
- Gemeentelijke relevantie: Relevantiescore 55/100 uit de relevantie-engine (module 5).
midden
- Technische ernst: Threat Score 42/100 x gewicht 25%.
- Exploitatie: Exploit Score 10/100 x gewicht 25%.
- Gemeentelijke relevantie: Relevantiescore 55/100 x gewicht 22%.
- Betrouwbaarheid van het signaal: Confidence 'likely' x gewicht 12%.
- Blootstellingskans: Geschatte blootstelling 47% x gewicht 10%.
- Betrouwbaarheid van de bron: Bronbetrouwbaarheid 88% x gewicht 6%.
De priority_score is de Action Urgency Score: een gewogen combinatie van de technische ernst, de exploitatie en de gemeentelijke relevantie.
Toelichting
# 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`: ```python 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: ```python 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: ```diff - 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 equival
Onderbouwing van de classificatie
Categorie 'supplier_incident' overgenomen van de bron; geen specifieker incidenttype gedetecteerd. Severity 'medium' bepaald op basis van: bronlabel 'medium'. Confidence 'likely': gerenommeerd securityonderzoek (GitHub Security Advisories). Geen bekende leveranciers of producten herkend.
Kwetsbaarheden
- CVSS
- —
- EPSS
- —
- KEV
- Nee
Gemeentelijke relevantie
Deze dreiging scoort 55/100 voor de gemeentelijke relevantie. Meegewogen: veelgebruikte gemeentelijke technologie, impact op identity of Microsoft 365 en een leveranciers- of ketenrisico. Geraakte processen: Microsoft 365 en identity, Leveranciersketen.
Bestuurlijke duiding
Deze dreiging is relevant voor de gemeente. Omdat het een leverancier betreft, is de gemeente afhankelijk van diens herstel en is regie op de keten nodig. De impact is beheersbaar mits de geadviseerde maatregelen tijdig worden opgevolgd. Laat de CISO de voortgang bewaken en escaleer richting directie zodra nieuwe signalen daartoe aanleiding geven.
Geraakte processen
Geraakte technologie
Betrokken rollen
CISO · ISO · SOC · ICT beheer · Leveranciersmanager
Operationele acties
- Inventariseer welke koppelingen en gegevensstromen met de leverancier lopen.
- Schakel waar nodig de koppeling met de leverancier tijdelijk uit.
- Vraag bewijs van herstel op voordat de dienstverlening wordt hervat.
Concrete stappen voor ICT-beheer en het securityteam.
Aanbevolen acties
- Breng in kaart welke leveranciers en koppelingen zijn geraakt.
- Vraag de leverancier om een statusupdate en een herstelplan.
- Beoordeel de impact op de eigen dienstverlening.
Dit zijn algemene handelingsperspectieven. Stem de opvolging af op de eigen omgeving en het ISMS van uw gemeente.
Kenmerken
- Ernst
- Midden
- Categorie
- Leveranciersincident
- Zekerheid
- Waarschijnlijk
- Status
- Verrijkt
- CVE's
- CVE-2026-46338
- Prioriteitsscore
- 45 / 100 · Laag
- Bron
- GitHub Security Advisories
- Gepubliceerd
- 19 mei 2026