<commit_before># typed: true
# frozen_string_literal: true

require "erb"
require "io/console"
require "pty"
require "tempfile"

# Helper class for running a sub-process inside of a sandboxed environment.
#
# @api private
class Sandbox
  extend T::Sig

  SANDBOX_EXEC = "/usr/bin/sandbox-exec"
  private_constant :SANDBOX_EXEC

  sig { returns(T::Boolean) }
  def self.available?
    OS.mac? && File.executable?(SANDBOX_EXEC)
  end

  sig { void }
  def initialize
    @profile = SandboxProfile.new
  end

  def record_log(file)
    @logfile = file
  end

  def add_rule(rule)
    @profile.add_rule(rule)
  end

  def allow_write(path, options = {})
    add_rule allow: true, operation: "file-write*", filter: path_filter(path, options[:type])
  end

  def deny_write(path, options = {})
    add_rule allow: false, operation: "file-write*", filter: path_filter(path, options[:type])
  end

  def allow_write_path(path)
    allow_write path, type: :subpath
  end

  def deny_write_path(path)
    deny_write path, type: :subpath
  end

  def allow_write_temp_and_cache
    allow_write_path "/private/tmp"
    allow_write_path "/private/var/tmp"
    allow_write "^/private/var/folders/[^/]+/[^/]+/[C,T]/", type: :regex
    allow_write_path HOMEBREW_TEMP
    allow_write_path HOMEBREW_CACHE
  end

  def allow_cvs
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/.cvspass"
  end

  def allow_fossil
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/.fossil"
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/.fossil-journal"
  end

  def allow_write_cellar(formula)
    allow_write_path formula.rack
    allow_write_path formula.etc
    allow_write_path formula.var
  end

  # Xcode projects expect access to certain cache/archive dirs.
  def allow_write_xcode
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/Library/Developer"
  end

  def allow_write_log(formula)
    allow_write_path formula.logs
  end

  def deny_write_homebrew_repository
    deny_write HOMEBREW_BREW_FILE
    if HOMEBREW_PREFIX.to_s == HOMEBREW_REPOSITORY.to_s
      deny_write_path HOMEBREW_LIBRARY
      deny_write_path HOMEBREW_REPOSITORY/".git"
    else
      deny_write_path HOMEBREW_REPOSITORY
    end
  end

  def exec(*args)
    seatbelt = Tempfile.new(["homebrew", ".sb"], HOMEBREW_TEMP)
    seatbelt.write(@profile.dump)
    seatbelt.close
    @start = Time.now

    begin
      command = [SANDBOX_EXEC, "-f", seatbelt.path, *args]
      # Start sandbox in a pseudoterminal to prevent access of the parent terminal.
      T.unsafe(PTY).spawn(*command) do |r, w, pid|
        # Set the PTY's window size to match the parent terminal.
        # Some formula tests are sensitive to the terminal size and fail if this is not set.
        winch = proc do |_sig|
          w.winsize = if $stdout.tty?
            # We can only use IO#winsize if the IO object is a TTY.
            $stdout.winsize
          else
            # Otherwise, default to tput, if available.
            # This relies on ncurses rather than the system's ioctl.
            [Utils.popen_read("tput", "lines").to_i, Utils.popen_read("tput", "cols").to_i]
          end
        end
        # Update the window size whenever the parent terminal's window size changes.
        old_winch = trap(:WINCH, &winch)
        winch.call(nil)

        $stdin.raw! if $stdin.tty?
        stdin_thread = Thread.new { IO.copy_stream($stdin, w) }

        r.each_char { |c| print(c) }

        Process.wait(pid)
      ensure
        stdin_thread&.kill
        $stdin.cooked! if $stdin.tty?
        trap(:WINCH, old_winch)
      end
      raise ErrorDuringExecution.new(command, status: $CHILD_STATUS) unless $CHILD_STATUS.success?
    rescue
      @failed = true<commit_msg>Use raw block to return tty to proper state<commit_after># typed: true
# frozen_string_literal: true

require "erb"
require "io/console"
require "pty"
require "tempfile"

# Helper class for running a sub-process inside of a sandboxed environment.
#
# @api private
class Sandbox
  extend T::Sig

  SANDBOX_EXEC = "/usr/bin/sandbox-exec"
  private_constant :SANDBOX_EXEC

  sig { returns(T::Boolean) }
  def self.available?
    OS.mac? && File.executable?(SANDBOX_EXEC)
  end

  sig { void }
  def initialize
    @profile = SandboxProfile.new
  end

  def record_log(file)
    @logfile = file
  end

  def add_rule(rule)
    @profile.add_rule(rule)
  end

  def allow_write(path, options = {})
    add_rule allow: true, operation: "file-write*", filter: path_filter(path, options[:type])
  end

  def deny_write(path, options = {})
    add_rule allow: false, operation: "file-write*", filter: path_filter(path, options[:type])
  end

  def allow_write_path(path)
    allow_write path, type: :subpath
  end

  def deny_write_path(path)
    deny_write path, type: :subpath
  end

  def allow_write_temp_and_cache
    allow_write_path "/private/tmp"
    allow_write_path "/private/var/tmp"
    allow_write "^/private/var/folders/[^/]+/[^/]+/[C,T]/", type: :regex
    allow_write_path HOMEBREW_TEMP
    allow_write_path HOMEBREW_CACHE
  end

  def allow_cvs
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/.cvspass"
  end

  def allow_fossil
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/.fossil"
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/.fossil-journal"
  end

  def allow_write_cellar(formula)
    allow_write_path formula.rack
    allow_write_path formula.etc
    allow_write_path formula.var
  end

  # Xcode projects expect access to certain cache/archive dirs.
  def allow_write_xcode
    allow_write_path "#{Dir.home(ENV.fetch("USER"))}/Library/Developer"
  end

  def allow_write_log(formula)
    allow_write_path formula.logs
  end

  def deny_write_homebrew_repository
    deny_write HOMEBREW_BREW_FILE
    if HOMEBREW_PREFIX.to_s == HOMEBREW_REPOSITORY.to_s
      deny_write_path HOMEBREW_LIBRARY
      deny_write_path HOMEBREW_REPOSITORY/".git"
    else
      deny_write_path HOMEBREW_REPOSITORY
    end
  end

  def exec(*args)
    seatbelt = Tempfile.new(["homebrew", ".sb"], HOMEBREW_TEMP)
    seatbelt.write(@profile.dump)
    seatbelt.close
    @start = Time.now

    begin
      command = [SANDBOX_EXEC, "-f", seatbelt.path, *args]
      # Start sandbox in a pseudoterminal to prevent access of the parent terminal.
      T.unsafe(PTY).spawn(*command) do |r, w, pid|
        write_to_pty = proc {
          # Set the PTY's window size to match the parent terminal.
          # Some formula tests are sensitive to the terminal size and fail if this is not set.
          winch = proc do |_sig|
            w.winsize = if $stdout.tty?
              # We can only use IO#winsize if the IO object is a TTY.
              $stdout.winsize
            else
              # Otherwise, default to tput, if available.
              # This relies on ncurses rather than the system's ioctl.
              [Utils.popen_read("tput", "lines").to_i, Utils.popen_read("tput", "cols").to_i]
            end
          end

          begin
            # Update the window size whenever the parent terminal's window size changes.
            old_winch = trap(:WINCH, &winch)
            winch.call(nil)

            stdin_thread = Thread.new { IO.copy_stream($stdin, w) }

            r.each_char { |c| print(c) }

            Process.wait(pid)
          ensure
            stdin_thread&.kill
            trap(:WINCH, old_winch)
          end
        }
        if $stdout.tty?
          $stdin.raw &write_to_pty
        else
          write_to_pty.call
        end
      end
      raise ErrorDuringExecution.new(command, status: $CHILD_STATUS) unless $CHILD_STATUS.success?
    rescue
      @failed = true
{'fraction_non_alphanumeric': 0.10052356020942409, 'fraction_numerical': 0.0, 'mean_word_length': 2.861040929762506, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1523669', 'n_tokens_mistral': 2669, 'n_tokens_neox': 2558, 'n_words': 630}