From 3f397f5f7807b3dbdfbfb8cbc08a7c85a316c851 Mon Sep 17 00:00:00 2001 From: Beat Hangartner Date: Sat, 19 Sep 2026 23:36:20 +0200 Subject: [PATCH] Explain why fetching by git tag is a supply chain risk The pinning comment stated that a tag is mutable but not what an attacker does with that, so the trade-off was hard to judge for anyone reviewing or later undoing the pins. Spell out the mechanism: a tag is a name pointing at a commit, anyone with push access upstream can force-push it elsewhere, and FetchContent resolves it at build time, so a stolen maintainer account or CI token makes every fresh build compile the attacker's code while nothing changes here and the tag name still reads correctly. A commit hash is derived from the content and cannot be moved that way. Name the two cases where this was actually exploited: tj-actions/changed- files in March 2025 (CVE-2025-30066), where tags v1 through v45.0.7 were retargeted to a commit leaking CI secrets into build logs across more than 23,000 repositories, and aquasecurity/trivy-action in March 2026 (CVE-2026-33634), where 76 of 77 version tags were force-pushed to a credential stealer for about twelve hours. Both were GitHub Actions rather than CMake dependencies, which the comment says, because the point is the shared mechanism of resolving a tag at build time. Also document how to upgrade a pin, including that git ls-remote reports the tag object for an annotated tag and the commit on the "^{}" line. The note lives in fetch_pugixml.cmake, which fetch_kdeaddons.cmake and fetch_singleapplication.cmake already refer to. Comments only; no build behaviour changes. Co-Authored-By: Claude Opus 5 (1M context) --- cmake/fetch_pugixml.cmake | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/cmake/fetch_pugixml.cmake b/cmake/fetch_pugixml.cmake index 11bf33872..6461701bd 100644 --- a/cmake/fetch_pugixml.cmake +++ b/cmake/fetch_pugixml.cmake @@ -22,9 +22,32 @@ option(BUILD_PUGIXML "Build pugixml library, use system one otherwise" YES) if(BUILD_PUGIXML) - # Pinned to the commit v1.15 points at, not to the tag itself: a tag is a - # mutable pointer that the upstream owner can move, so fetching by tag means - # a future build can silently get different code than this one did. + # Pinned to the commit v1.15 points at, not to the tag itself. + # + # A git tag is only a named pointer to a commit, and anyone with push access + # to the upstream repository can move it (git push --force) to any other + # commit. FetchContent fetches whatever the tag points at when the build + # runs, so if a maintainer account or CI token is compromised, the attacker + # can retarget a well-known release tag to malicious code: every fresh build + # of QElectroTech then compiles it, while nothing changes in this repository + # and the tag name still looks correct. A commit hash cannot be moved, because + # it is derived from the content: different code always has a different hash. + # + # This attack has been used in the wild: + # - March 2025, tj-actions/changed-files (CVE-2025-30066): tags v1 through + # v45.0.7 were retargeted to a commit that dumped CI secrets into build + # logs, affecting more than 23,000 repositories. + # - March 2026, aquasecurity/trivy-action (CVE-2026-33634): 76 of 77 + # version tags were force-pushed to a credential stealer and stayed + # malicious for about 12 hours. + # Both were GitHub Actions rather than CMake dependencies, but the mechanism + # is the same one FetchContent relies on here: resolving a git tag at build + # time. + # + # To upgrade, look up the new tag's commit with git ls-remote + # (for an annotated tag, take the hash on the "^{}" line, which is the + # commit; the other line is the tag object), check that it is the release you + # expect, and update both the hash and the trailing tag comment. FetchContent_Declare( pugixml GIT_REPOSITORY https://github.com/zeux/pugixml.git