Safeguard
Supply Chain•

How do I find package installs hiding in CI scripts and Dockerfiles?

Dependencies installed by commands in CI jobs, Dockerfiles and scripts never reach a lockfile. Search for install commands and pin or move each one.

Safeguard Security Team
3 min read

Search CI workflow files, Dockerfiles, shell scripts and Makefiles for install commands such as pip install, npm install, gem install and curl piped to a shell. Any package installed by a command and not by a lockfile is invisible to a manifest based scan. Record each, pin its version, and move it into a managed file.

Why these installs are shadow dependencies

A scan of lockfiles sees what the project declares. It does not see a line in a CI job that installs a helper tool, or a Dockerfile step that adds a utility with no version. Those packages run in your build, often with access to secrets and publishing credentials, and nobody lists them.

They are also easy to add. A developer fixing a failing build adds one line and moves on.

The search

Run these on a clone, or on a mirror of all your repositories. Git grep can limit the search to files matching pathspecs and prints file names and line numbers.

git grep -nE '(pip3?|python -m pip) install' -- '*.yml' '*.yaml' '*.sh' '*Dockerfile*' 'Makefile'
git grep -nE 'npm (install|i) (-g|--global)' -- '*.yml' '*.yaml' '*.sh' '*Dockerfile*'
git grep -nE '(gem|go|cargo) install' -- '*.yml' '*.yaml' '*.sh' '*Dockerfile*'
git grep -nE 'curl .*\| *(sudo )?(ba)?sh' -- '*.yml' '*.yaml' '*.sh' '*Dockerfile*'

Add the package managers your teams actually use. Expect hits that are fine: installing from a requirements file is declared. Your target is installs with a package name typed in the command.

Sort what you find

PatternRiskFix
Install with no versionVersion can change between runsPin the version
Global install in CITool is outside the project's lockfileAdd to a managed tools file
Install from a git URLSkips registry controlsSee git URL dependencies
curl piped to shellRuns whatever the URL servesDownload, verify a checksum, then run
Install with no hash checkNo integrity guaranteeUse hash checking where the tool supports it

For Python, hash checking in Dockerfiles is covered in make pip installs in a Dockerfile repeatable with hash checking. The risk of piping a download to a shell is covered in is curl pipe to shell safe to run.

Bring them under management

  1. Put every tool a pipeline needs in a file the repo owns, with versions.
  2. Cache or mirror them in an internal registry where you can.
  3. Add a pull request check that flags new install commands naming a package.
  4. Record the tools in your inventory, with an owner, so advisories can find them.

Try it yourself

Open the pull request gate demo and watch how a policy check evaluates a change before it merges. Imagine the same check looking at a new install line.

Click through it at your own pace, or open it full screen.

Quick check

Will a lockfile scan catch these?

Usually not. A lockfile scan reads declared files, while these installs happen at run time from commands.

Are build tools in scope for a dependency policy?

Yes. They run with your credentials, so they carry supply chain risk even if they never ship.

Safeguard offers SBOM and dependency scanning, policies and gates for pull requests, and a free scan at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.