This article covers the use of bundler
features to secure Ruby applications. In this day and age, we have to be
more and more careful about software supply chain security.
We'll show you how to start this journey by
relying on a Gemfile and bundler
to manage your project's dependencies.
By the end of the post, you will better understand how bundler audit
and bundler outdated
work. Both can
help you monitor the security state of your
project's dependency tree.
Let's dive in!
An Introduction to Bundler for Ruby
The history of Bundler is linked to RubyGems. RubyGems, first released in 2004 by Chad Fowler, is a package manager that makes it possible to distribute and manage Ruby libraries, applications, and their dependencies.
Bundler, on the other hand, is a dependency manager. It was first released in 2009 by Carl Lerche.
Bundler helps developers manage dependencies between different Ruby libraries and applications. It uses a Gemfile to define the libraries and versions that a project depends on, then installs and loads those libraries in the correct order. Bundler provides a simple way to manage dependencies and ensure that all the required libraries are installed and loaded correctly.
Supply Chain Security
RubyGems and Bundler can only solve some security issues. For example, in 2013, 2018, and 2020, several security issues emerged directly related to RubyGems. Each time, gems were compromised, potentially exposing thousands of projects and products to malicious actors.
Nowadays, the security of the software supply chain has become more high profile, due to major incidents like 2020's SolarWinds hack, EventStream in 2018, and Equifax, Maersk, Merck, and FedEx in 2017.
Let's see how Bundler can help us avoid some security issues.
Bundler's Security-Related Features for Ruby
Bundler comes with several features that allow us to secure a Ruby project:
- Source validation: ensures gems are installed from a specific, trusted source.
- With a dependency graph, we can see a whole list of dependencies for each gem, helping us to identify potential security risks.
- A
Gemfile.lock
file records the specific versions of each gem used in the project. As it's packaged with the project, it ensures that the same versions of gems are used across different environments. - Vulnerability detection: Thanks to an integration with the Ruby Advisory Database (RDB), Bundler can detect known security vulnerabilities in gems.
- An audit command lets you list known security vulnerabilities related to gems a project depends on.
- Checksum verification: Bundler verifies the checksum of each gem before installing it.
- Gem signing and signature verification: Gems can be signed cryptographically by a developer.
But, alas, signing gems is complicated and requires a lot of steps. As a consequence, not all gems are signed, thus putting many projects at risk.
Yet, in the meantime, we can rely on two features in particular to ensure that a project doesn't rely on packages that are too old or that have been identified as carrying a security risk.
These features are bundler audit
and bundler outdated
. Let's look at bundler audit
first.
About Bundler-audit
bundle audit
is a command-line tool that helps you identify security vulnerabilities in the Ruby libraries that your
project depends on. It is a plugin for the Ruby dependency manager Bundler and is included with Bundler version 1.10 and
above.
bundle audit
works by analyzing your Gemfile.lock file, which lists all the dependencies for your project and their
versions. It then compares this information with a database of known vulnerabilities in Ruby gems (maintained by Rubysec).
If a vulnerability is found, bundle audit
will provide you with information about the vulnerability, including its
severity level and which gem versions have been affected. It will also suggest actions you can take to address the
vulnerability, such as upgrading to a patched version of the gem or removing it entirely.
bundle audit
is a useful tool for ensuring the security of your Ruby projects, especially if you are using third-party
libraries or dependencies. By regularly running bundle audit
as part of your development workflow, you can stay
up-to-date on any vulnerabilities that may affect your project and take proactive steps to address them.
Usage of Bundler-audit in Ruby
The basic use of Bundler-audit is through the bundle audit
command:
As you can see, it outputs the name and version of a package used by the project we are testing, which contains a medium-level security issue. It also outputs the URL of the security advisory and a possible solution.
Keeping Bundler-audit's Database Up to Date
As the audit is basically comparing a list of gems used in the project with a list of known security issues in a local copy of the security advisory database, you need to keep that database up to date.
To do so, you just need to run the bundle audit update
command. This will fetch the latest version of the database.
You should only have to run the audit again to check for any new security risks.
Integration In a CI Pipeline
As with other tools aimed at keeping your project safe, it's best to ensure an audit runs automatically on a regular basis (if not after every commit and push to the Git repository).
You can rely on a git post commit hook to run the audit locally if the Gemfile.lock
changes. Here is an
example:
This is a bit radical, but it works. You can rely on the same approach within the CI pipeline. As the bundle audit
command will return a non-zero exit status, the CI pipeline will consider it to have failed. Unfortunately, we
don't have a proper audit report out of the box. Instead, you have to direct the output of the bundle audit
command towards a file and save it as an artifact of the build for the CI pipeline.
Here is how to direct the command's output to a file:
As each CI service handles artifacts differently, check the relevant documentation to see how to do this.
Bundler-audit in Summary
bundle audit
is not a complete solution, yet it still lets your team know if your project relies on
unsafe gems (without costing you an arm and a leg). Your team should put tools such as git hooks or
CI tasks in place, and relevant integrations to make any issues visible.
All things considered, I'd say it's not too much trouble. It probably requires a few hours to get started and have the basic information spit out as a comment in your favorite git hosting solution, or as a notice in a Slack channel.
Yet bundle audit
is only one part of what you can do. It only tells you if there is a security issue related to a gem
relied on by a project. It does not tell you if a gem is outdated. To handle that, bundler outdated
is the command to
use.
Bundler Outdated for Ruby Gems
Auditing for security risks is very important, yet listing outdated gems is also a big issue. The longer you wait to update a dependency, the higher the risk that your code breaks.
You should aim to work in small iterations, deployed frequently, to limit the amount of change contained in each deployment. Equally, you should aim to update any gem you use as soon as it's updated, or as close to that as possible.
Usage of Bundler Outdated
Just like bundler audit
, bundler outdated
is very simple to use. Just call it at the root of your Ruby project. It
will compare the Gemfile.lock
content to the current gem releases listed in it.
As you can see, three gems are outdated in this project. Let's see how we should read this table:
- The first column contains the name of the gem.
- The second column shows the currently installed version.
- The third column shows the latest version available.
- The fourth column shows the request version (from the Gemfile).
- The fifth column shows the group the gem is part of (in the Gemfile).
Here, we can see we don't have much to worry about for addressable
and devise
, as the difference in releases is
within the patch versions (third part of the release number). The version of capybara
is only behind one minor release, which
implies a few more changes, but this rarely means breaking changes.
Still, the logical thing to do here is to update all three as soon as possible.
Usage In a CI Pipeline
As bundle outdated
will return a non-zero exit status for outdated gems, a CI task it's based on is
considered failed. So you can have a simple and direct flag in your CI pipeline in case of outdated
dependencies.
Yet you might want to rely on a pair of flags to improve your use of bundle outdated
. The command can filter its
output to only list gems that have pending patch-level updates:
In the same manner, you can list only gems with pending minor version updates:
And in the same way, you can list only gems with pending major release updates:
Those three flags let you easily tailor a CI task to only warn you that a patch-level update is available but not block the build (or the reverse), for example.
A complimentary option allows you to only list outdated gems within the default group:
As gems within test and development groups don't impact the production release, they could be considered less of a priority to keep up to date, for example. Thus you can use the flag only to raise a warning or block the CI pipeline if gems within the default group are outdated.
Wrapping Up
Maintaining the security of your Ruby application is not just the result of one action. Rather, it's a cumulative effect of several actions put together. As we've covered in this post, Bundler provides us with two commands that can help you to improve your application's security:
bundle audit
quickly lets you know if any gem you add or use in your project could pose a security threat and what to do about it.bundle outdated
allows you to flag gems that should be updated. It complementsbundle audit
, and if you keep on top of it, you avoid having an update bundle that's too large.
These two commands are easy to use and integrate with local feedback loops and CI pipelines.
Happy coding!
P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!