diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index 94f9b004566b9..bb5f89eb57044 100644 --- a/Library/Homebrew/api/formula.rb +++ b/Library/Homebrew/api/formula.rb @@ -32,10 +32,13 @@ def self.source_download(formula) cache: HOMEBREW_CACHE_API_SOURCE/"#{tap}/#{git_head}/Formula", ) download.fetch - Formulary.factory(download.symlink_location, - formula.active_spec_sym, - alias_path: formula.alias_path, - flags: formula.class.build_flags) + + with_env(HOMEBREW_FORBID_PACKAGES_FROM_PATHS: nil) do + Formulary.factory(download.symlink_location, + formula.active_spec_sym, + alias_path: formula.alias_path, + flags: formula.class.build_flags) + end end def self.cached_json_file_path diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb index 8b18071d5f0ee..3489791b1260b 100644 --- a/Library/Homebrew/build.rb +++ b/Library/Homebrew/build.rb @@ -216,6 +216,7 @@ def fixopt(formula) end begin + ENV.delete("HOMEBREW_FORBID_PACKAGES_FROM_PATHS") args = Homebrew::Cmd::InstallCmd.new.args Context.current = args.context diff --git a/Library/Homebrew/cask/cask_loader.rb b/Library/Homebrew/cask/cask_loader.rb index 58c10b944ddcd..6b4a43a960ae9 100644 --- a/Library/Homebrew/cask/cask_loader.rb +++ b/Library/Homebrew/cask/cask_loader.rb @@ -103,6 +103,9 @@ def self.try_new(ref, warn: false) return if %w[.rb .json].exclude?(path.extname) return unless path.expand_path.exist? + return if Homebrew::EnvConfig.forbid_packages_from_paths? && + path.realpath.to_s.start_with?("#{Caskroom.path}/", "#{HOMEBREW_LIBRARY}/Taps/") + new(path) end @@ -159,6 +162,8 @@ class FromURILoader < FromPathLoader .returns(T.nilable(T.attached_class)) } def self.try_new(ref, warn: false) + return if Homebrew::EnvConfig.forbid_packages_from_paths? + # Cache compiled regex @uri_regex ||= begin uri_regex = ::URI::DEFAULT_PARSER.make_regexp diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 35b56c45eec87..5206592812664 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -214,6 +214,11 @@ module EnvConfig description: "A space-separated list of taps. Homebrew will refuse to install a " \ "formula if it or any of its dependencies is in a tap on this list.", }, + HOMEBREW_FORBID_PACKAGES_FROM_PATHS: { + description: "If set, Homebrew will refuse to read packages provided from file paths, " \ + "e.g. `brew install ./package.rb`.", + boolean: true, + }, HOMEBREW_FORCE_BREWED_CA_CERTIFICATES: { description: "If set, always use a Homebrew-installed `ca-certificates` rather than the system version. " \ "Automatically set if the system version is too old.", diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index a61d845579065..cefbd1b9ce03d 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -592,6 +592,8 @@ class FromBottleLoader < FormulaLoader .returns(T.nilable(T.attached_class)) } def self.try_new(ref, from: T.unsafe(nil), warn: false) + return if Homebrew::EnvConfig.forbid_packages_from_paths? + ref = ref.to_s new(ref) if HOMEBREW_BOTTLES_EXTNAME_REGEX.match?(ref) && File.exist?(ref) @@ -644,6 +646,9 @@ def self.try_new(ref, from: T.unsafe(nil), warn: false) return unless path.expand_path.exist? + return if Homebrew::EnvConfig.forbid_packages_from_paths? && + path.realpath.to_s.start_with?("#{HOMEBREW_CELLAR}/", "#{HOMEBREW_LIBRARY}/Taps/") + options = if (tap = Tap.from_path(path)) # Only treat symlinks in taps as aliases. if path.symlink? @@ -696,6 +701,8 @@ class FromURILoader < FormulaLoader .returns(T.nilable(T.attached_class)) } def self.try_new(ref, from: T.unsafe(nil), warn: false) + return if Homebrew::EnvConfig.forbid_packages_from_paths? + # Cache compiled regex @uri_regex ||= begin uri_regex = ::URI::DEFAULT_PARSER.make_regexp diff --git a/Library/Homebrew/postinstall.rb b/Library/Homebrew/postinstall.rb index a00f344f0845d..aab5073b64af7 100644 --- a/Library/Homebrew/postinstall.rb +++ b/Library/Homebrew/postinstall.rb @@ -14,6 +14,7 @@ require "json/add/exception" begin + ENV.delete("HOMEBREW_FORBID_PACKAGES_FROM_PATHS") args = Homebrew::Cmd::Postinstall.new.args error_pipe = UNIXSocket.open(ENV.fetch("HOMEBREW_ERROR_PIPE"), &:recv_io) error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) diff --git a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi index 2d527e640be92..517f69970ea45 100644 --- a/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi +++ b/Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi @@ -112,6 +112,9 @@ module Homebrew::EnvConfig sig { returns(Integer) } def fail_log_lines; end + sig { returns(T::Boolean) } + def forbid_packages_from_paths?; end + sig { returns(T.nilable(::String)) } def forbidden_casks; end diff --git a/Library/Homebrew/test.rb b/Library/Homebrew/test.rb index 7a13b716941fa..e47d1a7e9d415 100644 --- a/Library/Homebrew/test.rb +++ b/Library/Homebrew/test.rb @@ -19,6 +19,7 @@ TEST_TIMEOUT_SECONDS = 5 * 60 begin + ENV.delete("HOMEBREW_FORBID_PACKAGES_FROM_PATHS") args = Homebrew::DevCmd::Test.new.args Context.current = args.context diff --git a/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb b/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb index 1dc49aa1cf51e..317dbf27b1a4e 100644 --- a/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb +++ b/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb @@ -10,6 +10,11 @@ expect(described_class.try_new("https://brew.sh/")).not_to be_nil end + it "returns nil when path loading is disabled" do + ENV["HOMEBREW_FORBID_PACKAGES_FROM_PATHS"] = "1" + expect(described_class.try_new(URI("https://brew.sh/"))).to be_nil + end + it "returns nil when given a string with Cask contents containing a URL" do expect(described_class.try_new(<<~RUBY)).to be_nil cask 'token' do diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb index 907e8c7c4344d..48ef44923f1db 100644 --- a/Library/Homebrew/test/formulary_spec.rb +++ b/Library/Homebrew/test/formulary_spec.rb @@ -118,11 +118,25 @@ class Wrong#{described_class.class_s(formula_name)} < Formula expect(described_class.factory(formula_path)).to be_a(Formula) end + it "errors when given a path but paths are disabled" do + ENV["HOMEBREW_FORBID_PACKAGES_FROM_PATHS"] = "1" + expect do + described_class.factory(formula_path) + end.to raise_error(FormulaUnavailableError) + end + it "returns a Formula when given a URL", :needs_utils_curl, :no_api do formula = described_class.factory("file://#{formula_path}") expect(formula).to be_a(Formula) end + it "errors when given a URL but paths are disabled" do + ENV["HOMEBREW_FORBID_PACKAGES_FROM_PATHS"] = "1" + expect do + described_class.factory("file://#{formula_path}") + end.to raise_error(FormulaUnavailableError) + end + context "when given a bottle" do subject(:formula) { described_class.factory(bottle) } diff --git a/docs/Manpage.md b/docs/Manpage.md index 1131883df5813..aebf0c4e37c72 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -3771,6 +3771,11 @@ command execution e.g. `$(cat file)`. : A space-separated list of taps. Homebrew will refuse to install a formula if it or any of its dependencies is in a tap on this list. +`HOMEBREW_FORBID_PACKAGES_FROM_PATHS` + +: If set, Homebrew will refuse to read packages provided from file paths, e.g. + `brew install ./package.rb`. + `HOMEBREW_FORCE_BREWED_CA_CERTIFICATES` : If set, always use a Homebrew-installed `ca-certificates` rather than the diff --git a/manpages/brew.1 b/manpages/brew.1 index 718567b7340cf..4acc418ac048f 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -2455,6 +2455,9 @@ How to contact the \fBHOMEBREW_FORBIDDEN_OWNER\fP, if set and necessary\. \fBHOMEBREW_FORBIDDEN_TAPS\fP A space\-separated list of taps\. Homebrew will refuse to install a formula if it or any of its dependencies is in a tap on this list\. .TP +\fBHOMEBREW_FORBID_PACKAGES_FROM_PATHS\fP +If set, Homebrew will refuse to read packages provided from file paths, e\.g\. \fBbrew install \./package\.rb\fP\&\. +.TP \fBHOMEBREW_FORCE_BREWED_CA_CERTIFICATES\fP If set, always use a Homebrew\-installed \fBca\-certificates\fP rather than the system version\. Automatically set if the system version is too old\. .TP