scan is the unified entry point for all dependency lifecycle analysis. It accepts PURLs, GitHub URLs, CycloneDX SBOMs, go.mod files, and stdin pipes.
# NPM package
./uzomuzo scan pkg:npm/lodash@4.17.21
# Python package
./uzomuzo scan pkg:pypi/requests@2.28.1
# Maven package
./uzomuzo scan pkg:maven/org.springframework/spring-core@5.3.8
# GitHub repository
./uzomuzo scan github.com/microsoft/typescript
# Multiple inputs (PURL and GitHub URL can be mixed)
./uzomuzo scan pkg:npm/express@4.18.2 pkg:pypi/requests@2.28.1
./uzomuzo scan https://github.com/expressjs/express github.com/psf/requestsExample: Single PURL (detailed output)
$ uzomuzo scan pkg:npm/express@4.18.2
--- Summary Table ---
STATUS PURL LIFECYCLE BUILD
✅ ok pkg:npm/express@4.18.2 Active Hardened 9.3
── Summary ─────────────────────────────────────────────────
│ 1 dependencies | ✅ 1 ok | ⚠️ 0 caution | 🔴 0 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
--- Detailed Report ---
--- PURL 1 ---
── pkg:npm/express@4.18.2 ──────────────────────────────────
│ Fast, unopinionated, minimalist web framework for node.
│ ✅ Active: Actively maintained with recent releases
├─ Signals ─────────────────────────────────────────────────
│ Recent Stable Release: true
│ Last Human Commit: 2026-03-31
│ Maintained Score: 10/10
├─ Health ──────────────────────────────────────────────────
│ 68888 stars
│ Used by: 2211 packages
│ Depends on: 31 direct, 39 transitive
│ Scorecard Overall: 8.4/10 Maintained: 10.0/10
│ Last Commit: 2026-03-31
├─ Releases ────────────────────────────────────────────────
│ Stable: 5.2.1 (2025-12-01)
│ Pre-release: 5.0.0-beta.3 (2024-03-25)
│ Requested: 4.18.2 (2022-10-08)
├─ Build Integrity ─────────────────────────────────────────
│ ✅ Hardened 9.3/10 (5/6)
│ Dangerous Workflow 10 Branch Protection —
│ Code Review 9 Token Permissions 10
│ Binary Artifacts 10 Pinned Deps 6
│ → https://scorecard.dev/viewer/?uri=github.com%2Fexpressjs%2Fexpress
├─ Links ───────────────────────────────────────────────────
│ Homepage: https://expressjs.com
│ Repository: https://github.com/expressjs/express
│ Registry: https://www.npmjs.com/package/express
│ deps.dev: https://deps.dev/npm/express
└───────────────────────────────────────────────────────────
For ≤3 inputs, the detailed format is used automatically. Use --format detailed to force it for larger inputs.
# CycloneDX SBOM file
./uzomuzo scan --sbom bom.json
# Pipe from SBOM tools
trivy fs . --format cyclonedx | ./uzomuzo scan --sbom -
trivy image my-app:latest --format cyclonedx | ./uzomuzo scan --sbom -
syft . -o cyclonedx-json | ./uzomuzo scan --sbom -When the SBOM includes a CycloneDX dependencies section (produced by most modern SBOM generators), uzomuzo classifies dependencies as direct or transitive based on the dependency graph. By default, only direct dependencies are shown. In OSS health assessment (unlike vulnerability scanning), transitive dependency issues are not directly actionable by the user — if a transitive dependency has a problem, the resolution path is to update or replace the direct dependency that pulls it in. Filtering also reduces API calls for faster results.
Use --show-transitive to include transitive dependencies in the output:
# Direct dependencies only (default)
trivy fs . --format cyclonedx | ./uzomuzo scan --sbom -
# Include transitive dependencies
trivy fs . --format cyclonedx | ./uzomuzo scan --sbom - --show-transitiveWhen transitive dependencies are included, output shows a RELATION column indicating direct, transitive (via-parent), or Unknown (for SBOMs without a dependencies section). SBOMs without dependency graph information gracefully fall back to showing all components as Unknown.
./uzomuzo scan # auto-detect go.mod in cwd
./uzomuzo scan --file go.mod # explicit pathExample: go.mod (table output with RELATION column)
$ uzomuzo scan --file go.mod -f table
STATUS PURL RELATION LIFECYCLE BUILD
🔴 replace pkg:golang/github.com/dgrijalva/jwt-go@v3.2.0+incompatible direct EOL-Confirmed —
⚠️ caution pkg:golang/github.com/gorilla/mux@v1.8.1 direct Stalled Moderate 6.5
✅ ok pkg:golang/github.com/stretchr/testify@v1.9.0 direct Active Moderate 6.7
── Summary ─────────────────────────────────────────────────
│ 3 dependencies | ✅ 1 ok | ⚠️ 1 caution | 🔴 1 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
go.mod input adds a RELATION column showing direct or indirect dependency relationship.
Scan a GitHub Actions workflow YAML to evaluate the lifecycle health of referenced Actions:
./uzomuzo scan --file .github/workflows/ci.ymlThis extracts uses: directives (e.g., actions/checkout@v4) and evaluates each referenced Action as a GitHub repository.
Example: GitHub Actions workflow (detailed output)
$ uzomuzo scan --file .github/workflows/ci.yml -f detailed
── https://github.com/actions/checkout ─────────────────────
│ Description: Action for checking out a repo
│ ✅ Active
│ Reason: Recent human commits but no recent package publishing; maintenance score unavailable (Scorecard not found)
├─ Health ──────────────────────────────────────────────────
│ 7733 stars
│ Last Commit: 2026-01-09
├─ License ─────────────────────────────────────────────────
│ Project: MIT (github)
│ Requested Version: (none)
├─ Links ───────────────────────────────────────────────────
│ Homepage: https://github.com/features/actions
│ Repository: https://github.com/actions/checkout
└───────────────────────────────────────────────────────────
── https://github.com/golangci/golangci-lint-action ────────
│ Package: pkg:golang/github.com/golangci/golangci-lint-action@v1.2.2
│ Description: Official GitHub Action for golangci-lint from its authors
│ ✅ Active
│ Reason: Recent human commits (VCS-direct ecosystem; commits deliver updates to consumers); maintenance score ≥ 3
├─ Health ──────────────────────────────────────────────────
│ 1419 stars
│ Score: 6.9/10 Maintained: 10.0/10
│ Last Commit: 2026-04-01
├─ Releases ────────────────────────────────────────────────
│ Stable: v1.2.2 (2020-07-10)
│ Highest (SemVer): v1.2.3-0.20260105112450-f75c1c4ee8cf (2026-01-05)
├─ License ─────────────────────────────────────────────────
│ MIT (depsdev)
├─ Links ───────────────────────────────────────────────────
│ Homepage: https://github.com/marketplace/actions/golangci-lint
│ Repository: https://github.com/golangci/golangci-lint-action
│ Registry: https://pkg.go.dev/github.com%2Fgolangci%2Fgolangci-lint-action
│ deps.dev: https://deps.dev/go/github.com/golangci/golangci-lint-action
└───────────────────────────────────────────────────────────
When scanning GitHub URLs, --include-actions automatically fetches the target repository's workflow files and evaluates referenced Actions:
# Scan a repo AND its GitHub Actions dependencies
./uzomuzo scan https://github.com/owner/repo --include-actions
# Combined with fail policy
./uzomuzo scan https://github.com/owner/repo --include-actions --fail-on stalledOutput includes a SOURCE column to distinguish direct results from discovered Actions. JSON and CSV formats include a source field ("actions" for discovered entries).
Note:
--include-actionsis opt-in because it makes additional GitHub API calls to fetch workflow files. It requiresGITHUB_TOKENto be set (the Contents API is used to fetch workflow YAML). It is only supported for GitHub URL inputs (not--sbomor--file go.mod).
When combined with --show-transitive, --include-actions recursively resolves composite actions referenced by the discovered workflows. This includes:
- Remote composite actions: Actions that reference other Actions via
uses:in theiraction.yml - Local composite actions:
./references resolved via the GitHub Contents API relative to the repository root
# Include transitive composite action dependencies
./uzomuzo scan https://github.com/owner/repo --include-actions --show-transitiveCycle detection prevents infinite recursion when composite actions reference each other.
List one identifier per line (PURL or GitHub URL) in a text file:
pkg:npm/express@4.18.2
pkg:pypi/django@4.2.0
https://github.com/golang/go
pkg:cargo/serde@1.0.136
Run:
./uzomuzo scan --file input_file.txt --sample 500File mode is designed for large inputs (thousands of lines). --sample N (N > 0) enables random sampling. If --sample is omitted, the configured default sample size (APP_SAMPLE_SIZE) is applied when set; otherwise all entries are processed.
uzomuzo reads from stdin when piped:
echo "pkg:npm/express@4.18.2" | ./uzomuzo scanProcess only a contiguous subset of a large input file:
# Lines 1-250 (inclusive)
./uzomuzo scan --file input_file.txt --line-range=1:250
# Line 500 to end of file
./uzomuzo scan --file input_file.txt --line-range=500:
# Line range + random sampling (sampling applied after line range filter)
./uzomuzo scan --file input_file.txt --line-range=1001:2000 --sample=1000Rules:
- Format:
START:END(1-based, inclusive). Omit END to read to EOF - START must be >= 1. When END is specified, it must be >= START
- Requires
--file; specifying--line-rangewithout--fileresults in an error - Counts physical line numbers (blank lines and
#comments consume line numbers but are skipped during processing)
--sbomflag (CycloneDX JSON, or-for stdin)--fileflag (go.mod, CycloneDX JSON, or PURL/URL list — auto-detected)- Positional args (PURLs or GitHub URLs)
- Stdin pipe
- Auto-detect
go.modin current working directory
./uzomuzo scan pkg:npm/express@4.18.2 --format detailed # rich per-package output
./uzomuzo scan --sbom bom.json --format table # verdict summary table
./uzomuzo scan --sbom bom.json --format json # enriched JSON with full analysis
./uzomuzo scan --sbom bom.json --format csv # CSV for spreadsheet/pipelineSmart default: detailed for ≤3 inputs, table for bulk.
Example: --format table
$ uzomuzo scan pkg:npm/request@2.88.2 pkg:npm/express@4.18.2 --format table
STATUS PURL LIFECYCLE BUILD
🔴 replace pkg:npm/request@2.88.2 EOL-Confirmed —
✅ ok pkg:npm/express@4.18.2 Active Hardened 9.3
── Summary ─────────────────────────────────────────────────
│ 2 dependencies | ✅ 1 ok | ⚠️ 0 caution | 🔴 1 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
Example: --format table with SBOM input (RELATION column)
When scanning an SBOM that includes dependency graph information, a RELATION column is added:
$ trivy fs . --format cyclonedx | uzomuzo scan --sbom - --show-transitive --format table
STATUS RELATION PURL LIFECYCLE
✅ ok direct pkg:npm/express@4.18.2 Active
✅ ok transitive (express) pkg:npm/accepts@1.3.8 Active
🔴 replace transitive (express) pkg:npm/inflight@1.0.6 EOL-Confirmed
── Summary ─────────────────────────────────────────────────
│ 3 dependencies | ✅ 2 ok | ⚠️ 0 caution | 🔴 1 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
Without --show-transitive, only direct entries are displayed — transitive issues are addressed by updating the direct dependency that introduces them.
Example: --format json
{
"summary": {
"total": 1,
"ok": 0,
"caution": 0,
"replace": 1,
"review": 0,
"build_integrity": {
"hardened": 0,
"moderate": 0,
"weak": 0,
"ungraded": 1
}
},
"packages": [
{
"purl": "pkg:npm/request@2.88.2",
"verdict": "replace",
"lifecycle": "EOL-Confirmed",
"build_integrity": "Ungraded",
"repo_url": "https://github.com/request/request",
"overall_score": 3.6,
"dependent_count": 186349,
"stable_version": "2.88.2",
"project_license": "Apache-2.0",
"version_licenses": [
"Apache-2.0"
],
"advisory_count": 4,
"max_advisory_severity": "MEDIUM",
"max_cvss3_score": 6.5,
"direct_advisory_count": 1,
"transitive_advisory_count": 3,
"max_transitive_advisory_severity": "MEDIUM",
"max_transitive_cvss3_score": 6.5,
"reason": "Deprecated in npm registry"
}
],
"shown": 0
}The JSON format includes all analysis fields (verdict, lifecycle, EOL evidence, scores, license info), making it suitable for CI pipelines and downstream tooling without needing a separate detailed run.
Example: --format csv
$ uzomuzo scan pkg:npm/request@2.88.2 --format csv
verdict,purl,lifecycle,build_integrity,build_integrity_score,successor,advisory_count,max_advisory_severity,max_cvss3_score,direct_advisory_count,transitive_advisory_count,max_transitive_advisory_severity,max_transitive_cvss3_score,repo_url,source,via
replace,pkg:npm/request@2.88.2,EOL-Confirmed,Ungraded,,,4,MEDIUM,6.5,1,3,MEDIUM,6.5,https://github.com/request/request,,
Exit with code 1 when any dependency matches the specified lifecycle labels:
# Fail on confirmed EOL packages
./uzomuzo scan --sbom bom.json --fail-on eol-confirmed
# Fail on multiple lifecycle states
./uzomuzo scan --sbom bom.json --fail-on eol-confirmed,eol-effective,stalledValid labels: eol-confirmed, eol-effective, eol-scheduled, stalled, legacy-safe
Without --fail-on, exit code is always 0 regardless of scan results.
Example: --fail-on exit code behavior
Exit 1 — label matches a dependency:
$ uzomuzo scan pkg:npm/request@2.88.2 --fail-on eol-confirmed --format table
STATUS PURL LIFECYCLE BUILD
🔴 replace pkg:npm/request@2.88.2 EOL-Confirmed —
── Summary ─────────────────────────────────────────────────
│ 1 dependencies | ✅ 0 ok | ⚠️ 0 caution | 🔴 1 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
# exit code: 1 (request is EOL-Confirmed → matches --fail-on eol-confirmed)
Exit 0 — label does not match:
$ uzomuzo scan pkg:npm/request@2.88.2 --fail-on eol-effective --format table
STATUS PURL LIFECYCLE BUILD
🔴 replace pkg:npm/request@2.88.2 EOL-Confirmed —
── Summary ─────────────────────────────────────────────────
│ 1 dependencies | ✅ 0 ok | ⚠️ 0 caution | 🔴 1 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
# exit code: 0 (request is EOL-Confirmed, not EOL-Effective → no match)
Multiple labels — exit 1 if any label matches any dependency:
$ uzomuzo scan pkg:npm/request@2.88.2 pkg:npm/express@4.18.2 --fail-on eol-confirmed,stalled --format table
STATUS PURL LIFECYCLE BUILD
🔴 replace pkg:npm/request@2.88.2 EOL-Confirmed —
✅ ok pkg:npm/express@4.18.2 Active Hardened 9.3
── Summary ─────────────────────────────────────────────────
│ 2 dependencies | ✅ 1 ok | ⚠️ 0 caution | 🔴 1 replace | 🔍 0 review
└───────────────────────────────────────────────────────────
# exit code: 1 (request matches eol-confirmed)
--fail-on works with all output formats (table, json, csv). Output is produced normally before the exit code is set.
| Verdict | Lifecycle states | Action |
|---|---|---|
ok |
Active, Legacy-Safe | No action needed |
caution |
Stalled, EOL-Scheduled | Monitor; plan migration |
replace |
EOL-Confirmed, EOL-Effective, Archived | Migrate immediately |
review |
Insufficient data, analysis error | Manual investigation |
| Flag | Description |
|---|---|
--help, -h |
Show help for any command |
--version, -v |
Print version information |
Use uzomuzo --help for the full list, or uzomuzo scan --help for scan-specific help.
| Subcommand | Description |
|---|---|
scan |
Scan dependencies for lifecycle health (PURL, GitHub URL, SBOM, go.mod, file, pipe) |
update-spdx |
Update and regenerate the embedded SPDX license list from upstream |
uzomuzo combines data from deps.dev (package registry) and GitHub API (repository state). When GITHUB_TOKEN is not set, GitHub API calls are skipped and analysis relies on deps.dev data only, which significantly reduces lifecycle assessment precision.
| Data Source | Available Without Token | Requires GITHUB_TOKEN |
|---|---|---|
| deps.dev | Package versions & publish dates, Scorecard metrics (including archive detection via "Maintained" check), Advisory/CVE counts, Dependent counts, License info | — |
| GitHub API | — | Last human commit date, Bot vs. human commit ratio, Fork detection |
| Actual State | With Token | Without Token | Risk |
|---|---|---|---|
| Archived repository | EOL-Confirmed | EOL-Confirmed | Detected via Scorecard "Maintained" check — no token needed |
| Unpatched CVEs + no commits for 2+ years | EOL-Effective | Stalled | False negative — supply chain risk missed |
| Active Go/Composer package (commits but no registry publish) | Active | Stalled | False positive — healthy package flagged |
| Frozen utility with zero advisories | Legacy-Safe | Stalled | False positive — safe package flagged |
| Bot-only maintenance (Dependabot/Renovate) | Stalled | Active | False negative — automation masquerades as maintenance |
Without GITHUB_TOKEN, many packages fall into Review Needed instead of actionable categories because the assessor lacks commit-based signals to make a confident determination.
For production CI gates and security audits, always set GITHUB_TOKEN. The token requires no special scopes for public repositories — a default GITHUB_TOKEN from GitHub Actions or gh auth login is sufficient.
# GitHub Actions — automatic
# GITHUB_TOKEN is available by default in workflows
# Local — via GitHub CLI
gh auth login
export GITHUB_TOKEN=$(gh auth token)
./uzomuzo scan --sbom bom.json --fail-on eol-confirmedEnvironment variable-centric (12-factor). Unset / 0 falls back to safe defaults. See config.template.env for the complete list with comments.
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
Log level (debug/info/warn/error) |
info |
APP_TIMEOUT_SECONDS |
Overall timeout (seconds) | 14400 |
APP_SAMPLE_SIZE |
Batch input sampling size (0 = disabled) | 0 |
APP_OUTPUT_FORMAT |
Output format (csv) |
csv |
APP_MAX_PURLS |
Maximum PURLs per run (safety guard) | 5000 |
| Variable | Description | Default |
|---|---|---|
GITHUB_BASE_URL |
API base (for GitHub Enterprise) | https://api.github.com |
GITHUB_TIMEOUT |
Per-request timeout (Go duration, e.g., 45s) |
45s |
GITHUB_MAX_RETRIES |
Transient failure retry count | 3 |
GITHUB_MAX_CONCURRENCY |
GitHub concurrent request limit | 20 |
| Variable | Description | Default |
|---|---|---|
DEPSDEV_BASE_URL |
API base URL | https://api.deps.dev |
DEPSDEV_TIMEOUT |
Request timeout (duration) | 10s |
DEPSDEV_MAX_RETRIES |
Retry count | 3 |
DEPSDEV_BATCH_SIZE |
Max identifiers per batch request | 1000 |
DEPSDEV_REQUEST_INTERVAL_MS |
Sleep between batches (ms) | 500 |
Configure thresholds via LIFECYCLE_ASSESS_* environment variables.
LIFECYCLE_ASSESS_MAX_HUMAN_COMMIT_GAP_DAYS=150
LIFECYCLE_ASSESS_RESIDUAL_ADVISORY_THRESHOLD=1
LIFECYCLE_ASSESS_EOL_INACTIVITY_DAYS=730Normalization is applied after loading, so you only need to set the fields you want to change. All others inherit defaults:
export LIFECYCLE_ASSESS_RESIDUAL_ADVISORY_THRESHOLD=2
export LIFECYCLE_ASSESS_MAX_HUMAN_COMMIT_GAP_DAYS=120Operational tuning without code changes (low risk, reversible).
Uzomuzo outputs structured logs via Go's slog. Designed for batch/cron operation and incident investigation.
For automated monthly scanning with GitHub Actions, see Integration Examples: GitHub Actions Scheduled Scanning.
| Variable | Values | Purpose |
|---|---|---|
LOG_FORMAT |
json or text |
Structured JSON (recommended for production) / human-readable text |
LOG_LEVEL |
debug, info, warn, error |
Verbosity; default info |
RUN_ID |
arbitrary string | Correlate all logs for a single run; auto-generated if empty |
- Normal batch:
LOG_FORMAT=json+LOG_LEVEL=info - Set
RUN_IDuniquely per run (timestamp or job ID) for cross-system log correlation
Info-level summary is output at the end of each batch:
BatchSummary:total,missing_repo,missing_repo_pct,missing_project,missing_project_pct,missing_scorecard,missing_scorecard_pct- Automatic warn escalation:
HighMissingProjectRatio:missing_project_pct >= 0.20andtotal >= 10HighMissingScorecardRatio:missing_scorecard_pct >= 0.30andtotal >= 10
- Correlation:
run_id,app,ts,level,msg - Batch metrics:
BatchSummaryfield group - deps.dev client signals:
PURLsWithoutRepoURL,PackageInfoBatchEmpty,ProjectBatch* - Errors: under
errorkey
Pipe JSON logs to a log aggregation platform (ELK / OpenSearch / Cloud Logging) and correlate by run_id.
CSV output comprehensively covers security metrics:
- Basic info: PURL, repository URL, package metadata
- Scorecard metrics: All OpenSSF Scorecard check results
- Security assessment: Vulnerability scores, maintenance status
- Lifecycle assessment: Automatic classification (Active / Stalled / Legacy-Safe / EOL)
- Repository status: Stars, forks, archive status, last commit
Lifecycle label order in CLI detailed view (non-CSV):
- Active
- Stalled
- EOL
- Review Needed