Add a hash to every download. For archives, set URL_HASH with an algorithm and digest, and CMake checks the file against it. For Git sources, pin a full commit hash instead of a branch or tag. Both make the build fail if the content differs from what you reviewed.
Why verification matters
A build that downloads source at configure time trusts the network, the host and the account that publishes the file. If any of those change the content, your next clean build includes the change. A recorded hash turns that silent change into a loud failure.
CMake's ExternalProject documentation says specifying URL_HASH is strongly recommended for URL downloads, as it ensures the integrity of the downloaded content. It also lets CMake skip contacting the remote if a local file already matches the hash.
Archive example
include(FetchContent)
FetchContent_Declare(
mylib
URL https://example.org/releases/mylib-1.4.2.tar.gz
URL_HASH SHA256=<64 hex characters>
)
FetchContent_MakeAvailable(mylib)
The page for FetchContent shows the same URL_HASH keyword. Use a strong digest such as SHA256 and take the value from the project's release announcement or compute it yourself from an archive you trust.
Git example
For Git, the equivalent of a hash is the commit hash in GIT_TAG. The documentation advises it for content you do not control, because it is more secure and confirms the contents are what you expected. See the post on pinning FetchContent to a commit hash.
Steps to adopt
- List every FetchContent_Declare and ExternalProject_Add in your tree. Search the repository for those names.
- For each URL download, add URL_HASH.
- For each Git download, replace branch and tag names with commit hashes and keep the release name in a comment.
- Add a review rule: a pull request that changes a URL or GIT_TAG must also change the hash.
- Test by corrupting a hash locally and confirming the build fails.
What a hash does not prove
A hash proves the file matches the one you recorded. It does not prove the file was safe when you recorded it. If you copy the digest from the same page that hosts the file, an attacker who changes one changes both. Prefer a digest from a separate, signed or widely mirrored source, and read the upstream changes when a library parses untrusted input.
It also does not tell you about vulnerabilities. A pinned, verified library can still have a known flaw. That is a scanning job, covered by an inventory of what you pinned.
Quick check
Is URL_HASH required by CMake?
No, it is optional, but the documentation strongly recommends it for URL downloads.
Does a matching hash mean the library is free of vulnerabilities?
No. It means the content is the content you recorded. You still need to check that version against advisories.
Safeguard's SBOM and dependency scanning can build an inventory of the components in a repository, and the free scan at gold.safeguard.sh/scan is a quick way to see what turns up.
Sources
Last reviewed: 19 October 2026.