Dependencies
Vulnerable packages across forty ecosystems, direct and transitive, with the safe version to move to.
Software composition analysis: which packages your project uses, and which of them have known vulnerabilities.
Where the finding lands
On the line that declares the dependency, in the file you would edit to change it. Not at the top of the file, and not on a lockfile entry you never wrote.
That sounds obvious and is the hardest part of the feature. Package parsers produce a list of names and versions with no position information at all, so the extension re-reads each manifest with a position-aware parser: byte offsets for JSON, node positions for YAML, the module syntax tree for go.mod, line matching for requirements.txt and Gemfile.
Where a format genuinely cannot give an exact position, the finding is anchored as closely as possible and marked as approximate rather than silently pretending to precision.
Transitive dependencies have no line to point at, because you never declared them. Those anchor in the lockfile, and carry the chain that introduced them:
introduced via express@4.17.1 → qs@6.5.2
Ecosystems
npm, PyPI, Go, Maven and Gradle, Cargo, RubyGems, NuGet, Composer, Swift, Hex, Pub, Conda, CocoaPods, Conan, vcpkg, CRAN, Hackage, opam, Nix, Zig, Julia, Crystal, Deno, Erlang, and more, along with Docker images, Helm charts, CI pipeline definitions and shell scripts that install packages.
Both declared manifests and installed trees are read, so a dependency present in node_modules but missing from package.json is still found.
What a finding tells you
- Every advisory affecting the version in use, with CVSS, EPSS and SSVC
- Whether it is in CISA’s or the EU’s Known Exploited Vulnerabilities catalogue
- Whether public exploit code exists, and how mature it is
- The safe version to move to, and whether that is a major bump
- Whether the package is end-of-life or has been flagged as malicious
- Where it came from, if you did not add it yourself
Tuning
| Setting | Default | Effect |
|---|---|---|
vulnetix.sca.enabled | true | Check dependencies at all |
vulnetix.sca.safeHarbourStrategy | safest | Which replacement version a quick fix moves to: safest, stable or latest |
vulnetix.sca.maxMajorBump | 0 | How many major versions a suggested fix may cross |
vulnetix.sca.annotateLockfiles | true | Whether lockfiles get findings of their own |
vulnetix.diagnostics.minimumSeverity | low | Hide findings below a threshold |
vulnetix.diagnostics.showSuppressed | false | Show suppressed findings as hints instead of hiding them |
The strategy default differs from the CLI on purpose. vulnetix fix defaults to
stable; the editor defaults to safest, because a one-click fix behind a
lightbulb gets less deliberation than a command someone typed, so it should
prefer the smallest change that resolves the finding.
Network use
Matching a package to advisories requires the vulnerability database; there is no local copy. Only package coordinates are sent, never code. Results are cached, and a manifest save whose dependency set did not change produces no request at all. See privacy.
When each thing happens
Two passes, because the two questions cost different amounts.
Declared dependencies are checked in bulk when the workspace opens, and again when a manifest or lockfile changes. That pass answers the only thing the whole workspace needs to know: is anything wrong here.
Ranked replacement versions are resolved separately, for the manifest you have open. They are per-package work on the server and only useful for a file someone is looking at. So a package that came back clean shows a quiet checked marker straight away, while a vulnerable one shows a pending marker until its replacement versions arrive.
Ranked Safe-Harbour versions are a Pro feature. On the community tier the hover says so, rather than reporting that no fix exists — a different and much more discouraging claim.
Suppressing a finding
Rules in the repository’s .vulnetix/memory.yaml apply in the editor exactly as they do in CI. Point vulnetix.memory.path elsewhere if that file lives somewhere else.
vulnetix.suppressions adds editor-level rules on top, which take precedence. Use them to silence a finding for yourself without committing a change the whole team shares:
"vulnetix.suppressions": [
{ "findingId": "CVE-2021-23337", "reason": "not reachable from our entry points" },
{ "ruleId": "VNX-JS-001", "path": "test/fixtures", "reason": "intentional" }
]
A rule anchored on nothing is refused, because it would silence everything.