Scanning after install can be too late because packages may run code while installing. If a malicious package executes during install, a later scan only reports what already happened. For that threat the control has to act before download or before install, through a proxy, an allowlist or disabled install scripts.
What the documentation says
npm's configuration reference describes an option, ignore-scripts, which when true stops npm running scripts specified in package.json files. Its default is false, so scripts run by default. pip's secure installs page says that by default pip performs no checks against remote tampering and involves running arbitrary code from distributions. It names two ways to tighten that: hash checking mode, and disallowing source distributions with an option that accepts only binary packages.
You do not need a specific incident to see the timeline problem. The sequence is simple: resolve, download, install (code may run), then your scan. Anything that happens in the third step is already done.
Two threat types, two controls
| Threat | When it bites | Best control point |
|---|---|---|
| Known vulnerable version | When the vulnerable code runs in your product | Scan on pull request, build and schedule |
| Malicious package with install time code | At install, on whichever machine installs it | Before download or install |
| Tampered package in transit | At download | Hash checking, signatures |
| Typosquat or lookalike name | At the moment someone types the name | IDE hint, proxy or allowlist rule |
A vulnerability scan is the right tool for the first row. It is the wrong tool for the second.
Controls that act before install
- A registry proxy or approved package list. The decision happens at request time. See approved package allowlist or scan on install.
- Disable install scripts by default in CI and on build machines, and enable them per package where truly needed. Test your builds, since some packages rely on them.
- Hash and pin. With pip, hash checking mode requires every requirement to be pinned and hashed, which blocks silent substitution.
- Minimum release age. Holding very new versions for a short period gives the community time to flag bad ones. See minimum release age policies.
- Isolated runners. Run installs in ephemeral environments with no long lived secrets, so any code that does run has little to take.
What still belongs after install
Do not drop post install scanning. It finds known vulnerabilities, licence issues and drift. It just cannot be your only answer to malicious code, because it is not designed to run in time.
A sensible order
Start with ephemeral, low privilege CI runners and a decision on install scripts. Add a proxy or allowlist as the next step. Keep the scanning stages you already have.
Where Safeguard fits
Safeguard provides dependency scanning, SBOM generation and gates for the known vulnerability side, and policies you can apply to what your pipelines accept. Try a free scan at gold.safeguard.sh/scan.
Quick check
Does disabling install scripts break builds?
Some packages need them, for example to build native code. Disable by default, then allow the specific packages that require it, and test.
Is a vulnerability scan useless against malicious packages?
Not useless once a package is reported, but it relies on known data and runs after install. Pair it with a pre install control.
Sources
- npm Docs, config: ignore-scripts
- pip documentation, Secure installs
Last reviewed: 19 October 2026.