High severityOSV Advisory· Published Jan 16, 2026· Updated Jan 16, 2026
wlc Path traversal: Unsanitized API slugs in download command
CVE-2026-23535
Description
wlc is a Weblate command-line client using Weblate's REST API. Prior to 1.17.2, the multi-translation download could write to an arbitrary location when instructed by a crafted server. This vulnerability is fixed in 1.17.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
wlcPyPI | < 1.17.2 | 1.17.2 |
Affected products
1- Range: 0.1, 0.10, 0.2, …
Patches
1216e691c6e50fix: sanitize server-provided paths
3 files changed · +45 −7
wlc/main.py+7 −7 modified@@ -21,6 +21,8 @@ import wlc from wlc.config import NoOptionError, WeblateConfig, WLCConfigurationError +from .utils import sanitize_slug + COMMANDS: dict[str, type[Command]] = {} SORT_ORDER: list[str] = [] @@ -655,13 +657,11 @@ def download_component(self, component) -> None: raise CommandError("Output is needed for download!") directory = Path(self.args.output) - file_path = directory.joinpath(f"{component.project.slug}-{component.slug}.zip") - - if not directory.exists(): - directory.mkdir(exist_ok=True, parents=True) - - with open(file_path, "wb") as file: - file.write(content) + file_path = directory / ( + f"{sanitize_slug(component.project.slug)}-{sanitize_slug(component.slug)}.zip" + ) + directory.mkdir(exist_ok=True, parents=True) + file_path.write_bytes(content) def download_components(self, iterable) -> None: for component in iterable:
wlc/test_utils.py+21 −0 added@@ -0,0 +1,21 @@ +# Copyright © Michal Čihař <michal@weblate.org> +# +# SPDX-License-Identifier: GPL-3.0-or-later + +"""Utils tests.""" + +from unittest import TestCase + +from .utils import sanitize_slug + + +class UtilsTestCase(TestCase): + """Utils tests.""" + + def test_sanitize_slug(self): + self.assertEqual(sanitize_slug("slug"), "slug") + + def test_sanitize_slug_dangerous(self): + self.assertEqual(sanitize_slug("../\\slug"), "----slug") + self.assertEqual(sanitize_slug("slug/other"), "slug-other") + self.assertEqual(sanitize_slug("slug/"), "slug-")
wlc/utils.py+17 −0 added@@ -0,0 +1,17 @@ +# Copyright © Michal Čihař <michal@weblate.org> +# +# SPDX-License-Identifier: GPL-3.0-or-later +"""Utility helpers.""" + +from __future__ import annotations + +import re + +# This matches Django's SlugField validation minus dash which is +# excluded by Weblate's validate_slug +NON_SLUG_RE = re.compile(r"[^a-zA-Z0-9_]") + + +def sanitize_slug(slug: str) -> str: + """Sanitize slug for safe use as a filename component.""" + return NON_SLUG_RE.sub("-", slug)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-mmwx-79f6-67jgghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-23535ghsaADVISORY
- github.com/WeblateOrg/wlc/commit/216e691c6e50abae97fe2e4e4f21501bf49a585fghsax_refsource_MISCWEB
- github.com/WeblateOrg/wlc/pull/1128ghsax_refsource_MISCWEB
- github.com/WeblateOrg/wlc/releases/tag/1.17.2ghsax_refsource_MISCWEB
- github.com/WeblateOrg/wlc/security/advisories/GHSA-mmwx-79f6-67jgghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.