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
| Pattern | Risk | Fix |
|---|---|---|
| Install with no version | Version can change between runs | Pin the version |
| Global install in CI | Tool is outside the project's lockfile | Add to a managed tools file |
| Install from a git URL | Skips registry controls | See git URL dependencies |
| curl piped to shell | Runs whatever the URL serves | Download, verify a checksum, then run |
| Install with no hash check | No integrity guarantee | Use 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
- Put every tool a pipeline needs in a file the repo owns, with versions.
- Cache or mirror them in an internal registry where you can.
- Add a pull request check that flags new install commands naming a package.
- 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.