Much of the code in a modern application comes from open-source packages your team didn't write, and many of those arrive as dependencies of your dependencies. Securing them comes down to five practices. Know exactly what you use with software composition analysis (SCA). Control what gets installed with lockfiles and pinning. Keep versions current with automated update bots. Vet new packages before adopting them, using signals like the OpenSSF Scorecard. And start asking how the software you depend on was built, which is where SLSA comes in. This post covers each one, plus malicious packages and license risk.

Why do open-source dependencies need their own security process?

When you add a package, you inherit its code, its bugs and its maintainers' security practices. You also inherit everything it depends on. A single direct dependency can pull in dozens of transitive packages maintained by people you'll never talk to.

The risks fall into a few groups:

  • Known vulnerabilities in the versions you use
  • Malicious packages, either published deliberately or created by taking over a legitimate one
  • Abandoned projects that will never ship a fix
  • Weak build and release practices that make tampering easier
  • License obligations you didn't plan for

Server patching tools don't address any of these well. Dependencies live in your code repositories and build pipelines, so that's where the controls need to go.

What is software composition analysis (SCA)?

Software composition analysis tools build an inventory of the open-source components in your application and check it against vulnerability and license data. They read manifests and lockfiles, and some also inspect built artifacts or container images. The output is a list of components, known vulnerabilities, available fixed versions and licenses.

Vulnerability data typically comes from the National Vulnerability Database, the open-source OSV database and the advisory databases maintained by individual package ecosystems. Coverage differs between sources, so a tool that draws on several will usually catch more.

Run SCA in three places:

  1. On pull requests, so developers see issues introduced by their change.
  2. In CI, as a gate on new critical findings.
  3. On a schedule against your main branch and released versions, because new vulnerabilities are disclosed in packages you already ship.

Pay attention to how fixes are delivered for transitive dependencies. Often you can't upgrade the vulnerable package directly and need to upgrade the direct dependency that pulls it in, or use your package manager's override mechanism as a stopgap.

How does reachability analysis cut the noise?

Many SCA findings concern code your application never calls. A library might have a vulnerable parsing function that you don't use at all. Reachability analysis traces calls from your code into dependencies to estimate whether the vulnerable function can actually be reached.

This is a prioritization aid, not a way to dismiss findings. Dynamic languages, reflection and plugin loading all make call analysis imperfect, and code that isn't reachable today may be reachable after next month's feature. Use reachability to decide what to fix first, and still plan to upgrade unreachable vulnerable packages during routine maintenance.

Why do lockfiles and pinning matter?

Most package manifests allow version ranges. That means two builds of the same commit, a week apart, can install different versions of a dependency. A lockfile records the exact version resolved for every direct and transitive dependency, usually along with a cryptographic hash of each package.

Lockfiles give you three things: reproducible builds, an accurate inventory for SCA, and protection against a package being silently swapped for a different version. To get those benefits:

  • Commit lockfiles (such as package-lock.json, poetry.lock, Cargo.lock, go.sum or Gemfile.lock) to version control.
  • Use install commands in CI that fail if the lockfile and manifest disagree, rather than ones that quietly update the lockfile.
  • Turn on hash verification where your ecosystem supports it.
  • Review lockfile changes in pull requests, especially new packages you didn't expect.

Pinning applies beyond application packages. Pin CI actions and reusable workflow steps to a full commit hash, and pin container base images by digest. A mutable tag can point to different code tomorrow.

Pinning has an obvious downside. Pinned dependencies don't pick up security fixes on their own, so pinning only works if you pair it with a reliable way to update.

How do automated dependency update bots help?

Update bots watch your manifests and lockfiles and open pull requests when new versions are available. Each pull request shows the change, links to release notes and runs your tests. Keeping current in small steps is far easier than a large, risky upgrade after two years of drift.

A few settings make bots workable rather than noisy:

  • Separate security updates from routine ones. Security updates should go out promptly. Routine updates can be batched on a weekly schedule.
  • Group related packages so a framework and its plugins update together.
  • Auto-merge low-risk updates, such as patch versions of well-tested dependencies, but only if your test suite is trustworthy.
  • Give each repository an owner who reviews and merges bot pull requests.

Some teams also wait a few days before adopting non-security releases. That short delay gives the community time to spot a broken or malicious version before it reaches your builds.

How do you evaluate a package before adopting it?

The cheapest dependency to secure is the one you don't add. Before adopting a new package, ask whether you need it at all, whether a package you already use can do the job, and whether the project looks healthy.

Useful signals include recent commits and releases, more than one active maintainer, a published security policy, a history of responding to vulnerability reports and a clear license. Popularity helps a little, since widely used packages get more scrutiny, but it guarantees nothing.

Using the OpenSSF Scorecard

The Open Source Security Foundation's Scorecard project automates many of these checks. It examines a project's public repository and scores it from 0 to 10 across a set of security practices, including:

  • Maintained: whether the project is actively maintained
  • Code-Review: whether changes are reviewed before merging
  • Branch-Protection: whether branch protection is enabled on the default and release branches
  • Pinned-Dependencies: whether the project pins its own dependencies
  • Security-Policy: whether there's a published way to report vulnerabilities
  • Signed-Releases: whether release artifacts are cryptographically signed
  • Vulnerabilities: whether the project has open, unfixed vulnerabilities

Treat the score as input to a decision, not a verdict. A small, well-run library can score modestly because it doesn't use every practice Scorecard looks for. What matters most is spotting clear red flags, such as no maintenance activity or no code review at all, before a package becomes load-bearing in your product. You can also run Scorecard against your own repositories to see how your projects look to others.

What about malicious and typosquatted packages?

Not every risky package is merely buggy. Attackers publish packages with names that are one character away from popular ones, hoping someone mistypes an install command. Others publish public packages that share a name with a company's internal package, so a misconfigured build pulls the public one instead, a technique known as dependency confusion. Legitimate packages can also turn malicious when a maintainer account is taken over or an abandoned project changes hands.

Many package ecosystems run install scripts automatically, so malicious code can execute on a developer laptop or build server as soon as the package is installed. Defenses that work:

  • Route installs through an internal registry or proxy, and control which packages it will serve.
  • Use scoped or namespaced names for internal packages, and configure builds to resolve them only from your internal registry.
  • Disable install scripts where your ecosystem allows it and the package doesn't need them.
  • Review any new dependency in a pull request, including its exact name and publisher.
  • Rely on lockfiles and hash verification, so a replaced package fails the build.

Where does SLSA fit?

SLSA (Supply-chain Levels for Software Artifacts, pronounced "salsa") is an OpenSSF framework for build integrity. Where Scorecard looks at a project's development practices, SLSA focuses on how artifacts are built and whether you can verify where they came from. The current specification defines four levels:

Level What it requires
1 The build process is fully scripted or automated and generates provenance
2 Version control and a hosted build service that generates authenticated provenance
3 Source and build platforms meet specific standards for auditability and integrity
4 Two-person review of all changes and a hermetic, reproducible build process

Provenance is the key idea. It's a record, ideally signed, of what source, build process and inputs produced an artifact, so you can verify that the package you downloaded came from the repository you expect.

For most small and mid-sized organizations, SLSA is a direction rather than a checklist to complete this quarter. Ecosystem support for publishing and verifying provenance is still developing. Two practical steps now are to move your own builds to a hosted, scripted pipeline that produces provenance, and to favor dependencies that publish signed releases and provenance when you have a choice.

How should you handle open-source license risk?

SCA tools report licenses alongside vulnerabilities, and it's worth acting on them. Permissive licenses generally require little beyond attribution. Copyleft licenses can require you to release source code under certain conditions, often depending on whether and how you distribute the software.

Agree a short policy with your legal counsel listing licenses that are approved, need review, or aren't allowed. Flag packages with no license at all, since that's a risk in its own right. SPDX license identifiers make this easier to automate.

Frequently asked questions

Should we fix every vulnerability SCA reports?

No. Prioritize by severity, whether a fix exists, whether the vulnerability is known to be exploited, whether the application is exposed, and reachability. Then schedule the rest into routine dependency maintenance so the backlog doesn't grow indefinitely.

Is it safe to auto-merge dependency updates?

For patch-level updates to well-tested code, often yes, provided your test suite would catch a breaking change. Keep major version updates, new dependencies and anything touching authentication or cryptography under human review.

Do we need a private package registry?

It isn't mandatory, but it helps a lot. A registry or caching proxy gives you one place to block known-bad packages, prevent dependency confusion and keep builds working when a public registry is unavailable.

Next steps

  1. Turn on SCA for every repository, with results visible in pull requests.
  2. Commit lockfiles everywhere and make CI fail when they're out of sync.
  3. Enable an update bot, separate security updates from routine ones, and assign owners.
  4. Add a lightweight review for new dependencies, using Scorecard results as one input.
  5. Pin CI actions and base images, and route package installs through a registry you control.
  6. Write a one-page license policy with your legal counsel.