Your dependency tree, in full — direct and transitive — with known CVEs flagged inline and
fixed in one click. No more npm ls, no more guessing which project pulled in that vulnerable
transitive package. Works across npm, pnpm and NuGet, and across every project in your workspace
at once.
Why you'll want this
- See the whole tree, not just
package.json. Every project in your workspace — npm, pnpm, NuGet, mixed monorepos — shows up as its own node, direct dependencies on top, the full transitive graph underneath, expandable as deep as you need. - Know before you're paged. Vulnerable packages (via the free OSV.dev database) are flagged red, and anything upstream of one is flagged yellow — so you can see exactly which direct dependency dragged in the problem, three levels down.
- Fix it without leaving the tree. Update or pin a version and the manifest is edited for you — correctly, for your package manager and versioning scheme (npm ranges, pnpm overrides, NuGet CPM). Preview the blast radius first; nothing is written until you click Apply.
- Fix everything at once. One command finds every vulnerable direct dependency across a single project, a chosen subset, or your entire workspace, and bumps each to the nearest safe version.
- Or just get current. A second command bumps every direct dependency — not only the vulnerable ones — to its latest published version, and you can flag the packages you always want on latest so they jump there on any upgrade.
✨ Features
- 🌳 Full dependency tree for npm, pnpm (including workspaces) and NuGet — every project in the workspace as its own top-level node, monorepos and multi-project solutions fully supported.
- 🛡️ Vulnerability flags via OSV.dev: red = this exact version is vulnerable (advisory IDs linked in the tooltip); yellow = something deeper in the subtree is. Vulnerable projects are badged right in the tree so you know where to look before you even expand anything.
- 🔎 "Why Is This Here?" — right-click any dependency (direct or transitive) to see every chain from your project's direct dependencies down to that package. Shortest chains first (the most actionable — update the first link to move the package), dev-only chains tagged, and a warning when a package resolves to more than one version. Answers "which of my dependencies dragged in this transitive package?" without hunting through an expanded tree. Works across npm, pnpm and NuGet.
- 🔧 Fix All Vulnerabilities — one command, three scopes: a single project, a chosen subset, or your whole workspace. Bumps every vulnerable direct dependency to the nearest non-vulnerable version. Sub-dependencies are skipped so the run stays fast — it never fetches versions for the whole transitive tree — and any vulnerable ones it skips are reported by name. To fix a specific transitive, pin it via its right-click Override / Pin Version…, or add it to the prefer-latest list to include it here. Re-run after install to mop up anything a resolver needs a second pass on.
- ⬆️ Update All Packages to Latest — same three scopes, but for every direct package rather than just the vulnerable ones. Each is bumped to its latest published version; anything already current is skipped. Sub-dependencies are skipped so a routine "update to latest" stays fast and never pins the whole transitive tree — update one on demand via its right-click Override / Pin Version…, or add it to the prefer-latest list to include it here.
- 🚀 Bump .NET & Aspire — upgrade a whole .NET Aspire solution in one pass. Pick a single target
Aspire version and it's applied to the
Aspire.AppHost.Sdkand every first-partyAspire.*package across your chosen scope (they ship in lockstep, so they move together), and — optionally, in the same flow — bump the .NET<TargetFramework>too. Every package, the SDK, and each framework edit is shown in a confirmation list before anything is written; an Aspire package the feed doesn't publish at the chosen version is left alone and reported. Honors Central Package Management and a<TargetFramework>centralized inDirectory.Build.props. - 🎯 Bump .NET on its own — a standalone Bump .NET Version… command changes just the
<TargetFramework>across a chosen set of projects, for plain .NET solutions with no Aspire. - ⭐ Prefer-latest list — flag packages that should favour the newest version during the two
bulk operations (right-click a dependency → Prefer Latest Version; the menu toggles to Remove
from Prefer-Latest List once it's flagged — or edit the
dependencyExplorer.alwaysLatestPackagessetting, which supports*wildcards like@myorg/*). Flagging also opts a sub-dependency back into the bulk runs, which otherwise skip transitive packages entirely — so it's how you keep a specific transitive on latest without giving up the speed of skipping the rest. In Fix All Vulnerabilities a flagged package targets its latest version instead of the smallest safe bump — but only when that latest version is itself non-vulnerable; otherwise it quietly falls back to the nearest safe version, so it never lands on a known-vulnerable one. In Update All Packages to Latest it's tagged★ prefer-latestin the confirmation list. It has no effect when you update a single package by hand. - 🚫 Never-update list — the opposite of prefer-latest: flag packages to hold back from both
bulk operations (right-click a dependency → Never Update This Package; the menu toggles to
Remove from Never-Update List once it's flagged — or edit the
dependencyExplorer.neverUpdatePackagessetting,*wildcards supported). Held-back packages are skipped by Fix All Vulnerabilities and Update All Packages to Latest — even a vulnerable one stays put — and are listed afterwards so you know what was left untouched. Updating one by hand still works; you'll just get an update anyway? confirmation first. If a package is on both lists, never-update wins during bulk runs. - ↑ Update a direct dependency or 📌 override/pin a transitive one, with the manifest edited
in place — npm range style preserved, pnpm
overrides, NuGet<PackageReference>/Central Package Management all handled automatically. - 🎯 Pin or use a range. After you pick a version, choose how it's written: an exact pin, or a
range — npm
^/~/>=, or NuGet floating (1.2.*) and bracket ([1.2.3],[1.2.3,)) syntax — with a custom-range escape hatch. The dependency preview is computed from the concrete version you picked. - 👀 Preview before you apply. Every version change opens a diff of what that version's own dependencies look like versus your current one — added, removed, changed — pulled live from the registry, before anything touches disk.
- 🧭 Know what an update means before you take it. Every update prompt is annotated so you can
decide without leaving the editor:
- Semver risk badges — each bump is tagged
⚠ major/minor/patch/pre-releasein the bulk checklists, the version picker, and the preview panel. In Update All Packages to Latest, major (potentially breaking) bumps start unchecked unless the package is on your prefer-latest list, so a routine "update everything" never silently opts you into breaking changes. - Deprecation flags — a version the registry marks deprecated is shown with a
⛔marker in the tree, picker and preview (with the registry's deprecation message). Covers npm's per-versiondeprecatedfield and NuGet's registrationdeprecationmetadata. - Changelog / release-notes links — a dependency's tooltip links to its repository, changelog / releases page, homepage and registry page, plus a "latest published" hint with its own risk badge. Best-effort and cached; a feed that doesn't expose the metadata simply shows less.
- Semver risk badges — each bump is tagged
- 🔁 Bump a shared package everywhere it's used, in one action — across all projects, a chosen few, or just the one you're looking at.
- 🔒 Private feeds just work. Version lookups and previews respect your project's own
.npmrc/NuGet.config— scoped registries, auth tokens, source mapping — so this works the same way behind a corporate proxy as it does on the public registries. - ⚡ After any edit, the extension offers to run the right install command (
npm install,pnpm install,dotnet restore) for you, and the tree refreshes itself once it's done. - ♻️ Re-install on demand — right-click a project → Re-install Dependencies to run its install
command, or use the title-bar ♻️ button to re-install every open project at once (npm, pnpm and
NuGet together, each command run once). Handy for restoring a freshly-cloned NuGet project that
has no
project.assets.jsonyet.
Getting started
- Install the extension, then open a folder containing npm, pnpm and/or NuGet projects.
- Click the Dependencies icon in the activity bar. Each project appears collapsed with a vulnerability badge if it has one — expand what you want to dig into.
- Click the 🛡️ Fix All Vulnerabilities button in the view title to clean up everything at once, or use the ↑ / 📌 icons on an individual package.
- Working in a .NET Aspire solution? Use the 🚀 Bump .NET & Aspire Versions… button (view title,
or right-click a NuGet project) to move the Aspire SDK, every
Aspire.*package, and — if you like — the target framework in a single reviewed step. For a framework-only change, use Bump .NET Version….
How it reads the tree
| Ecosystem | Source of truth | Requirement |
|---|---|---|
| npm | package-lock.json (v2/v3 packages map, real node_modules-style resolution) |
run npm install once (npm 7+) |
| pnpm | pnpm-lock.yaml (v6, v9, and the pnpm 11 multi-document layout, including workspaces) |
run pnpm install once (pnpm 8–11) |
| NuGet | obj/project.assets.json (classic + Central Package Management, up to .NET 10 / net10.0) |
run dotnet restore once |
These are the resolved graphs, so versions match exactly what your build uses — not what a semver range merely allows.
Central Package Management (NuGet)
Central Package Management
(CPM) — where package versions live in a Directory.Packages.props file instead of on each
<PackageReference> — is fully supported. The extension discovers the nearest
Directory.Packages.props by walking up from the project folder and edits the right file
automatically:
| Action | Classic project | CPM project |
|---|---|---|
| Update a direct dependency | edits Version="…" on the <PackageReference> in the .csproj |
edits the <PackageVersion> in Directory.Packages.props (or a VersionOverride on the reference if one exists) |
| Override a transitive dependency | adds a versioned <PackageReference> pin to the .csproj |
adds a <PackageVersion> to Directory.Packages.props; if transitive pinning is off, it also adds a versionless <PackageReference> to force the pin |
Because a CPM version is shared, updating it fixes every project under that props file at once — which pairs naturally with "bump across all projects."
Private and custom feeds
Version lists and preview diffs are resolved through your project's own configuration, not a hardcoded public registry:
- npm/pnpm — reads
.npmrc(project directory upward to your home directory): the default registry,@scope:registryoverrides, and_authToken/_auth/username+_passwordcredentials, with${ENV_VAR}expansion for tokens. - NuGet — reads
NuGet.config(project directory upward, plus your user-level config):<packageSources>,<disabledPackageSources>,<packageSourceCredentials>, and<packageSourceMapping>. - Azure DevOps & other private feeds — when a feed needs auth but there are no credentials in
NuGet.configor the environment, the extension asks the Microsoft Artifacts Credential Provider (the token storedotnetand Visual Studio use), so signed-in Azure DevOps feeds work with no extra config. It also readsARTIFACTS_CREDENTIALPROVIDER_EXTERNAL_FEED_ENDPOINTS/VSS_NUGET_EXTERNAL_FEED_ENDPOINTS(CI), and honorsNUGET_PLUGIN_PATHSfor a custom provider.
No extra setup — if your terminal's npm install / dotnet restore can already reach a feed, so
can the extension. If an Azure feed reports an authentication error, run dotnet restore once to
sign in, then refresh.
Known limitations
- npm workspaces: packages inside a monorepo without their own
package-lock.jsonshow a "run npm install" hint; open the workspace root (where the lock lives) to see the full graph. - NuGet multi-targeted projects show the first target framework's graph.
- The bulk commands ("Fix All Vulnerabilities", "Update All Packages to Latest") only act on direct dependencies, so they stay fast on large trees. Fix a sub-dependency via its right-click Override / Pin Version…, or add it to the prefer-latest list to include it in the bulk runs (where it's a pin rather than a smarter upstream bump — safe to re-run after installing).
- Vulnerability severity levels aren't fetched (only advisory IDs, linked to osv.dev); circular references are shown once and marked circular.
Contributing
Development setup, project layout
npm install
npm run bundle:watch # esbuild bundle → dist/, rebuilt on save (with sourcemaps)
Press F5 in VS Code to launch an Extension Development Host, then open any folder containing
an npm, pnpm or .NET project. The extension entry point is the bundled dist/extension.js, so a
bundle has to exist before F5 — bundle:watch keeps it current. (npm run compile still builds the
unbundled out/ used by npm test, and npm run check-types type-checks without emitting.)
To package a .vsix for local installation:
npx @vscode/vsce package
The vscode:prepublish step type-checks and produces the minified bundle automatically. Then install
it via Extensions: Install from VSIX….
Project layout
src/extension.ts— activation, tree view registration, file watcherssrc/tree/dependencyTree.ts—TreeDataProvider(lazy expansion, icons, tooltips, bulk-fix helpers)src/providers/npmProvider.ts— npm/pnpm provider, adapting both lockfiles to a common resolved graphsrc/providers/npmGraph.ts/pnpmGraph.ts— lockfile parsing + resolution for each formatsrc/providers/nugetProvider.ts—project.assets.jsonparsingsrc/services/vulnClosure.ts— cycle-safe "subtree contains a vulnerability" reachabilitysrc/services/osvService.ts— batched OSV.dev vulnerability queries (cached per session)src/services/registryService.ts— version lists, feed-awaresrc/services/feedConfig.ts—.npmrc/NuGet.configparsing and resolutionsrc/services/metadataService.ts— best-effort package metadata (deprecation flags, repository / changelog links), cached per sessionsrc/services/semverRisk.ts— major/minor/patch bump classification for the risk badgessrc/services/whyService.ts— reverse-dependency ("why is this here?") path enumeration over a provider-agnostic graphsrc/services/manifestEditor.ts— pure text edits forpackage.json,.csproj,Directory.Packages.props(package versions,<TargetFramework>,Aspire.AppHost.Sdk)src/services/aspire.ts— first-party Aspire package detection + target-framework option listsrc/services/previewService.ts— fetches a version's declared dependencies and diffs current vs targetsrc/services/fixPlanner.ts— nearest-safe-version resolution for bulk fixessrc/services/packageMatch.ts— case-insensitive, wildcard package-name matching for the prefer-latest listsrc/ui/previewPanel.ts— webview showing the transitive-dependency impact, with Apply / Cancelsrc/ui/whyPanel.ts— webview listing every dependency chain that pulls a package into a projectsrc/commands.ts— update/override/bulk-fix commands, .NET & Aspire version bump, preview + confirm, cross-project scope picker, install prompt