Class: Homebrew::Vulns::AdvisoryOverrides Private

Inherits:
Object
  • Object
show all
Defined in:
vulns/advisory_overrides.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.

Reviewed formula identity and candidate-specific corrections for advisory matching. These are intentionally keyed by formula, and range corrections are further keyed by upstream advisory identifier, so a bad upstream range cannot silently change unrelated matches.

Defined Under Namespace

Classes: Entry, Error, RegistryPackageOverride

Class Method Summary collapse

Instance Method Summary collapse

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.

Keep formula and advisory entry validation, including the effective override requirement, in sync with advisory-database spec/overrides_spec.rb.

Parameters:

  • data (T.untyped)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
# File 'vulns/advisory_overrides.rb', line 43

def initialize(data)
  @skipped_formulae = T.let({}, T::Hash[String, T::Boolean])
  @registry_packages = T.let({}, T::Hash[String, RegistryPackageOverride])
  @advisories = T.let({}, T::Hash[String, T::Hash[String, Entry]])
  @preserved_homebrew_ranges = T.let({}, T::Hash[String, T::Array[String]])
  @excluded_aliases = T.let({}, T::Hash[String, T::Hash[String, T::Array[String]]])

  root = hash(data, "top level")
  root.each do |formula_name, raw_formula|
    raise Error, "Formula names must be strings" unless formula_name.is_a?(String)

    formula = hash(raw_formula, formula_name)
    reject_unknown_keys(formula, %w[skip advisories registry_package], formula_name)

    skip = formula.fetch("skip", false)
    raise Error, "#{formula_name}.skip must be true or false" unless [true, false].include?(skip)

    @skipped_formulae[formula_name] = true if skip

    if formula.key?("registry_package")
      location = "#{formula_name}.registry_package"
      package = hash(formula.fetch("registry_package"), location)
      reject_unknown_keys(package, REGISTRY_PACKAGE_KEYS, location)
      missing = REGISTRY_PACKAGE_KEYS - package.keys
      raise Error, "#{location} is missing key(s): #{missing.join(", ")}" if missing.any?

      ecosystem = nonblank_string(package.fetch("ecosystem"), "#{location}.ecosystem")
      name = nonblank_string(package.fetch("name"), "#{location}.name")
      unless Identify.registry_package_for(ecosystem:, name:)
        raise Error, "#{location} must identify a supported registry package with a canonical OSV name"
      end

      @registry_packages[formula_name] = RegistryPackageOverride.new(ecosystem:, name:)
    end
    next unless formula.key?("advisories")

    raw_advisories = hash(formula.fetch("advisories"), "#{formula_name}.advisories")
    parsed = T.let({}, T::Hash[String, Entry])
    raw_advisories.each do |identifier, raw_entry|
      raise Error, "#{formula_name} advisory identifiers must be strings" unless identifier.is_a?(String)

      entry = hash(raw_entry, "#{formula_name}.advisories.#{identifier}")
      reject_unknown_keys(entry, %w[range_state upstream_fixed_in preserve_homebrew_ranges excluded_aliases],
                          "#{formula_name}.advisories.#{identifier}")

      excluded = entry.fetch("excluded_aliases", [])
      if !excluded.is_a?(Array) || excluded.any? { |id| !id.is_a?(String) || !id.match?(/\S/) } ||
         excluded.include?(identifier) || (entry.key?("excluded_aliases") && excluded.empty?)
        raise Error, "#{formula_name}.advisories.#{identifier}.excluded_aliases must be a non-empty " \
                     "array of other advisory identifiers"
      end
      if excluded.any?
        (@excluded_aliases[formula_name] ||= {})[identifier] = excluded.uniq.freeze
      end

      preserve_ranges = entry.fetch("preserve_homebrew_ranges", false)
      unless [true, false].include?(preserve_ranges)
        raise Error, "#{formula_name}.advisories.#{identifier}.preserve_homebrew_ranges must be true or false"
      end

      (@preserved_homebrew_ranges[formula_name] ||= []) << identifier if preserve_ranges

      state = T.let(nil, T.nilable(Symbol))
      if entry.key?("range_state")
        raw_state = entry.fetch("range_state")
        state = raw_state.to_sym if raw_state.is_a?(String)
        if state.nil? || VALID_STATES.exclude?(state)
          raise Error, "#{formula_name}.advisories.#{identifier}.range_state must be " \
                       "affected, fixed, or not_applicable"
        end
      end

      fixed_in_overridden = entry.key?("upstream_fixed_in")
      fixed_in = entry["upstream_fixed_in"]
      if !fixed_in.nil? && !fixed_in.is_a?(String)
        raise Error, "#{formula_name}.advisories.#{identifier}.upstream_fixed_in must be a string or null"
      end
      if state.nil? && !fixed_in_overridden && !preserve_ranges && excluded.empty?
        raise Error, "#{formula_name}.advisories.#{identifier} must override at least one field"
      end

      if state || fixed_in_overridden
        parsed[identifier] = Entry.new(state:, fixed_in:, fixed_in_overridden:)
      end
    end
    @advisories[formula_name] = parsed.freeze
  end
  @skipped_formulae.freeze
  @registry_packages.freeze
  @advisories.freeze
  @preserved_homebrew_ranges.each_value(&:freeze)
  @preserved_homebrew_ranges.freeze
  @excluded_aliases.each_value(&:freeze)
  @excluded_aliases.freeze
end

Class Method Details

.from_file(path) ⇒ 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.

Parameters:

Returns:



34
35
36
37
38
# File 'vulns/advisory_overrides.rb', line 34

def self.from_file(path)
  new(YAML.safe_load(path.read, permitted_classes: [], permitted_symbols: [], aliases: false))
rescue Psych::Exception => e
  raise Error, "Failed to parse advisory overrides at #{path}: #{e.message}"
end

Instance Method Details

#advisory_override(formula_name, identifiers) ⇒ Entry?

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.

Parameters:

Returns:



156
157
158
159
160
161
162
163
164
165
# File 'vulns/advisory_overrides.rb', line 156

def advisory_override(formula_name, identifiers)
  formula = @advisories[formula_name]
  return unless formula

  identifiers.each do |identifier|
    override = formula[identifier]
    return override if override
  end
  nil
end

#excluded_aliases(formula_name, identifier) ⇒ 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.

Key by the actual source record, before alias resolution or grouping.

Parameters:

Returns:



151
152
153
# File 'vulns/advisory_overrides.rb', line 151

def excluded_aliases(formula_name, identifier)
  @excluded_aliases.fetch(formula_name, {}).fetch(identifier, [])
end

#preserve_homebrew_ranges?(formula_name, identifiers) ⇒ 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.

A protection under any alias applies to the whole family, independently of whichever alias supplies the upstream state or version override.

Parameters:

Returns:

  • (Boolean)


170
171
172
# File 'vulns/advisory_overrides.rb', line 170

def preserve_homebrew_ranges?(formula_name, identifiers)
  @preserved_homebrew_ranges.fetch(formula_name, []).intersect?(identifiers)
end

#registry_package_override(formula_name) ⇒ RegistryPackageOverride?

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.

Parameters:

Returns:



145
146
147
# File 'vulns/advisory_overrides.rb', line 145

def registry_package_override(formula_name)
  @registry_packages[formula_name]
end

#skip_formula?(formula_name) ⇒ 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.

Parameters:

Returns:

  • (Boolean)


140
141
142
# File 'vulns/advisory_overrides.rb', line 140

def skip_formula?(formula_name)
  @skipped_formulae.key?(formula_name)
end