Join our Newsletter — 33% off our NHI Course

What should teams do first when an npm package can run preinstall scripts in CI/CD?

First, decide whether install-time scripts are allowed in the environment at all. If they are necessary, separate trusted build paths from general package consumption and restrict them to controlled pipelines. The goal is to prevent arbitrary code execution during dependency install from becoming a normal identity pathway into secrets and release credentials.

When a package can execute preinstall code, what is the first control decision?

The first decision is whether install-time scripts are allowed at all in that CI/CD environment. If they are permitted, they should be limited to trusted build paths with tightly controlled execution rather than general package consumption. That boundary matters because dependency installation can become an execution route into secrets, release credentials, and other privileged pipeline material.

Why install-time scripts change the trust model

Preinstall scripts are not just a packaging convenience, they are an execution step with the same practical effect as running code from an external source during the build. That means the install event is no longer passive dependency retrieval. It becomes a trust decision about whether unreviewed package code is allowed to run in an environment that may hold tokens, signing material, deployment credentials, or access to internal systems.

In CI/CD, the most important implication is that package installation can inherit the privileges of the pipeline runner. If that runner can reach artifact registries, cloud APIs, source control, or secret stores, a malicious script may be able to observe, exfiltrate, or misuse whatever the job can access. The relevant control question is therefore not whether the package is popular or useful, but whether the environment is prepared to absorb arbitrary code execution safely.

This is why teams should treat install-time execution as a policy boundary rather than a routine build detail. A restricted build lane can be acceptable when the package source is trusted, the dependency set is pinned and reviewed, and the runner is isolated enough that script execution cannot directly reach high-value credentials. In general consumption paths, though, the safer default is to disable or block such scripts so that dependency resolution does not implicitly grant execution authority.

How teams should separate trusted build paths from general package use

The practical split is between controlled pipelines that need to build from source and ordinary CI jobs that merely install dependencies to test, lint, or package applications. Trusted build paths can be granted narrower, documented exceptions because they have a clear business purpose and can be wrapped with stronger observation and tighter credential scope. General package consumers should not receive that same exception simply because they install from the same registry.

That separation works best when the pipeline design assumes compromise of one job should not expose everything else. Use isolated runners, minimal token scope, short-lived credentials, and distinct secret sets for build lanes versus downstream jobs. If install-time scripts are truly required, the script should run in a context with only the access it needs, not in a shared environment that also holds signing keys, publish tokens, or cloud administrative credentials.

Teams should also prefer a policy that is easy to explain and enforce. If a job class does not explicitly need package lifecycle execution, block it. If a job class does need it, document the allowed packages, the owners of the exception, and the controls that make the exception safe. That avoids the common drift where a temporary workaround becomes a standing build habit.

What changes when arbitrary code execution reaches the pipeline

The risk is not limited to a single malicious package. Once script execution becomes normal during install, attackers can aim at any dependency, any maintainer account, or any transitive package path that reaches the pipeline. The attack surface then includes secret theft, token reuse, poisoned builds, and unauthorized access to downstream systems that trust the CI/CD environment.

For that reason, dependency install should be treated as a potentially privileged operation whenever the runner can access sensitive material. Good hygiene includes narrowing secret exposure, restricting egress where possible, and ensuring the runner cannot write to release channels, sign artifacts, or publish packages unless that function is explicitly part of the job. A package that can execute code during install is only dangerous because the environment around it is too permissive.

Risk and Threat Considerations

Install-time scripts in CI/CD can turn a routine dependency event into a code-execution path with direct access to secrets, build credentials, and release trust. The main failure mode is not just malicious code in one package, it is an over-permissive pipeline that lets that code reach material privileges.

Failure mechanism: A package’s preinstall script runs in a job that already has access to tokens, cloud credentials, repository permissions, or signing material, then reads or abuses that access before the build completes.

Impact: Attackers can steal secrets, alter artifacts, publish malicious releases, or pivot from the build system into adjacent services that trust the pipeline.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

OWASP Non-Human Identity Top 10 addresses the attack and risk surface, while CIS Controls v8, NIST Zero Trust (SP 800-207), SLSA and OWASP ASVS set the governance and control requirements practitioners need to meet.

Framework Control / Reference Relevance
OWASP Non-Human Identity Top 10 NHI-02 — Secret Leakage Preinstall code can exfiltrate secrets from CI/CD runners.
NHI-05 — Overprivileged NHI Pipeline tokens and build credentials are often broader than needed.
NHI-07 — Long-Lived Secrets Long-lived build secrets increase the value of install-time script abuse.
Recommendation — Block install-time scripts in untrusted jobs and keep secrets out of those runners. Reduce pipeline credential scope before allowing any dependency script execution. Replace durable CI/CD credentials with short-lived, tightly scoped secrets.
CIS Controls v8 CIS-5 — Account Management CI/CD access and service accounts must be separated and limited for scriptable installs.
Recommendation — Separate and minimize CI/CD accounts that can reach dependency installs and release paths.
NIST Zero Trust (SP 800-207) ZT-207 — Zero Trust Architecture The answer depends on limiting trust at the build boundary.
Recommendation — Treat each pipeline job as untrusted until it proves it needs script execution.
SLSA Supply-chain Levels for Software Artifacts Build provenance and controlled build steps are central to safe script handling.
Recommendation — Use controlled, reproducible build steps for any job that permits package scripts.
OWASP ASVS V15 — Secure Coding and Architecture Build-time code execution policy is an architecture and trust-boundary issue.
Recommendation — Design CI/CD so dependency scripts cannot reach sensitive build assets by default.

Practitioner Guidance

What to prioritise: Decide first whether install-time scripts are allowed by job class, not package by package. If the answer is “sometimes,” formalise that exception as a controlled build path rather than leaving it to developer discretion.

What to verify: Confirm that jobs which permit script execution run with tightly scoped credentials, isolated runners, and no unnecessary access to release, signing, or production secrets. If the same runner also consumes untrusted dependencies, assume the blast radius is too broad.

Common mistake: Teams often block postinstall in production but leave preinstall open in shared CI, which still allows arbitrary code execution before the security boundary is applied.

Practitioner takeaway: Treat package-install scripts as an execution privilege, not a build feature, and only permit them where the pipeline has been deliberately engineered so that compromise cannot become secret exposure or release abuse.