168 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| require "development_tools"
 | |
| module Cask
 | |
|   module Quarantine
 | |
|     module_function
 | |
| 
 | |
|     QUARANTINE_ATTRIBUTE = "com.apple.quarantine".freeze
 | |
| 
 | |
|     QUARANTINE_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/quarantine.swift").freeze
 | |
| 
 | |
|     # @private
 | |
|     def swift
 | |
|       @swift ||= DevelopmentTools.locate("swift")
 | |
|     end
 | |
| 
 | |
|     # @private
 | |
|     def xattr
 | |
|       @xattr ||= DevelopmentTools.locate("xattr")
 | |
|     end
 | |
| 
 | |
|     def check_quarantine_support
 | |
|       odebug "Checking quarantine support"
 | |
| 
 | |
|       if !system_command(xattr, print_stderr: false).success?
 | |
|         odebug "There's not a working version of xattr."
 | |
|         :xattr_broken
 | |
|       elsif swift.nil?
 | |
|         odebug "Swift is not available on this system."
 | |
|         :no_swift
 | |
|       else
 | |
|         api_check = system_command(swift,
 | |
|                                    args:         [QUARANTINE_SCRIPT],
 | |
|                                    print_stderr: false)
 | |
| 
 | |
|         case api_check.exit_status
 | |
|         when 5
 | |
|           odebug "This feature requires the macOS 10.10 SDK or higher."
 | |
|           :no_quarantine
 | |
|         when 2
 | |
|           odebug "Quarantine is available."
 | |
|           :quarantine_available
 | |
|         else
 | |
|           odebug "Unknown support status"
 | |
|           :unknown
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def available?
 | |
|       @status ||= check_quarantine_support
 | |
| 
 | |
|       @status == :quarantine_available
 | |
|     end
 | |
| 
 | |
|     def detect(file)
 | |
|       return if file.nil?
 | |
| 
 | |
|       odebug "Verifying Gatekeeper status of #{file}"
 | |
| 
 | |
|       quarantine_status = !status(file).empty?
 | |
| 
 | |
|       odebug "#{file} is #{quarantine_status ? "quarantined" : "not quarantined"}"
 | |
| 
 | |
|       quarantine_status
 | |
|     end
 | |
| 
 | |
|     def status(file)
 | |
|       system_command(xattr,
 | |
|                      args:         ["-p", QUARANTINE_ATTRIBUTE, file],
 | |
|                      print_stderr: false).stdout.rstrip
 | |
|     end
 | |
| 
 | |
|     def toggle_no_translocation_bit(attribute)
 | |
|       fields = attribute.split(";")
 | |
| 
 | |
|       # Fields: status, epoch, download agent, event ID
 | |
|       # Let's toggle the app translocation bit, bit 8
 | |
|       # http://www.openradar.me/radar?id=5022734169931776
 | |
| 
 | |
|       fields[0] = (fields[0].to_i(16) | 0x0100).to_s(16).rjust(4, "0")
 | |
| 
 | |
|       fields.join(";")
 | |
|     end
 | |
| 
 | |
|     def release!(download_path: nil)
 | |
|       return unless detect(download_path)
 | |
| 
 | |
|       odebug "Releasing #{download_path} from quarantine"
 | |
| 
 | |
|       quarantiner = system_command(xattr,
 | |
|                                    args:         [
 | |
|                                      "-d",
 | |
|                                      QUARANTINE_ATTRIBUTE,
 | |
|                                      download_path,
 | |
|                                    ],
 | |
|                                    print_stderr: false)
 | |
| 
 | |
|       return if quarantiner.success?
 | |
| 
 | |
|       raise CaskQuarantineReleaseError.new(download_path, quarantiner.stderr)
 | |
|     end
 | |
| 
 | |
|     def cask!(cask: nil, download_path: nil, action: true)
 | |
|       return if cask.nil? || download_path.nil?
 | |
| 
 | |
|       return if detect(download_path)
 | |
| 
 | |
|       odebug "Quarantining #{download_path}"
 | |
| 
 | |
|       quarantiner = system_command(swift,
 | |
|                                    args:         [
 | |
|                                      QUARANTINE_SCRIPT,
 | |
|                                      download_path,
 | |
|                                      cask.url.to_s,
 | |
|                                      cask.homepage.to_s,
 | |
|                                    ],
 | |
|                                    print_stderr: false)
 | |
| 
 | |
|       return if quarantiner.success?
 | |
| 
 | |
|       case quarantiner.exit_status
 | |
|       when 2
 | |
|         raise CaskQuarantineError.new(download_path, "Insufficient parameters")
 | |
|       else
 | |
|         raise CaskQuarantineError.new(download_path, quarantiner.stderr)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def propagate(from: nil, to: nil)
 | |
|       return if from.nil? || to.nil?
 | |
| 
 | |
|       raise CaskError, "#{from} was not quarantined properly." unless detect(from)
 | |
| 
 | |
|       odebug "Propagating quarantine from #{from} to #{to}"
 | |
| 
 | |
|       quarantine_status = toggle_no_translocation_bit(status(from))
 | |
| 
 | |
|       resolved_paths = Pathname.glob(to/"**/*", File::FNM_DOTMATCH)
 | |
| 
 | |
|       system_command!("/usr/bin/xargs",
 | |
|                       args:  [
 | |
|                         "-0",
 | |
|                         "--",
 | |
|                         "/bin/chmod",
 | |
|                         "-h",
 | |
|                         "u+w",
 | |
|                       ],
 | |
|                       input: resolved_paths.join("\0"))
 | |
| 
 | |
|       quarantiner = system_command("/usr/bin/xargs",
 | |
|                                    args:         [
 | |
|                                      "-0",
 | |
|                                      "--",
 | |
|                                      xattr,
 | |
|                                      "-w",
 | |
|                                      "-s",
 | |
|                                      QUARANTINE_ATTRIBUTE,
 | |
|                                      quarantine_status,
 | |
|                                    ],
 | |
|                                    input:        resolved_paths.join("\0"),
 | |
|                                    print_stderr: false)
 | |
| 
 | |
|       return if quarantiner.success?
 | |
| 
 | |
|       raise CaskQuarantinePropagationError.new(to, quarantiner.stderr)
 | |
|     end
 | |
|   end
 | |
| end
 | 
