Skip to content

Repository files navigation

still_active

How do you know the dependencies you ship are still maintained?

Install · Quick start · GitHub Action & CI · Cross-ecosystem · Configuration · Rules

Your package manager tells you what's outdated and what has a known CVE. Neither tells you whether anyone's still working on the thing. An abandoned dependency is a quiet liability: when it finally breaks or a vulnerability lands, there's no release coming and no one to ping, and you find out at the worst time.

still_active flags that risk before it bites, across your whole dependency graph: archived repos, no release in years, low OpenSSF scores, unfixable vulnerabilities, and the poison-pill case where a dormant package holds one of your deps below its own security patch. Native on Ruby gems; npm, PyPI, Cargo, Go, Maven, and NuGet via a CycloneDX SBOM.

Name            Version          Activity  OpenSSF  Vulns  License
────────────────────────────────────────────────────────────────────
backbone-rails  1.2.3 (latest)   archived  2.6/10   0      -
nokogiri        1.19.4 (latest)  ok        5.9/10   0      MIT
paperclip       6.1.0 (latest)   archived  2/10     0      MIT
  ↳ poison: caps terrapin ~> 0.6.0 (1 major behind, latest 1.x)
rake            13.4.2 (latest)  ok        5.5/10   0      MIT

4 gems: 4 up to date · 2 active, 2 archived · 0 vulnerabilities · 1 poison-pill
Ruby 4.0.5 (latest)

Why still_active?

No package ecosystem's standard tooling answers "is anyone still maintaining this?" npm audit, cargo audit, pip-audit, and bundler-audit find known CVEs; the outdated commands find version drift. None tell you a dependency's upstream is archived, hasn't shipped in three years, or is holding one of your deps below its security fix. That maintenance gap is what still_active fills. It's complementary to, not a replacement for, the tools you already run.

On Ruby, where it runs natively:

bundle outdated bundler-audit libyear-bundler still_active
Outdated versions Yes - Yes Yes
Known vulnerabilities (CVEs) - Yes (ruby-advisory-db) - Yes (deps.dev + OSV + ruby-advisory-db)
Libyear drift - - Yes Yes
Last-commit activity - - - Yes
Archived repo detection - - - Yes
OpenSSF Scorecard - - - Yes
Poison-pill / below-the-fix - - - Yes
Cross-ecosystem (npm/PyPI/Cargo/Go/Maven/NuGet) - - - Yes (--sbom)
CI quality gates - Exit code - Yes (6 gate flags)

With bundler-audit installed alongside, still_active merges its ruby-advisory-db advisories with its own deps.dev + OSV sources, so running both no longer means reconciling two vuln counts by hand.

Installation

gem install still_active

Requires an actively-maintained Ruby (the gemspec floor tracks Ruby's EOL schedule). You don't have to run it on the Ruby you audit: it reports on the version pinned in Gemfile.lock, so run it from any current Ruby and it still flags an EOL target.

Quick start

# audit your Gemfile (auto-detects output format)
still_active

# cross-ecosystem: audit any CycloneDX SBOM (npm, PyPI, Cargo, Go, Maven, NuGet)
syft dir:. -o cyclonedx-json > sbom.json && still_active --sbom=sbom.json

# fail CI if any gem is critically stale or vulnerable
still_active --fail-if-critical --fail-if-vulnerable

# markdown table for a pull request
still_active --markdown

Full flags: docs/cli.md. Tokens for private registries and self-hosted forges: docs/authentication.md.

GitHub Action & CI

The still_active-action uploads findings to your GitHub Security tab as SARIF, with inline PR annotations on Gemfile.lock:

permissions:
  contents: read
  security-events: write   # required for SARIF upload

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with: { ruby-version: '3.4' }
      - uses: SeanLF/still_active-action@v0
        with:
          github-token: ${{ github.token }}
          sarif: still_active.sarif.json
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: still_active.sarif.json }

Gate the build on what you care about (all off by default, so nothing breaks until you opt in), and use --baseline for PR review.

CI gate flags and PR-review diff
still_active --fail-if-critical              # upstream critically stale or archived
still_active --fail-if-vulnerable            # any known vulnerability (or =low|medium|high|critical)
still_active --fail-if-outdated=3            # more than 3 libyears behind latest
still_active --fail-if-poison                # a dormant package caps a dep below its latest major
still_active --fail-if-language-ceiling      # a pin strands you on an EOL language runtime
still_active --fail-if-deprecated            # a maintainer has declared a dependency deprecated

# PR review: report only what got worse since a saved snapshot, exit 1 on any regression
still_active --json > /tmp/main.json && still_active --baseline=/tmp/main.json

Building on top of still_active? The versioned JSON envelope is the supported interface (the Ruby constants are internal): docs/schema.md.

Rule reference (SA001-SA010), suppression, and composing with dependency-review-action: docs/rules.md, docs/ci.md. This repo audits itself every push, so you can browse live findings in its Code Scanning tab.

Cross-ecosystem audit

Point --sbom at a CycloneDX SBOM (from Syft, Trivy, npm sbom, cyclonedx-npm, cyclonedx-py, or any producer; see generator compatibility) and still_active assesses npm, pypi, cargo, go, maven, and nuget packages the same way it does gems, via deps.dev and ecosyste.ms. The SBOM is treated as untrusted input (only ecosystem/name/version are read, repositories resolve from deps.dev, and anything unassessable is surfaced rather than faked as ok). Most signals apply everywhere; a few are deliberately scoped.

Which signal covers which ecosystem
Signal (rule) Ruby (native) --sbom (cross-ecosystem)
Archived (SA001), no recent release (SA002), low OpenSSF (SA005), libyear (SA004) Yes Yes (all six ecosystems)
Vulnerabilities (SA003) deps.dev + OSV + ruby-advisory-db deps.dev + OSV
  ↳ "below the fix" (a dead package pins a vulnerable dep) Yes npm, Cargo, PyPI
Poison-pill (SA008) rubygems PyPI (flat resolution only)
Language-runtime ceiling (SA009) Ruby Python
Ruby EOL (SA006), yanked (SA007) Yes n/a

Full rule detail in docs/rules.md.

The play is maintenance, not CVE scanning, so compose Trivy/Grype for full vulnerability coverage.

Generator compatibility

CycloneDX producers disagree about almost everything a reader could naively depend on, so the SBOM path derives what it needs structurally rather than from any one tool's convention. Each of these is exercised in the test suite against verbatim output from the tool, not a hand-written fixture:

generator spec metadata.component bom-ref convention
Syft 1.7 file, and not a graph node at all pkg:npm/…?package-id=…
Trivy 1.7 application UUID
npm sbom 1.5 library name@version
cyclonedx-npm 1.6 application parent@ver|child@ver
cyclonedx-py 1.6 absent requirements-L1

Given the same project, all of them produce the same verdict.

Two behaviours worth knowing, because they are deliberate rather than incidental:

  • The scanned project is never audited as one of its own dependencies. Syft and npm sbom both list it as an ordinary library component with a PURL, which is indistinguishable from a real dependency by shape alone; it is identified through the dependency graph instead.
  • direct and dependency_path are only reported when the SBOM's dependency graph actually places a package. A generator can emit a graph with no usable edges (cyclonedx-py from a requirements file does exactly that, and a Syft directory scan is often sparse), and claiming everything is transitive would be a positive claim about a document that never said.

An SPDX document is refused with a clear error rather than read as an empty, all-clear audit.

Output formats

Auto-detected: a coloured terminal table on a TTY (above), JSON when piped. Or ask explicitly.

JSON (--json): a versioned, contract-tested schema for automation
{
  "gems": {
    "nested_form": {
      "source_type": "git",
      "version_used": "0.3.2",
      "repository_url": "https://github.com/ryanb/nested_form",
      "archived": true,
      "scorecard_score": 3.3,
      "vulnerability_count": 0,
      "status": "archived"
    }
  },
  "ruby": { "version": "4.0.5", "eol": false, "latest_version": "4.0.5" }
}

Fields are documented in docs/schema.md.

Markdown (--markdown): a table for pull requests, docs, or wikis
activity up to date? OpenSSF vulns name version last commit
5.9/10 nokogiri 1.19.4 2026/06
🚩 2/10 paperclip 6.1.0 2021/03

SARIF feeds GitHub Code Scanning (see GitHub Action & CI). CycloneDX (--cyclonedx) emits a standards-track SBOM so your graph and still_active's signals flow into Trivy, Dependency-Track, or Snyk.

Configuration

A committed .still_active.yml keeps policy in version control and replaces the blunt --ignore=GEM (which mutes every gate for a gem) with granular, expiring suppression. A vulnerability suppression must name an advisory id (so a new CVE is never pre-silenced), an expires: date makes accepted risk re-surface instead of rotting, and a suppressed finding still shows in output (and as a dismissed SARIF entry).

.still_active.yml example
fail_if_vulnerable: high        # true, or a minimum severity: low|medium|high|critical
fail_if_poison: warning         # true (=warning), or a tier: note|warning|critical

ignore:
  # accept ONE advisory by id; a different or new CVE on nokogiri still fails
  - advisory: CVE-2024-1234
    gem: nokogiri
    reason: "no fix released; not reachable from our code path"
    expires: 2026-09-01         # re-surfaces as a normal failure after this date

Full semantics, thresholds, and transitive behaviour: docs/configuration.md.

Data sources

Release dates and licenses from RubyGems / GitHub Packages / Artifactory; repo activity from the GitHub / GitLab / Codeberg API, or ecosyste.ms tokenless (CC-BY-SA 4.0); OpenSSF Scorecard and CVSS from deps.dev; advisory severities and fixed-version ranges from OSV (CVSS-4 scoring needs the optional cvss-suite); extra advisories from ruby-advisory-db; runtime EOL from endoflife.date.

Development

bin/setup installs dependencies and wires git hooks; rake runs the full lint + test suite. A pre-push hook runs rake automatically. Releases publish to rubygems.org automatically on a GitHub Release (trusted publishing). See CONTRIBUTING.md.

License

Open source under the MIT License.

About

Cross-ecosystem dependency maintenance auditor: flags unmaintained, archived, stale, and below-the-fix deps across the full transitive graph. Ruby gems natively; npm/pypi/cargo/go/maven/nuget via CycloneDX SBOM. Signals: activity, OpenSSF Scorecard, OSV advisories, libyear, poison-pill, runtime-ceiling. Outputs SARIF/JSON/CycloneDX with CI gates.

Topics

Resources

Contributing

Security policy

Stars

18 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages