diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb index 7f35f724257d9..2c0c308eceff2 100644 --- a/Library/Homebrew/sandbox.rb +++ b/Library/Homebrew/sandbox.rb @@ -902,6 +902,7 @@ def home_write_paths = [] sig { params(tmpdir: String).returns(T::Hash[String, String]) } def sandbox_environment(tmpdir) + allow_read path: tmpdir, type: :subpath allow_write_path(tmpdir) allow_network path: tmpdir, type: :subpath { "HOMEBREW_TEMP" => tmpdir, "TMPDIR" => tmpdir, "TEMP" => tmpdir, "TMP" => tmpdir } diff --git a/Library/Homebrew/test/sandbox_shared_spec.rb b/Library/Homebrew/test/sandbox_shared_spec.rb index 30ac240c92764..ba2b1bb82cf2a 100644 --- a/Library/Homebrew/test/sandbox_shared_spec.rb +++ b/Library/Homebrew/test/sandbox_shared_spec.rb @@ -269,6 +269,26 @@ allow(described_class).to receive(:isolate_operation?).and_call_original end + it "allows reading only its private temporary directory under a denied parent" do + temporary_directory = mktmpdir + outside = temporary_directory/"outside" + outside.write("secret") + sandbox.deny_read_path temporary_directory + result = sandbox.capture(RbConfig.ruby, temporary_directory:, args: ["-e", <<~RUBY, outside]) + file = File.join(ENV.fetch("TMPDIR"), "archive") + File.write(file, "contents") + puts File.read(file) + begin + File.read(ARGV.fetch(0)) + abort "Read escaped the sandbox" + rescue Errno::EPERM, Errno::EACCES + nil + end + RUBY + + expect(result.stdout).to eq("contents\n") + end + it "allows its output directory and denies writes through an escaping symlink" do directory = mktmpdir output = directory/"output"