Information disclosure / Environment variable leakage

MEDIUM
rails/rails
Commit: d8e15b10bec7
Affected: < 8.1.3 (i.e., 8.1.0 through 8.1.2); fixed in 8.1.3+
2026-04-05 12:59 UTC

Description

This commit implements explicit cleanup of environment variables to prevent ENV leakage across processes and test boundaries. Specifically, it changes the migrations task to delete ENV['FROM'] after use and uses the deleted value for logic, reducing the risk that the value remains in the process environment. It also strengthens test cleanup by removing or restoring ENV['RAILS_ENV'] (and related vars) in teardown and via helpers, ensuring environment vars are not leaked into child processes. Collectively, this reduces potential information disclosure where sensitive data or configuration exposed in ENV could be inherited by spawned processes or future operations.

Proof of Concept

PoC steps (conceptual): 1) In a Ruby process (simulating a Rails runner), set sensitive environment variables such as RAILS_ENV and a fake FROM value that would be used to influence which components are loaded by a subprocess. Spawn a child process and observe that the child inherits these environment variables, revealing internal configuration data. 2) After applying the fix (as in the commit), the parent deletes FROM (ENV.delete('FROM')) and ensures RAILS_ENV is restored or cleared in teardown, so the child process no longer sees the leaked values. The PoC should demonstrate that the child no longer observes RAILS_ENV or FROM after the fix. 3) Practical validation: write a small Ruby script that prints ENV['RAILS_ENV'] and ENV['FROM'] in a child process. Run it once with the leak in place (to show exposure) and once after applying the fix (to show no exposure).

Commit Details

Author: Jean Boussier

Date: 2026-01-12 19:56 UTC

Message:

Merge pull request #56574 from zzak/railties-env-leak-cont Clean up ENV leaks continued

Triage Assessment

Vulnerability Type: Information disclosure

Confidence: MEDIUM

Reasoning:

The commit eliminates ENV leakage by removing ENV['FROM'] after use and improving cleanup of environment variables in tests (e.g., ensuring ENV vars like RAILS_ENV are not leaked across processes). This reduces information exposure and potential security side effects from ENV leakage, which can be exploited if sensitive data is exposed via environment leakage.

Verification Assessment

Vulnerability Type: Information disclosure / Environment variable leakage

Confidence: MEDIUM

Affected Versions: < 8.1.3 (i.e., 8.1.0 through 8.1.2); fixed in 8.1.3+

Code Diff

diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 799571f1a125e..87cd51ad4bab7 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -592,7 +592,8 @@ namespace :railties do namespace :install do # desc "Copy missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2 and database to copy to with DATABASE=database." task migrations: :'db:load_config' do - to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map(&:strip) + from = ENV.delete("FROM") + to_load = from.blank? ? :all : from.split(",").map(&:strip) railties = {} Rails.application.migration_railties.each do |railtie| next unless to_load == :all || to_load.include?(railtie.railtie_name) diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb index f15a2908dab9c..47dbd50dce70d 100644 --- a/railties/test/application/asset_debugging_test.rb +++ b/railties/test/application/asset_debugging_test.rb @@ -2,11 +2,13 @@ require "isolation/abstract_unit" require "rack/test" +require "env_helpers" module ApplicationTests class AssetDebuggingTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation include Rack::Test::Methods + include EnvHelpers def setup build_app(initializers: true) @@ -30,8 +32,6 @@ def setup class PostsController < ActionController::Base end RUBY - - ENV["RAILS_ENV"] = "production" end def teardown @@ -40,18 +40,19 @@ def teardown test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do # config.assets.debug and config.assets.compile are false for production environment - ENV["RAILS_ENV"] = "production" - rails "assets:precompile", "--trace" + with_rails_env("production") do + rails "assets:precompile", "--trace" - # Load app env - app "production" + # Load app env + app "production" - class ::PostsController < ActionController::Base ; end + class ::PostsController < ActionController::Base ; end - # the debug_assets params isn't used if compile is off - get("/posts?debug_assets=true", {}, "HTTPS" => "on") - assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body) - assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body) + # the debug_assets params isn't used if compile is off + get("/posts?debug_assets=true", {}, "HTTPS" => "on") + assert_match(/<script src="\/assets\/application-([0-z]+)\.js"><\/script>/, last_response.body) + assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body) + end end test "assets are debug when compile is true is on and debug_assets params is true" do diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 74e6b2278b16a..3841cd2f0046a 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -136,7 +136,7 @@ class MyLogger < ::Logger end test "raises an error if cache does not support recyclable cache keys" do - build_app(initializers: true) + restore_default_config add_to_env_config "production", "config.cache_store = Class.new {}.new" add_to_env_config "production", "config.active_record.cache_versioning = true" @@ -1535,7 +1535,7 @@ def index end test "config.action_view.cache_template_loading with config.enable_reloading in an environment" do - build_app(initializers: true) + restore_default_config add_to_env_config "development", "config.enable_reloading = true" # These requires are to emulate an engine loading Action View before the application @@ -2031,8 +2031,7 @@ def index end test "config.active_record.dump_schema_after_migration is false on production" do - build_app - + restore_default_config app "production" assert_not ActiveRecord.dump_schema_after_migration @@ -3320,21 +3319,19 @@ class Post < ActiveRecord::Base end test "config.active_job.verbose_enqueue_logs defaults to true in development" do - build_app + restore_default_config app "development" assert ActiveJob.verbose_enqueue_logs end test "config.active_job.verbose_enqueue_logs defaults to false in production" do - build_app app "production" assert_not ActiveJob.verbose_enqueue_logs end test "config.active_job.enqueue_after_transaction_commit defaults to true for new apps" do - build_app app "production" assert ActiveRecord::Base @@ -3342,8 +3339,6 @@ class Post < ActiveRecord::Base end test "config.active_job.enqueue_after_transaction_commit can be set to false for new apps" do - build_app - app_file "config/initializers/enqueue_after_transaction_commit.rb", <<-RUBY Rails.application.config.active_job.enqueue_after_transaction_commit = false RUBY @@ -3734,14 +3729,13 @@ def index end test "config.action_dispatch.verbose_redirect_logs is true in development" do - build_app + restore_default_config app "development" assert ActionDispatch.verbose_redirect_logs end test "config.action_dispatch.verbose_redirect_logs is false in production" do - build_app app "production" assert_not ActionDispatch.verbose_redirect_logs diff --git a/railties/test/application/view_reloading_test.rb b/railties/test/application/view_reloading_test.rb index ac78ff89ad107..dd1910b3b3508 100644 --- a/railties/test/application/view_reloading_test.rb +++ b/railties/test/application/view_reloading_test.rb @@ -2,11 +2,13 @@ require "isolation/abstract_unit" require "rack/test" +require "env_helpers" module ApplicationTests class ViewReloadingTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation include Rack::Test::Methods + include EnvHelpers def setup build_app @@ -36,21 +38,22 @@ def teardown Before! RUBY - ENV["RAILS_ENV"] = "development" - require "#{app_path}/config/environment" + with_rails_env("development") do + require "#{app_path}/config/environment" - get "/pages/foo" - get "/pages/foo" - assert_equal 200, last_response.status, last_response.body - assert_equal "Before!", last_response.body.strip + get "/pages/foo" + get "/pages/foo" + assert_equal 200, last_response.status, last_response.body + assert_equal "Before!", last_response.body.strip - app_file "app/views/pages/show.html.erb", <<-RUBY - After! - RUBY + app_file "app/views/pages/show.html.erb", <<-RUBY + After! + RUBY - get "/pages/foo" - assert_equal 200, last_response.status - assert_equal "After!", last_response.body.strip + get "/pages/foo" + assert_equal 200, last_response.status + assert_equal "After!", last_response.body.strip + end end end end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 0d3e602b3f937..0dc721c577fec 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -301,15 +301,19 @@ def test_restart_command_contains_customized_options end def test_served_url - args = %w(-u webrick -b 127.0.0.1 -p 4567) - server = Rails::Server.new(parse_arguments(args)) - assert_equal "http://127.0.0.1:4567", server.served_url + with_rails_env "development" do + args = %w(-u webrick -b 127.0.0.1 -p 4567) + server = Rails::Server.new(parse_arguments(args)) + assert_equal "http://127.0.0.1:4567", server.served_url + end end def test_served_url_when_server_prints_it - args = %w(-u puma -b 127.0.0.1 -p 4567) - server = Rails::Server.new(parse_arguments(args)) - assert_nil server.served_url + with_rails_env "development" do + args = %w(-u puma -b 127.0.0.1 -p 4567) + server = Rails::Server.new(parse_arguments(args)) + assert_nil server.served_url + end end private diff --git a/railties/test/generators/action_mailbox_install_generator_test.rb b/railties/test/generators/action_mailbox_install_generator_test.rb index 1822b04d4500c..ff1709168f3ec 100644 --- a/railties/test/generators/action_mailbox_install_generator_test.rb +++ b/railties/test/generators/action_mailbox_install_generator_test.rb @@ -23,13 +23,13 @@ def teardown end def test_create_action_mailbox_files - with_database_configuration { run_generator } + run_generator assert_file "app/mailboxes/application_mailbox.rb" end def test_add_action_mailbox_production_environment_config - with_database_configuration { run_generator } + run_generator assert_file "config/environments/production.rb" do |content| assert_match("Prepare the ingress controller used to receive mail", content) @@ -38,9 +38,14 @@ def test_add_action_mailbox_production_environment_config end def test_create_migrations - with_database_configuration { run_generator } + run_generator assert_migration "db/migrate/create_active_storage_tables.active_storage.rb" assert_migration "db/migrate/create_action_mailbox_tables.action_mailbox.rb" end + + private + def run_generator + quietly { with_database_configuration { super } } + end end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 98ca0f066d18d..cae200b93b26a 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -151,7 +151,11 @@ def reset_environment_configs end def teardown_app - ENV["RAILS_ENV"] = @prev_rails_env if @prev_rails_env + if @prev_rails_env + ENV["RAILS_ENV"] = @prev_rails_env + else + ENV.delete("RAILS_ENV") + end Rails.app_class = @prev_rails_app_class if @prev_rails_app_class Rails.application = @prev_rails_application if @prev_rails_application FileUtils.rm_rf(tmp_path)
← Back to Alerts View on GitHub →