Securing CI/CD workflows: setup actions versus manual curl and mise-en-place
An engineering analysis of tool installation strategies in GitHub Actions, comparing third-party setup actions, manual curl scripts, and universal tool managers on security, maintenance, and speed.…
An engineering analysis of tool installation strategies in GitHub Actions, comparing third-party setup actions, manual curl scripts, and universal tool managers on security, maintenance, and speed.
The verdict up front
For engineering teams prioritizing security and local-to-CI parity, we recommend using a declarative tool manager like mise-en-place pinned by commit SHA. This approach centralizes tool versions in a single configuration file, enforces checksum verification, and eliminates the sprawl of third-party GitHub Actions. You should skip specialized, community-maintained install-* actions entirely. They introduce unnecessary supply chain risk. If you only need a single tool, a manual curl script with hardcoded SHA-256 verification is a viable, zero-dependency alternative.
Methodology
This analysis is prompted by a community query on r/devops by user Juloblairot regarding strategies to reduce third-party supply chain exposure in GitHub Actions. We evaluate three distinct architectures: specialized setup actions, raw shell scripts (curl/wget), and declarative tool managers (specifically mise-en-place). Our evaluation criteria focus on three dimensions: security exposure (attack surface, dependency tree, and hash verification), maintenance overhead (handling architecture detection, caching, and version updates), and execution speed. This review draws on the architectural designs of these installation methods and established security hardening guidelines from the OpenSSF Scorecard. Independent performance benchmarks on runner startup times are pending.
Three paths to tool installation
The most common way to install tools in GitHub Actions is through dedicated actions, such as hashicorp/setup-terraform or community-developed actions like install-poetry. These actions run Node.js or Docker code inside the runner, modifying the environment and path variables automatically.
The second approach relies on manual shell commands. Developers write standard run blocks using curl or wget to fetch binaries directly from release pages, manually verifying hashes and updating the $GITHUB_PATH variable.
The third approach uses universal tool managers like mise-en-place (written in Rust) or aqua (written in Go). These tools read a local configuration file (such as .mise.toml) and install verified binaries. This requires bootstrapping the tool manager itself, but once installed, it handles all other dependencies.
Security exposure versus maintenance overhead
Many official setup-* actions still pull dynamic payloads at runtime. Pinning the action to a commit SHA prevents the action code itself from being tampered with, but if the action's internal code fetches unverified binaries from a CDN, the supply chain risk remains. Community-maintained actions are rarely audited and present a significant attack vector if the maintainer's account is compromised.
Writing custom shell scripts to download, extract, verify SHA-256 checksums, and cache binaries across multiple operating systems is a recipe for broken builds. It introduces silent failures when upstream CDNs change their URL structures, requiring constant developer maintenance.
Universal tool managers solve the local-to-CI parity problem. Developers run the exact same binary versions locally as the CI runner does, configured via a single checked-in file. By bootstrapping mise via a single, SHA-pinned official action or a direct, checksum-verified curl, you reduce your GitHub Actions dependency footprint to exactly one tool.
Pricing
All discussed methods (GitHub Actions, curl, and mise-en-place) are open-source and free to use. GitHub Actions usage is billed under standard GitHub runner minute pricing. As of May 2026, free tiers include 2,000 minutes per month for public repositories.
Verdict
If you run a multi-language repository or have more than three external CLI dependencies, migrate to mise-en-place. It eliminates action sprawl and ensures local-CI parity. If you only need a single tool and want zero external dependencies, use a manual curl script with hardcoded SHA-256 verification. Avoid community-maintained install-* actions entirely; their security posture is rarely audited, and they represent unneeded supply-chain risk.
What we would test next
We plan to benchmark the cold-start latency of bootstrapping mise-en-place versus caching individual setup-* actions. We also want to measure the failure rate of manual curl scripts when upstream CDNs experience transient network issues.
The investor read
Software supply chain security is a major enterprise spend driver. Tools like Chainguard, Aqua Security, and open-source alternatives like mise-en-place or aqua CLI are gaining traction because they consolidate the runtime environment. For investors, this signals that the market for fragmented, single-purpose CI integrations is decaying. Enterprise buyers want consolidated, declarative, and auditable toolchains. Startups building in the CI security space must target the local-to-production configuration layer rather than just scanning static YAML files.
Pull quote: “Universal tool managers solve the local-to-CI parity problem.”
Every claim ties to a primary source. See our methodology.