Module: StillActive::RubyAdvisoryDb

Extended by:
RubyAdvisoryDb
Included in:
RubyAdvisoryDb
Defined in:
lib/helpers/ruby_advisory_db.rb

Overview

Optional second vulnerability source: rubysec/ruby-advisory-db, read through bundler-audit's own loader when the user has it installed. We are a consumer — no YAML parsing or version-range matching of our own. Advisories are mapped into the same shape as deps.dev results and merged by VulnerabilityHelper.

Verified against bundler-audit 0.9.3: Advisory CVSS scores live in #to_h (:cvss_v3 / :cvss_v2), not in dedicated methods; Database.new raises ArgumentError when the ~/.local/share/ruby-advisory-db checkout is absent.

Defined Under Namespace

Classes: GemRef

Constant Summary collapse

STALE_AFTER_SECONDS =

30 days

30 * 24 * 60 * 60
OLDER_THAN_FLAW_OPERATORS =

Requirement operators whose safe versions are OLDER than the flaw, i.e. not a forward fix a consumer can upgrade to. Anything else ("> X" etc.) is.

["<", "<="].freeze

Instance Method Summary collapse

Instance Method Details

#advisories_for(database:, gem_name:, version:) ⇒ Object

Maps advisories the database reports for gem_name@version into our vulnerability shape. Returns [] when the database is unavailable or the version can't be parsed (e.g. a git sha). A malformed advisory in the checkout (a corrupt/partial bundle audit update) is surfaced, not swallowed — silently returning [] there would hide a missed vulnerability.



45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/helpers/ruby_advisory_db.rb', line 45

def advisories_for(database:, gem_name:, version:)
  return [] if database.nil?

  parsed = parse_version(version)
  return [] if parsed.nil?

  advisories = []
  database.check_gem(GemRef.new(gem_name, parsed)) { |advisory| advisories << to_vulnerability(advisory) }
  advisories
rescue Gem::Requirement::BadRequirementError => e
  warn("still_active: ruby-advisory-db has a malformed advisory for #{gem_name} (#{e.message}) — run `bundle audit update` to repair the checkout")
  []
end

#loadObject

Returns a loaded bundler-audit Database, or nil when bundler-audit isn't installed or its advisory checkout is absent. Never raises — a missing second source just means we fall back to deps.dev only.



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/helpers/ruby_advisory_db.rb', line 27

def load
  require "bundler/audit"
  require "bundler/audit/database"
  database = Bundler::Audit::Database.new
  warn_if_stale(database)
  database
rescue LoadError
  nil # bundler-audit not installed
rescue ArgumentError
  warn("still_active: ruby-advisory-db not found — run `bundle audit update` to enable dual-source vulnerability data")
  nil
end

#to_vulnerability(advisory) ⇒ Object

Translates a bundler-audit Advisory into the deps.dev-compatible hash. bundler-audit has no CVSS vector, so cvss3_vector is always nil here.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/helpers/ruby_advisory_db.rb', line 61

def to_vulnerability(advisory)
  primary = advisory.ghsa_id || advisory.cve_id || advisory.id
  details = advisory.to_h
  {
    id: primary,
    url: details[:url],
    title: details[:title],
    aliases: advisory.identifiers.reject { |identifier| identifier == primary },
    cvss3_score: details[:cvss_v3],
    cvss3_vector: nil,
    cvss2_score: details[:cvss_v2],
    # No safe version a consumer can upgrade TO: the correlation bundler-audit
    # + `bundle outdated` can't produce alone. A factual read of the DB.
    no_fix_available: no_forward_fix?(advisory),
    source: "ruby-advisory-db",
  }
end