EresusSecurity
Back to Research
Guide

CI/CD Build Script Security: postinstall and build.rs Risks

August 26, 2026
4 min read
GuideSoftware Supply Chain

In the ArrayRef attack, the payload executed during cargo build. In the axios attack, it executed during npm install. The common thread: in both ecosystems, package managers run code you did not write, under your identity, at install and build stages. This guide maps the build-time execution surface ecosystem by ecosystem and ties it to auditable controls.

Why build scripts are a distinct security problem

Dependency code runs at two different times, and the risks differ:

  • Import/runtime: Runs when your code calls a dependency's function. Static analysis, SCA tools, and code review see this surface.
  • Build/install time: Runs the moment a package is downloaded — before any function is called. It bypasses code review, is not covered by application tests, and typically executes with full user privileges on a developer machine or CI runner.

For attackers, the second surface is more valuable: an execution point inside a secret-rich environment, with network access and weak logging.

The execution surface by ecosystem

npm / Node.js

Mechanism When it runs Notes
preinstall / postinstall / prepare During npm install Lifecycle scripts of transitive dependencies also run
node-gyp rebuild Native module installs The compile toolchain itself is an attack surface
Lifecycle script env vars During install npm_config_* injection is a known vector

Control: setting npm config set ignore-scripts true locally is a reasonable default, but teams doing so must manage an allowlist for packages that genuinely need scripts.

Cargo / Rust

Mechanism When it runs Notes
build.rs cargo build, cargo check, IDE indexing The ArrayRef payload lived here
build-dependencies Compiled before build.rs A build-dependency containing network crates (ureq, reqwest) is a red flag
proc-macros Generate code during compilation A malicious proc-macro can evade static analysis

Control: automatically flag new dependencies containing a build.rs in PR diffs; give special review to network crates appearing under [build-dependencies].

Python / Java / Go

  • Python: arbitrary execution via setup.py/pip install is still possible; prefer wheels and use --require-hashes.
  • Maven/Gradle: plugins execute code within the build lifecycle; pin plugin versions.
  • Go: go generate and cgo compilation; restrict egress beyond the module proxy.

Concrete audit steps

1. Map your current surface

# npm: which lifecycle scripts are defined?
rg -n '"(pre)?(post)?install"|"prepare"' --glob 'package.json' node_modules -g '!**/.bin/**' | head -40

# Cargo: which packages ship build.rs, and what do they build-depend on?
fd -H 'build.rs' ~/.cargo/registry/src | head
rg -n '\[build-dependencies\]' --glob 'Cargo.toml' ~/.cargo/registry/src -l | head

2. Gate new build scripts at the CI door

Turn these diff patterns into automatic warnings on dependency-bot PRs:

  • a new "postinstall" hook or build.rs file,
  • a new networking library under [build-dependencies],
  • network/exec API transitions inside an existing build script (require('http'), Command::new, urllib, fetch).

3. Harden the build environment

  • Build on ephemeral runners; never feed workspace caches from untrusted PRs.
  • Enforce registry/mirror egress allowlists (for example, only registry.npmjs.org and your internal mirror).
  • Separate signing and deployment credentials from build steps; use OIDC-based short-lived tokens.

4. Permanent control after incidents

In incidents like ArrayRef, finding the malicious version in a lockfile is not enough — treat everything that ran in that environment as compromised. Our ArrayRef analysis and axios breakdown cover real-world versions of this chain.

How Eresus approaches this

Eresus Security reviews the build-time execution surface across three layers together: repository, CI configuration, and runner isolation. Our source code analysis work — including dependency security and CI/CD pipeline security reviews — delivers these controls adapted to your pipeline with evidence.

Frequently asked questions

Is ignore-scripts sufficient protection?

No. Native modules and some popular packages cannot install without lifecycle scripts, allowlist maintenance degrades over time, and the policy quietly gets relaxed. ignore-scripts is meaningful only alongside mirrors, quarantine, and diff review.

Doesn't SCA scanning catch build.rs risks?

Most classic SCA tools check known-vulnerability databases; a brand-new malicious package (like ArrayRef) will not be listed yet. Behavior-focused checks are required: new build scripts, unexpected build-dependencies, network access from build stages.

Isn't a private registry enough?

A mirror narrows the attack window but does not stop a compromised legitimate package. Mirrors deliver value when combined with minimum release age policies and automated behavioral scanning.

AI Security Starter Training

Request a practical checklist for prompt injection, RAG data leakage, MCP risks, and model-file security before launch.

Prompt injection and guardrail bypass checks.
RAG data leakage and permission-boundary review.
MCP identity, transport, and command-risk controls.

No spam. Used only to send the resource and related security notes.

Related Research

Related Services

Scope Estimator

Get a rough engagement size before the scoping call.

Estimated engagement

5–7 days

Request exact scope