Class: Homebrew::DevCmd::AdvisoryMatch Private
- Inherits:
-
AbstractCommand
- Object
- AbstractCommand
- Homebrew::DevCmd::AdvisoryMatch
- Defined in:
- dev-cmd/advisory-match.rb,
sorbet/rbi/dsl/homebrew/dev_cmd/advisory_match.rbi
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.
Defined Under Namespace
Classes: Args, CountEmitter, DirEmitter, Emitter, JsonEmitter
Instance Method Summary collapse
- #args ⇒ Homebrew::DevCmd::AdvisoryMatch::Args private
- #build_emitter ⇒ Emitter private
- #each_formula ⇒ T::Enumerator[Formula] private
- #emit_index(matcher) ⇒ void private
- #local_overrides ⇒ Homebrew::Vulns::AdvisoryOverrides? private
-
#local_repology ⇒ Homebrew::Vulns::Repology?
private
A CI run that has just built the index locally (advisory-database's Ingest) reads it directly instead of fetching the published copy.
- #report(matcher, formula, hits) ⇒ void private
- #run ⇒ void private
- #text_mode? ⇒ Boolean private
Methods inherited from AbstractCommand
command, command_name, dev_cmd?, #initialize, parser, ruby_cmd?
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
This class inherits a constructor from Homebrew::AbstractCommand
Instance Method Details
#args ⇒ Homebrew::DevCmd::AdvisoryMatch::Args
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.
10 |
# File 'sorbet/rbi/dsl/homebrew/dev_cmd/advisory_match.rbi', line 10 def args; end |
#build_emitter ⇒ Emitter
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.
636 637 638 639 640 641 642 643 644 |
# File 'dev-cmd/advisory-match.rb', line 636 def build_emitter if (dir = args.output) DirEmitter.new(dir, verbose: args.verbose?, close_open_ranges: !args.no_history?) elsif args.json? JsonEmitter.new else CountEmitter.new end end |
#each_formula ⇒ T::Enumerator[Formula]
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.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'dev-cmd/advisory-match.rb', line 177 def each_formula return args.named.to_resolved_formulae.each unless args.all? raise UsageError, "`--all` does not take named arguments" if args.named.any? tap = CoreTap.instance raise TapUnavailableError, tap.name unless tap.installed? Enumerator.new do |y| tap.formula_names.each do |name| y << Formulary.factory(name) rescue => e onoe "Error loading formula '#{name}': #{e}" end end end |
#emit_index(matcher) ⇒ 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.
This method returns an undefined value.
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'dev-cmd/advisory-match.rb', line 647 def emit_index(matcher) tap = CoreTap.instance raise TapUnavailableError, tap.name unless tap.installed? index = tap.formula_names.each_with_object({}) do |name, h| identity = matcher.identify(Formulary.factory(name)) next unless identity.identifiable? h[name] = { git_repo: identity.git_repo, git_tag: identity.git_tag, primary_package: identity.primary_package&.to_h, resource_packages: identity.resource_packages.transform_values(&:to_h), distro_packages: identity.distro_packages, }.compact rescue => e onoe "Error loading formula '#{name}': #{e}" end puts JSON.pretty_generate(index) end |
#local_overrides ⇒ Homebrew::Vulns::AdvisoryOverrides?
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.
170 171 172 173 174 |
# File 'dev-cmd/advisory-match.rb', line 170 def local_overrides return unless (path = args.overrides) Homebrew::Vulns::AdvisoryOverrides.from_file(Pathname(path)) end |
#local_repology ⇒ Homebrew::Vulns::Repology?
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.
A CI run that has just built the index locally (advisory-database's Ingest) reads it directly instead of fetching the published copy.
163 164 165 166 167 |
# File 'dev-cmd/advisory-match.rb', line 163 def local_repology return unless (path = args.repology) Homebrew::Vulns::Repology.from_file(Pathname(path)) end |
#report(matcher, formula, hits) ⇒ 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.
This method returns an undefined value.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'dev-cmd/advisory-match.rb', line 203 def report(matcher, formula, hits) ohai "#{formula.name} #{formula.pkg_version}" if hits.empty? puts " No advisories matched." return end hits.sort_by { |h| [-h.vulnerability.severity_level, h.canonical_id] }.each do |hit| v = hit.vulnerability status, = matcher.range_status(hit, formula_name: formula.name) state = case status&.state when nil then "uncomparable" when :affected then "AFFECTED#{", upstream fix #{status&.fixed_in}" if status&.fixed_in}" when :fixed then "fixed (upstream #{status&.fixed_in || "?"})" else "not applicable" end summary = v.summary&.slice(0, 60) puts " #{hit.canonical_id} [#{hit.strategy}, #{matcher.confidence_for(hit, status)}] " \ "#{v.severity_display} #{state}" \ "#{" (resource: #{hit.resource})" if hit.resource}" \ "#{" — #{summary}" if summary}" end end |
#run ⇒ 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.
This method returns an undefined value.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'dev-cmd/advisory-match.rb', line 59 def run Formulary.enable_factory_cache! Homebrew::API.with_no_api_env do latest_macos = MacOSVersion.new((HOMEBREW_MACOS_NEWEST_UNSUPPORTED.to_i - 1).to_s).to_sym Homebrew::SimulateSystem.with(os: latest_macos, arch: :arm) do matcher = Homebrew::Vulns::Match.new(repology: local_repology, overrides: local_overrides, bulk: args.all? || args.index?) next emit_index(matcher) if args.index? emitter = build_emitter begin matcher.each_advisory_batch(each_formula) do |formula, hits| report(matcher, formula, hits) if text_mode? # A below-introduced hit would otherwise look open to OSV # consumers; it must not participate in alias maintenance. actionable = hits.filter_map do |hit| status, = matcher.range_status(hit, formula_name: formula.name) [hit, status] if status&.state != :not_applicable end record_ids_by_canonical = actionable.to_h do |hit, _status| ids = matcher.record_ids(formula, hit) [ids.fetch(0), ids] end alias_errors = emitter.prepare_aliases(formula.name, record_ids_by_canonical) if alias_errors.any? alias_errors.each { |error| onoe error } Homebrew.failed = true next end actionable.each do |hit, status| record_id = matcher.record_id(formula, hit) next if emitter.alias_protected?(record_id) reviewed_state = emitter.reviewed_range_state(record_id) has_open_range = [:open, :mixed].include?(reviewed_state) has_terminal_range = [:terminal, :mixed].include?(reviewed_state) transition = (status&.fixed? && has_open_range) || (status&.affected? && has_terminal_range) if args.no_history? && transition opoo "#{record_id}: reviewed range transition needs history; leaving it unchanged" next end walk_history = !args.no_history? && status&.fixed? walk_history &&= emitter.history_required?(record_id) if args.new_history? emitter.record_history_walk if walk_history first_fixed = matcher.first_fixed_version(formula, hit) if walk_history fixed_boundary = T.let(nil, T.nilable(String)) case first_fixed when String fixed_boundary = first_fixed when nil # No history walk was required. when :never_affected next when :history_unavailable emitter.record_history_unavailable(formula.name) opoo "#{record_id}: formula history is unavailable; skipping automatic update" next else raise TypeError, "unexpected fixed-history result: #{first_fixed.inspect}" end if fixed_boundary && !emitter.fixed_boundary_valid?(record_id, fixed_boundary) onoe "#{record_id}: fixed #{fixed_boundary} does not follow its reviewed range" Homebrew.failed = true next end first_reintroduced = T.let(nil, T.nilable(String)) if status&.affected? && has_terminal_range emitter.record_history_walk reintroduced = matcher.first_reintroduced_version(formula, hit) unless reintroduced.is_a?(String) onoe "#{record_id}: could not find a prior non-affected version for its reviewed fixed range" Homebrew.failed = true next end unless emitter.reintroduction_boundary_valid?(record_id, reintroduced) onoe "#{record_id}: reintroduction #{reintroduced} does not follow its reviewed range" Homebrew.failed = true next end first_reintroduced = reintroduced end emitter << matcher.to_brew_record(formula, hit, first_fixed: fixed_boundary, first_reintroduced:) end end rescue Homebrew::Vulns::OSV::Error => e onoe "OSV query failed: #{e.}" Homebrew.failed = true end emitter.finish end end end |
#text_mode? ⇒ 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.
195 196 197 |
# File 'dev-cmd/advisory-match.rb', line 195 def text_mode? !args.json? && args.output.nil? end |