The language famous for eliminating memory bugs at compile time has the most insecure CI/CD pipelines in our dataset. Your Rust code is memory-safe. Your Rust CI is not.
The Setup
When we scanned GitHub’s top 50K repos for CI/CD vulnerabilities, we expected the vulnerability distribution to be roughly uniform across programming languages. It isn’t. The spread from top to bottom is over 3x - from Rust at 77.9% to JavaScript at 23.6%. Language choice doesn’t cause CI/CD vulnerabilities, but it strongly correlates with them. Understanding why tells us something fundamental about where supply chain risk concentrates.
The Full Matrix

| Language | Repos | Vuln Repos | Vuln Rate |
|---|---|---|---|
| Rust | 2,903 | 2,261 | 77.9% |
| Elixir | 278 | 187 | 67.3% |
| TypeScript | 4,338 | 2,675 | 61.7% |
| Haskell | 221 | 121 | 54.8% |
| PHP | 2,508 | 1,373 | 54.7% |
| Scala | 380 | 207 | 54.5% |
| Go | 3,878 | 2,088 | 53.8% |
| Dart | 858 | 453 | 52.8% |
| Ruby | 1,954 | 1,000 | 51.2% |
| Kotlin | 1,716 | 832 | 48.5% |
| C++ | 3,561 | 1,471 | 41.3% |
| Python | 4,810 | 1,900 | 39.5% |
| C# | 2,759 | 994 | 36.0% |
| C | 3,147 | 1,009 | 32.1% |
| JavaScript | 4,460 | 1,064 | 23.6% |
This data comes from 2,903 Rust repos and 4,460 JavaScript repos - large enough samples that the difference isn’t noise. The Rust sample tripled from our initial 1,042-repo checkpoint to the full 2,903 across the 50K scan, and Rust held its position at number one. The pattern is real.
The Rust Paradox
Rust’s headline is designed to provoke - and it should. The language that prevents use-after-free, buffer overflows, and data races at compile time has the worst CI/CD security posture in our dataset. Nearly four out of five Rust repos have at least one vulnerable workflow.
The paradox resolves when you look at what Rust’s build toolchain requires:
Cross-compilation complexity. Rust’s value proposition includes zero-cost abstractions across platforms - but cross-compiling for Linux, macOS, Windows, ARM, and WASM requires multi-step CI configurations with platform-specific actions. Each target in a build matrix adds action dependencies.
Toolchain management. Most Rust repos depend on dtolnay/rust-toolchain for compiler setup. As we detail in The Software Supply Chain Crisis, rust-toolchain is a single-maintainer action with 989 unpinned repos and 44 unique mutable refs - including 201 repos pinned to @master. This single dependency is the largest contributor to Rust’s vulnerability rate.
Cache management. Rust builds are slow. To compensate, most repos use Swatinem/rust-cache - another single-maintainer action - to cache compiled artifacts. That’s a second single-person dependency in nearly every Rust CI pipeline.
Release pipeline complexity. Rust projects typically build and publish binaries for multiple platforms, using actions like cargo-dist, cargo-release, and various cross-compilation tools. Each one adds to the dependency chain.
The result: a typical Rust CI workflow has 3-5 third-party action dependencies before it even runs cargo test. Each dependency is another supply chain trust decision that nobody is SHA-pinning.
The irony is structural: the language that eliminates entire classes of runtime bugs through its type system has its trust model end at the compiler binary. Everything about how that compiler gets into the CI environment - which action downloads it, which version, from whose repository - operates on implicit trust.
Why JavaScript Is Lowest
JavaScript’s 23.6% vulnerability rate is the mirror image of Rust’s 77.9%. The explanation is similarly structural:
Simple CI pipelines. A typical JavaScript/Node.js CI workflow is three lines: actions/checkout, actions/setup-node, and npm test. Both actions/checkout and actions/setup-node are first-party GitHub actions with broad adoption and often SHA-pinned. The third-party dependency count is minimal.
Mature ecosystem tooling. NPM, Yarn, and pnpm handle dependency management, building, and publishing without requiring third-party CI actions. The build toolchain lives in the package manager, not in the workflow file.
Lower CI/CD ambition. JavaScript repos less frequently attempt multi-platform builds, binary distribution, or complex release automation in their GitHub Actions workflows. Less complexity means less attack surface.
JavaScript’s low vulnerability rate doesn’t mean JavaScript projects are more secure overall - they face their own supply chain risks through npm package dependencies. But at the CI/CD pipeline level, simpler workflows mean fewer action dependencies, and fewer dependencies mean fewer findings.
The Middle Ground - Correlation, Not Causation
It’s important to be precise about what this data shows. The language itself doesn’t cause CI/CD vulnerabilities. The CI/CD complexity that each language ecosystem demands is the causal factor.
Languages cluster by CI complexity:
High complexity (50%+ vuln rate): Rust, Elixir, TypeScript, Haskell, PHP, Scala, Go, Dart, Ruby. These languages share characteristics: complex build toolchains, cross-compilation needs, or heavy reliance on third-party CI actions for setup and deployment.
Medium complexity (35-50%): Kotlin, C++, Python. Moderate CI needs - some third-party action usage, but less platform-specific complexity than the top tier.
Low complexity (under 35%): C#, C, JavaScript. Simpler CI pipelines with fewer third-party dependencies.
The pattern holds across the dataset: languages whose ecosystems drive developers toward complex, multi-action CI workflows have higher vulnerability rates. Languages where a basic CI pipeline requires minimal third-party actions have lower rates.
PHP and Ruby - The Setup Action Trap
PHP at 54.7% and Ruby at 51.2% share a common pattern: their CI vulnerability rates are driven largely by one action each.
PHP repos rely on shivammathur/setup-php - a single-maintainer action used by 1,147 repos in our dataset. It’s THE PHP setup action. There’s no meaningful alternative. If you’re running PHP CI on GitHub Actions, you’re almost certainly using this action, and you’re almost certainly using it with a mutable version tag.
This action is maintained by a single account. 1,147 repos - spanning major CMS platforms, e-commerce frameworks, and API tooling - depend on one person’s GitHub account to deliver their PHP build environment. This is the single-maintainer problem concentrated in a single language ecosystem.
Ruby repos show a similar pattern with ruby/setup-ruby at 1,275 repos. The difference: ruby/setup-ruby is maintained by the Ruby GitHub organization, not an individual - so the fragility risk is lower. But the concentration risk remains. Over a thousand repos depend on the same action, overwhelmingly unpinned.
Both ecosystems also have secondary dependencies. PHP repos commonly pair setup-php with codecov/codecov-action for coverage reporting. Ruby repos pair setup-ruby with various gem publishing and test reporting actions. Each additional action extends the chain.
Go - The Container Builder
Go at 53.8% vulnerability rate sits squarely in the “high complexity” tier, driven by two factors:
Go’s compilation model produces static binaries, which encourages multi-platform binary distribution. Go CI workflows frequently build for Linux (amd64, arm64), macOS (amd64, arm64), and Windows - each target potentially using different actions for setup, build, and release. The release pipeline often involves goreleaser (a popular release automation tool) and Docker actions for container packaging.
Go repos are among the heaviest users of Docker actions - login, buildx, build-push - because Go’s compiled binaries are commonly packaged as container images. This means Go repos inherit Docker’s concentration risk on top of their own language-specific dependencies.
The Go ecosystem doesn’t have a single dominant setup action like Rust’s rust-toolchain or PHP’s setup-php - actions/setup-go is first-party and commonly SHA-pinned. But the release and distribution phase adds complexity that drives the overall rate above 50%.
The Elixir Surprise
Elixir at 67.3% - second only to Rust - is the less-obvious finding. Elixir’s sample is smaller (278 repos), but the rate is striking for a language not typically associated with complex CI/CD.
The explanation maps to the same pattern: Elixir’s Mix-based build system drives complex CI configurations with Erlang/OTP version management, multiple release targets, and Phoenix-specific deployment workflows. The Elixir community is small but passionate about automation, and that automation means more action dependencies.
Haskell at 54.8% follows the same logic - cabal and stack toolchains require specific CI setup actions, and the Haskell CI ecosystem has fewer maintained options, concentrating dependencies.
The Trust Paradox by Language
The Trust Paradox - where the most trusted organizations have the most exposed pipelines - manifests differently across language ecosystems.
Rust repos from major organizations have even higher vulnerability rates than the Rust average, because organizational Rust projects tend to have the most complex CI - cross-compiling for embedded targets, running Miri and Clippy in CI, building for WebAssembly alongside native targets.
Python repos from major organizations show a different pattern: lower overall vulnerability rates (39.5%) but higher rates of critical findings, because Python CI often involves deployment workflows with cloud credential access.
The language risk matrix isn’t just about frequency - it’s about the type of exposure. Rust’s 77.9% is mostly medium-severity unpinned actions. Some languages with lower overall rates have higher concentrations of critical taint-to-execution chains.
The AGPL Paradox
License choice tells a parallel story:
| License | Vuln Rate |
|---|---|
| AGPL-3.0 | 65.5% |
| Apache-2.0 | 49.1% |
| GPL-3.0 | 46.8% |
| MIT | 42.0% |
| BSD-2-Clause | 35.0% |
AGPL repos - the most restrictive about code freedom - have the highest CI/CD vulnerability rate. BSD-2-Clause - the most permissive - has the lowest. The same structural explanation applies: AGPL repos tend to be self-hosted platforms with elaborate multi-stage build and deployment pipelines (think Git hosting platforms, low-code tools, workflow automation). Simpler MIT-licensed libraries have simpler CI.
The correlation between project complexity, CI complexity, and CI vulnerability is the throughline across both the language and license analyses.
The Popularity Penalty - Amplified by Language
The Popularity Penalty - where more starred repos have higher vulnerability rates - is amplified when viewed through the language lens.
High-star Rust repos (10K+ stars) have even higher vulnerability rates than the Rust average. These are the foundational tools - Tokio, Serde, Clap, and their peers - with the most complex CI matrices targeting the most platforms. The repos the Rust ecosystem depends on most are the most exposed.
The same pattern holds for TypeScript: popular TypeScript frameworks with 50K+ stars have vulnerability rates approaching 70%, driven by complex build, test, and release automation across npm, CDN distribution, and documentation deployment.
The Popularity Penalty and the Language Risk Matrix compound: a high-star Rust repo is in the highest-risk position in the dataset, hit by both the complexity of Rust’s toolchain and the complexity that popularity drives.
C and C++ - Deceptively Low
C at 32.1% and C++ at 41.3% are lower than you might expect for languages with complex build toolchains. The explanation: many C and C++ projects predate GitHub Actions and use traditional CI systems (Jenkins, Travis CI, self-hosted runners) instead of, or alongside, GitHub Actions workflows.
C and C++ repos that do use GitHub Actions tend to have simpler workflow configurations - often just a single workflow running make or cmake with actions/checkout. The complexity lives in the build system (Makefiles, CMake), not in the workflow file. This shifts the attack surface from the CI pipeline to the build toolchain itself - a different risk that Runner Guard doesn’t measure.
The implication: C and C++ vulnerability rates may be understated relative to their actual CI/CD risk, because the risk lives in build systems that GitHub Actions workflow scanning doesn’t cover.
The Clean Repos - What They’re Doing Right
Approximately 29,747 repos in our dataset have zero findings. What distinguishes them?
JavaScript and Python lead the clean repos - reinforcing the “simpler CI is safer” thesis. But the clean repos aren’t exclusively simple projects. Some are complex projects that have invested in CI/CD security: SHA-pinning their actions, scoping their permissions, using first-party GitHub actions where possible.
The common patterns among clean repos: - Minimal third-party action usage - first-party GitHub actions (actions/*) where possible - SHA-pinned references where third-party actions are necessary - Scoped permissions (permissions: read-all or explicit per-job grants) - Simple workflow structures without complex matrix builds or multi-step release pipelines
These aren’t impossible standards. They’re the baseline that the Fix It guide walks through.
What You Can Do About It
-
Know your language’s risk profile. If you’re writing Rust, Go, or TypeScript, your CI/CD is statistically more likely to have supply chain vulnerabilities. That’s not a reason to change languages - it’s a reason to scan more frequently.
-
Audit your toolchain actions. Each language ecosystem has its dominant setup actions - dtolnay/rust-toolchain for Rust, shivammathur/setup-php for PHP, ruby/setup-ruby for Ruby. Identify yours and SHA-pin them.
-
Simplify where possible. Not every build matrix needs 12 targets. Not every release needs 5 platforms on day one. Complexity is the driver - reducing it reduces attack surface.
-
Watch single-maintainer dependencies. The Rust ecosystem’s concentration on
rust-toolchainandrust-cacheis a specific, addressable risk. PHP’s concentration onsetup-phpis another. Know which single-maintainer actions control your build toolchain. -
Run Runner Guard against your language’s starter templates. If you’re creating new repos from templates, those templates probably have unpinned actions. Fix the template, and every new repo starts secure.
The language risk matrix isn’t a ranking of “good” and “bad” languages. It’s a map of where CI/CD complexity concentrates. The most complex build ecosystems need the most attention - and right now, they’re getting the least.
Every language community has its own version of this conversation to have. Rust needs to address its rust-toolchain/rust-cache dependency concentration. PHP needs to address its setup-php single-point-of-failure. TypeScript needs to address its release pipeline complexity. JavaScript is closest to the right model - simple CI that minimizes third-party dependencies - but even JavaScript isn’t immune, as npm supply chain attacks have shown in a different layer of the stack.
Related Articles
Scan your repos today. Runner Guard is Vigilant’s free, open-source CI/CD security scanner - the same tool that powered this research. Install it in under a minute:
brew install Vigilant-LLC/tap/runner-guard
runner-guard scan github.com/owner/repo
14 security rules. Zero configuration. One command.
The detection capabilities described above are active across Vigilant client environments today. If your organization wants to assess its current exposure to this attack chain — or understand how our managed services align to your specific environment — contact your Vigilant account team or reach us at vigilantdefense.com.
This event reinforces what Vigilant has long asserted:
Nation-state adversaries are not probing our networks — they are preparing battlefields.
Stay alert, stay aggressive, stay Vigilant,
Chris Nyhuis
CEO, Vigilant
Vigilant, 7570 Bales Street
Suite 250, West Chester
Ohio 45069, United States
855-238-4445
Background
CEO of Vigilant, a global cybersecurity firm he has led for 16 years. 30+ years of experience across offensive security, SCADA/IoT, and critical infrastructure defense. Holds multiple patents including Forensically Validated Detection Systems and Secure Protocol Translation. Former instructor at a US intelligence school. Certified human trafficking investigator and OSINT practitioner. Vigilant dedicates 25% of profits to combating human trafficking, child exploitation, and supporting orphan care worldwide.