Adam Prescott

Automated vulnerability checking with bundler-audit and Travis

Every application has dependencies. Those dependencies can have bugs. Worse, those bugs can be security vulnerabilities. Security bugs are, obviously, bad. They’re also hard to detect. Everything will continue to run as if all is well, except there’s a hole in your application. How do you find out the hole even exists?

In the absence of automated tooling, you could subscribe to every mailing list related to security announcements. You could keep track of every transitive dependency for your application, and follow the development work of every single library you depend on. For each and every application. But that would be exhausting.

For Ruby, you can automate all of that with bundler-audit, which sits on top of Bundler.

Bundler is the de facto way of managing dependencies. It provides, among other things, a clear way of specifying required libraries and their versions, by keeping track of everything for you through Gemfile and (for applications) Gemfile.lock. Exactly the sort of information you’d need when checking for security vulnerabilities.

bundler-audit takes this information from Gemfile.lock and cross-references it with a list of known vulnerabilities, by relying on existing public information like the CVE database.

With a simple bundle-audit check, bundler-audit will look for any known problems for the specific versions of all your dependencies, and list them all. Much better than doing all the work manually.

Running the check is a one-liner in your terminal:

bundle-audit update && bundle-audit check

And here’s some example output from the bundle-audit check, straight from bundler-audit’s README:

Name: actionpack
Version: 3.2.10
Advisory: OSVDB-91452
Criticality: Medium
Title: XSS vulnerability in sanitize_css in Action Pack
Solution: upgrade to ~> 2.3.18, ~> 3.1.12, >= 3.2.13

This is, of course, extremely useful. But you don’t want to constantly remember to run it manually, right? So automate it!

Hopefully you already have a continuous integration system in use that runs a test suite against your application. That’s a great place to add vulnerability checks. In this case I’ll use Travis, but the idea is the same.

Setup is super simple.

First, follow the bundler-audit README and install the gem. For your Ruby application, you can add it to the development and test groups of your Gemfile, that way it’s available for everyone else, and for Travis.

group :development, :test do
  gem "bundle-audit"

After a bundle install, you should be able to run bundle-audit update && bundle-audit check. I strongly recommend running this before doing anything else. You want no warnings:

$ bundle-audit check
No unpatched versions found

If you have vulnerabilities, patch them first.

Note that if you have any github: references in your Gemfile, you will almost definitely have issues with unsafe sources. Until Bundler 2.0 is released, github: by default will generate git:// URLs. To get around that, add this to the top of your Gemfile:

# top of Gemfile

# NOTE: this is temporary until Bundler 2.0
# changes how github: references work.
git_source(:github) do |repo_name|
  repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")

When you make this change, you will need to run bundle install to generate a new Gemfile.lock that uses https: in place of git:. Git commit hashes may change unless you’re locking commit references with ref: or something similar. Either go with the updated git commit, lock the reference to a specific SHA, or edit Gemfile.lock after a bundle install. (Editing Gemfile.lock is usually not recommended. Just make sure you bundle install afterwards to confirm there isn’t something broken.)

With all this in place, you can now automatically check for known vulnerabilities with each Travis build.

Open up .travis.yml and append the bundle-audit commands to your script section.

# .travis.yml

  # - ...
  # - existing test execution here
  # - ...

  - "bundle exec bundle-audit update && bundle exec bundle-audit check"

By using the script: section like this, the build will fail if there are security problems. However, your test suite still runs, so you get valuable feedback even if the vulnerability check fails and the overall build fails.

You could configure things so that the build will only output warnings from bundle-audit check but then you have to remember to look at build output from builds for warnings. With your human eyes. Gross. I recommend treating security vulnerabilities as loud failures — the alternative is to place unnecessary responsibility on maintainers and developers.

Finally, a caveat. This entire approach assumes you run builds frequently enough for issues to be found early. If you have an application which doesn’t see many builds from day to day, you may want a separate task somewhere that runs the same check. Especially if the application is a core piece of infrastructure or protects sensitive information.