From b23fa58da45629ff6fdd8fe5d5db877ebd0f8fa7 Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Wed, 24 Jul 2024 05:39:06 +0100 Subject: [PATCH] Add env to disable finding packages from paths --- Library/Homebrew/api/formula.rb | 11 +++++++---- Library/Homebrew/build.rb | 1 + Library/Homebrew/cask/cask_loader.rb | 5 +++++ Library/Homebrew/env_config.rb | 5 +++++ Library/Homebrew/formulary.rb | 7 +++++++ Library/Homebrew/postinstall.rb | 1 + .../sorbet/rbi/dsl/homebrew/env_config.rbi | 3 +++ Library/Homebrew/test.rb | 1 + .../cask/cask_loader/from_uri_loader_spec.rb | 5 +++++ Library/Homebrew/test/formulary_spec.rb | 18 ++++++++++++++++++ docs/Manpage.md | 5 +++++ manpages/brew.1 | 3 +++ 12 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index 94f9b004566b99..bb5f89eb57044c 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 8b18071d5f0ee6..3489791b1260ba 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 58c10b944ddcd1..d45d55113fc23b 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 35b56c45eec878..5206592812664c 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 a61d845579065a..9c998487efdfc3 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 a00f344f0845dc..aab5073b64af7c 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 2d527e640be92a..517f69970ea45e 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 7a13b716941fa3..e47d1a7e9d415f 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 1dc49aa1cf51e6..317dbf27b1a4ec 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 907e8c7c4344d3..30021b08f24cf5 100644 --- a/Library/Homebrew/test/formulary_spec.rb +++ b/Library/Homebrew/test/formulary_spec.rb @@ -118,11 +118,29 @@ 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" + FileUtils.cp formula_path, HOMEBREW_TEMP + temp_formula_path = HOMEBREW_TEMP/formula_path.basename + expect do + described_class.factory(temp_formula_path) + ensure + temp_formula_path.unlink + 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 1131883df58138..aebf0c4e37c722 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 718567b7340cf0..4acc418ac048f5 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