Module: StillActive::DepsDevClient
- Extended by:
- DepsDevClient
- Included in:
- DepsDevClient
- Defined in:
- lib/still_active/deps_dev_client.rb
Constant Summary collapse
- BASE_URI =
URI("https://api.deps.dev/")
- ADVISORY_CANARY =
A known-vulnerable package used to canary the
advisoryKeysschema. deps.dev is an explicitly ALPHA API (v3alpha), and every cross-ecosystem vulnerability count flows through theadvisoryKeysfield with a field-level degrade to[]-- so a rename or drop of that field would silently turn every count to 0 and read a known-vulnerable package as clean (exit 0). django 3.0.0 carries 30+ permanent advisories; if the canary returns none, the schema drifted. { system: "pypi", name: "django", version: "3.0.0" }.freeze
Instance Method Summary collapse
- #advisory_detail(advisory_id:) ⇒ Object
-
#advisory_schema_ok? ⇒ Boolean
Is deps.dev still returning advisories in the shape we parse? False when the canary comes back empty (schema drift) or unreachable (can't confirm).
-
#default_version_info(name:, system: :rubygems) ⇒ Object
The package's default version and its release date: { version:, published_at: }, or nil.
-
#latest_release_date(name:, system: :rubygems) ⇒ Object
The package's most recent release date (ISO8601 string), or nil.
- #project_scorecard(project_id:) ⇒ Object
-
#version_info(gem_name:, version:, system: :rubygems) ⇒ Object
systemis the deps.dev package system, lowercased: rubygems, npm, pypi, cargo, go, maven, nuget.
Instance Method Details
#advisory_detail(advisory_id:) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/still_active/deps_dev_client.rb', line 99 def advisory_detail(advisory_id:) return if advisory_id.nil? path = "/v3alpha/advisories/#{encode(advisory_id)}" body = HttpHelper.get_json(BASE_URI, path) return if body.nil? { id: body.dig("advisoryKey", "id"), url: body["url"], title: body["title"], # deps.dev's v3alpha returns aliases as bare id strings (["CVE-..."]); # tolerate the legacy object shape ({"id":...}) too since it's an alpha API. aliases: Array(body["aliases"]).filter_map { |a| normalize_alias(a) }, cvss3_score: body["cvss3Score"], cvss3_vector: body["cvss3Vector"], cvss2_score: body["cvss2Score"], source: "deps.dev", } end |
#advisory_schema_ok? ⇒ Boolean
Is deps.dev still returning advisories in the shape we parse? False when the canary comes back empty (schema drift) or unreachable (can't confirm). The caller warns loudly rather than presenting a possibly-understated "all clear".
22 23 24 25 |
# File 'lib/still_active/deps_dev_client.rb', line 22 def advisory_schema_ok? info = version_info(gem_name: ADVISORY_CANARY[:name], version: ADVISORY_CANARY[:version], system: ADVISORY_CANARY[:system]) !(info.nil? || info[:advisory_keys].empty?) end |
#default_version_info(name:, system: :rubygems) ⇒ Object
The package's default version and its release date: { version:, published_at: }, or nil. deps.dev's default version is the latest stable; when none is flagged (all pre-release), the newest publishedAt stands in so a still-active package isn't mis-read as dormant. The version is exposed (not just the date) so a caller can recover the package's repo link from it when an exact locked version isn't indexed (yanked/normalization mismatch).
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/still_active/deps_dev_client.rb', line 54 def default_version_info(name:, system: :rubygems) return if name.nil? path = "/v3alpha/systems/#{encode(system)}/packages/#{encode(name)}" body = HttpHelper.get_json(BASE_URI, path) return if body.nil? versions = body["versions"] return unless versions.is_a?(Array) && !versions.empty? entry = versions.find { |v| v.is_a?(Hash) && v["isDefault"] } || newest_version(versions) return if entry.nil? { version: entry.dig("versionKey", "version"), published_at: entry["publishedAt"] } end |
#latest_release_date(name:, system: :rubygems) ⇒ Object
The package's most recent release date (ISO8601 string), or nil. This is the cross-ecosystem freshness signal, and deps.dev's publishedAt is more reliable than ecosyste.ms's latest_release_published_at, which can lag badly (mpmath: 2023 vs 2026).
74 75 76 |
# File 'lib/still_active/deps_dev_client.rb', line 74 def latest_release_date(name:, system: :rubygems) default_version_info(name: name, system: system)&.dig(:published_at) end |
#project_scorecard(project_id:) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/still_active/deps_dev_client.rb', line 78 def project_scorecard(project_id:) return if project_id.nil? path = "/v3alpha/projects/#{encode(project_id)}" body = HttpHelper.get_json(BASE_URI, path) return if body.nil? scorecard = body["scorecard"] return if scorecard.nil? { score: scorecard["overallScore"], date: scorecard["date"], # The "Maintained" sub-check (0-10) scores recent commit and issue # activity directly -- still_active's core question -- so we surface it # alongside the aggregate. nil when the check is absent (never 0, which # would read as "unmaintained" rather than "not measured"). maintained: maintained_check_score(scorecard), } end |
#version_info(gem_name:, version:, system: :rubygems) ⇒ Object
system is the deps.dev package system, lowercased: rubygems, npm, pypi,
cargo, go, maven, nuget. It matches the ecosystem symbol SbomReader emits,
so a cross-ecosystem caller threads it straight through. An unknown system
404s and degrades to nil (HttpHelper swallows 404), never raising.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/still_active/deps_dev_client.rb', line 31 def version_info(gem_name:, version:, system: :rubygems) return if gem_name.nil? || version.nil? path = "/v3alpha/systems/#{encode(system)}/packages/#{encode(gem_name)}/versions/#{encode(version)}" body = HttpHelper.get_json(BASE_URI, path) return if body.nil? { advisory_keys: body.dig("advisoryKeys")&.map { |a| a["id"] } || [], project_id: extract_project_id(body), # The locked version's release date -- the cross-ecosystem libyear input # (paired with the package's latest-release date). Already in this response, # so no extra fetch; nil when the feed omits it. published_at: body["publishedAt"], } end |