Safeguard
Developer Tools•

Should CMake FetchContent pin a commit hash instead of a tag?

CMake advises a commit hash for GIT_TAG when you do not control the remote, since tags can move. How to pin sources, verify archives, and build offline.

Safeguard Security Team
3 min read

Yes. The CMake documentation advises using a commit hash for GIT_TAG rather than a branch or tag name when you do not control the remote server. A hash is more secure and lets you confirm the downloaded contents are what you expected, which matters when a firmware build fetches third-party C or C++ code.

Why a tag is weaker than a hash

A branch name moves by design. A tag usually does not, but a tag is only a label that the repository owner can reassign. A full commit hash names exact content. If the upstream project is compromised or force-pushes, a hash pin fails loudly instead of building something different.

CMake's FetchContent examples pin git sources with the full hash in GIT_TAG and keep a comment naming the release, for example # release-1.10.0. That gives you both the guarantee and a readable label.

FetchContent_Declare(
  mylib
  GIT_REPOSITORY https://example.com/mylib.git
  GIT_TAG        0123456789abcdef0123456789abcdef01234567 # release 2.4.1
)

Archives need a hash too

If you download a tarball instead, add URL_HASH so CMake checks the file after download. The docs show URL_HASH in their archive examples. Pick a strong algorithm rather than the MD5 used in the example.

Building offline

Release builds for devices often run on locked-down machines. Two options matter:

  • FETCHCONTENT_FULLY_DISCONNECTED is off by default. When on, CMake does not try to download or update anything, so the content must already exist from an earlier run or be supplied through FETCHCONTENT_SOURCE_DIR_name. The docs warn it is not an appropriate way to prevent all network access on the first run.
  • FETCHCONTENT_UPDATES_DISCONNECTED only stops the update step from contacting remotes for git and Mercurial. Downloads still happen, and switching to an unknown commit hash will fail.

A short checklist

  1. Replace every branch or tag in GIT_TAG with a full commit hash.
  2. Add URL_HASH to every archive download.
  3. Record each fetched library and hash in your SBOM.
  4. Mirror sources you depend on so a deleted upstream cannot stop a release.
  5. Review hash changes in pull requests the way you review code.

Where Safeguard fits

Safeguard provides SBOM and dependency scanning, reachability analysis, and policy gates for the source repositories and manifests it supports, and you can run a free scan at gold.safeguard.sh/scan. Check coverage for your build system before relying on it.

Try it yourself

Find a component whose source is a fetched repository and see how an exact version makes it traceable.

Click through it at your own pace, or open it full screen.

Quick check

Does pinning a hash protect me from a vulnerable library?

No. It makes the build repeatable and tamper evident. You still need to watch advisories for the pinned version and update on purpose.

What if the library has no git tags at all?

Pin the commit hash and describe the version in a comment. The hash is what carries the guarantee.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.