Skip to content

Commit b372f6e

Browse files
committed
Fix xz download + discovery URLs — tukaani.org non-authoritative post-CVE-2024-3094
After the March 2024 xz-utils supply-chain backdoor (CVE-2024-3094), the xz project moved release hosting and authoritative release metadata from Lasse Collin's tukaani.org infrastructure to the community-maintained github.com/tukaani-project/xz repository. Tarball paths under `tukaani.org/xz/` now 404 (the listing page at `https://tukaani.org/xz/` remains up but its anchor hrefs point directly at GitHub releases), which broke the Auto-Update Versions workflow when it tried to fetch xz-5.8.4: Downloading http://tukaani.org/xz/xz-5.8.4.tar.gz... HTTP Error 404: Not Found relenv.common.RelenvException: Error fetching url ... Confirmed via curl: http://tukaani.org/xz/xz-5.8.4.tar.gz -> 301 -> https https://tukaani.org/xz/xz-5.8.4.tar.gz -> 404 https://github.com/tukaani-project/xz/releases/ download/v5.8.4/xz-5.8.4.tar.gz -> 302 -> 200 This commit makes two related changes: 1. Switch every hardcoded xz download URL to the canonical GitHub-releases template `https://github.com/tukaani-project/xz/releases/download/ v{version}/xz-{version}.tar.gz`. 2. Switch xz version *discovery* off tukaani.org too. Even though `https://tukaani.org/xz/` still returns 200 today, we shouldn't rely on that host as authoritative post-CVE. `detect_xz_versions()` now queries the tukaani-project Releases API (`https://api.github.com/repos/tukaani-project/xz/releases`), which is what tukaani.org itself now links to; drafts and prereleases (e.g. `v5.7.2beta`) are filtered. The CPE identifier `cpe:2.3:a:tukaani:xz:...` in build/windows.py is a vulnerability-DB name, not a URL, and is left alone. Files touched: - relenv/python-versions.json (5.8.2, 5.8.3 URL templates) - relenv/pyversions.py (download URL for Auto-Update Versions, stored template for new releases, detect_xz_versions -> GitHub API) - relenv/build/linux.py (fallback URL when JSON lookup fails) - relenv/build/darwin.py (fallback URL when JSON lookup fails) - tests/test_pyversions_runtime.py (mock updated to GitHub API JSON, asserts endpoint + prerelease/draft filtering) Impact: `relenv versions --check-deps` now reports `XZ 5.8.3 -> 5.8.4 (update available)` correctly, and `relenv versions --update-deps` can actually fetch the tarball. No version pin is bumped by this commit — that is the downstream job of the Auto-Update Versions workflow once this fix lands.
1 parent 39f9a74 commit b372f6e

5 files changed

Lines changed: 59 additions & 23 deletions

File tree

‎relenv/build/darwin.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def build_python(env: MutableMapping[str, str], dirs: Dirs, logfp: IO[str]) -> N
197197
xz_checksum = xz_info["sha256"]
198198
else:
199199
xz_version = "5.8.1"
200-
xz_url = "http://tukaani.org/xz/xz-{version}.tar.gz"
200+
xz_url = "https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.gz"
201201
xz_checksum = "ed4d5589c4cfe84e1697bd02a9954b76af336931"
202202

203203
build.add(

‎relenv/build/linux.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ def build_python(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:
689689
else:
690690
# Fallback to hardcoded values
691691
xz_version = "5.8.1"
692-
xz_url = "http://tukaani.org/xz/xz-{version}.tar.gz"
692+
xz_url = "https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.gz"
693693
xz_checksum = "ed4d5589c4cfe84e1697bd02a9954b76af336931"
694694

695695
build.add(

‎relenv/python-versions.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@
367367
},
368368
"xz": {
369369
"5.8.2": {
370-
"url": "http://tukaani.org/xz/xz-{version}.tar.gz",
370+
"url": "https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.gz",
371371
"sha256": "ce09c50a5962786b83e5da389c90dd2c15ecd0980a258dd01f70f9e7ce58a8f1",
372372
"platforms": [
373373
"linux",
@@ -376,7 +376,7 @@
376376
]
377377
},
378378
"5.8.3": {
379-
"url": "http://tukaani.org/xz/xz-{version}.tar.gz",
379+
"url": "https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.gz",
380380
"sha256": "3d3a1b973af218114f4f889bbaa2f4c037deaae0c8e815eec381c3d546b974a0",
381381
"platforms": [
382382
"linux",

‎relenv/pyversions.py‎

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -331,16 +331,36 @@ def detect_sqlite_versions() -> list[tuple[str, str]]:
331331

332332
def detect_xz_versions() -> list[str]:
333333
"""
334-
Detect available XZ versions from tukaani.org.
334+
Detect available XZ versions from the tukaani-project GitHub releases API.
335+
336+
Sourcing xz version metadata from tukaani.org is discouraged after
337+
CVE-2024-3094 (the March 2024 xz-utils supply-chain backdoor). Both
338+
release tarball hosting and the authoritative release listing moved
339+
to https://github.com/tukaani-project/xz, so we consult that project's
340+
Releases API and skip drafts / prereleases.
335341
"""
336-
url = "https://tukaani.org/xz/"
342+
url = "https://api.github.com/repos/tukaani-project/xz/releases"
337343
content = fetch_url_content(url)
338-
# Find xz-X.Y.Z.tar.gz
339-
pattern = r"xz-(\d+\.\d+\.\d+)\.tar\.gz"
340-
matches = re.findall(pattern, content)
341-
# Deduplicate and sort
342-
versions = sorted(set(matches), key=lambda v: [int(x) for x in v.split(".")], reverse=True)
343-
return versions
344+
try:
345+
releases = json.loads(content)
346+
except json.JSONDecodeError:
347+
return []
348+
versions: list[str] = []
349+
for release in releases:
350+
if not isinstance(release, dict):
351+
continue
352+
if release.get("draft") or release.get("prerelease"):
353+
continue
354+
tag = release.get("tag_name", "")
355+
if not isinstance(tag, str):
356+
continue
357+
# Tags are like "v5.8.4"; accept plain "5.8.4" too for robustness.
358+
if tag.startswith("v"):
359+
tag = tag[1:]
360+
if not re.fullmatch(r"\d+\.\d+\.\d+", tag):
361+
continue
362+
versions.append(tag)
363+
return sorted(set(versions), key=lambda v: [int(x) for x in v.split(".")], reverse=True)
344364

345365

346366
def detect_libffi_versions() -> list[str]:
@@ -691,13 +711,16 @@ def update_dependency_versions(path: pathlib.Path, deps_to_update: list[str] | N
691711
if "xz" not in dependencies:
692712
dependencies["xz"] = {}
693713
if latest not in dependencies["xz"]:
694-
url = f"http://tukaani.org/xz/xz-{latest}.tar.gz"
714+
url = (
715+
f"https://github.com/tukaani-project/xz/releases/download/"
716+
f"v{latest}/xz-{latest}.tar.gz"
717+
)
695718
print(f"Downloading {url}...")
696719
download_path = download_url(url, cwd)
697720
checksum = sha256_digest(download_path)
698721
print(f"SHA-256: {checksum}")
699722
dependencies["xz"][latest] = {
700-
"url": "http://tukaani.org/xz/xz-{version}.tar.gz",
723+
"url": "https://github.com/tukaani-project/xz/releases/download/v{version}/xz-{version}.tar.gz",
701724
"sha256": checksum,
702725
"platforms": ["linux", "darwin", "win32"],
703726
}

‎tests/test_pyversions_runtime.py‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,24 +170,37 @@ def fake_fetch(url: str) -> str:
170170

171171

172172
def test_detect_xz_versions(monkeypatch: pytest.MonkeyPatch) -> None:
173-
"""Test XZ version detection from tukaani.org."""
174-
mock_html = """
175-
<html>
176-
<a href="xz-5.8.1.tar.gz">xz-5.8.1.tar.gz</a>
177-
<a href="xz-5.8.0.tar.gz">xz-5.8.0.tar.gz</a>
178-
<a href="xz-5.6.3.tar.gz">xz-5.6.3.tar.gz</a>
179-
</html>
180-
"""
173+
"""Test XZ version detection from the tukaani-project GitHub releases API."""
174+
import json
175+
176+
mock_releases = json.dumps(
177+
[
178+
{"tag_name": "v5.8.1", "draft": False, "prerelease": False},
179+
{"tag_name": "v5.7.2beta", "draft": False, "prerelease": True},
180+
{"tag_name": "v5.8.0", "draft": False, "prerelease": False},
181+
{"tag_name": "v5.6.3", "draft": False, "prerelease": False},
182+
{"tag_name": "v5.9.0-draft", "draft": True, "prerelease": False},
183+
]
184+
)
185+
186+
captured_url: dict[str, str] = {}
181187

182188
def fake_fetch(url: str) -> str:
183-
return mock_html
189+
captured_url["url"] = url
190+
return mock_releases
184191

185192
monkeypatch.setattr(pyversions, "fetch_url_content", fake_fetch)
186193
versions = pyversions.detect_xz_versions()
194+
assert captured_url["url"] == (
195+
"https://api.github.com/repos/tukaani-project/xz/releases"
196+
)
187197
assert isinstance(versions, list)
188198
assert "5.8.1" in versions
189199
assert "5.8.0" in versions
190200
assert "5.6.3" in versions
201+
# Draft and prerelease tags must be skipped
202+
assert "5.7.2beta" not in versions
203+
assert not any(v.startswith("5.9") for v in versions)
191204
# Verify sorting (latest first)
192205
assert versions[0] == "5.8.1"
193206

0 commit comments

Comments
 (0)