Skip to content

Commit

Permalink
Update DSL to make it more flexible
Browse files Browse the repository at this point in the history
  • Loading branch information
Rylan12 committed Jul 15, 2024
1 parent 300fcf1 commit c7ba27b
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 33 deletions.
1 change: 1 addition & 0 deletions Library/Homebrew/ast_constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
[{ name: :needs, type: :method_call }],
[{ name: :allow_network_access!, type: :method_call }],
[{ name: :deny_network_access!, type: :method_call }],
[{ name: :allow_in_sandbox!, type: :method_call }],
[{ name: :install, type: :method_definition }],
[{ name: :post_install, type: :method_definition }],
[{ name: :caveats, type: :method_definition }],
Expand Down
4 changes: 3 additions & 1 deletion Library/Homebrew/dev-cmd/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,16 @@ def run
sandbox = Sandbox.new
f.logs.mkpath
sandbox.record_log(f.logs/"test.sandbox.log")
sandbox.allow_write_temp_and_cache(f)
sandbox.allow_write_temp_and_cache
sandbox.allow_write_log(f)
sandbox.allow_write_xcode
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/cache")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/homebrew/locks")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/log")
sandbox.allow_write_path(HOMEBREW_PREFIX/"var/run")
sandbox.deny_all_network_except_pipe(error_pipe) unless f.class.network_access_allowed?(:test)
sandbox.allow_write_global_temp if f.allowed_in_sandbox?(:write_to_temp, phase: :test)
sandbox.deny_signal if f.allowed_in_sandbox?(:signal, phase: :test)
sandbox.exec(*exec_args)
else
exec(*exec_args)
Expand Down
66 changes: 50 additions & 16 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1551,13 +1551,17 @@ def link_overwrite?(path)
# @see .disable!
delegate disable_reason: :"self.class"

# Sandbox rules that should be skipped when installing or testing this {Formula}.
# Returns `nil` if there are no sandbox rules to skip.
# @!method reduced_sandbox
# @return [Array<Symbol>]
# @see .reduce_sandbox
def reduced_sandbox
self.class.reduced_sandbox || []
# Whether or not the given sandbox rule should be skipped during the given phase in this {Formula}.
# @!method allowed_in_sandbox?
# @param type [Symbol] the type of sandbox rule
# @param phase [Symbol] the phase to check
# @return [Boolean]
# @see .allow_in_sandbox
def allowed_in_sandbox?(type, phase:)
return false unless self.class.allowed_in_sandbox
return false unless self.class.allowed_in_sandbox.key?(phase)

self.class.allowed_in_sandbox[phase].include?(type)

Check warning on line 1564 in Library/Homebrew/formula.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula.rb#L1564

Added line #L1564 was not covered by tests
end

sig { returns(T::Boolean) }
Expand Down Expand Up @@ -3287,7 +3291,7 @@ def freeze
attr_reader :keg_only_reason

# The types of sandbox restrictions that should be lifted from the formula.
attr_reader :reduced_sandbox
attr_reader :allowed_in_sandbox

# A one-line description of the software. Used by users to get an overview
# of the software and Homebrew maintainers.
Expand Down Expand Up @@ -4309,29 +4313,59 @@ def link_overwrite(*paths)
link_overwrite_paths.merge(paths)
end

# Skip certain sandbox restrictions when installing this formula.
# Skip certain sandbox restrictions when installing and testing this formula.
# This can be useful if the upstream build system needs to write to
# locations that are protected by sandbox restrictions.
# locations that are protected by sandbox restrictions. Passing a
# phase is optional, and if not provided, the rule will be applied to
# all phases. The possible phases are `:build`, `:postinstall`, and `:test`.
#
# ### Example
#
# If upstream needs to write to `/private/tmp`:
# If the formula needs to write to `/private/tmp` in all phases:
#
# ```ruby
# reduce_sandbox :allow_write_to_temp
# allow_in_sandbox! :write_to_temp
# ```
def reduce_sandbox!(*types)
invalid_types = types.select { |type| Sandbox::SANDBOX_REDUCTIONS.exclude?(type) }
#
# If the formula needs to send signals in the `:test` phase:
# ```ruby
# allow_in_sandbox! :signal, phase: :test
# ```
#
# If the formula needs to write to `/private/tmp` and send signals
# in the `:test` and `:install` phase:
# ```ruby
# allow_in_sandbox! :write_to_temp, :signal, phase: [:test, :install]
# ```
def allow_in_sandbox!(*types, phase: nil)
invalid_types = types.select { |type| Sandbox::SANDBOX_DSL_RULES.exclude?(type) }

Check warning on line 4341 in Library/Homebrew/formula.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula.rb#L4341

Added line #L4341 was not covered by tests
if invalid_types.any?
noun = if invalid_types.count > 1
"types"
else
"type"
end
raise ArgumentError, "Unsupported sandbox reduction #{noun}: #{invalid_types.join(", ")}"
raise ArgumentError, "Unsupported allow in sandbox item #{noun}: #{invalid_types.join(", ")}"

Check warning on line 4348 in Library/Homebrew/formula.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula.rb#L4348

Added line #L4348 was not covered by tests
end

phase ||= Sandbox::SANDBOX_DSL_PHASES
phases = Array(phase)
invalid_phases = phases.select { |p| Sandbox::SANDBOX_DSL_PHASES.exclude?(p) }

Check warning on line 4353 in Library/Homebrew/formula.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula.rb#L4351-L4353

Added lines #L4351 - L4353 were not covered by tests
if invalid_phases.any?
noun = if invalid_phases.count > 1
"phases"
else
"phase"
end
raise ArgumentError, "Unsupported sandbox phase #{noun}: #{invalid_phases.join(", ")}"

Check warning on line 4360 in Library/Homebrew/formula.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula.rb#L4360

Added line #L4360 was not covered by tests
end

@reduced_sandbox = types
@allowed_in_sandbox ||= {}
phases.each do |p|
@allowed_in_sandbox[p] ||= []
@allowed_in_sandbox[p].concat(types)
@allowed_in_sandbox[p] = @allowed_in_sandbox[p].uniq

Check warning on line 4367 in Library/Homebrew/formula.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula.rb#L4363-L4367

Added lines #L4363 - L4367 were not covered by tests
end
end
end
end
Expand Down
9 changes: 6 additions & 3 deletions Library/Homebrew/formula_installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -937,14 +937,15 @@ def build
formula.logs.mkpath
sandbox.record_log(formula.logs/"build.sandbox.log")
sandbox.allow_write_path(Dir.home) if interactive?
sandbox.allow_write_temp_and_cache(formula)
sandbox.allow_write_temp_and_cache
sandbox.allow_write_log(formula)
sandbox.allow_cvs
sandbox.allow_fossil
sandbox.allow_write_xcode
sandbox.allow_write_cellar(formula)
sandbox.deny_signal(formula)
sandbox.deny_all_network_except_pipe(error_pipe) unless formula.network_access_allowed?(:build)
sandbox.allow_write_global_temp if formula.allowed_in_sandbox?(:write_to_temp, phase: :build)
sandbox.deny_signal if formula.allowed_in_sandbox?(:signal, phase: :build)
sandbox.exec(*args)
else
exec(*args)
Expand Down Expand Up @@ -1154,7 +1155,7 @@ def post_install
sandbox = Sandbox.new
formula.logs.mkpath
sandbox.record_log(formula.logs/"postinstall.sandbox.log")
sandbox.allow_write_temp_and_cache(formula)
sandbox.allow_write_temp_and_cache
sandbox.allow_write_log(formula)
sandbox.allow_write_xcode
sandbox.deny_write_homebrew_repository
Expand All @@ -1163,6 +1164,8 @@ def post_install
Keg::KEG_LINK_DIRECTORIES.each do |dir|
sandbox.allow_write_path "#{HOMEBREW_PREFIX}/#{dir}"
end
sandbox.allow_write_global_temp if formula.allowed_in_sandbox?(:write_to_temp, phase: :postinstall)
sandbox.deny_signal if formula.allowed_in_sandbox?(:signal, phase: :postinstall)
sandbox.exec(*args)
else
exec(*args)
Expand Down
26 changes: 13 additions & 13 deletions Library/Homebrew/sandbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class Sandbox
SANDBOX_EXEC = "/usr/bin/sandbox-exec"
private_constant :SANDBOX_EXEC

SANDBOX_REDUCTIONS = [:allow_write_to_temp, :allow_signal].freeze
SANDBOX_DSL_RULES = [:write_to_temp, :signal].freeze
SANDBOX_DSL_PHASES = [:build, :postinstall, :test].freeze

sig { returns(T::Boolean) }
def self.available?
Expand Down Expand Up @@ -57,23 +58,22 @@ def deny_write_path(path)
deny_write path:, type: :subpath
end

sig { params(formula: T.nilable(Formula)).void }
def allow_write_temp_and_cache(formula = nil)
if formula&.reduced_sandbox&.include?(:allow_write_to_temp)
allow_write_path "/private/tmp"
allow_write_path "/private/var/tmp"
end
sig { void }
def allow_write_temp_and_cache
allow_write path: "^/private/var/folders/[^/]+/[^/]+/[C,T]/", type: :regex
allow_write_path HOMEBREW_TEMP
allow_write_path HOMEBREW_CACHE
end

sig { params(formula: T.nilable(Formula)).void }
def deny_signal(formula = nil)
puts "deny_signal: #{formula&.reduced_sandbox&.include?(:allow_signal)}"
unless formula&.reduced_sandbox&.include?(:allow_signal)
add_rule allow: false, operation: "signal", filter: "target others"
end
sig { void }
def allow_write_global_temp
allow_write_path "/private/tmp"
allow_write_path "/private/var/tmp"

Check warning on line 71 in Library/Homebrew/sandbox.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/sandbox.rb#L70-L71

Added lines #L70 - L71 were not covered by tests
end

sig { void }
def deny_signal
add_rule allow: false, operation: "signal", filter: "target others"

Check warning on line 76 in Library/Homebrew/sandbox.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/sandbox.rb#L76

Added line #L76 was not covered by tests
end

sig { void }
Expand Down

0 comments on commit c7ba27b

Please sign in to comment.