Information disclosure / Environment variable leakage
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)