| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Dependency Radar inspects your Node.js dependency graph and makes structural risk visible.
Unlike basic audit tools, it builds the graph from lockfiles, understands PNPM workspaces, validates declared vs inferred licences, and highlights structural risks before they become production problems.
No account or report upload is required. Source code and generated reports stay on your machine; online collectors send package identifiers to the package manager, configured registry, and the bounded maintenance services described below.
The simplest way to get started is to go to your project root and run:
npx dependency-radarThis runs a scan against the current project and writes a self-contained dependency-radar.html report you can open locally, share with teammates, or attach to tickets and documentation.
You can see a Dependency Radar example report.
List view: search, filter, and drill into every dependency, with licence, vulnerabilities, install risk, sub-dependency and removal counts, and more.
Expanded dependency view: key risk signals first, then status, origins, measured install size, removal preview, licence, vulnerabilities, and upgrade blockers.
Graph view: explore the full dependency tree visually. Selecting a package colours the routes that keep it installed in amber and what it depends on in blue, with a docked side panel for search and per-package details.
Flame view: a profiler-style icicle over the dominator tree. Bar width is the number of packages that would leave node_modules if you deleted it, so the heaviest direct dependencies are obvious at a glance.
Balloon view: every direct dependency is a system orbiting your project, its sub-dependencies fanning out behind it, coloured by lineage.
It is reasonable to be cautious about running a new CLI tool inside your project.
Dependency Radar is designed to be inspectable and low-friction to evaluate:
You can inspect the source on GitHub, view the npm package metadata, or start with an offline scan:
npx dependency-radar --offlineSecurity issues should be reported privately; see SECURITY.md.
| Area | What happens |
|---|---|
| Project files | Reads package manifests, lockfiles, and installed dependency metadata |
| node_modules | Reads package metadata and selected files for dependency analysis |
| Output files | Writes reports/SBOMs only where requested |
| Network | Runs package-manager audit/outdated checks, npm registry maintenance-signal lookups, and bounded ecosyste.ms repo-archived checks by default |
| Offline mode | --offline skips audit, outdated, signature verification, maintenance signals, and targeted registry enrichment checks |
| Source code upload | No source code or generated reports are uploaded during a normal CLI scan |
| Project mutation | Dependency Radar does not install, update, remove, or rewrite dependencies |
Dependency Radar is a review and triage tool. It makes dependency risk easier to see, but it does not make security decisions for you.
Modern Node projects pull in hundreds (or thousands) of transitive dependencies, and most of the risk is structural, not obvious.
Dependency Radar exists to make those hidden signals visible in one place, from the dependencies you actually have installed, with a single report you can review, share, and use to set practical CI guardrails.
Dependency Radar checks the npm registry for maintenance signals on every online scan — free, no account, no upload:
When a repository push timestamp is available (collected during the same bounded ecosyste.ms lookups used for archived checks), both the registry and the repository must be quiet before a package escalates past Slowing. This stops actively developed packages that simply publish rarely from being labelled unmaintained.
How it works, and its limits:
Maintenance data appears in the HTML report (Maintenance column, filter, and per-dependency detail), the JSON model (dependencies[id].maintenance), findings, and the deprecated-dependency / unmaintained-dependency / new-deprecated CI rules.
Dependency Radar matches every scanned package against the community-maintained module-replacements catalogue — part of the e18e (ecosystem performance) initiative — and surfaces suggestions such as:
How it works:
Credit: replacement data is from the MIT-licensed es-tooling/module-replacements project. Refresh the vendored copy with npm run update:replacements.
Install lifecycle scripts and unusual package sources are each individually worth a look, but the combination is materially riskier: a package that runs code at install time and whose content can change without a registry publish (or cannot be integrity-checked) deserves a louder callout. Dependency Radar flags these pairings as dedicated findings:
| Combination | Severity |
|---|---|
| Install scripts + git dependency source | error |
| Install scripts + non-registry tarball source | error |
| Install scripts + unexpected registry host | error |
| Install scripts + missing lockfile integrity | warning |
Each finding explains why the pairing matters and feeds the per-dependency overall risk in the HTML report. The opt-in supply-chain-combo CI rule (see --fail-on) fails the scan when any combination is present.
Each dependency gets a derived upgrade.risk band so reports and tooling can sort on one field:
It is recomputed after registry enrichment so a registry-discovered deprecation (which adds a blocker) is reflected.
The report's Graph View offers five switchable layouts (toolbar buttons), all sharing the same docked side panel and package search:
Shared behaviour:
Run a scan against the current project (writes dependency-radar.html):
npx dependency-radarThe scan command is the default and can also be run explicitly as npx dependency-radar scan.
Options:
| Flag | Description |
|---|---|
| --project <path> | Path to the project to scan (defaults to current directory) |
| --quiet | Suppress progress/info logs, browser opening, and footer messaging while keeping the final summary and failures visible |
| --out <path> | Output path for the report file |
| --format <format> | Output format: html, json, sarif, cyclonedx, or spdx |
| --sbom <format> | Convenience alias for SBOM output: cyclonedx or spdx |
| --target-node <major> | Add Node major compatibility findings based on local engines.node metadata |
| --audit-signatures | Run npm audit signatures for registry signature/provenance verification (opt-in; skipped with --offline) |
| --schema | Print the current Dependency Radar JSON schema, or write it with --out <path> |
| --offline | Skip registry lookups: npm audit, npm outdated, signature checks, maintenance signals, and targeted registry enrichment |
| --no-maintenance | Skip registry maintenance signals (deprecated/unmaintained/archived checks) while keeping audit and outdated checks |
| --json | Output JSON instead of HTML (dependency-radar.json) |
| --timestamp | Add a local timestamp to generated report filenames (dependency-radar.YYYY-MM-DD_HH-mm-ss.html) |
| --strict | Exit with code 2 when an enabled collector is incomplete or unavailable |
| --version, -V | Print the installed Dependency Radar version |
| --no-report | Run analysis only; no HTML/JSON output written |
| --keep-temp | Keep the temporary .dependency-radar/ folder for debugging |
| --open | Open the generated report using the system default browser |
| --fail-on <rules> | Fail with exit code 1 when selected policy rules are violated (see below) |
| --help | Show all options |
slide scans and writes a single 16:9 SVG dashboard designed for slide decks and PR comments: measured install size with a treemap of the largest packages, dependency counts, vulnerable packages, maintenance concerns, licence issues, upgrade blockers, and duplicate versions. Counts mirror the HTML report's header chips, and anything a collector never checked shows as "Not checked" rather than zero.
npx dependency-radar slideWrites dependency-radar-slide.svg (dark). Add --light-mode for the light palette. Add --png to also render a 1920x1080 PNG; this borrows a locally installed Chrome, Edge, Chromium, or rsvg-convert because the CLI itself ships no rasteriser. Without one, use the HTML report's Scorecard view, which exports PNG, JPEG, and SVG from the browser.
The same scorecard lives in the HTML report as the Scorecard view, with a theme toggle and export buttons in its toolbar.
Use explain when you want a fast terminal view for one package without generating HTML or JSON output:
npx dependency-radar explain lodashThis reuses the normal scan model and then filters it in memory. explain does not add its own extra lookup pipeline and does not write dependency-radar.html, but it can still trigger the same network-dependent audit and outdated steps as a normal scan unless you pass --offline.
explain shows the signals already present in Dependency Radar's scan model, including:
Examples:
npx dependency-radar explain lodashnpx dependency-radar explain lodash --project ./my-appnpx dependency-radar explain lodash --project ./my-app --offlineNotes:
Use why to print shortest dependency paths from direct dependencies to a package:
npx dependency-radar why lodashThis uses the same local scan model as the HTML report. When full paths are unavailable, it falls back to the package origins and direct parent evidence available in the report model.
Use compare to scan the current project and compare it with an earlier JSON report:
npx dependency-radar compare ./dependency-radar-before.json --json --offlineThe comparison highlights added dependencies, removed dependencies, one-version package changes, new findings, and resolved findings. This is useful in pull requests and release checks.
Compare mode can also fail CI when a dependency change introduces a new risky trait compared with a committed JSON baseline:
npx dependency-radar compare ./dependency-radar-baseline.json --offline --fail-on new-supply-chain-signal,new-install-script,new-child-processThis is intentionally not a generic "any dependency changed" gate. It is a review guardrail for targeted local signals such as a newly introduced install script, native build surface, CLI executable, direct dependency, local execution capability signal, packaging signal, or lockfile supply-chain source signal. These signals mean "review this change"; they are not malware verdicts.
Local execution capability signals are derived by bounded static inspection of lifecycle script commands, referenced install files, package bin targets, entry files, and a small capped subset of text-like package source files. Dependency Radar currently reports signals such as child process APIs, network access references, environment access, home directory access, SSH-related references, and obfuscation-like code shape. Scanning is capped by file count and bytes per file so large packages do not become expensive to inspect.
Packaging signals are local metadata/content cues such as declared bundled dependencies or an embedded npm-shrinkwrap.json. They are review aids for unusual packaging patterns, not proof of compromise.
Dependency Radar can perform a small number of targeted npm registry metadata lookups for packages that already show local review signals, such as install hooks, native bindings, executable bins, supply-chain source signals, or suspicious execution/packaging signals.
This enrichment is bounded and selective:
When metadata is available, Dependency Radar may derive additional review signals such as:
These are heuristic review signals, not proof that a package is malicious or compromised.
These signals can appear in the report, JSON output, compare mode, and CI fail rules when supported by the current scan and baseline data.
npx dependency-radar --fail-on directly-imported-vuln,licence-mismatch
Supported rules:
| Rule | Description |
|---|---|
| directly-imported-vuln | Fail if at least one runtime vulnerability has direct static-import evidence in project source |
| reachable-vuln | Deprecated alias for directly-imported-vuln; this is not exploit reachability analysis |
| production-vuln | Fail if at least one runtime vulnerability is present (static-import evidence ignored) |
| high-severity-vuln | Fail if at least one high/critical vulnerability is present |
| licence-mismatch | Fail if at least one dependency has a declared-vs-inferred licence mismatch |
| copyleft-detected | Fail if strong copyleft (GPL/AGPL) appears in runtime dependencies |
| unknown-licence | Fail if at least one dependency has neither declared nor inferred licence data |
| supply-chain-source | Fail if lockfile source signals detect git/local/tarball sources, missing integrity, or unexpected registry hosts |
| supply-chain-combo | Fail if any dependency pairs install lifecycle scripts with a mutable or unverifiable source (git dependency, non-registry tarball, unexpected registry host, or missing lockfile integrity) |
| deprecated-dependency | Fail if at least one dependency is marked deprecated (registry or local metadata) |
| unmaintained-dependency | Fail if at least one dependency has an unmaintained or archived maintenance status |
The following rules are evaluated only by compare <previous dependency-radar.json> and use the previous JSON report as the baseline:
| Rule | Description |
|---|---|
| new-deprecated | Fail if a dependency is now marked deprecated and no same-named dependency in the baseline was (note: baselines produced before schema 1.5 rarely carry deprecation data, so this rule can fire once on the first post-upgrade compare) |
| new-supply-chain-signal | Fail if the current scan has a lockfile supply-chain signal that was not present in the baseline for the same package, or was not present at all when the signal is not package-specific |
| new-install-script | Fail if a dependency now exposes install lifecycle hooks and the baseline did not show install hooks for that package |
| new-native-binding | Fail if a dependency now exposes native build or binary surface and the baseline did not show native surface for that package |
| new-bin | Fail if a dependency now declares a package bin executable and the baseline did not show a bin for that package |
| new-direct-dependency | Fail if a package is now direct and was not direct in the baseline |
| new-child-process | Fail if a dependency newly shows local child process API usage |
| new-network-access | Fail if a dependency newly shows local network access references |
| new-env-access | Fail if a dependency newly shows local environment variable access |
| new-home-access | Fail if a dependency newly shows local home directory access |
| new-ssh-usage | Fail if a dependency newly shows SSH-related path, command, or environment references |
| new-obfuscation-signal | Fail if a dependency newly shows an obfuscation-like local code shape |
| new-bundled-dependencies | Fail if a dependency newly declares bundled dependencies |
| new-shrinkwrap | Fail if a dependency newly contains an embedded npm-shrinkwrap.json |
| new-recent-package | Fail if a dependency newly shows the recent-package registry review signal |
| new-recent-version | Fail if a dependency newly shows the recent-version registry review signal |
| new-low-release-history | Fail if a dependency newly shows the low-release-history registry review signal |
| new-reactivated-package | Fail if a dependency newly shows the reactivated-package registry review signal |
| new-old-major-patch | Fail if a dependency newly shows the old-major-new-patch registry review signal |
Every --fail-on rule requires its underlying evidence collector to be available. If a required collector fails, is partial, or was deliberately skipped (for example, a vulnerability rule combined with --offline), Dependency Radar fails closed with exit code 2 instead of silently treating missing evidence as a clean result.
Exit codes are stable for scripting:
| Code | Meaning |
|---|---|
| 0 | Scan completed and no selected policy was violated |
| 1 | One or more selected policy rules were violated |
| 2 | Invalid usage or incomplete evidence required by --fail-on / --strict |
--strict applies the same exit-code-2 behaviour to any enabled collector that is partial or unavailable, even when no policy rule depends on it. Collectors deliberately disabled with --offline or --no-maintenance are recorded as skipped; they only block a policy that requires them.
For compare-mode delta rules, the failure output includes the package and the specific change, for example:
- 1 new install script surface
- lodash@4.17.21 introduced install hooks: postinstall
- 1 new execution signal: child-process
- foo@1.2.3 introduced execution signal: child-process
- 1 new registry risk signal: recent-version
- bar@4.5.6 introduced registry risk signal: recent-version
To use this in CI, commit a known-good JSON report, regenerate it intentionally when reviewed dependency changes are accepted, and compare pull requests against that file.
npx dependency-radar --opennpx dependency-radar --project ./my-app --out ./reports/dependency-radar.html
npx dependency-radar --format sarif --out ./reports/dependency-radar.sarifnpx dependency-radar --sbom cyclonedx --out ./reports/bom.cdx.jsonnpx dependency-radar --sbom spdx --out ./reports/bom.spdx.jsonnpx dependency-radar --target-node 22npx dependency-radar --audit-signaturesThis runs npm audit signatures as an opt-in online check. It is skipped when --offline is used.
npx dependency-radar --schema --out ./reports/dependency-radar.schema.jsonnpx dependency-radar --keep-tempnpx dependency-radar --offlinenpx dependency-radar --no-report --fail-on directly-imported-vuln,licence-mismatchnpx dependency-radar scan --quiet --no-report--quiet is quiet, not silent:
Note: When used with --no-report, the --keep-temp flag is ignored. Temporary files are normally deleted automatically. If you intentionally use --keep-temp (without --no-report) for debugging, the .dependency-radar/ folder may contain dependency metadata and should not be committed.
At the end of each scan, the CLI prints a summary block with high-level counts, for example:
Summary:
• Direct dependencies scanned: 6
• Transitive dependencies scanned: 62
• Vulnerabilities: 2 in 1 package (0 directly imported)
• Dependencies with no static import reference: 0
• License mismatches: 3
• Major upgrade blockers: 24
- 1 strict peer dependency constraint
- 22 narrow engine ranges
- 2 native bindings
- 1 install lifecycle script
The blocker detail counts can overlap: a single package may contribute to multiple blocker categories.
| Package Manager | Dependency Tree | Audit | Outdated | Workspaces |
|---|---|---|---|---|
| npm | ✅ Lockfile-first (package-lock.json / npm-shrinkwrap.json) | ✅ | ✅ | ✅ |
| pnpm | ✅ Lockfile-first (pnpm-lock.yaml) | ✅ | ✅ | ✅ |
| Yarn Classic (v1) | ✅ Lockfile-first (yarn.lock) | ✅ | ✅ | ✅ |
| Yarn Berry (v2+, node-modules linker) | ✅ Lockfile-first (yarn.lock) | ✅ | ⚠️ Plugin-dependent | ✅ |
| Yarn Plug'n'Play | ⚠️ Lockfile-derived graph only; package metadata may be incomplete without node_modules | ✅ | ⚠️ Plugin-dependent | ✅ |
| Bun | ⚠️ Text bun.lock parsing; binary bun.lockb is reported with a migration hint | N/A | ❌ | ⚠️ package.json workspaces only |
When you run npx dependency-radar (or dependency-radar scan), the CLI executes this pipeline:
The scan runs on your machine: package metadata is read from node_modules. Audit/outdated commands, optional signature checks, maintenance-signal lookups, and targeted registry enrichment require network access and are skipped or disabled with --offline.
The explain command reuses this same pipeline with report writing disabled, then filters the in-memory model down to a single package for terminal output.
These two fields are inferred from local signals. They are intended as review hints, not strict truth.
runtimeImpact is inferred from the import graph and file-path classification:
introduction is inferred from dependency graph roots, scope, and runtime impact:
upgrade.blockers is a local, static heuristic for upgrade friction. It does not run package code and does not query external APIs.
For each installed dependency, Dependency Radar inspects local package metadata and install-surface signals and may add one or more blockers:
upgrade.blocksNodeMajor is only emitted when local signals suggest Node major upgrades may be risky for that package. It is set from a subset of blockers:
It is not set from peerDependency or deprecated alone.
Dependency Radar validates SPDX licenses declared in package.json and can infer licenses from LICENSE files when declarations are missing or invalid. It works offline and uses a checked-in SPDX identifier list with no runtime network access. Maintainers update that list explicitly with npm run update:spdx; normal builds are reproducible and do not fetch mutable upstream data. Each dependency gets a structured license record with:
This logic applies to all dependencies (direct and transitive). Inferred licenses are never treated as authoritative over valid declared SPDX expressions.
licenseRisk is derived from SPDX IDs, with one escalation rule for safety: when status is mismatch (declared SPDX differs from inferred LICENSE text), risk is promoted to at least amber. In the HTML report, the License badge shows a trailing * when status is mismatch. When a dependency repository resolves to GitHub, the expanded License section links to package.json and LICENSE source files for faster verification.
Use --json to write the aggregated scan data as JSON (defaults to dependency-radar.json).
The JSON schema matches the AggregatedData TypeScript interface in src/types.ts. For quick reference:
export interface AggregatedData {
schemaVersion: '1.8'; // Report schema version for compatibility checks
generatedAt: string; // ISO timestamp when the scan finished
dependencyRadarVersion: string; // CLI version that produced the report
git: {
branch: string; // Git branch name, empty when unavailable/detached
};
project: {
projectDir: string; // Project path relative to the user's home directory (e.g. /Developer/app)
name?: string; // package.json#name from the scanned project root
version?: string; // package.json#version from the scanned project root
description?: string; // package.json#description
license?: string; // package.json#license
keywords?: string[]; // package.json#keywords
homepage?: string; // package.json#homepage
repository?: string; // repository URL (string or repository.url)
constraints?: {
os?: string[]; // package.json#os constraints
cpu?: string[]; // package.json#cpu constraints
enginesNode?: string; // package.json#engines.node
};
dependencyPolicy?: {
overrides?: Record<string, unknown>; // package.json overrides plus pnpm workspace overrides
resolutions?: Record<string, unknown>; // package.json#resolutions
};
dependencyPolicySummary?: {
hasOverrides: boolean;
overrideCount: number; // Top-level override entries
overriddenPackageNames?: string[]; // Package names parsed from override selectors
hasResolutions: boolean;
resolutionCount: number; // Top-level resolution entries
resolvedPackageNames?: string[]; // Package names parsed from resolution selectors
sources?: string[]; // Where policy came from (e.g. package.json#overrides, pnpm-workspace.yaml#overrides)
};
};
environment: {
nodeVersion: string; // Node.js version from process.versions.node
runtimeVersion: string; // Node.js runtime version from process.version
minRequiredMajor: number; // Strictest Node major required by dependency engines (0 if unknown)
targetNodeMajor?: number; // Node major passed through --target-node
platform?: string; // OS platform (process.platform)
arch?: string; // CPU architecture (process.arch)
ci?: boolean; // True when CI indicators are detected
packageManagerField?: string; // package.json packageManager field (e.g. pnpm@9.1.0)
packageManager?: 'npm' | 'pnpm' | 'yarn' | 'bun'; // Package manager used for dependency/audit/outdated collection
packageManagerVersion?: string; // Version of the selected package manager (when available)
toolVersions?: {
npm?: string;
pnpm?: string;
yarn?: string;
};
};
workspaces: {
enabled: boolean; // True when the scan used workspace aggregation
type?: 'npm' | 'pnpm' | 'yarn' | 'bun' | 'none'; // Workspace mode (CLI currently always emits this)
packageCount?: number; // Number of workspace packages scanned (CLI currently always emits this)
workspacePackages?: WorkspacePackage[]; // Lightweight first-party workspace metadata
};
summary: {
dependencyCount: number; // Total EXTERNAL dependencies in the graph
directCount: number; // External dependencies listed in package.json
transitiveCount: number; // External dependencies pulled in by other dependencies
findingCount?: number; // Number of normalized findings generated from the dependency model
};
scanStatus?: {
complete: boolean; // True when requested collectors did not fail or return partial data
collectors: Record<
| 'dependencyTree'
| 'audit'
| 'imports'
| 'maintenance'
| 'registryMetadata'
| 'supplyChain',
'available' | 'partial' | 'unavailable' | 'skipped'
>;
warnings: string[]; // Consequences of missing, partial, or deliberately skipped evidence
};
supplyChain?: {
signals: Array<{
type:
| 'git-dependency'
| 'file-dependency'
| 'non-registry-tarball'
| 'missing-integrity'
| 'unexpected-registry-host'
| 'signature-verification-failed'
| 'signature-verification-unavailable';
packageName?: string;
packageVersion?: string;
packageId?: string;
source: string;
detail: string;
}>;
signatureAudit?: {
attempted: boolean;
ok: boolean;
output?: string;
error?: string;
};
};
findings?: DependencyFinding[]; // Normalized review/CI findings
dependencies: Record<string, DependencyRecord>; // External third-party packages keyed by name@version
}
export interface WorkspacePackage {
name: string; // Workspace package name from package.json
relativePath: string; // Workspace-relative path (e.g. apps/web)
directExternal: {
runtime: number; // Unique direct external deps from dependencies + optionalDependencies
dev: number; // Unique direct external deps from devDependencies
};
}
export interface DependencyRecord {
package: {
id: string; // Stable identifier in the form name@version
name: string; // Package name from npm metadata
version: string; // Installed version from npm ls
description?: string; // Description from the installed package.json (if present)
fileCount?: number; // Number of files in the installed package folder (excluding nested node_modules)
installSize?: {
totalBytes: number; // Measured on-disk bytes (uncompressed, excluding nested node_modules)
codeBytes: number; // Bytes in JS/TS source files
typesBytes: number; // Bytes in .d.ts/.d.mts/.d.cts declaration files
mapBytes: number; // Bytes in source maps
otherBytes: number; // Everything else (assets, manifests, docs)
}; // Omitted when any file could not be read, so partial sums are never presented as measurements
platform?: {
os?: string[]; // package.json#os constraints (npm ! negations preserved)
cpu?: string[]; // package.json#cpu constraints
};
hasBin?: true; // True if package.json declares at least one executable in `bin`
deprecated: boolean; // True when deprecated on the npm registry (installed or latest version), or via a local package.json deprecated flag
links: {
npm: string; // npm package page URL
repository?: string; // Repository URL (if present)
homepage?: string; // Homepage URL (if present)
bugs?: string; // Issue tracker URL (if present)
};
};
compliance: {
license: {
declared?: {
spdxId: string; // SPDX ID or expression from package.json
expression: boolean; // True when SPDX expression (AND/OR/WITH)
deprecated: boolean; // True if SPDX ID is deprecated
valid: boolean; // True if SPDX ID/expression is valid
};
inferred?: {
spdxId: string; // SPDX ID inferred from LICENSE text
confidence: 'high' | 'medium' | 'low'; // Heuristic confidence
};
exception?: {
id: string; // SPDX exception id
deprecated: boolean; // True if exception is deprecated
valid: boolean; // True if exception id is valid
};
status:
| 'declared-only'
| 'inferred-only'
| 'match'
| 'mismatch'
| 'invalid-spdx'
| 'unknown';
};
licenseRisk: 'green' | 'amber' | 'red'; // Risk classification derived from declared/inferred SPDX ids (mismatch is escalated to at least amber)
};
security: {
summary: {
critical: number; // npm audit counts for critical issues
high: number; // npm audit counts for high issues
moderate: number; // npm audit counts for moderate issues
low: number; // npm audit counts for low issues
highest: 'low' | 'moderate' | 'high' | 'critical' | 'none'; // Highest severity present
risk: 'green' | 'amber' | 'red'; // Risk classification derived from audit counts
};
advisories?: Array<{
id: string; // Advisory identifier (GHSA, npm advisory ID, or source-specific fallback)
title: string; // Human-readable advisory title
severity: 'low' | 'moderate' | 'high' | 'critical';
vulnerableRange: string; // Semver range
fixAvailable: boolean; // True if npm audit indicates a fix exists
url: string; // Advisory URL (may be empty when unavailable)
}>;
};
upgrade: {
nodeEngine: string | null; // engines.node from the package.json (if present)
outdatedStatus?: 'current' | 'patch' | 'minor' | 'major' | 'unknown'; // Derived from npm outdated (field is omitted rather than set to 'current')
latestVersion?: string; // Latest version from outdated data (present for patch/minor/major when known)
blockers?: Array<'nodeEngine' | 'peerDependency' | 'nativeBindings' | 'installScripts' | 'deprecated'>; // Reasons for upgrade friction
blocksNodeMajor?: boolean; // Present when local signals indicate a Node major bump is risky
};
usage: {
direct: boolean; // True if declared in package.json (dependencies/devDependencies/etc.)
scope: 'runtime' | 'dev' | 'optional' | 'peer'; // Scope inferred from the declaring root package(s)
depth: number; // Minimum dependency tree depth observed in npm ls
origins: {
rootPackageCount: number; // Number of direct roots that introduce this dependency
topRootPackages: Array<{ name: string; version: string }>; // Up to 10 root packages (name/version)
parentPackageCount: number; // Number of direct parents
topParentPackages: string[]; // Up to 5 direct parent ids (name@version)
workspaces?: string[]; // Workspace packages that declare/use this dependency
};
introduction?: 'direct' | 'tooling' | 'framework' | 'testing' | 'transitive' | 'unknown'; // Heuristic for why the dependency exists
runtimeImpact?: 'runtime' | 'build' | 'testing' | 'tooling' | 'mixed'; // Heuristic based on import locations
importUsage?: {
fileCount: number; // Number of project files importing this package (import graph)
topFiles: string[]; // Top import locations (bounded to 5)
};
tsTypes: 'bundled' | 'definitelyTyped' | 'none' | 'unknown'; // TypeScript type availability
};
graph: {
fanIn: number; // Number of packages that depend on this package
fanOut: number; // Number of packages this package depends on
subDeps?: {
// Declared outgoing dependency edges; values are tuples.
// tuple[0] = declared version range, tuple[1] = resolved dependency id or null if not installed.
// Only installed dependencies have full dependency records in the top-level list.
dep?: Record<string, [string, string | null]>; // Declared runtime deps
dev?: Record<string, [string, string | null]>; // Declared dev deps
opt?: Record<string, [string, string | null]>; // Declared optional deps
peer?: Record<string, [string, string | null]>; // Declared peer deps
};
};
execution?: {
risk: 'amber' | 'red'; // Install-time risk (green implied when absent)
native?: true; // True if native bindings or build tooling are detected
scripts?: {
hooks: Array<'preinstall' | 'install' | 'postinstall' | 'prepare'>; // Lifecycle hooks detected
complexity?: number; // Heuristic complexity (stored only when high)
signals?: Array<
| 'network-access'
| 'dynamic-exec'
| 'child-process'
| 'encoding'
| 'obfuscated'
| 'reads-env'
| 'reads-home'
| 'uses-ssh'
>; // Review-worthy install-time signals (sparse)
};
};
maintenance?: {
attempted: true; // Present whenever a registry maintenance lookup ran (or was served from cache)
ok: boolean; // False when the lookup failed (status is then 'unknown')
status: 'deprecated' | 'archived' | 'unmaintained' | 'stale' | 'active' | 'unknown';
deprecated?: {
installedVersion: boolean; // Installed version carries a registry deprecation notice
latestVersion: boolean; // dist-tags.latest carries a registry deprecation notice
message?: string; // Author-provided deprecation message (capped)
};
repoArchived?: boolean; // Source repository archived (best-effort ecosyste.ms check)
repoCheckedAt?: string; // When the repo-archived check ran
packageModifiedAt?: string; // Packument `modified` timestamp (any registry write)
monthsSinceModified?: number; // Whole months since packageModifiedAt
latestVersion?: string; // dist-tags.latest (also backfills upgrade.latestVersion)
fetchedAt?: string; // When the underlying registry data was fetched
fromCache?: true; // Served from the local 7-day cache
error?: string; // Lookup failure detail
};
}For full details and any future changes, see src/types.ts.
New or changed user-facing functionality should include automated tests where practical. For larger dependency graph, package manager, report output, or supply-chain signal changes, run the relevant fixture tests as well as the unit tests.
npm install
npm run build| Script | Description |
|---|---|
| npm run build | Build report assets and compile TypeScript to dist/ |
| npm run dev | Run a scan from source (tsx src/cli.ts scan) |
| npm run scan | Run a scan from the built output (node dist/cli.js scan) |
| npm run dev:report | Run the report UI dev server |
| npm run update:spdx | Refresh bundled SPDX identifiers from the pinned upstream revision |
| npm run build:report-ui | Build report UI assets |
| npm run build:report | Rebuild report assets used by the CLI |
| npm run test:unit | Run Vitest unit tests |
| npm run test:schemas | Validate Dependency Radar, CycloneDX, SPDX, and SARIF output against pinned schemas |
| npm run test:unit:watch | Watch mode for fast local iteration |
| npm run test:fixtures | Run curated fixture integration tests (mostly offline scans) |
| npm run test:fixtures:online | Run online fixture checks (audit/outdated regression coverage) |
| npm run test:fixtures:all | Run all fixture integration tests |
| npm run test:docker:node14 | Pack the published artifact and smoke-test it in Docker on Node 14.21.3 |
| npm run test:docker | Alias for the Node 14.21.3 Docker compatibility smoke test |
| npm run test:release | Full pre-release gate (build + unit + schema + fixture + Docker Node 14 smoke test + package dry run) |
Fixture orchestration lives in /test-fixtures/package.json with helper scripts under /test-fixtures/scripts. The intentionally vulnerable npm-registry-signals fixture stores its package definition as fixture-manifest.json; the fixture runner materializes an ignored package.json only while testing so GitHub's dependency graph does not report fixture vulnerabilities as repository alerts.
The Docker smoke test uses the packed tarball, installs it inside node:14.21.3-bullseye, and runs an offline scan against test-fixtures/license-edge-cases. This verifies the published CLI on the oldest Node version we currently exercise in automation without requiring local Node 14 installation.
The HTML report UI is developed in a separate Vite project located in report-ui/. This provides a proper development environment with hot reload, TypeScript support, and sample data.
Start the development server:
npm run dev:reportThis opens the report UI in your browser with sample data covering all dependency states (various licenses, vulnerability severities, usage statuses, etc.).
Build workflow:
File structure:
Bug reports and pull requests are welcome via GitHub.
For bug reports, please include:
Please remove secrets, private package names, or sensitive project details before sharing logs or reports.
For pull requests, small focused changes are easiest to review. Before opening a PR, please make sure the project builds and the relevant tests pass:
npm run build
npm run test:unitFor larger dependency graph, package manager, report output, or supply-chain signal changes, also run the relevant fixture tests where practical.
Please do not report suspected security vulnerabilities in public issues. See SECURITY.md for private reporting guidance.
Release notes are published through GitHub Releases, which act as the project changelog.
Each release summarises notable changes, fixes, and compatibility notes where relevant.
| Back | FazBrowse Home | New Git URL |