Information Disclosure (ENV leakage in tests)
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