Snyk can find vulnerabilities in your dependencies, but it’s the transitive dependencies that often hide the real danger.

Let’s say you’re building a web app and you explicitly add express (version 4.17.1) to your package.json. Snyk scans express and tells you about any known vulnerabilities in that specific package. Great. But express itself depends on other packages, which in turn depend on others, forming a vast, invisible tree of code. A vulnerability in debug (version 4.1.1), a package that express depends on, might go unnoticed if you only look at your direct dependencies.

Here’s express (version 4.17.1) in action, and how a transitive vulnerability might manifest.

// server.js
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

If you run npm list debug, you’ll see debug listed, but it might be nested several levels deep under node_modules/express/node_modules/debug. Snyk, when configured to scan transitive dependencies, will traverse this entire tree and report vulnerabilities found in debug, even though you never explicitly added debug to your package.json.

The problem Snyk solves here is the blind spot created by dependency trees. Developers often focus on the security of packages they directly include, assuming their chosen libraries are safe. However, a single vulnerable package deep in the dependency chain can compromise the entire application. Think of it like a chain reaction: one weak link can break the whole thing. Snyk mitigates this by acting as a thorough auditor, inspecting not just the packages you’ve hand-picked, but every single package that gets pulled in as a consequence of your choices.

Internally, Snyk maintains a massive, continuously updated database of known vulnerabilities (CVEs) and maps them to specific package versions. When you run a Snyk scan, it first enumerates your project’s entire dependency tree, including all transitive dependencies. For each package and version in that tree, it queries its vulnerability database. If a match is found, it reports the vulnerability, indicating the vulnerable package, the affected version range, and crucially, the path through your dependency tree that leads to this vulnerable package. This path is vital for understanding how the vulnerability was introduced and which of your direct dependencies is responsible for bringing it in.

The exact levers you control are primarily through your package.json and how you configure Snyk.

  • package.json: This is your explicit declaration. The versions you specify here, and the packages you include, are the starting point for the dependency tree. Keeping this file lean and up-to-date with the latest stable versions of your direct dependencies is the first line of defense.
  • Snyk CLI/Integrations: When you run snyk test or snyk monitor, Snyk analyzes your project. By default, it does scan transitive dependencies. You can, for example, use the --dev flag to include development dependencies, or --all-sub-projects if you have a monorepo structure. The key is that Snyk’s default behavior is designed to catch these indirect issues.
  • Fixing Vulnerabilities: Snyk often suggests snyk wizard or npm update <package-name>. This command attempts to upgrade the vulnerable package to a version that fixes the vulnerability, while also trying to ensure compatibility with other dependencies. It intelligently navigates the versioning constraints to find a safe upgrade path.

The most surprising thing about transitive dependencies is how often they are the real source of the problem, and how a fix for one direct dependency can cascade into a requirement to update many others. When Snyk reports a vulnerability in, say, lodash (version 3.9.0) that was pulled in by some-other-utility (version 1.2.0), and some-other-utility itself is only compatible with express (version 4.15.0), you suddenly have a complex upgrade decision. You might need to upgrade express to a newer version that some-other-utility also supports, which then might require upgrading other packages that depend on express. It’s not just about updating one package; it’s about managing the ripple effect through the entire tree.

The next concept you’ll likely encounter is managing license compliance across your entire dependency graph.

Want structured learning?

Take the full Snyk course →