Information Disclosure (ENV leakage in tests)

HIGH
rails/rails
Commit: f59a28e5cccc
Affected: 8.1.x prior to this fix (e.g., <= 8.1.3)
2026-04-05 12:53 UTC

Description

The commit fixes an information-disclosure vulnerability where the DATABASE_URL environment variable could leak into the test environment. The previous approach relied on a YAML-based config (config/database.yml) and sometimes set DATABASE_URL globally, allowing leakage to test logs or leakage checks. The fix removes the YAML config, provides a helper to delete it, and scopes the DATABASE_URL usage to explicit blocks via with_env, ensuring credentials are not exposed outside the controlled test context.

Proof of Concept

PoC steps: 1) Reproduce leak in pre-fix code path: a test sets a global DATABASE_URL variable and does not isolate it. Example (pre-fix behavior): # illustrative test (pre-fix) class DbDbsTest < ActiveSupport::TestCase # vulnerable: DATABASE_URL is set globally for the duration of the process ENV['DATABASE_URL'] = 'sqlite3:/tmp/leak_test.db' test 'run' do puts "DATABASE_URL=#{ENV['DATABASE_URL']}" end end If a leak checker is running, you may see a leak report like: Environment leak detected! DATABASE_URL 2) Reproduce fix after the commit: The test code now ensures the environment is not leaked by deleting any YAML config and scoping DATABASE_URL inside with_env blocks: # post-fix illustrative snippet def delete_database_config! FileUtils.rm_rf("path/to/app/config/database.yml") end # within a test delete_database_config! with_env DATABASE_URL: 'sqlite3:/path/to/leak_test.db' do # execute tests that rely on DATABASE_URL db_create_and_drop 'path/to/db.sqlite3' end 3) Verification steps: - Run the test suite and observe that the leak checker no longer reports DATABASE_URL leakage. - Optionally try to set DATABASE_URL globally in another test and verify that, with the fix, it must be scoped with with_env to avoid leakage.

Commit Details

Author: zzak

Date: 2025-12-29 06:06 UTC

Message:

Don't leak DATABASE_URL to env in railties/dbs_test ``` Failure: ApplicationTests::RakeTests::RakeDbsTest#test_db:create_failure_because_bad_permissions [/home/zzak/code/rails/tools/support/leak_checker.rb:22]: Environment leak detected! DATABASE_URL bin/test test/application/rake/dbs_test.rb:226 Failure: ApplicationTests::RakeTests::RakeDbsTest#test_db:create_and_db:drop_with_database_URL_don't_use_YAML_DBs [/home/zzak/code/rails/tools/support/leak_checker.rb:22]: Environment leak detected! DATABASE_URL bin/test test/application/rake/dbs_test.rb:55 Failure: ApplicationTests::RakeTests::RakeDbsTest#test_db:create_and_db:drop_with_database_URL [/home/zzak/code/rails/tools/support/leak_checker.rb:22]: Environment leak detected! DATABASE_URL bin/test test/application/rake/dbs_test.rb:49 Failure: ApplicationTests::RakeTests::RakeDbsTest#test_db:drop_failure_because_bad_permissions [/home/zzak/code/rails/tools/support/leak_checker.rb:22]: Environment leak detected! DATABASE_URL bin/test test/application/rake/dbs_test.rb:234 Failure: ApplicationTests::RakeTests::RakeDbsTest#test_db:create_failure_because_database_exists [/home/zzak/code/rails/tools/support/leak_checker.rb:22]: Environment leak detected! DATABASE_URL bin/test test/application/rake/dbs_test.rb:209 ```

Triage Assessment

Vulnerability Type: Information Disclosure

Confidence: HIGH

Reasoning:

The commit prevents leaking the DATABASE_URL environment variable into the test environment by removing the YAML-based config and ensuring the DATABASE_URL is only set explicitly within controlled test blocks. This mitigates potential exposure of database credentials during test runs and environment leakage checks.

Verification Assessment

Vulnerability Type: Information Disclosure (ENV leakage in tests)

Confidence: HIGH

Affected Versions: 8.1.x prior to this fix (e.g., <= 8.1.3)

Code Diff

diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 3c1ea96cd78eb..7cf74295f14aa 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -21,8 +21,7 @@ def database_url_db_name "db/database_url_db.sqlite3" end - def set_database_url - ENV["DATABASE_URL"] = "sqlite3:#{database_url_db_name}" + def delete_database_config! # ensure it's using the DATABASE_URL FileUtils.rm_rf("#{app_path}/config/database.yml") end @@ -48,13 +47,15 @@ def db_create_and_drop(expected_database, environment_loaded: true) test "db:create and db:drop with database URL" do require "#{app_path}/config/environment" - set_database_url - db_create_and_drop database_url_db_name + delete_database_config! + with_env DATABASE_URL: "sqlite3:#{database_url_db_name}" do + db_create_and_drop database_url_db_name + end end test "db:create and db:drop with database URL don't use YAML DBs" do require "#{app_path}/config/environment" - set_database_url + delete_database_config! File.write("#{app_path}/config/database.yml", <<~YAML) test: @@ -66,10 +67,12 @@ def db_create_and_drop(expected_database, environment_loaded: true) database: storage/development.sqlite3 YAML - with_rails_env "development" do - db_create_and_drop database_url_db_name do - assert_not File.exist?("#{app_path}/storage/test.sqlite3") - assert_not File.exist?("#{app_path}/storage/development.sqlite3") + with_env DATABASE_URL: "sqlite3:#{database_url_db_name}" do + with_rails_env "development" do + db_create_and_drop database_url_db_name do + assert_not File.exist?("#{app_path}/storage/test.sqlite3") + assert_not File.exist?("#{app_path}/storage/development.sqlite3") + end end end end @@ -199,7 +202,6 @@ def db_create_and_drop(expected_database, environment_loaded: true) def with_database_existing Dir.chdir(app_path) do - set_database_url rails "db:create" yield rails "db:drop" @@ -207,15 +209,17 @@ def with_database_existing end test "db:create failure because database exists" do - with_database_existing do - output = rails("db:create") - assert_match(/already exists/, output) + delete_database_config! + with_env DATABASE_URL: "sqlite3:#{database_url_db_name}" do + with_database_existing do + output = rails("db:create") + assert_match(/already exists/, output) + end end end def with_bad_permissions Dir.chdir(app_path) do - set_database_url FileUtils.chmod("-w", "db") yield FileUtils.chmod("+w", "db") @@ -224,19 +228,25 @@ def with_bad_permissions unless Process.uid.zero? test "db:create failure because bad permissions" do - with_bad_permissions do - output = rails("db:create", allow_failure: true) - assert_match("Couldn't create '#{database_url_db_name}' database. Please check your configuration.", output) - assert_equal 1, $?.exitstatus + delete_database_config! + with_env DATABASE_URL: "sqlite3:#{database_url_db_name}" do + with_bad_permissions do + output = rails("db:create", allow_failure: true) + assert_match("Couldn't create '#{database_url_db_name}' database. Please check your configuration.", output) + assert_equal 1, $?.exitstatus + end end end test "db:drop failure because bad permissions" do - with_database_existing do - with_bad_permissions do - output = rails("db:drop", allow_failure: true) - assert_match(/Couldn't drop/, output) - assert_equal 1, $?.exitstatus + delete_database_config! + with_env DATABASE_URL: "sqlite3:#{database_url_db_name}" do + with_database_existing do + with_bad_permissions do + output = rails("db:drop", allow_failure: true) + assert_match(/Couldn't drop/, output) + assert_equal 1, $?.exitstatus + end end end end
← Back to Alerts View on GitHub →