Module: Brew::Vulns::OsvExport
- Defined in:
- lib/brew/vulns/osv_export.rb
Overview
Emits OSV-schema records for a Homebrew ecosystem describing CVEs that
homebrew-core formulae resolve via shipped patches.
One record is written per (formula, vulnerability id) pair found in
patches[].resolves. The record states that the formula was affected up
to (but not including) the currently shipped version+revision; this is the
"fixed at or before what we ship today" approximation, since the precise
fix boundary requires homebrew-core git archaeology.
Record shape follows the OSV 1.7 schema and mirrors the Debian DSA layout
(upstream listing the source CVE, affected[].ranges of type
ECOSYSTEM, ecosystem_specific carrying the patch detail).
Constant Summary collapse
- SCHEMA_VERSION =
"1.7.3"- ECOSYSTEM =
"Homebrew"- ID_PREFIX =
"BREW"
Class Method Summary collapse
- .affected_entry(formula, vuln_id) ⇒ Object
- .fetch_upstream(client, vuln_id) ⇒ Object
- .patch_ref(patch) ⇒ Object
- .record_for(formula, vuln_id, upstream: nil, now: Time.now.utc) ⇒ Object
- .record_id(formula, vuln_id) ⇒ Object
- .run(formulae, dir, client: OsvClient.new, now: Time.now.utc) ⇒ Object
Class Method Details
.affected_entry(formula, vuln_id) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/brew/vulns/osv_export.rb', line 69 def affected_entry(formula, vuln_id) { package: { ecosystem: ECOSYSTEM, name: formula.name, purl: formula.purl(with_version: false), }, ranges: [ { type: "ECOSYSTEM", events: [ { introduced: "0" }, { fixed: formula.full_version }, ], }, ], ecosystem_specific: { fix: "patch", patches: formula.patches_resolving(vuln_id).filter_map { |p| patch_ref(p) }, }, } end |
.fetch_upstream(client, vuln_id) ⇒ Object
102 103 104 105 106 |
# File 'lib/brew/vulns/osv_export.rb', line 102 def fetch_upstream(client, vuln_id) client.get_vulnerability(vuln_id) rescue OsvClient::Error nil end |
.patch_ref(patch) ⇒ Object
92 93 94 95 96 97 98 99 100 |
# File 'lib/brew/vulns/osv_export.rb', line 92 def patch_ref(patch) ref = {} ref[:type] = patch["type"] if patch["type"] ref[:url] = patch["url"] if patch["url"] ref[:file] = patch["file"] if patch["file"] ref[:apply] = patch["apply"] if patch["apply"] ref[:data] = true if patch["data"] ref.empty? ? nil : ref end |
.record_for(formula, vuln_id, upstream: nil, now: Time.now.utc) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/brew/vulns/osv_export.rb', line 45 def record_for(formula, vuln_id, upstream: nil, now: Time.now.utc) record = { schema_version: SCHEMA_VERSION, id: record_id(formula, vuln_id), modified: now.strftime("%Y-%m-%dT%H:%M:%SZ"), upstream: [vuln_id], affected: [affected_entry(formula, vuln_id)], } if upstream record[:summary] = upstream["summary"] if upstream["summary"] record[:details] = upstream["details"] if upstream["details"] record[:severity] = upstream["severity"] if upstream["severity"] record[:upstream] = ([vuln_id] + Array(upstream["aliases"])).uniq record[:references] = upstream["references"] if upstream["references"] end record end |
.record_id(formula, vuln_id) ⇒ Object
65 66 67 |
# File 'lib/brew/vulns/osv_export.rb', line 65 def record_id(formula, vuln_id) "#{ID_PREFIX}-#{formula.name}-#{vuln_id}" end |
.run(formulae, dir, client: OsvClient.new, now: Time.now.utc) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/brew/vulns/osv_export.rb', line 28 def run(formulae, dir, client: OsvClient.new, now: Time.now.utc) FileUtils.mkdir_p(dir) written = [] formulae.each do |formula| formula.resolved_vulnerability_ids.each do |vuln_id| upstream = fetch_upstream(client, vuln_id) record = record_for(formula, vuln_id, upstream: upstream, now: now) path = File.join(dir, "#{record[:id]}.json") File.write(path, JSON.pretty_generate(record)) written << path end end written end |