OS X XQuartz Vulnerability Test Using R

It’s usually a good thing when my #rstats and infosec worlds collide. Unfortunately, this time it’s a script that R folk running on OS X can use to see if they are using a version of XQuartz that has a nasty vulnerability in the framework it uses to auto-update. If this test comes back with the warning, try to refrain from using XQuartz on insecure networks until the developers fix the issue.


Thanks to a gist prodding by @bearloga, here’s a script to scan all your applications for the vulnerability:

read_plist <- safely(readKeyValueDB)
safe_compare <- safely(compareVersion)
apps <- list.dirs(c("/Applications", "/Applications/Utilities"), recursive=FALSE)
# if you have something further than this far down that's bad you're on your own
for (i in 1:4) {
  moar_dirs <- grep("app$", apps, value=TRUE, invert=TRUE)
  if (length(moar_dirs) > 0) { apps <- c(apps, list.dirs(moar_dirs, recursive=FALSE)) }
apps <- unique(grep("app$", apps, value=TRUE))
pb <- txtProgressBar(0, length(apps), style=3)
suppressWarnings(map_df(1:length(apps), function(i) {
  x <- apps[i]
  setTxtProgressBar(pb, i)
  is_vuln <- FALSE
  version <- ""
  app_name <- sub("\\.app$", "", basename(x))
  app_loc <- sub("^/", "", dirname(x))
  to_look <- c(sprintf("%s/Contents/Frameworks/Autoupdate.app/Contents/Info.plist", x),
               sprintf("%s/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist", x),
               sprintf("%s/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist", x))
  is_there <- map_lgl(c(sprintf("%s/Contents/Frameworks/Sparkle.framework/", x), to_look), file.exists)
  has_sparkle <- any(is_there)
  to_look <- to_look[which(is_there[-1])]
  discard(map_chr(to_look, function(x) {
    read_plist(x)$result$CFBundleShortVersionString %||% NA
  }), is.na) -> vs
  if (any(map_dbl(vs, function(v) { safe_compare(v, "1.16.1")$result %||% -1 }) < 0)) {
    is_vuln <- TRUE
    version <- vs[1]
  data_frame(app_loc, app_name, has_sparkle, is_vuln, version)
})) -> app_scan_results
select(arrange(filter(app_scan_results, has_sparkle), app_loc, app_name), -has_sparkle)
Buy on AmazonDDS Blog
DDS PodcastAmazon Author Page

4 Comments OS X XQuartz Vulnerability Test Using R

  1. Mikhail Popov

    I’m trying to generalize your awesome vulnerability checker to check other applications too. Not sure where the “NAs introduced by coercion” errors/warnings are coming from :\

    Looks like you're safe with the version of /Applications/GPG Keychain.app
    Looks like you're safe with the version of /Applications/Sequel Pro.app
    Looks like you're safe with the version of /Applications/Telephone.app
    Looks like you're safe with the version of /Applications/Texpad.app
    Looks like you're safe with the version of /Applications/Utilities/NoSleep.app
    Looks like you're safe with the version of /Applications/TeX/BibDesk.app
    Looks like you're safe with the version of /Applications/TeX/LaTeXiT.app
    Looks like you're safe with the version of /Applications/TeX/TeX Live Utility.app
    Warning messages:
    1: In .f(.x[[i]], ...) : NAs introduced by coercion
    2: In FUN(X[[i]], ...) : /Applications/Manuscripts.app is vulnerable
    3: In FUN(X[[i]], ...) : /Applications/Papers.app is vulnerable
    4: In .f(.x[[i]], ...) : NAs introduced by coercion
    5: In FUN(X[[i]], ...) : /Applications/SourceTree.app is vulnerable
    6: In FUN(X[[i]], ...) : /Applications/VLC.app is vulnerable
    7: In FUN(X[[i]], ...) : /Applications/Utilities/XQuartz.app is vulnerable
    8: In .f(.x[[i]], ...) : NAs introduced by coercion
    9: In FUN(X[[i]], ...) : /Applications/TeX/TeXShop.app is vulnerable


    # Original code by Bob Rudis (@hrbrmstr)
    # "OS X XQuartz Vulnerability Test Using R" (http://rud.is/b/2016/03/07/os-x-xquartz-vulnerability-test-using-r/)
    # Modified to check for Sparkle vulnerability in other applications the user has installed.
    apps <- paste0("/Applications/", list.files("/Applications", pattern = ".*\.app", include.dirs = TRUE))
    utils <- paste0("/Applications/Utilities/", list.files("/Applications/Utilities", pattern = ".*\.app", include.dirs = TRUE))
    tex <- paste0("/Applications/TeX/", list.files("/Applications/TeX", pattern = ".*\.app", include.dirs = TRUE))
    invisible(lapply(c(apps, utils, tex), function(app) {
      info_locations <- c(paste0(app, "/Contents/Frameworks/Autoupdate.app/Contents/Info.plist"),
                          paste0(app, "/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist"))
      if (!dir.exists(paste0(app, "/Contents/Frameworks/Sparkle.framework"))) {
        # message(app, " doesn't use Sparkle")
      discard(map_chr(info_locations, function(x) { 
        if (file.exists(x)) {
        } else {
      }), is.na) -> vs
      if ((length(vs) > 0) & (any(map_int(vs, compareVersion, "1.16.1") < 0))) { 
        warning(app, " is vulnerable") 
      } else {
        message("Looks like you're safe with the version of ", app)
  2. hrbrmstr

    +1. The particular extension I use for formatting code will work in comments :-) All I had to do was wrap R code segments in <pre lang="rsplus"></pre> tags.

  3. Pingback: OS X XQuartz Vulnerability Test Using R – Mubashir Qasim

Leave a Reply