Skip to content

feat(tools): RubyGems enrichment in get_dependency_blast_radius - #104

Open
manus-use wants to merge 1 commit into
mainfrom
feat/enrich-rubygems
Open

feat(tools): RubyGems enrichment in get_dependency_blast_radius#104
manus-use wants to merge 1 commit into
mainfrom
feat/enrich-rubygems

Conversation

@manus-use

Copy link
Copy Markdown
Owner

Summary

Adds _enrich_rubygems(name) to get_dependency_blast_radius.py, completing the ecosystem-enrichment trilogy extension: npm ✓ PyPI ✓ Maven ✓ crates.io (PR #103) ✓ RubyGems ✓.

Ruby packages are a significant attack surface — rack, nokogiri, rails, devise, and bundler each have hundreds of millions of downloads. This PR surfaces that exposure clearly in blast-radius output.

What changed

src/manus_agent/tools/get_dependency_blast_radius.py

New constants:

  • _RUBYGEMS_GEM_URL = "https://rubygems.org/api/v1/gems/{}.json" — gem metadata
  • _RUBYGEMS_VERSIONS_URL = "https://rubygems.org/api/v1/versions/{}.json" — version history

New helper _extract_rubygems_first_release(versions):

  • Parses a RubyGems version list (each entry has number + created_at ISO 8601)
  • Finds the version with the oldest timestamp → {first_version, first_release_date, age_years}
  • Handles ISO variants: millisecond fraction, Z suffix, +00:00 offset, no sub-second
  • Empty list / all-malformed timestamps → returns {} (graceful)

New enricher _enrich_rubygems(name):

  • Call 1: /api/v1/gems/{name}.jsontotal_downloads, recent_downloads (current-version DLs), latest_version, description (truncated to 120 chars), home_page
  • Call 2: /api/v1/versions/{name}.json → first-release metadata (isolated try/except; failure does not prevent metadata from being returned)
  • No API key required; no User-Agent header required (RubyGems public API)

_enrich_package dispatch:

  • Routes rubygems / ruby / gem ecosystem strings to _enrich_rubygems

_blast_score update:

  • RubyGems does not expose a weekly download endpoint; fallback: weekly_proxy = total_downloads / max(age_years * 52, 1) so gems still get a meaningful blast label

Output rendering:

  • New RubyGems-specific block: Latest version, All-time DLs, Current ver DLs, First released (YYYY-MM-DD, N yrs old, first version), Description

Docstring:

tests/test_dependency_blast_radius.py

+20 tests across 5 new classes (94 passing in module, 1178 suite-wide):

Class Tests Coverage
TestExtractRubygemsFirstRelease 8 oldest-version selection, empty list, malformed timestamps, mixed valid/invalid, single version, no-milliseconds ISO, UTC offset, missing created_at
TestEnrichRubygems 5 full metadata + first-release, versions failure degrades gracefully, gem fetch failure, homepage_uri preference, description truncation
TestEnrichPackageDispatchRubyGems 3 rubygems, ruby, gem ecosystem strings all route correctly
TestBlastScoreRubyGemsFallback 3 large gem total_downloads → high score, small gem → low score, explicit weekly_downloads takes precedence
TestBlastRadiusRubyGemsOutput 1 full integration: rubygems:rack@2.2.6 produces output with All-time DLs, First released, RubyGems label

Design decisions

Checked against existing open PRs — no overlap

Verified no duplicate coverage against all open PRs:
#51, #53, #54, #58, #60, #64, #65, #67, #74, #75, #76, #77, #78, #79, #80, #82, #83, #85, #86, #87, #88, #89, #90, #92, #93, #94, #95, #96, #97, #98, #100, #103

None of these add RubyGems enrichment to get_dependency_blast_radius.

Testing

1178 passed, 3 deselected, 2 warnings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant