npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dependencyiq

v2.10.0

Published

Multi-language dependency vulnerability agent: OSV-Scanner + GitLab Orbit blast-radius analysis + automated MRs

Readme

DependencyIQ

A multi-language dependency-vulnerability agent for GitLab — built on GitLab Orbit and the GitLab Duo Agent Platform.

DependencyIQ does not just tell you a package is vulnerable. It tells you whether that vulnerability is real risk for your code, what breaks if you upgrade, whether the dependency is dead weight that should be deleted, and — when you ask it to — it applies the fix and opens the merge request itself. The intelligence is grounded in GitLab Orbit's import graph, not in guesses.


Table of contents


What it is

DependencyIQ is one tested engine exposed through several surfaces:

  • A command-line engine (dependencyiq, published to npm) that scans, scores, plans, fixes, and reports.
  • Custom Chat Agents on the GitLab Duo Agent Platform — an interactive control surface for triage and remediation.
  • An ambient Custom Flow — an autonomous, event-triggered pipeline that runs the engine in CI/CD and opens a remediation merge request.
  • An Agent Skill — a slash-command / natural-language entry point in Duo Chat.
  • A live dashboard published to GitLab Pages.

Every surface calls the same engine. The logic exists once.

The problem it solves

Every scanner can say "this package has a CVE." Almost none can answer what actually matters next:

  • Is this real risk? A high CVSS score on a package that only your test fixtures import is not the same as one your public API depends on.
  • What breaks if we upgrade? One file, or forty, with a breaking change baked into a major version bump?
  • Can we just delete it? If nothing in your codebase actually imports a package, patching its CVE is wasted effort — removing it is the fix.
  • Across every language at once. A single repository can mix npm, PyPI, Go, Maven, and more; each needs its own toolchain.

Answering these by hand means grepping the codebase, reading changelogs, and writing the fix yourself, for every vulnerable package, in every ecosystem. DependencyIQ does it instead, grounded in real import-graph evidence.

How it works end to end

Scan ─► Score ─► Plan ─► Fix ─► Verify ─► Track ─► Report
 │       │        │       │       │         │         │
 OSV-    Orbit    impact  per-    tests    GitLab    summary
 Scanner blast    report  eco     run      issues/   + clean
 (any    radius   + migra system  +        MR notes  comment
 eco)    + risk   -tion   fixers  pipeline + plans
         score    plan            tools
  1. Scan — OSV-Scanner reads every manifest/lockfile present and reports known vulnerabilities across all ecosystems in one pass.
  2. Score — each finding is combined with real Orbit exposure data and a usage count, minus a test-coverage discount, into a 0–100 risk score.
  3. Plan — for the top finding, an Upgrade Impact Report estimates affected files, semver distance, breaking-change risk, and effort, and produces an ordered migration plan.
  4. Fix — the appropriate per-ecosystem fixer edits the manifest (version bump, transitive override, or removal) and commits the change.
  5. Verify — the test suite runs; failures are surfaced and the remediation is marked blocked if anything breaks.
  6. Track — the work becomes visible GitLab artifacts: a tracking issue, MR notes carrying the evidence, and a task plan for follow-ups.
  7. Report — a concise, honest summary is posted where the run was triggered.

Architecture

┌──────────────────────────────────────────────────────────────┐
│                        Entry points                          │
│   Chat Agents      Ambient Flow      Agent Skill      CLI     │
│   (interactive)    (autonomous CI)   (Duo Chat)    (terminal) │
└───────────────┬──────────────┬─────────────┬──────────────┬──┘
                └──────────────┴─────────────┴──────────────┘
                                  │
                                  ▼
              ┌───────────────────────────────────────┐
              │   The engine — `dependencyiq` CLI     │
              │   (src/agent.js + modules)            │
              └───────────────────┬───────────────────┘
                                  │
        ┌──────────────┬──────────┴───────────┬───────────────┐
        ▼              ▼                       ▼               ▼
   OSV-Scanner   GitLab Orbit            GitLab REST     Package
   (scan)        (import graph)          (commits, MRs,  registries
                                          issues, vulns)  (freshness)

Directory map:

src/                                # the engine
├── agent.js                        # CLI entry point and orchestrator
├── scanners/
│   ├── osvScanner.js               # multi-ecosystem vulnerability scan
│   ├── cvss.js                     # CVSS v3 base score from a vector string
│   ├── manifestParser.js           # lists every direct dependency
│   ├── dependencyTreeBuilder.js    # direct-vs-transitive resolution
│   ├── ecosystemFixers.js          # per-ecosystem bump / override / remove
│   └── supplyChainTrustSignals.js  # maintainer-compromise signals
├── orbitClient.js                  # REST client for the Orbit API
├── blastRadius.js                  # single-project exposure analysis
├── riskCalculator.js               # the 0–100 risk score + dead-dep detection
├── impactReport.js                 # affected files + semver + effort estimate
├── upgradeImpactSimulator.js       # three remediation strategies
├── strategyGenerator.js            # markdown templates (plans, MR descriptions)
├── freshnessChecker.js             # registry lookups + stability-aware version
├── freshnessPolicy.js              # enforces AGENTS.md freshness thresholds
├── configLoader.js                 # parses the AGENTS.md config block
├── prGenerator.js                  # commits the fix + opens the merge request
├── remoteFixer.js                  # patch a manifest via the API (no checkout)
├── crossProjectFanOut.js           # group-wide emergency response
├── mrReviewer.js                   # safety-review incoming dependency MRs
├── executiveSummary.js             # leadership-facing roll-up
├── dashboardGenerator.js           # static HTML dashboard
├── fleet*.js                       # cross-project aggregation
├── activityFetcher.js              # live "agent activity" feed
├── gitlabAuth.js                   # token resolution (GITLAB_TOKEN / CI_JOB_TOKEN)
└── httpRetry.js                    # shared retry/backoff for HTTP calls

.gitlab/duo/
├── agent-config.yml                # flow execution environment
├── mcp.json                        # connects the Orbit MCP server
├── agents/                         # the Custom Chat Agent definitions
└── flows/                          # the ambient Custom Flow definition

skills/dependency-analysis/SKILL.md # the Agent Skill
scripts/validate-config.js          # CI guard for the Duo agent/flow YAML
.gitlab-ci.yml                      # the pipeline
docs/                               # per-capability deep dives

The engine (src/)

The engine is a Node.js application with a Commander-based CLI. It is pure logic where it can be — the content transforms (version bumps, removals, overrides), the risk math, the dependency-tree resolution, and the report templates are all deterministic, side-effect-free functions wrapped by thin I/O layers. That is what makes the behaviour testable and reproducible.

The orchestration lives in agent.js::analyzeRepository(): detect ecosystems → scan → per-vulnerability Orbit exposure → risk score → rank → (optionally) choose a target version, apply the fix, and open the MR. Every other module is a single responsibility called from there.

Risk scoring

The score is a plain weighted sum minus a discount — no hidden multipliers — so the per-finding breakdown shown on the dashboard always adds up to the final number.

score = (cvss / 10 × 0.45)      # raw severity
      + (exposure  × 0.35)      # how exposed the affected files are
      + (usage     × 0.10)      # how many files import the package
      − (testCoverage × 0.10)   # discount when usage is test-only

Scaled to 0–100, then bucketed: URGENT ≥ 80, HIGH ≥ 50, MEDIUM ≥ 20, LOW < 20 (thresholds are configurable in AGENTS.md).

  • cvss is the real CVSS base score when OSV provides one; otherwise it is computed from the CVSS v3 vector string (src/scanners/cvss.js); only as a last resort does it fall back to a coarse severity-bucket midpoint.
  • exposure comes from a real Orbit query. When Orbit is unavailable it contributes exactly 0 — the score becomes purely CVSS-driven rather than carrying a fabricated baseline. Which case applied is reported as exposureDataSource.
  • File classification (public-API / internal / test) honours public_api_paths / test_paths from AGENTS.md, falling back to built-in heuristics.

Decision — why a transparent linear model. A black-box score that a developer cannot reconstruct is a score they will not trust. Every contribution is shown on the dashboard and in the prefilled issue description, and the parts always sum to the total. Severity is a signal, not the answer; exposure is what turns a CVE into a priority.

Orbit blast-radius analysis

The differentiator is src/blastRadius.js over src/orbitClient.js: it asks GitLab Orbit's knowledge graph which files actually import a vulnerable package (ImportedSymbol → File relationships) and whether that code is public-API, internal, or test-only. This is what separates "vulnerable" from "vulnerable and reachable from your public surface."

  • If Orbit confirms zero importers anywhere, the recommendation flips from "patch" to remove — you do not upgrade a dependency nothing uses; you delete it.
  • The Orbit query DSL is shape-sensitive: single-node lookups use query_type: "search" with a singular node; joins between entities use query_type: "traversal" with nodes and relationships. The client enforces the correct shape.
  • Honest degradation: every Orbit interaction can fail (graph not yet indexed, transient error). When it does, the analysis says so explicitly and proceeds with exposure = 0 rather than inventing importers. The same rule applies everywhere in the engine — see Key design decisions.

For chat sessions, the Orbit MCP server is connected through .gitlab/duo/mcp.json so the agents can query the graph directly.

Remediation logic

src/scanners/ecosystemFixers.js chooses one of three actions per finding, then src/prGenerator.js commits it and opens the MR. The fixers never regenerate lockfiles themselves — they print the exact follow-up command (npm install, go mod tidy, …) which CI runs before the commit.

| Situation | Action | Mechanism | |---|---|---| | Direct dependency with a fixed version | Bump the manifest line | per-ecosystem text transform | | Transitive npm dependency (no direct line to change) | Override | adds an overrides pin forcing >= floor | | Orbit confirms the package is unused | Remove | deletes the dependency entry |

Decision — transitive vulnerabilities must still produce a fix. A vulnerable package you do not declare directly is the most common and most ignored case. The fixer first tries a direct bump; if there is no direct line to change, it falls back to an npm overrides pin rather than giving up with no change. That guarantees a transitive vulnerability still yields a real, committable diff and therefore a real merge request — not a dead end.

Decision — choose the least-disruptive secure version. For an upgrade, the engine does not blindly take OSV's minimum fixed version. It picks the least-disruptive settled version at or above that floor, preferring to stay within the current major where possible, and flags explicitly when a fix is forced to cross a major boundary.

Safety: the engine applies fixes and opens MRs only when asked (--fix --create-pr), and it never merges. The merge request is the human approval gate.

The GitLab Duo integration

The engine is exposed to the GitLab Duo Agent Platform through four artifact types under .gitlab/duo/.

Execution environment — agent-config.yml

Read by GitLab from the project's default branch, this defines the environment every flow runs in:

  • image: node:20 — the Duo runtime CLI requires Node ≥ 20.17.
  • setup_script installs the engine as a global CLI (npm install -g dependencyiq) and downloads the OSV-Scanner binary. Because the engine is installed rather than read from the checked-out repository, the flow scans whatever project it runs in.
  • network_policy allow-lists the registries the engine needs.

The Custom Flow (ambient) — flows/vulnerability-analysis-flow.yaml

components are nodes, routers are edges, flow.entry_point is the start. It runs environment: ambient — a real CI job — so unlike a chat session it can execute the tested engine.

The entry component is a triage router. It inspects the trigger and answers with a single word, and a conditional router (condition.input + routes, the flow-registry mechanism for branching) maps that word to one of three routes:

triage_router ─┬─ analyze   → discover_assess → plan → remediate → verify → track → report → end
               ├─ guardian  → guardian_review ───────────────────────────────────────────────→ end
               └─ freshness → freshness_check ───────────────────────────────────────────────→ end
  • analyze runs the full six-stage remediation pipeline. Each stage runs an exact dependencyiq … command and is instructed to report only command output and never invent findings. The analyze route also accepts a target branch: if the trigger names one, stage 1 checks it out (git fetch && git checkout) before scanning, and degrades safely to the current branch if the checkout fails.
  • guardian_review runs dependencyiq review-mr --post to safety-review an incoming dependency MR.
  • freshness_check runs dependencyiq freshness for a tech-debt policy report.
  • The final report stage posts one clean, professional summary comment on the triggering issue (not just the run log), constrained to the actual findings of this run.

There is deliberately no human-approval node: an ambient flow cannot pause for chat, so the MR that the analyze route opens (but never merges) is the approval gate. scripts/validate-config.js graph-checks the flow on every CI run — orphan components, dangling router targets, prompt/component mismatches, and that the pipeline reaches end.

Decision — one router, many intents. Rather than a separate flow per task, a single ambient flow routes by the triggering context. This keeps one trigger surface (the flow's service account) serving analyse, MR review, and freshness, while the routes themselves remain independent.

The Custom Chat Agents — agents/*.yaml

Interactive, conversational surfaces. Each defines a name, description, public visibility, a staged system_prompt, and a tools list:

  • DependencyIQ — single-repository triage. Follows a staged operator protocol: check Orbit health, get the vulnerability signal, read manifests, run the blast-radius query, show the decision-trail math, then stop at an explicit approval gate before committing a simple version-pin fix.
  • DependencyIQ Emergency Response — organisation-wide incident triage. Given a compromised package, it queries Orbit's group graph for every project that imports it and classifies exposure. (Kept as a chat agent rather than an ambient route because it is high-stakes and parameterised — it should be run deliberately, not autonomously.)
  • DependencyIQ Freshness — read-only. It explains what the AGENTS.md freshness policy will enforce and which declared versions are pinned, then hands off to the CLI/flow for the registry-backed answer.
  • DependencyIQ Guardian — interactive safety review of an incoming dependency merge request, mirroring the mr_safety_review CI logic before the pipeline has even run.

Decision — what a chat agent may and may not do. Web-UI chat cannot run shell commands, so the chat agents never claim to have run a scanner; they query Orbit and read manifests, and can commit a simple version-pin edit because the Commits API can create a branch atomically. Anything heavier — deterministic per-ecosystem fixers, high-volume runs — is the flow's or CLI's job. Each agent's prompt states this boundary as a hard rule.

The Agent Skill — skills/dependency-analysis/SKILL.md

Registers /dependency-analysis plus natural-language trigger keywords in Duo Chat.

Orbit connection — mcp.json

Registers the Orbit MCP server so chat sessions get the Orbit graph tools.

Distribution: the dependencyiq npm package

The engine is published to npm as dependencyiq, with a bin entry exposing the dependencyiq command.

Decision — install the engine, do not vendor it. Earlier the flow invoked node src/agent.js, which required the engine's source to live inside the target repository. That couples the tool to one repo. Publishing the engine as a package and installing it in setup_script decouples them: any project can run the flow without carrying the engine's source. For a security tool this is also the correct trust model — the engine runs inside the customer's own CI, so their dependency data never leaves their environment. The package ships only src/ (no tests, no tooling).

CI/CD pipeline

.gitlab-ci.yml both dog-foods the engine on this repository and ships the dashboard.

| Stage | Job | Purpose | |---|---|---| | validate | validate_config | graph-checks the Duo agent/flow YAML and the AGENTS.md block | | lint | lint | ESLint (non-blocking) | | test | test | Jest with coverage | | security | npm_audit | npm advisory database, a second signal alongside OSV | | security | mr_safety_review | on MRs touching package.json, posts an approve/review/block verdict | | analyze | analyze_vulnerabilities | full scan + Orbit risk scoring, uploads a report artifact | | analyze | freshness_check | tech-debt drift report (non-blocking) | | remediate | remediate_top_vulnerability | manual-gate job that applies the top fix and opens an MR | | deploy | pages | publishes the dashboard to GitLab Pages on the default branch | | publish | catalog_publish | on a semver tag, publishes the agents/flow to the AI Catalog |

Decision — tokens. Same-project read calls fall back to the automatic CI_JOB_TOKEN, so the common path needs zero manual setup. A broader-scoped GITLAB_TOKEN is required only where it genuinely is: opening real merge requests and any cross-project work. The catalog_publish job is gated to semver tags only, so branch and merge-request pipelines are never affected by it.

The live dashboard

The pages job runs dependencyiq dashboard and publishes a self-contained, interactive HTML report (no backend) to GitLab Pages:

  • A decision trail per finding — the weighted score breakdown, the real evidence files grouped by exposure (public-API / internal / test), and that finding's share of the project's total flagged risk.
  • Direct-vs-transitive badges with the resolved dependency chain, from a real BFS over the lockfile.
  • An executive summary — remediation engineer-hours, how many findings are urgent/high, how many are likely-breaking major bumps, how many unused dependencies can be removed.
  • One-click "create issue" links and a copy-paste fix command per row.

Configuration (AGENTS.md)

AGENTS.md carries a fenced yaml block parsed by src/configLoader.js and merged over defaults. It is real configuration, not documentation:

risk_thresholds:        # the URGENT/HIGH/MEDIUM/LOW cut-offs for the 0–100 score
  urgent: 80
  high: 50
  medium: 20
  low: 0
excluded_packages:      # reported, but never proposed for auto-fix
  - "aws-sdk"
public_api_paths:       # files weighted as public-facing exposure
  - "src/api/**"
test_paths:             # files that count toward the test-coverage discount
  - "test/**"
freshness_policy:       # tech-debt thresholds, applied to every direct dependency
  max_minor_versions_behind: 2
  max_days_behind: 180
  stability_window_days: 14

Decision — configuration is data the code reads live. Thresholds and path hints change behaviour on the next run with no code change, and the freshness policy is enforced separately from CVE urgency so tech-debt drift is visible even for packages with no known vulnerability.

Using it in your own project

Because the engine is a published package, adopting DependencyIQ does not require copying its source.

  1. Enable GitLab Duo and GitLab Orbit on your group, with hosted runners available.
  2. Add .gitlab/duo/agent-config.yml to your repository:
    image: node:20
    network_policy:
      include_recommended_allowed: true
      allowed_domains: [registry.npmjs.org, github.com, objects.githubusercontent.com, api.github.com]
    setup_script:
      - npm install -g dependencyiq
      - curl -fsSL -o /usr/local/bin/osv-scanner "https://github.com/google/osv-scanner/releases/download/v1.9.2/osv-scanner_linux_amd64"
      - chmod +x /usr/local/bin/osv-scanner
  3. Register the flow (and any agents) from the AI Catalog and enable its triggers.
  4. Optionally add an AGENTS.md config block to tune thresholds and path hints.

The flow then scans and remediates your repository — the engine installs itself.

CLI reference

npm install -g dependencyiq

dependencyiq analyze   .  --fix --create-pr     # scan, score, fix, open an MR
dependencyiq analyze   .  --impact              # Upgrade Impact Report + migration plan
dependencyiq review-mr .  --post                # safety-review an incoming dependency MR
dependencyiq freshness .                         # tech-debt freshness policy check
dependencyiq dashboard .                         # static HTML report → public/index.html
dependencyiq emergency <group> <package> --dry-run   # org-wide incident triage

When run via the flow or chat agents, GitLab injects the project id and tokens under composite identity; no manual token setup is needed there. For a local run, set GITLAB_TOKEN (api scope) and GITLAB_PROJECT_ID.

Key design decisions and why

A consolidated list of the choices that shape everything else.

  1. One engine, many surfaces. The CLI, chat agents, flow, and skill are different entry points into one tested codebase, never copies of the logic. New surfaces add reach, not duplication.
  2. Never fabricate. Every feature degrades honestly. If Orbit is unavailable, exposure contributes 0 and the report says so; if a CVSS score is missing, it is computed from the vector or marked coarse; the report stage may only restate this run's real findings. A plausible-looking invented number is worse than an honest gap.
  3. Run the tested engine, do not approximate it. The flow executes dependencyiq … via run_command; the flow's behaviour is the engine's behaviour. The agents do not re-derive scanning logic from prose.
  4. Exposure over severity. Risk is driven by what your code actually imports (Orbit), not raw CVSS. A critical CVE in a test-only dependency outranks nothing.
  5. Transparent scoring. A plain weighted sum with no hidden multipliers, shown in full on the dashboard, so the score is auditable and therefore trusted.
  6. Transitive vulnerabilities still get a fix. The fixer falls back to an overrides pin when there is no direct line to change, so the most-ignored class of vulnerability still produces a real MR.
  7. Remove dead weight instead of patching it. Zero Orbit importers flips the recommendation from upgrade to removal.
  8. Never merge. The engine opens MRs but never merges; the MR is the human gate. The ambient flow has no approval node by design.
  9. One ambient flow, routed by intent. A triage router dispatches to analyze / guardian / freshness, keeping one trigger surface for several jobs.
  10. Install the engine, don't vendor it. Publishing dependencyiq to npm decouples the tool from any one repository and keeps analysis inside the customer's own CI.
  11. Configuration is live data. Thresholds, path hints, and the freshness policy are read from AGENTS.md on every run.

Testing

npm test       # Jest with coverage
npm run lint   # ESLint

The pure transforms, the risk math, the dependency-tree resolution, the CVSS computation, the supply-chain signals, the MR-review verdicts, and the dashboard generation are all covered by unit tests.

Requirements

  • GitLab Premium/Ultimate with GitLab Orbit and the Duo Agent Platform enabled.
  • OSV-Scanner on PATH (or set OSV_SCANNER_PATH).
  • For local API operations: a GITLAB_TOKEN with api scope.
  • No external AI API key — reasoning inside the agents and flow uses GitLab's managed model.

License

MIT — see LICENSE.