Skip to content

Commit ea8f1fa

Browse files
committed
fix: reject empty database passwords in validator
Empty or whitespace-only DB_PASSWORD and TEST_DB_PASSWORD values previously bypassed the weak-password check, reaching the DSN builder. Fail fast with a clear security error instead. Signed-off-by: Grant Ramsay <seapagan@gmail.com>
1 parent 1c79fd5 commit ea8f1fa

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

app/config/settings.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ def validate_db_password(
259259
) -> SecretStr:
260260
"""Ensure database password is not a weak or default value."""
261261
raw_value = unwrap_secret(value)
262+
field_name = (info.field_name or "db_password").upper()
263+
if not raw_value.strip():
264+
msg = (
265+
"\n"
266+
"=" * 70 + "\n"
267+
f"SECURITY ERROR: {field_name} must not be empty!\n"
268+
"=" * 70 + "\n"
269+
"Set a strong database password via environment, .env, "
270+
"or secrets dir:\n"
271+
f" {field_name}=your_secure_password_here\n"
272+
"=" * 70
273+
)
274+
raise ValueError(msg)
262275
weak_passwords = [
263276
"CHANGE_ME_IN_ENV_FILE",
264277
"Sup3rS3cr3tP455w0rd",
@@ -267,7 +280,6 @@ def validate_db_password(
267280
"admin",
268281
]
269282
if raw_value in weak_passwords:
270-
field_name = (info.field_name or "db_password").upper()
271283
msg = (
272284
"\n"
273285
"=" * 70 + "\n"

tests/unit/test_validation.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,43 @@ def test_weak_test_db_password_rejected(self, monkeypatch) -> None:
170170
):
171171
Settings()
172172

173+
def test_empty_db_password_rejected(self, monkeypatch) -> None:
174+
"""Test that an empty DB_PASSWORD is rejected."""
175+
monkeypatch.setenv("DB_USER", "testuser")
176+
monkeypatch.setenv("SECRET_KEY", "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6")
177+
monkeypatch.setenv("DB_PASSWORD", "")
178+
179+
with pytest.raises(
180+
ValueError,
181+
match=r"SECURITY ERROR: DB_PASSWORD must not be empty!",
182+
):
183+
Settings()
184+
185+
def test_whitespace_db_password_rejected(self, monkeypatch) -> None:
186+
"""Test that a whitespace-only DB_PASSWORD is rejected."""
187+
monkeypatch.setenv("DB_USER", "testuser")
188+
monkeypatch.setenv("SECRET_KEY", "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6")
189+
monkeypatch.setenv("DB_PASSWORD", " ")
190+
191+
with pytest.raises(
192+
ValueError,
193+
match=r"SECURITY ERROR: DB_PASSWORD must not be empty!",
194+
):
195+
Settings()
196+
197+
def test_empty_test_db_password_rejected(self, monkeypatch) -> None:
198+
"""Test that an empty TEST_DB_PASSWORD is rejected."""
199+
monkeypatch.setenv("DB_USER", "testuser")
200+
monkeypatch.setenv("SECRET_KEY", "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6")
201+
monkeypatch.setenv("DB_PASSWORD", "ValidPassword123!")
202+
monkeypatch.setenv("TEST_DB_PASSWORD", "")
203+
204+
with pytest.raises(
205+
ValueError,
206+
match=(r"SECURITY ERROR: TEST_DB_PASSWORD must not be empty!"),
207+
):
208+
Settings()
209+
173210

174211
class TestDbUserValidator:
175212
"""Test the DB_USER validator."""

0 commit comments

Comments
 (0)