Unquarantine Mac Apps

On every Mac I manage – including my personal Mac I’m using right now – I use https://brew.sh to manage my installed apps. But – there’s one thing I truly despise – and that is running brew as my logged-in, low-privilege, non-sudo user. So I run brew as a different, privileged user. (And – of course – you run the same?)

Oh wait – “brew is not supported in a multi-user mode! It could break any INSTANT!”. Yes, I’ve actually had that complaint made. What a bunch of FUD. Please – pay no attention to the naysayers – I’ve run brew as a separate privileged user to manage a whole lab of Macs. For many years. With almost zero problems. (Yes there are a few – but very few – problems. To be covered later in more detail later.)

One problem is that brew casks installed as the separate user continuously prompt the user about “Unsafe program downloaded from Internet – Do you want to open?” Which of course is meaningless to the user. And since the “unquarantine” only applies if *the current user* is the same user who ran brew to install the cask – the problem Never Goes Away. So I want to fix that problem – and here is how.

I use Chef to manage all of my Macs – so one of the steps is to install all the required casks, and then another is to run the script to unquarantine those casks. And for you, dear reader, here is that awesome script. Enjoy!


# save my path

# locate sed (latest brew installs only to gsed)
the_sed=$(which gsed 2>/dev/null)
[ x"$the_sed" = x ] && the_sed=$(which sed)
#echo "Using '$the_sed'"

# locate the brew-user
the_brew_user=$(ls -ld $(which brew) | awk '{print $3}')

# these are in here to allow this script to work directly in chef recipe

# are we the current brew user?
if [ x"$the_brew_user" != x"$THE_CUR_USER" ] ; then
  # run command as different user
  echo "We are user '$THE_CUR_USER'."
  echo "We must become brew user '$the_brew_user'."
  echo "**Enter password for '$the_brew_user' when prompted:"
  while [ $the_rc -ne 0 ] ; do
    su $the_brew_user -c "$THE_PATH"
    [ $the_rc -ne 0 ] && echo 'Try again...'
  exit $the_rc

# now we are the current brew user - issue a sudo cmd
echo ''
echo "We are now the brew user '$the_brew_user'."
echo "We are executing a privileged command."
echo "**Enter '$the_brew_user' password if prompted:"
sudo echo 'Privileged command executed OK.'
[ $the_rc -ne 0 ] && echo 'Failed privileged command. Exiting.' && exit $the_rc

# iterate over all installed casks
echo ''
echo '**Unquarantine brew applications...'
for i in $(sudo -u $the_brew_user brew cask list) ; do
  echo -n "Unquarantine $i: "
  the_dir=$(find /usr/local/Caskroom -name $i -type d 2>/dev/null)
  l_rc=$? ; [ $l_rc -ne 0 ] && echo "find /usr/local/Caskroom fail with $l_rc" && continue
  [ x"$the_dir" = x ] && echo 'empty the_dir' && continue

  # are there artifacts available?
  if sudo -u $the_brew_user brew cask info $i 2>/dev/null | grep -ie '^==> Artifacts' >/dev/null 2>&1 ; then
    # *must* have an app
    the_app_name=$(sudo -u $the_brew_user brew cask info $i | grep -A10 -ie '^==> Artifacts' | grep -ie '(App)' | $the_sed -e "s/^\(.*\) \+(App)/$fool_ruby_parser/i")
    if echo "$the_app_name" | grep -e ' -> ' >/dev/null 2>&1 ; then
      # link specified - extract
      the_app_name=$(echo "$the_app_name" | awk -F' -> ' '{print $2}')
      if echo "$the_app_name" | grep -e ')$' ; then
        # some kind of parenthetical indicator
        the_app_name=$(echo "$the_app_name" | sed -e 's/^\(.*\) ([A-Za-z]\+)$/\1/')
    [ x"$the_app_name" != x ] && echo "$the_app_path" | grep -v '/' >/dev/null 2>&1 && the_app_path="/Applications/$the_app_name"

  # if no app lookup from install file
  if [ x"$the_app_path" = x ] ; then
    # get the install file first
    the_installer_file=$(sudo -u $the_brew_user brew cask info $i | grep -ie '^from: ' | $the_sed -e "s/^from: \+\(.*\)/$fool_ruby_parser/i; s/^.*\/\([^\/]\+\)\$/$fool_ruby_parser/" | head -n 1)
    l_rc=$? ; [ $l_rc -ne 0 ] && echo "brew cask info fail with $l_rc" && continue
    [ x"$the_installer_file" = x ] && echo 'empty the_installer_file' && continue

    # where is it in brew?
    the_installer_path=$(find "$the_dir" -type f -name "$the_installer_file" 2>/dev/null | head -n 1)
    l_rc=$? ; [ $l_rc -ne 0 ] && echo "find $the_dir fail with $l_rc" && continue
    [ x"$the_installer_path" = x ] && echo "empty the_installer_path" && continue

    # extract the app name
    the_app_path=$(cat "$the_installer_path" | grep -e '/Applications/' | $the_sed -e "s/^.*\(\/Applications\/.*\.app\).*/$fool_ruby_parser/")
    l_rc=$? ; [ $l_rc -ne 0 ] && echo "grep /Applications/ fail with $l_rc" && continue
  [ x"$the_app_path" = x ] && echo 'empty the_app_path' && continue

  # app must be a valid name
  [ ! -d "$the_app_path" ] && echo "missing app '$the_app_path'" && continue

  # disable the quarantine bit on the discovered application
  sudo xattr -r -d com.apple.quarantine "$the_app_path"
  l_rc=$? ; [ $l_rc -ne 0 ] && echo "xattr fail $l_rc" && continue
  echo 'OK'

Let’s give a few notes before I move on to my next project 😉

  • One problem I ran into when deploying this script from Chef was that I wanted to use ERB so I could do substitutions if I ever needed to. But my regex failed because the Chef parser kept killing my \1 replacement references. So I have a trick or two to fool the Chef ruby parser. Just FYI.
  • Not every app will have an actual app_path I can deterministically identify. Here’s an actual output:
    $ ~/bin/lcl-disable-brew-quarantine-bit.sh
    Enter password for 'none-of-your-beeswax' when prompted...
    Unquarantine docker: Password:
    Unquarantine firefox: OK
    Unquarantine gimp: OK
    Unquarantine google-chrome: OK
    Unquarantine idrive: empty the_app_path
    Thus far I haven’t found this to be a problem – all the applications that my users *can* open interactively are properly unquarantined.
  • I use the term chef-user in the code above to identify the user which owns brew. So, yes, that could be “brew-user” instead, but in my case since all is managed using Chef – I haven’t modified it for this post.

Happy Computing!

Team-oriented systems mentor with deep knowledge of numerous software methodologies, technologies, languages, and operating systems. Excited about turning emerging technology into working production-ready systems. Focused on moving software teams to a higher level of world-class application development. Specialties:Software analysis and development...Product management through the entire lifecycle...Discrete product integration specialist!

Tagged with:

Leave a Reply

Your email address will not be published. Required fields are marked *


Human Verification: In order to verify that you are a human and not a spam bot, please enter the answer into the following box below based on the instructions contained in the graphic.