Class: Homebrew::Vulns::CPANSec Private
- Inherits:
-
CachedFeed
- Object
- CachedFeed
- Homebrew::Vulns::CPANSec
- Defined in:
- vulns/cpan_sec.rb
Overview
This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.
Loader for the CPAN Security Advisory database. Source: https://github.com/briandfoy/cpan-security-advisory
The upstream repository ships a compiled cpan-security-advisory.json
keyed on CPAN distribution name. This class fetches and caches that file
and exposes advisories per distribution. Evaluating affected_versions
range strings against a formula version is left to Match.
Defined Under Namespace
Classes: Advisory
Constant Summary collapse
- DATA_URL =
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://raw.githubusercontent.com/briandfoy/cpan-security-advisory/" \ "master/cpan-security-advisory.json"
Instance Attribute Summary collapse
- #meta ⇒ Hash{String => T.untyped} readonly private
Class Method Summary collapse
- .cache_filename ⇒ String private
- .data_url ⇒ String private
- .lower_bounds(conjunction) ⇒ Array<Version> private
-
.range_status(advisory, version) ⇒ Vulnerability::RangeStatus
private
CPANSA constraints: each
affected_versionsarray entry is a comma-joined AND of</<=/>/>=/==/=/bare-version terms; the array is an OR of those. - .satisfies?(target, conjunction) ⇒ Boolean private
Instance Method Summary collapse
- #advisories_for(distribution) ⇒ Array<Advisory> private
- #build_advisory(raw) ⇒ Advisory? private
- #distributions ⇒ Array<String> private
- #initialize(data) ⇒ void constructor private
Methods inherited from CachedFeed
#as_hash, default_max_age, from_file, load, refresh
Methods included from Utils::Output::Mixin
#issue_reporting_message, #odebug, #odeprecated, #odie, #odisabled, #ofail, #oh1, #oh1_title, #ohai, #ohai_title, #onoe, #opoo, #opoo_outside_github_actions, #opoo_without_github_actions_annotation, #pretty_deprecated, #pretty_disabled, #pretty_duration, #pretty_install_status, #pretty_installed, #pretty_uninstalled, #pretty_unmarked, #pretty_upgradable, #pretty_warning
Constructor Details
#initialize(data) ⇒ void
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.
33 34 35 36 37 38 39 40 |
# File 'vulns/cpan_sec.rb', line 33 def initialize(data) super raise Error, "CPANSA data is not a JSON object" unless (top = as_hash(data)) raise Error, "CPANSA data missing 'dists' key" unless (dists = as_hash(top["dists"])) @dists = T.let(dists, T::Hash[String, T.untyped]) @meta = T.let(as_hash(top["meta"]) || {}, T::Hash[String, T.untyped]) end |
Instance Attribute Details
#meta ⇒ Hash{String => T.untyped} (readonly)
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.
43 44 45 |
# File 'vulns/cpan_sec.rb', line 43 def @meta end |
Class Method Details
.cache_filename ⇒ 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.
24 |
# File 'vulns/cpan_sec.rb', line 24 def self.cache_filename = "cpansa.json" |
.data_url ⇒ 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.
21 |
# File 'vulns/cpan_sec.rb', line 21 def self.data_url = DATA_URL |
.lower_bounds(conjunction) ⇒ Array<Version>
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.
105 106 107 108 109 110 |
# File 'vulns/cpan_sec.rb', line 105 def self.lower_bounds(conjunction) conjunction.split(",").filter_map do |term| match = term.match(CONSTRAINT) Version.new(T.must(match[2])) if match && LOWER_BOUND_OPS.include?(match[1]) end end |
.range_status(advisory, version) ⇒ Vulnerability::RangeStatus
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.
CPANSA constraints: each affected_versions array entry is a
comma-joined AND of </<=/>/>=/==/=/bare-version terms; the
array is an OR of those. fixed_versions uses the same grammar.
Compared with Version; Perl's decimal-vs-dotted equivalence
(1.002003 == v1.2.3) is not modelled since homebrew-core CPAN
formulae uniformly use the decimal form.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'vulns/cpan_sec.rb', line 65 def self.range_status(advisory, version) target = Version.new(version.sub(/\Av/i, "")) affected = advisory.affected_versions.empty? || advisory.affected_versions.any? { |c| satisfies?(target, c) } bounds = advisory.fixed_versions.flat_map { |c| lower_bounds(c) } if affected fixed_in = bounds.select { |v| target < v }.min&.to_s Vulnerability::RangeStatus.new(state: :affected, fixed_in:).freeze elsif advisory.fixed_versions.any? { |c| satisfies?(target, c) } fixed_in = bounds.select { |v| target >= v }.max&.to_s Vulnerability::RangeStatus.new(state: :fixed, fixed_in:).freeze else Vulnerability::RangeStatus.new(state: :not_applicable, fixed_in: nil).freeze end end |
.satisfies?(target, conjunction) ⇒ Boolean
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.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'vulns/cpan_sec.rb', line 88 def self.satisfies?(target, conjunction) conjunction.split(",").all? do |term| match = term.match(CONSTRAINT) next false unless match bound = Version.new(T.must(match[2])) case match[1] when "<" then target < bound when "<=" then target <= bound when ">" then target > bound when ">=" then target >= bound else target == bound end end end |
Instance Method Details
#advisories_for(distribution) ⇒ Array<Advisory>
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.
51 52 53 54 55 56 |
# File 'vulns/cpan_sec.rb', line 51 def advisories_for(distribution) entry = @dists[distribution] return [] unless entry.is_a?(Hash) Array(entry["advisories"]).filter_map { |a| build_advisory(a) if a.is_a?(Hash) } end |
#build_advisory(raw) ⇒ Advisory?
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.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'vulns/cpan_sec.rb', line 113 def build_advisory(raw) id = raw["id"] return if id.nil? Advisory.new( id:, cves: Array(raw["cves"]).map(&:to_s), affected_versions: Array(raw["affected_versions"]).map(&:to_s), fixed_versions: Array(raw["fixed_versions"]).map(&:to_s), severity: raw["severity"], description: raw["description"], references: Array(raw["references"]).map(&:to_s), reported: raw["reported"], ).freeze end |
#distributions ⇒ 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.
46 47 48 |
# File 'vulns/cpan_sec.rb', line 46 def distributions @dists.keys end |