How to identify Bundler Gemfile files vulnerable to CVE-2020-36327 and how to mitigate the issue
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:
- A
Gemfilecombines external and internal sources. - An internal gem included in the
Gemfilehas an internal dependency that is not listed in theGemfile. - The installed
Bundlerversion 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:
- Content from github.com is not included.gemirro
- Content from guides.rubygems.org is not included.gem server
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:
- Use only gems provided by an internal infrastructure
- List all necessary dependencies in the
Gemfile - Apply the fixed Bundler versions
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.
| Product | Ruby version | Bundler version | Fixed in |
|---|---|---|---|
| RHEL 8.5 | Ruby 3.0 | Bundler 2 | Not affected; fixed in the initial release RHEA-2021:4282 |
| RHEL 8.5 | Ruby 2.7 | Bundler 2 | RHSA-2021:3020 (fixed in RHEL 8.4) |
| RHEL 8.5 | Ruby 2.6 | Bundler 1 | RHSA-2022:0543 |
| RHEL 8.5 | Ruby 2.5 | Bundler 1 | RHSA-2022:0545 |
| RHEL 8.4 | Ruby 2.7 | Bundler 2 | RHSA-2021:3020 |
| RHEL 8.4 EUS | Ruby 2.6 | Bundler 1 | RHSA-2022:0544 |
| RHEL 8.4 EUS | Ruby 2.5 | Bundler 1 | RHSA-2022:0546 |
| RHEL 8.2 EUS | Ruby 2.6 | Bundler 1 | RHSA-2022:0582 |
| RHEL 8.2 EUS | Ruby 2.5 | Bundler 1 | RHSA-2022:0547 |
| RHEL 8.1 E4S | Ruby 2.6 | Bundler 1 | RHSA-2022:0581 |
| RHEL 8.1 E4S | Ruby 2.5 | Bundler 1 | RHSA-2022:0548 |
| RHEL 7.9 | Ruby 2.0 | Bundler 1 | Not affected - Bundler version 1.7.8 is earlier than the affected versions |
| RHSCL | Ruby 3.0 | Bundler 2 | RHSA-2021:3982 |
| RHSCL | Ruby 2.7 | Bundler 2 | RHSA-2021:3559 |
| RHSCL | Ruby 2.6 | Bundler 1 | RHSA-2022:0708 |
3.3.1 How the fixed Bundler version 1 works
Bundler version 1 prints an error in the following two scenarios:
- For a source that does not have the dependency API implemented
- For a gem dependency not listed in your
Gemfile
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:
- A gem dependency from an internal gem server is not listed in your
Gemfile. - 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.