diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index ef9b72ea21..0424212119 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -11,6 +11,11 @@ module Homebrew module_function ENVS = { + HOMEBREW_ALLOWED_TAPS: { + description: "A space-separated list of taps. Homebrew will refuse to install a " \ + "formula unless it and all of its dependencies are in an official tap " \ + "or in a tap on this list.", + }, HOMEBREW_API_AUTO_UPDATE_SECS: { description: "Check Homebrew's API for new formulae or cask data every " \ "`HOMEBREW_API_AUTO_UPDATE_SECS` seconds. Alternatively, disable API auto-update " \ diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index e038dd8f50..d73fc59797 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -1379,18 +1379,31 @@ on_request: installed_on_request?, options:) EOS end + sig { params(tap: Tap, allowed_taps: T::Set[Tap], forbidden_taps: T::Set[Tap]).returns(T::Boolean) } + def allowed_tap?(tap, allowed_taps, forbidden_taps) + (tap.official? || allowed_taps.blank? || allowed_taps.include?(tap)) && forbidden_taps.exclude?(tap) + end + sig { void } def forbidden_tap_check - forbidden_taps = Homebrew::EnvConfig.forbidden_taps - return if forbidden_taps.blank? + forbidden_taps = Homebrew::EnvConfig.forbidden_taps.to_s.split + allowed_taps = Homebrew::EnvConfig.allowed_taps.to_s.split + return if forbidden_taps.blank? && allowed_taps.blank? - forbidden_taps_set = Set.new(forbidden_taps.split.filter_map do |tap| + forbidden_taps_set = Set.new(forbidden_taps.filter_map do |tap| Tap.fetch(tap) rescue Tap::InvalidNameError opoo "Invalid tap name in `HOMEBREW_FORBIDDEN_TAPS`: #{tap}" nil end) + allowed_taps_set = Set.new(allowed_taps.filter_map do |tap| + Tap.fetch(tap) + rescue Tap::InvalidNameError + opoo "Invalid tap name in `HOMEBREW_ALLOWED_TAPS`: #{tap}" + nil + end) + owner = Homebrew::EnvConfig.forbidden_owner owner_contact = if (contact = Homebrew::EnvConfig.forbidden_owner_contact.presence) "\n#{contact}" @@ -1400,11 +1413,12 @@ on_request: installed_on_request?, options:) compute_dependencies.each do |(dep, _options)| dep_tap = dep.tap next if dep_tap.blank? - next unless forbidden_taps_set.include?(dep_tap) + next if allowed_tap?(dep_tap, allowed_taps_set, forbidden_taps_set) raise CannotInstallFormulaError, <<~EOS The installation of #{formula.name} has a dependency #{dep.name} - but the #{dep_tap} tap was forbidden by #{owner} in `HOMEBREW_FORBIDDEN_TAPS`.#{owner_contact} + but #{owner} has either not allowed the #{dep_tap} tap in `HOMEBREW_ALLOWED_TAPS` or + has forbidden the #{dep_tap} tap in `HOMEBREW_FORBIDDEN_TAPS`.#{owner_contact} EOS end end @@ -1413,11 +1427,12 @@ on_request: installed_on_request?, options:) formula_tap = formula.tap return if formula_tap.blank? - return unless forbidden_taps_set.include?(formula_tap) + return if allowed_tap?(formula_tap, allowed_taps_set, forbidden_taps_set) raise CannotInstallFormulaError, <<~EOS The installation of #{formula.full_name} has the tap #{formula_tap} - which was forbidden by #{owner} in `HOMEBREW_FORBIDDEN_TAPS`.#{owner_contact} + which is either not allowed by #{owner} in `HOMEBREW_ALLOWED_TAPS` or + is forbidden by #{owner} in `HOMEBREW_FORBIDDEN_TAPS`.#{owner_contact} EOS end diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi index 938cd076f0..e6a1945bbe 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi @@ -9,6 +9,9 @@ module Homebrew::EnvConfig sig { returns(T.nilable(::String)) } def all_proxy; end + sig { returns(T.nilable(::String)) } + def allowed_taps; end + sig { returns(Integer) } def api_auto_update_secs; end