Module: Homebrew::Vulns::OsvExport Private
- Defined in:
- vulns/osv_export.rb
Overview
This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.
Emits OSV-schema records for the Homebrew ecosystem describing CVEs that
homebrew-core formulae resolve via shipped patches.
One record is written per (formula, vulnerability id) pair found in
serialized_patches[].resolves. The record states that the formula was
affected up to (but not including) the currently shipped version+revision;
this is a "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 resolving patch detail).
See DevCmd::GenerateVulnsAdvisories for the entry point and https://github.com/Homebrew/homebrew-advisory-database for the published feed.
Constant Summary collapse
- SCHEMA_VERSION =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
https://ossf.github.io/osv-schema/ — value of the emitted
schema_versionfield, pinning the OSV schema release these records target.HomebrewandBREWwere registered in that schema in ossf/osv-schema#576. "1.7.3"- ECOSYSTEM =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
"Homebrew"- ID_PREFIX =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
"BREW"- PatchRef =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
T.type_alias { T::Hash[Symbol, T.any(String, T::Array[String])] }
Class Method Summary collapse
- .affected_entry(formula, vuln_id, patches) ⇒ Hash{Symbol => T.untyped} private
- .fetch_upstream(vuln_id) ⇒ Hash{String => T.untyped}? private
- .patch_ref(patch) ⇒ PatchRef? private
- .patches_resolving(serialized_patches, vuln_id) ⇒ Array<Hash{String => T.untyped}> private
- .purl(name) ⇒ String private
- .record_for(formula, vuln_id, patches: formula.serialized_patches, upstream: nil, now: Time.now.utc) ⇒ Hash{Symbol => T.untyped} private
-
.run(annotated, dir, now: Time.now.utc) ⇒ Array<String>
private
annotatedis a list of[formula, serialized_patches]pairs.
Class Method Details
.affected_entry(formula, vuln_id, patches) ⇒ Hash{Symbol => T.untyped}
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'vulns/osv_export.rb', line 93 def self.affected_entry(formula, vuln_id, patches) { package: { ecosystem: ECOSYSTEM, name: formula.name, purl: purl(formula.name), }, ranges: [ { type: "ECOSYSTEM", events: [{ introduced: "0" }, { fixed: formula.pkg_version.to_s }], }, ], ecosystem_specific: { fix: "patch", patches: patches_resolving(patches, vuln_id).filter_map { |p| patch_ref(p) }, }, } end |
.fetch_upstream(vuln_id) ⇒ Hash{String => T.untyped}?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
149 150 151 152 153 |
# File 'vulns/osv_export.rb', line 149 def self.fetch_upstream(vuln_id) OSV.vulnerability(vuln_id) rescue OSV::Error nil end |
.patch_ref(patch) ⇒ PatchRef?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
139 140 141 142 143 144 145 146 |
# File 'vulns/osv_export.rb', line 139 def self.patch_ref(patch) ref = T.let({}, PatchRef) 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.presence end |
.patches_resolving(serialized_patches, vuln_id) ⇒ Array<Hash{String => T.untyped}>
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
129 130 131 132 133 134 |
# File 'vulns/osv_export.rb', line 129 def self.patches_resolving(serialized_patches, vuln_id) target = vuln_id.upcase serialized_patches.select do |p| Array(p["resolves"]).any? { |r| r.is_a?(Hash) && r["type"] == "security" && r["id"].to_s.upcase == target } end end |
.purl(name) ⇒ String
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
121 122 123 |
# File 'vulns/osv_export.rb', line 121 def self.purl(name) "pkg:brew/#{name.gsub(/[@+]/, PURL_NAME_ENCODE)}" end |
.record_for(formula, vuln_id, patches: formula.serialized_patches, upstream: nil, now: Time.now.utc) ⇒ Hash{Symbol => T.untyped}
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'vulns/osv_export.rb', line 69 def self.record_for(formula, vuln_id, patches: formula.serialized_patches, upstream: nil, now: Time.now.utc) record = T.let({ schema_version: SCHEMA_VERSION, id: "#{ID_PREFIX}-#{formula.name}-#{vuln_id}", modified: now.strftime("%Y-%m-%dT%H:%M:%SZ"), upstream: [vuln_id], affected: [affected_entry(formula, vuln_id, patches)], }, T::Hash[Symbol, T.untyped]) 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 |
.run(annotated, dir, now: Time.now.utc) ⇒ Array<String>
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
annotated is a list of [formula, serialized_patches] pairs. The
patches are passed in rather than read from the formula so callers can
supply the union across OS/architecture variations (a patch inside an
on_linux/on_intel block only appears in Formula#serialized_patches
under the matching SimulateSystem).
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'vulns/osv_export.rb', line 46 def self.run(annotated, dir, now: Time.now.utc) FileUtils.mkdir_p(dir) written = [] upstream_cache = T.let({}, T::Hash[String, T.nilable(T::Hash[String, T.untyped])]) annotated.each do |formula, patches| Scanner.resolved_ids(patches).each do |vuln_id| upstream = upstream_cache.fetch(vuln_id) { upstream_cache[vuln_id] = fetch_upstream(vuln_id) } record = record_for(formula, vuln_id, patches:, upstream:, now:) path = File.join(dir, "#{record.fetch(:id)}.json") File.write(path, "#{JSON.pretty_generate(record)}\n") written << path end end written end |