<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/r%e2%81%b6-exploring-macos-applications-with-codesign-gatekeeper-r/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://rud.is/b/2017/08/13/r%E2%81%B6-exploring-macos-applications-with-codesign-gatekeeper-r/"> R – rud.is</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>(General reminder abt “R⁶” posts in that they are heavy on code-examples, minimal on expository. I try to design them with 2-3 “nuggets” embedded for those who take the time to walk through the code examples on their systems. I’ll always provide further expository if requested in a comment, so don’t hesitate to ask if something is confusing.)</p>
<p>I had to check something on the macOS systems across the abode today and — on a lark — decided to do all the “shell” scripting in R vs <code>bash</code> for a change. After performing the tasks, it occurred to me that not all R users on macOS realize there are hidden gems of information spread across the “boring” parts of the filesystem in SQLite databases. So, I put together a small example that:</p>
<ul>
<li>identifies all the top-level apps in <code>/Applications</code></li>
<li>extracts code signing information from them into a tibble</li>
<li>grabs the <a rel="nofollow" href="https://support.apple.com/en-us/HT202491" target="_blank">Gatekeeper</a> whitelist database</li>
<li>uses an internal SQLite transformation function inside a <code>dplyr</code> chain</li>
<li>merges the info and gives you a basis to explore your apps</li>
<li>shows off the <em>wicked cool</em> <code>processx</code> package by <a rel="nofollow" href="http://twitter.com/gaborcsardi" target="_blank">@gaborcsardi</a> that you should be using in place of <code>system()</code> / <code>system2()</code></li>
</ul>
<p>A quick note about this Gatekeeper database: If an app is not already recognized by macOS and you allow it to run your local system is updated to “whitelist” this app so you don’t get the notification in the future. Apple maintains a large signature list that gets updated throughout the year on your system and your own list is merged with it.</p>
<p>A second quick note (I guess I’m doing more expository than promised <img alt="🙂" src="https://s.w.org/images/core/emoji/2/72x72/1f642.png" class="wp-smiley"> about <code>QUOTE()</code>: In certain <code>dplyr</code> / <code>dbplyr</code> contexts, what look like local function calls are actually passed over to the SQL side vs used locally. Even though <code>quote()</code> is an internal R function, the use of the lowercase version of it would still go over to the SQL side vs be eval’d locally. However, to avoid confusing others, I try to uppercase conflicts like this when they occur.</p>
<pre><code>library(processx)
library(stringi)
library(docxtractr) # install_github(&quot;hrbrmstr/docxtractr&quot;)
library(tidyverse)

list.files(&quot;/Applications&quot;, pattern = &quot;app$&quot;, full.names = TRUE) %>% 
  map_df(~{
    message(.x)
    res &lt;- run(&quot;codesign&quot;, args = c(&quot;-dvvv&quot;, .x), error_on_status = FALSE)
    if (any(grepl(&quot;not signed at all&quot;, res$stderr))) {
      list(Executable = .x)
    } else {
      stri_split_lines(res$stderr)[[1]] %>% 
        keep(~grepl(&quot;=&quot;, .)) %>% 
        stri_split_fixed(&quot;=&quot;, 2, simplify = TRUE) -> res
      as.list(set_names(res[,2], res[,1]))
    }
  }) %>% 
  mcga() %>% 
  mutate(short_name = stri_replace_last_fixed(basename(executable), &quot;.app&quot;, &quot;&quot;)) -> my_apps

glimpse(my_apps)
## Observations: 102
## Variables: 20
## $ executable                  </code></pre>