Skip to content

Add --no-build-extension and --no-install-plugin options to gem install#9473

Open
hsbt wants to merge 9 commits intomasterfrom
ignore-scripts
Open

Add --no-build-extension and --no-install-plugin options to gem install#9473
hsbt wants to merge 9 commits intomasterfrom
ignore-scripts

Conversation

@hsbt
Copy link
Copy Markdown
Member

@hsbt hsbt commented Apr 10, 2026

Background

Installing a gem can execute arbitrary code through two mechanisms: native extension builds (via extconf.rb, Rakefile, etc.) and plugin evaluation (via rubygems_plugin.rb). This is a known supply chain attack vector, analogous to npm's postinstall scripts.

This PR adds --no-build-extension and --no-install-plugin options to gem install, allowing users to opt out of these behaviors. Bundler also gains corresponding no_build_extension and no_install_plugin configuration settings.

When extensions or plugins are skipped, a warning message is displayed with recovery instructions pointing to gem pristine --extensions or gem pristine --only-plugins, so users can re-enable them later without reinstalling the gem.

These options only take effect when explicitly specified by the user. The default behavior remains unchanged: extensions are built and plugins are installed as before.

Making --no-build-extension the default would be a significant behavioral change that requires careful investigation into how many gems in the ecosystem rely on native extensions being built at install time, how tools like bundler and CI pipelines depend on this behavior, and what the migration path would look like for gem authors.

That analysis is outside the scope of this PR.

Make sure the following tasks are checked

Copilot AI review requested due to automatic review settings April 10, 2026 06:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds opt-out controls to RubyGems and Bundler to reduce supply-chain risk during installation by allowing users to skip native extension builds and RubyGems plugin installation/loading when explicitly requested.

Changes:

  • Add --[no-]build-extension and --[no-]install-plugin options to RubyGems install/update option parsing and propagate them through dependency installation.
  • Update Gem::Installer (and Bundler’s RubyGems installer wrapper) to conditionally skip extension builds and plugin installation/loading, with warnings and recovery guidance.
  • Add test coverage in RubyGems (Minitest) and Bundler (RSpec) for no_build_extension.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/rubygems/test_gem_installer.rb Adds RubyGems installer tests for skipping extension builds and plugin installation/loading.
spec/install/gems/no_build_extension_spec.rb Adds Bundler spec verifying no_build_extension prevents extension build artifacts.
lib/rubygems/request_set.rb Skips rebuilding extensions for already-installed specs when :build_extension == false.
lib/rubygems/installer.rb Gates extension builds, plugin generation, and plugin loading; adds warning helpers; avoids loading missing plugin wrappers.
lib/rubygems/install_update_options.rb Defines the new CLI flags for install/update commands.
lib/rubygems/dependency_installer.rb Stores and forwards new options into the request set install flow.
bundler/lib/bundler/source/rubygems.rb Passes Bundler settings (no_build_extension, no_install_plugin) into RubyGems installer options.
bundler/lib/bundler/source/path/installer.rb Skips building extensions for path installs when no_build_extension is set.
bundler/lib/bundler/settings.rb Registers no_build_extension and no_install_plugin as boolean settings.
bundler/lib/bundler/rubygems_gem_installer.rb Mirrors RubyGems installer gating behavior for Bundler’s installer wrapper.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +285 to +289
if options[:build_extension] == false
warn_skipped_extensions
else
build_extensions
end
Comment on lines 533 to +538
wrappers: true,
env_shebang: true,
build_args: options[:build_args],
bundler_extension_cache_path: extension_cache_path(spec)
bundler_extension_cache_path: extension_cache_path(spec),
build_extension: Bundler.settings[:no_build_extension] ? false : nil,
install_plugin: Bundler.settings[:no_install_plugin] ? false : nil
Comment on lines 183 to 186
sorted_requests.each do |req|
if req.installed? && @always_install.none? {|spec| spec == req.spec.spec }
req.spec.spec.build_extensions
req.spec.spec.build_extensions unless options[:build_extension] == false
yield req, nil if block_given?
Comment on lines 293 to +298
generate_bin
generate_plugins
if options[:install_plugin] == false
warn_skipped_plugins
else
generate_plugins
end
Comment on lines +42 to +46
if options[:install_plugin] == false
warn_skipped_plugins
else
generate_plugins
end
hsbt and others added 9 commits April 13, 2026 09:04
…nstall

These options allow users to opt out of building native extensions and
installing plugins during gem installation, providing an equivalent to
npm's --ignore-scripts for mitigating arbitrary code execution vectors.

Both options default to true to maintain backward compatibility. Users
can disable them per-command or globally via gemrc configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When plugins are not installed (e.g. via --no-install-plugin), the
plugin files do not exist on disk. Without this check, load_plugin
would attempt to load non-existent files and produce spurious
LoadError warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend the --no-build-extension and --no-install-plugin support to
Bundler's installation paths. RubyGemsGemInstaller#install now
respects these options, and the settings are propagated from
Bundler::Settings through Source::RubyGems to the installer.

Path::Installer also respects no_build_extension for git/path sources.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lugins

When extensions or plugins are skipped via --no-build-extension or
--no-install-plugin, warn the user and point them to the appropriate
gem pristine command to re-enable later.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this change, reinstalling or upgrading a gem with
--no-install-plugin would still execute a pre-existing plugin wrapper
left by a previous install via load_plugin. This defeats the opt-out.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…config

The quality spec requires all Bundler settings to be documented in
the bundle-config man page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a gem upgrades from a version with plugins to one without,
generate_plugins normally removes the old wrapper files. Skipping
generate_plugins entirely with --no-install-plugin prevented this
cleanup, leaving stale wrappers that would still be loaded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Whether Bundler should include a checksums section in new lockfiles, to protect from compromised gem sources\. Defaults to true\.
.TP
\fBno_build_extension\fR (\fBBUNDLE_NO_BUILD_EXTENSION\fR)
Whether Bundler should skip building native extensions during installation\. When set, gems are installed without compiling their C extensions\. To build extensions later, run \fBgem pristine <gem> \-\-extensions\fR\.
Copy link
Copy Markdown
Collaborator

@Edouard-chin Edouard-chin Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the gem pristine <gem> command is not going to work if a user has set BUNDLE_PATH (either in env or config). Wouldn't running bundle install again build the extensions ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants