How to identify Bundler Gemfile files vulnerable to CVE-2020-36327 and how to mitigate the issue

Updated

1. When the issue occurs
2. Identifying vulnerable configuration
2.1 Examples of vulnerable configuration
2.2 Dependency API
2.3 Bundler versions
3. Workarounds and fixes
3.1 Using only gems provided by an internal infrastructure
3.2 Listing all necessary dependencies in the Gemfile
3.3 Applying the fixed Bundler versions
3.3.1 How the fixed Bundler version 1 works
3.3.1.1 Bundler version 1 prints an error for a source not implementing the dependency API
3.3.1.2 Bundler version 1 prints an error for a gem dependency not listed in your Gemfile
3.3.1.3 Examples
3.3.2 Customizing Bundler version 1 errors by environment variables

Ruby is distributed with Bundler, a dependencies resolver and installer for RubyGem packages.

The CVE-2020-36327 vulnerability in Bundler affects configurations that specify multiple sources from which RubyGem packages are installed. This can cause dependencies to be installed from a different source than intended.

When resolving dependencies of a RubyGem package that is set to be installed from a specific source, Bundler is supposed to check this specific source first and install the dependency from the same source. Only if the dependency is not provided by this source, Bundler is supposed to fall back to check other defined sources. However, this flaw caused Bundler to always consider all sources and install the latest version of the dependency RubyGem available in any of the source repositories.

This can be a problem when one of the sources is an external RubyGems hosting service, such as Content from rubygems.org is not included.RubyGems.org, where anyone can register and provide a RubyGem package with a name that has not been registered by anyone else. An attacker able to guess the name of an internal RubyGem package might register their own malicious package with the same name on an external hosting service, and cause it to override the package provided by the internal repository.

1. When the issue occurs

A Gemfile is a configuration file that specifies what RubyGem packages Bundler should install and from which repositories (referred to as source in a Gemfile). The Gemfile lists all direct dependencies required by a Ruby application, so that Bundler can correctly handle resolving and installing any indirect dependencies.

Dependencies resolution and subsequent Gemfile.lock generation or re-generation can trigger the vulnerability for certain configurations.

However, installing dependencies from a safely generated Gemfile.lock file using the bundle install --frozen or bundle install --deployment commands does not trigger this issue.

2. Identifying vulnerable configuration

A configuration becomes vulnerable if all of the following conditions are met:

  1. A Gemfile combines external and internal sources.
  2. An internal gem included in the Gemfile has an internal dependency that is not listed in the Gemfile.
  3. The installed Bundler version does not include the fix for this CVE, or the dependency API is not available on an internal gem server.

2.1 Examples of vulnerable configuration

The internal_gem dependency is always installed from an internal gem server. The dependent_gem dependency can potentially be installed from an unsafe external source instead if the external RubyGem version is higher than the internal version.

An example of a vulnerable Gemfile:

# external source
source "https://rubygems.org/"

# internal gem server
source "https://gems.example/" do
  # this requires 'dependent_gem'
  gem "internal_gem"
end

NOTE: For simplicity, all versions are omitted.

An alternative notation for a vulnerable Gemfile might look as follows:

# external source
source "https://rubygems.org/"

# internal gem server
# this requires 'dependent_gem'
gem "internal_gem", source: "https://gems.example/"

2.2 Dependency API

IMPORTANT: The fix for this issue depends on all defined sources to provide the dependency API. If any of the defined sources fails to provide the API, Bundler silently falls back to the unsafe legacy index dependency resolution.

Gem server implementations that are known to provide the dependency API:

Gem server implementations that do not currently provide the dependency API, and should not be used in configurations that combine internal and external sources:

2.3 Bundler versions

Vulnerable Bundler versions are listed in on the CVE-2020-36327 page.

3. Workarounds and fixes

To mitigate or fix the issue, use one of the following approaches:

3.1 Using only gems provided by an internal infrastructure

You can securely provide gems in several ways:

  • The recommended and most secure approach is to download all required dependencies to your private gem server.
  • Alternatively to hosting internal dependencies, an internal gem server can provide a secure proxy to an external source. To achieve this, configure the internal gem server to serve only selected external gems.
  • The gem dependencies can also be packaged as RPMs.

3.2 Listing all necessary dependencies in the Gemfile

To mitigate the issue, set all necessary internal gem dependencies (dependent_gem) explicitly in the Gemfile:

# external source
source "https://rubygems.org/"

# internal gem server
source "https://gems.example/" do
  # this requires 'dependent_gem'
  gem "internal_gem"
  gem "dependent_gem"
end

An alternative notation with the listing dependencies mitigation might look as follows:

# external source
source "https://rubygems.org/"

# internal gem server
# this requires 'dependent_gem'
gem "internal_gem", source: "https://gems.example/"
gem "dependent_gem", source: "https://gems.example/"

See also examples in the Bundler prints an error for a gem dependency not listed in your Gemfile section.

3.3 Applying the fixed Bundler versions

The fixes in Bundler version 2 and version 1 are implemented in a different way.

In Bundler version 2 (available in Ruby 2.7 and later), the vulnerability is fixed only for the RubyGem sources that have the dependency API implemented. Sources without the dependency API, remain vulnerable. Therefore, Red Hat recommends using only sources implementing dependency API in production.

In Bundler version 1 (available with Ruby 2.6 and earlier; using a fix specific to Red Hat Bundler 1 packages), the fixed Bundler prints errors or warnings that guide users on how to prevent the vulnerable case from being executed. When Bundler exits with an error, it prevents the risk but Bundler might fail to install dependencies from configurations that previously worked. You can customize Bundler behavior but make sure you evaluate the risk first. Note that Bundler 1 in Ruby 2.0 on RHEL 7.9 is not affected because the Bundler version 1.7.8 is earlier than the affected versions.

The following table lists available Bundler fixes by product and Ruby version.

ProductRuby versionBundler versionFixed in
RHEL 8.5Ruby 3.0Bundler 2Not affected; fixed in the initial release RHEA-2021:4282
RHEL 8.5Ruby 2.7Bundler 2RHSA-2021:3020 (fixed in RHEL 8.4)
RHEL 8.5Ruby 2.6Bundler 1RHSA-2022:0543
RHEL 8.5Ruby 2.5Bundler 1RHSA-2022:0545
RHEL 8.4Ruby 2.7Bundler 2RHSA-2021:3020
RHEL 8.4 EUSRuby 2.6Bundler 1RHSA-2022:0544
RHEL 8.4 EUSRuby 2.5Bundler 1RHSA-2022:0546
RHEL 8.2 EUSRuby 2.6Bundler 1RHSA-2022:0582
RHEL 8.2 EUSRuby 2.5Bundler 1RHSA-2022:0547
RHEL 8.1 E4SRuby 2.6Bundler 1RHSA-2022:0581
RHEL 8.1 E4SRuby 2.5Bundler 1RHSA-2022:0548
RHEL 7.9Ruby 2.0Bundler 1Not affected - Bundler version 1.7.8 is earlier than the affected versions
RHSCLRuby 3.0Bundler 2RHSA-2021:3982
RHSCLRuby 2.7Bundler 2RHSA-2021:3559
RHSCLRuby 2.6Bundler 1RHSA-2022:0708

3.3.1 How the fixed Bundler version 1 works

Bundler version 1 prints an error in the following two scenarios:

3.3.1.1 Bundler version 1 prints an error for a source not implementing the dependency API

If your Gemfile includes a source not implementing the dependency API, Bundler prints the following error and exits with a non-zero exit status:

$ bundle install
Fetching source index from https://gems.example/
Fetching gem metadata from https://rubygems.org/
Fetching source index from https://rubygems.org/
Your Gemfile contains scoped sources that don't implement a dependency API, namely:

  * rubygems repository https://gems.example/ or installed locally

Using the above gem servers may result in installing unexpected gems. To resolve this warning, make sure you use gem servers that implement dependency APIs, such as gemstash or geminabox gem servers. Or set the environment variable BUNDLE_WARN_ON_DEPENDENCY_CONFUSION.

NOTE: The rubygems repository https://gems.example/ or installed locally message is a string expression of Bundler::Source::Rubygems class managing a source. The or installed locally expression means a case when a gem belonging to a RubyGem repository is already installed locally.

To mitigate the issue, make sure to use only gem servers that implement a dependency API in your Gemfile.

To suppress the error and change it to a warning, set the environment variable BUNDLE_WARN_ON_DEPENDENCY_CONFUSION=1.
WARNING: When you set this environment variable, you are re-introducing the risk of the security issue. See Customizing Bundler errors by environment variables for more information.

3.3.1.2 Bundler version 1 prints an error for a gem dependency not listed in your Gemfile

If your Gemfile includes only sources that implement a dependency API, Bundler prints the following error and exits with a non-zero exit status if both of the following conditions are met:

  1. A gem dependency from an internal gem server is not listed in your Gemfile.
  2. A gem dependency from an internal gem server also exists in another gem server (external or internal source).
$ bundle install
Fetching gem metadata from https://gems.example/....
Fetching gem metadata from https://rubygems.org/.
Fetching gem metadata from https://rubygems.org/..
Fetching gem metadata from https://rubygems.org/..
Your Gemfile contains implicit dependency gems dependent_gem_a, dependent_gem_b on the scoped sources, namely:

  * rubygems repository https://gems.example/ or installed locally

Using implicit dependency gems on the above sources may result in installing unexpected gems. To suppress this message, make sure you set the gems explicitly in the Gemfile. Or set the environment variable BUNDLE_WARN_ON_DEPENDENCY_CONFUSION.

To mitigate the issue, set all necessary gem dependencies explicitly in the Gemfile.

To only suppress the error, you can change the error message to a warning by setting the environment variableBUNDLE_WARN_ON_DEPENDENCY_CONFUSION=1.
WARNING: When you set this environment variable, you are re-introducing the risk of the security issue. See Customizing Bundler errors by environment variables for more information.

3.3.1.3 Examples

An example of a vulnerable Gemfile where not all necessary gem dependencies are listed:

source "https://rubygems.org/"

source "https://gems.example/" do
  # The 'internal_gem' requires 'dependent_gem_a', 'dependent_gem_b' and 'dependent_gem_c'.
  # The 'dependent_gem_a' and 'dependent_gem_b' exist in both this internal source and another source, the external source in this case.
  # The 'dependent_gem_c only exists in this internal source.
  gem "internal_gem"
end

In the example above, Bundler prints the following error message about vulnerable 'dependent_gem_a' and 'dependent_gem_b' (not about dependent_gem_c):

Your Gemfile contains implicit dependency gems dependent_gem_a, dependent_gem_b on the scoped sources, namely: … 

An example of a Gemfile where all necessary dependencies are listed:

source "https://rubygems.org/"

source "https://gems.example/" do
  gem "internal_gem"
  gem "dependent_gem_a"
  gem "dependent_gem_b"
end

If a dependent_gem_a is provided by an internal source but you want to allow installation of newer versions from a reliable external source, you can list it as unscoped dependency as in the following Gemfile example:

source "https://rubygems.org/"

gem "dependent_gem_a"
gem "dependent_gem_b"

source "https://gems.example/" do
  gem "internal_gem"
end

3.3.2 Customizing Bundler version 1 errors by environment variables

The current behavior of Bundler version 1 provided in Red Hat Enterprise Linux and Red Hat Software Collections prevents you from using vulnerable configurations but might impact your RubyGem usage. For your convenience, for example to give you time for a transition to a gem server with a dependency API, Bundler version 1 recognizes the following environment variables:

  • BUNDLE_WARN_ON_DEPENDENCY_CONFUSION=1 - prints a warning message instead of raising an error.
  • BUNDLE_DISABLE_DEPENDENCY_CONFUSION_CHECK=1 - disables the dependency confusion check. This option can be used as a workaround in cases where the logic check causes performance issues.

NOTE: These environment variables are not managed in the Bundler configuration file and cannot be set by using the bundle config command.

WARNING: Using these environment variables only suppresses the error messages but does not mitigate the vulnerability.

Components
Article Type