With find_package, CMake uses a library already installed on the machine, so whoever installs it (an OS package, a package manager, your base image) patches it. With FetchContent, CMake downloads and builds source inside your project, so you own every update. Choose by deciding who should carry the patch burden.
The two approaches side by side
| Approach | Where the library comes from | Who patches it |
|---|---|---|
| find_package | Already installed: OS package, package manager, prebuilt | Whoever maintains that installation |
| FetchContent | Source fetched at configure time and built with your project | You, by changing the pinned reference |
| Both (CMake 3.24+) | find_package first, FetchContent if not found | Depends on which path the build took |
CMake's guide on using dependencies says projects should generally prefer prebuilt packages found with find_package, and use FetchContent to build from source when you need control over versions and patches, at the cost of build time.
What this means for security
A system library receives fixes through the channel that supplies it. If that channel is your operating system, an update there fixes every program that links the library, though programs linked statically still need rebuilding.
A fetched library is frozen at the reference in your CMake file. Nothing updates it unless someone edits the file. Pinned code is predictable, but it ages.
Mixing them with FIND_PACKAGE_ARGS
From CMake 3.24, adding FIND_PACKAGE_ARGS to a FetchContent declaration makes FetchContent_MakeAvailable try find_package first and build from source only if that fails. Two cautions from the documentation:
- Both paths must define the same namespaced targets, or your own CMake code will break on one of them.
- Do not add REQUIRED to those arguments. The page says it would not normally be appropriate.
The result is that the same project may use a patched system copy on one machine and a pinned source copy on another. Record which one each build used, for example in the build log or your SBOM, or you cannot answer "which version shipped?".
A decision guide
- Is the library installed and patched by a channel you trust on every target? Prefer find_package.
- Do you need a specific version, a local patch, or a platform without a package? Use FetchContent with a pinned hash.
- Do you need both? Use FIND_PACKAGE_ARGS, and log the outcome.
- Do you ship a container? The base image becomes the channel. Rebuild it on a schedule.
Try it yourself
Open the demo and look at how an inventory shows both direct and transitive components. Ask which of your C++ libraries would be listed and where each came from.
Click through it at your own pace, or open it full screen.
Quick check
Is FetchContent less secure than find_package?
Not by itself. It shifts the update duty to you. With a pinned hash and a schedule it is fine. Without them it ages.
Can one project use both?
Yes, with FIND_PACKAGE_ARGS on CMake 3.24 or later. Make sure both paths define the same targets.
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.