Skip to content

brew.sh: enforce HOMEBREW_FORCE_BREW_WRAPPER more strictly #20400

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

carlocab
Copy link
Member

@carlocab carlocab commented Aug 8, 2025

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew typecheck with your changes locally?
  • Have you successfully run brew tests with your changes locally?

HOMEBREW_FORCE_BREW_WRAPPER can be used as a security/compliance
feature, but allowing it to be disabled by setting
HOMEBREW_NO_FORCE_BREW_WRAPPER leaves a pretty large hole in it that
allows it to be sidestepped.

Let's fix that by actually checking the path of the process that called
brew, and the verify that that path matches the configured value of
HOMEBREW_NO_FORCE_BREW_WRAPPER.

`HOMEBREW_FORCE_BREW_WRAPPER` can be used as a security/compliance
feature, but allowing it to be disabled by setting
`HOMEBREW_NO_FORCE_BREW_WRAPPER` leaves a pretty large hole in it that
allows it to be sidestepped.

Let's fix that by actually checking the path of the process that called
`brew`, and the verify that that path matches the configured value of
`HOMEBREW_NO_FORCE_BREW_WRAPPER`.
@carlocab carlocab requested a review from Bo98 August 8, 2025 19:23
@carlocab carlocab self-assigned this Aug 8, 2025
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @carlocab! A bunch of comments, sorry!

Comment on lines -90 to -92
HOMEBREW_BREW_WRAPPER: {
description: "If set, use wrapper to call `brew` rather than auto-detecting it.",
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to go through a deprecation cycle before dropping this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, will do that.

Comment on lines -396 to -399
HOMEBREW_NO_FORCE_BREW_WRAPPER: {
description: "If set, disables `$HOMEBREW_FORCE_BREW_WRAPPER` behaviour, even if set.",
boolean: true,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Could add something HOMEBREW_DISABLE_NO_FORCE_BREW_WRAPPER if you want the ability to disable this functionality.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sounds good.


require "fiddle"

libproc = Fiddle.dlopen("/usr/lib/libproc.dylib")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if this file doesn't exist?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually won't; it lives in the dyld cache. But, I suppose you were asking what happens if it fails. It exits immediately with an error like:

❯ ./test.rb
/Users/carlocab/.rbenv/versions/3.4.5/lib/ruby/3.4.0/fiddle.rb:93:in 'Fiddle::Handle#initialize': dlopen(/path/to/nonexistent.dylib, 0x0009): tried: '/path/to/nonexistent.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/path/to/nonexistent.dylib' (no such file), '/path/to/nonexistent.dylib' (no such file) (Fiddle::DLError)
	from /Users/carlocab/.rbenv/versions/3.4.5/lib/ruby/3.4.0/fiddle.rb:93:in 'Class#new'
	from /Users/carlocab/.rbenv/versions/3.4.5/lib/ruby/3.4.0/fiddle.rb:93:in 'Fiddle.dlopen'
	from ./test.rb:4:in '<main>'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks. Does the caller handle this case appropriately i.e. the exit code is as expected?


libproc = Fiddle.dlopen("/usr/lib/libproc.dylib")

proc_pidpath = Fiddle::Function.new(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
proc_pidpath = Fiddle::Function.new(
libproc_proc_pidpath_function = Fiddle::Function.new(

Comment on lines +15 to +16
pid = ARGV[0]&.to_i
exit 1 unless pid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do this before anything else. Should produce an error message if there's no argument passed.

Comment on lines +18 to +23
bufsize = 4 * 1024 # PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
buf = "\0" * bufsize
ptr = Fiddle::Pointer.to_ptr(buf)

ret = proc_pidpath.call(pid, ptr, bufsize)
puts ptr.to_s.strip if ret.positive?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs better comments here.

Comment on lines +18 to +23
bufsize = 4 * 1024 # PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
buf = "\0" * bufsize
ptr = Fiddle::Pointer.to_ptr(buf)

ret = proc_pidpath.call(pid, ptr, bufsize)
puts ptr.to_s.strip if ret.positive?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bufsize = 4 * 1024 # PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
buf = "\0" * bufsize
ptr = Fiddle::Pointer.to_ptr(buf)
ret = proc_pidpath.call(pid, ptr, bufsize)
puts ptr.to_s.strip if ret.positive?
buffer_size = 4 * 1024 # PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN
null_buffer = "\0" * buffer_size
pointer = Fiddle::Pointer.to_ptr(buffer)
return_value = proc_pidpath.call(pid, pointer, buffer_size)
puts pointer.to_s.strip if return_value.positive?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants