Snyk’s Reachability Analysis doesn’t just tell you if a vulnerability exists; it tells you if it can actually be reached from your application’s entry points.
Let’s watch it in action. Imagine you have a simple Node.js app with Express, and you’ve installed a vulnerable version of lodash (say, version 4.17.10, which has CVE-2019-10744).
// index.js
const express = require('express');
const _ = require('lodash'); // Vulnerable lodash version
const app = express();
const port = 3000;
app.get('/', (req, res) => {
const input = req.query.data;
// This is where the vulnerability could be exploited if input is crafted correctly
const processedData = _.get(JSON.parse(input), 'user.name');
res.send(`Processed data: ${processedData}`);
});
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
If you were to run snyk test without reachability analysis enabled, Snyk would likely flag lodash for CVE-2019-10744. But what if your application never actually uses the vulnerable function (_.get in this case) with user-controlled input? That’s where reachability analysis shines.
When Snyk performs reachability analysis, it instruments your application’s code. It identifies your application’s "entry points" – places where external input can enter the system, like HTTP request handlers, message queue consumers, or even file reads. Then, it traces the flow of data from these entry points through your code, mapping out all the paths that data can take.
For a vulnerability to be considered "exploitable" by Snyk’s reachability analysis, two conditions must be met:
- Vulnerable Function Call: The vulnerable function within a dependency must be called.
- Data Flow: Data originating from an entry point must be able to reach that vulnerable function call.
If data from an entry point cannot reach the vulnerable function, Snyk will mark that specific vulnerability instance as "not reachable" and de-prioritize it in your results. This means you can focus your precious developer time on the vulnerabilities that actually pose a risk.
The magic happens through a combination of static and dynamic analysis techniques. Snyk analyzes your code’s structure to understand how data could flow. It also uses runtime instrumentation (when you run snyk test --reachable) to observe actual data flows during a test run. By correlating these, it builds a precise map of exploitability.
You enable this powerful feature by running snyk test --reachable. This command triggers Snyk to instrument your application and analyze the data flow. You’ll see output like this for a vulnerable dependency:
Testing /path/to/your/app for known vulnerabilities...
✗ High severity vulnerability found in lodash
Description: Prototype Pollution in lodash
Info: https://snyk.io/vuln/npm:lodash:20190314
Introduced through: lodash@4.17.10
Remediation:
Upgrade lodash to version 4.17.21 or later.
Or, upgrade `lodash` to version 4.17.21.
Reachability:
- Vulnerability CVE-2019-10744 is reachable through the following path:
- Entry Point: HTTP GET /
- Data Flow: req.query.data -> JSON.parse -> _.get
This tells you exactly how the vulnerability is reachable. If, on the other hand, the _.get call was guarded by checks that ensured input was always sanitized or never came from req.query.data, Snyk would report it as not reachable:
Testing /path/to/your/app for known vulnerabilities...
✓ No vulnerable paths found for lodash.
The real power comes when you have hundreds of dependencies. Manually tracing data flow is impossible. Snyk automates this, giving you a prioritized list of actionable vulnerabilities. You control the scope by defining your entry points. For web applications, this is often HTTP request handlers. For other applications, it might be message queue consumers or specific API endpoints.
One aspect that often surprises developers is how Snyk infers data flow. It doesn’t just look at direct variable assignments. It understands common patterns like serialization/deserialization (e.g., JSON.parse), template rendering, and even some forms of reflection. This allows it to trace data through complex transformations, uncovering vulnerabilities that might be hidden behind seemingly innocuous code. For example, if you pass user input to a function that then calls eval, Snyk can often trace that path.
Once you’ve addressed all the reachable vulnerabilities, the next logical step is to ensure your dependency versions are themselves up-to-date and free of any unreachable but still present vulnerabilities, which you’d typically handle with snyk upgrade.