Bundler has a major security vulnerability that affects all stable versions. The vulnerability allows an attacker to inject arbitrary code into your application via any secondary gem source declared in your gemfile, whether or not that source is scoped to specific gems.

CVE ID

CVE-2016-7954 is assigned to this vulnerability.

Over two years ago, a similar vulnerability (CVE-2013-0334) was discovered and is described in a post on Bundler’s blog. The proposed resolution was to upgrade to Bundler 1.7+. This no longer resolves the issue and in fact, never did. All of the broken behaviors described below also apply to Bundler 1.7.

Example

Bundler allows you to specify multiple sources for your gem dependencies. In some cases, which source to use for a given gem is unclear. To maximize that ambiguity for our examples, let’s consider a gemfile with two sources and three gems, all three of which are available from both sources.

source "http://public.org" source "http://private.com" gem "foo" gem "bar" gem "baz"

Source Ambiguity Warnings

Ever since version 1.7.0, Bundler displays helpful warning messages whenever a gem could have been installed from any one of the multiple sources available. Bundler’s default behavior is to honor the last source. The output of bundle install for the gemfile above (using Bundler 1.13.2) is as follows:

Fetching source index from http://private.com/ Fetching source index from http://public.org/ Resolving dependencies... Installing foo 1.0.0 Installing bar 1.0.0 Installing baz 1.0.0 Using bundler 1.13.2 Bundle complete! 3 Gemfile dependencies, 4 gems now installed. Use `bundle show [gemname]` to see where a bundled gem is installed. Warning: the gem 'foo' was found in multiple sources. Installed from: http://private.com/ Also found in: * http://public.org/ You should add a source requirement to restrict this gem to your preferred source. For example: gem 'foo', :source => 'http://private.com/' Then uninstall the gem 'foo' (or delete all bundled gems) and then install again. Warning: the gem 'bar' was found in multiple sources. Installed from: http://private.com/ Also found in: * http://public.org/ You should add a source requirement to restrict this gem to your preferred source. For example: gem 'bar', :source => 'http://private.com/' Then uninstall the gem 'bar' (or delete all bundled gems) and then install again. Warning: the gem 'baz' was found in multiple sources. Installed from: http://private.com/ Also found in: * http://public.org/ You should add a source requirement to restrict this gem to your preferred source. For example: gem 'baz', :source => 'http://private.com/' Then uninstall the gem 'baz' (or delete all bundled gems) and then install again.

These warning messages point out two ways to resolve the source ambiguity:

a source block

block the :source option

For demonstration purposes, let’s assume we only want to install the bar gem from the secondary ( private.com ) source. The foo and baz gems should be installed from our primary ( public.org ) source.

The :source Option

Let’s start with trying to use the :source option since the warning messages give precise examples of how to do so. The updated gemfile is as follows:

source "http://public.org" gem "foo" gem "bar", source: "http://private.com" gem "baz"

When I run bundle install with this new gemfile (after uninstalling all gems), here’s the output I get:

Fetching source index from http://private.com/ Fetching source index from http://public.org/ Resolving dependencies... Installing foo 1.0.0 Installing bar 1.0.0 Installing baz 1.0.0 Using bundler 1.13.2 Bundle complete! 3 Gemfile dependencies, 4 gems now installed. Use `bundle show [gemname]` to see where a bundled gem is installed.

I didn’t receive any warnings, so my assumption is that foo and baz installed from my primary source ( public.org ) while bar installed from my secondary source ( private.com ). Unfortunately, that is not the case.

All three gems are installed from the secondary source.

I was able to test this behavior by starting two gem servers on separate ports, each with all three gems available. Then I ran bundle from a clean slate and watched the gem server logs to determine which gems were requested of each server.

As you can see from the server logs of the two gem servers, all three gems were installed from the secondary gem server. This is not the expected behavior.

source Blocks

Another potential solution is to wrap gem declarations within a source block for the secondary source. Here’s the updated gemfile:

source "http://public.org" gem "newcomb" source "http://private.com" do gem "fair_dice_roll" end gem "insecure_random"

Same problem.

So what?

Bundler’s unexpected behavior means that the addition of a secondary source opens all of your gems up to being installed from an unknown or unexpected location. At its worst, an attacker can introduce arbitrary code to phone home with your sensitive configuration values or data.

Consider the following gemfile:

source "https://rubygems.org" gem "rails" group :development, :test, :console do gem "handy_dev_helpers", source: "https://helpfuldeveloper.org" end

You may already be wary of secondary gem sources, so perhaps you bundle open the "handy_dev_helpers" gem to inspect its contents and determine that it’s safe to use.

However, if the helpfuldeveloper.org gem server also makes a "rails" gem available, that "rails" gem will be used instead of the gem from rubygems.org . The imposter gem may behave exactly like Rails, but it may also have additional code meant to compromise your information.

With the gemfile above, Bundler would have given no warning or indication that the source for "rails" is ambiguous.

How To Protect Yourself

Use multiple source blocks.

The only way I was able to properly map each gem to its proper source was to put every gem declaration within its own source block rather than having any concept of a primary source.

source "http://public.org" do gem "foo" gem "baz" end source "http://private.com" do gem "bar" end

This is currently the only way (besides having a single source) to ensure that all of your gems install from their expected sources.

Fixing Bundler

I discovered this vulnerability in March, 2016. After running and documenting my intitial tests, I wrote a report (much like this blog post) and sent it to the Bundler team on April 1.

It seems as though this vulnerability has been patched in the development of Bundler 2. However, I’m told that there is no plan to backport the fix to Bundler 1 since that requires backwards incompatible changes. As of this writing, I have received no response to my suggestions to update warning message recommendations and/or to add warning messages for the silent problematic cases.

Fifteen versions of Bundler have been released since my initial report in April, four since my latest email with the team.

Bundle safely! Steer clear of global sources!