Software Securityhome
Fix a findingSearch
Phase 3 · Development

Dependency selection

Dependency selection is the check you make before adding an open source package: whether you need it, whether it is the real package, who maintains and publishes it, and what it runs when installed. The decision is recorded in the pull request, and the exact version is pinned in a lockfile.

Every package you add runs with your application's privileges, and some run code on developer machines and CI runners as soon as they install. Adding is the cheapest point to say no. In September 2025 the Shai-Hulud worm used stolen npm credentials to publish malicious versions of over 500 packages. Vulnerability scanners later report known CVEs; they cannot tell you a package was the wrong choice.

Who
The developer adding the package, with a reviewer or code owner approving every change to manifests and lockfiles
When
Before a new direct dependency or a new major version is merged
How often
On every pull request that adds a dependency, and again when a package's maintainers, publishers or provenance change
You end up with
A short intake note in the pull request, a lockfile entry with an integrity hash, and a named owner for updates

Do this

  1. Ask whether you need it

    Check whether the standard library, your framework or a package you already use does the job. The OpenSSF Concise Guide for Evaluating Open Source Software starts here, and warns that every new dependency is a potential support problem or supply chain attack. Look at what the package pulls in too, and at whether development-only packages end up in production.

  2. Confirm it is the package you meant

    Check the exact name on the registry, look for a more popular look-alike that would signal typosquatting, and confirm the project it claims to come from. Take extra care with names an AI assistant suggested: a USENIX Security 2025 study of 576,000 generated code samples found that models suggest packages that do not exist, and an attacker can register such a name.

  3. Check maintenance and security practices

    Look for commits and a release in the last 12 months, more than one maintainer, instructions for reporting vulnerabilities, and no long-known unfixed vulnerabilities in the version you will use. Read the individual OpenSSF Scorecard checks on deps.dev, such as Code-Review, Maintained and Dangerous-Workflow, rather than the total. OSV dependency lookup checks exact versions against OSV.

  4. Check who publishes it and what runs at install

    List the publishers, for example with npm view <package> maintainers, and check whether the version has provenance that links it to a source commit, as deps.dev shows for npm packages. Read any preinstall, install or postinstall script before allowing it: npm 12 blocks dependency install scripts by default until you approve them, and pnpm runs only builds listed in allowBuilds.

  5. Pin it in a committed lockfile

    Commit the lockfile with the manifest and install from it in CI. npm ci exits with an error if package.json and the lockfile disagree and never rewrites the lockfile; pip's hash-checking mode requires every requirement pinned with == and a hash for every dependency. Review the lockfile diff in the pull request, because that is where unexpected transitive packages show up.

  6. Install only from the registry you intend

    Give each internal package name one source: an npm scope mapped to your registry (@acme:registry=...), a single pip index that proxies PyPI instead of --extra-index-url, a uv index marked explicit = true, or NuGet packageSourceMapping. Otherwise a public package with the same name and a higher version can win. OWASP ASVS 5.0 V15.2.4 (level 3) requires this.

  7. Record the decision and an owner

    Write the intake in the pull request: why this package, the exact version, what you checked, any gap you accepted and who owns its updates. Route manifest and lockfile changes to a code owner, and reopen the decision when maintainers or publishers change, a release loses provenance its predecessors had, or a new major version adds native code or network access.

Done when

New dependencies are reviewed in the pull request, and the lockfile pins every one.

Mark it in the gap check

Common mistakes

  • Choosing by stars, downloads or one score. A Scorecard total combines unrelated checks; Scorecard itself says a low score is not proof of risk, so read the checks that matter for how you will use the package.
  • Treating the repository link on the registry page as proof of origin. It is often unverified metadata; only provenance binds a published version to a source commit.
  • Reaching internal packages with pip's --extra-index-url. pip searches every index together and installs the highest version, so a public look-alike with a higher version number wins.
  • Installing an AI-suggested package without checking that it exists and is the one you meant, which is exactly the name an attacker may have registered.
  • Approving a package once and never looking again. A new maintainer or owner can turn trusted code hostile, as in Case study: event-stream and ua-parser-js, two npm package takeovers and Case study: the polyfill.io CDN takeover (2024).

What each signal tells you, and what it does not

No single signal decides intake. Collect several, record them in the pull request, and weigh them against how the package will be used: a parser for untrusted uploads deserves more scrutiny than a development formatter.

What each signal tells you, and what it does not: table
SignalWhere to lookWhat it does not prove
MaintenanceCommits and a release in the last 12 months; the Scorecard Maintained checkA quiet project may be stable or abandoned; activity alone cannot tell
Maintainers and publishersMore than one maintainer, ideally from different organizations; npm view <package> maintainersA newly added maintainer can mean better continuity or a takeover
ProvenanceVerified SLSA provenance on deps.dev or the registryThat the code is safe; only which source and build produced the package
Scorecard checksdeps.dev or the Scorecard API: Code-Review, Maintained, Dangerous-Workflow, Signed-ReleasesA guarantee: checks see only what they can detect automatically
Known vulnerabilitiesOSV, through deps.dev or OSV dependency lookupAnything about flaws nobody has reported yet
Install scriptspreinstall, install and postinstall in the package's package.jsonWhat the package does once your code imports it

Tools and guides

Standards

NIST SSDF 1.1
PW.4 Reuse Existing, Well-Secured Software When Feasible Instead of Duplicating Functionality

Sources

  1. OpenSSF: Concise Guide for Evaluating Open Source Software
  2. OpenSSF Scorecard: checks documentation
  3. OWASP Top 10:2025 A03 Software Supply Chain Failures
  4. OWASP ASVS 5.0.0: V15 Secure Coding and Architecture (V15.2.4). Version 5.0.0.
  5. CISA: Widespread supply chain compromise impacting npm ecosystem. 23 September 2025.
  6. Spracklen et al.: We have a package for you! Package hallucinations by code-generating LLMs (USENIX Security 2025)
  7. npm docs: npm ci. npm v12.
  8. npm docs: npm install-scripts. npm v12.
  9. pnpm docs: build settings (allowBuilds, strictDepBuilds)
  10. pip docs: secure installs (hash-checking mode)
  11. pip docs: pip install (index options and dependency confusion warning)
  12. uv docs: package indexes (first-index strategy, explicit indexes)
  13. npm docs: scope (associating a scope with a registry). npm v12.
  14. NuGet docs: Package Source Mapping
  15. deps.dev API v3 (provenance, attestations, Scorecard and advisories)
  16. OWASP SAMM: Secure Build (Software Dependencies stream)
  17. NIST SP 800-218: Secure Software Development Framework (SSDF) Version 1.1. February 2022.

Find your next answer

Type to search. Search runs in your browser.

Searches run in your browser, but the host can infer them from the index files they load. The address shows your search so you can bookmark or share it.