From b7b191a5ee9c32b5a769ae5c9cb5c5e72ccb02a8 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:46:06 -0500 Subject: [PATCH] fix: Truncate Long Passwords (>72 bytes) (#6335) --- mealie/core/security/hasher.py | 7 +++-- tests/unit_tests/core/test_security.py | 40 ++++++++++++++++++++------ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/mealie/core/security/hasher.py b/mealie/core/security/hasher.py index 8b702482a..9bceb3b1c 100644 --- a/mealie/core/security/hasher.py +++ b/mealie/core/security/hasher.py @@ -21,13 +21,16 @@ class FakeHasher: class BcryptHasher: + def _get_password_bytes(self, password: str) -> bytes: + return password.encode("utf-8")[:72] + def hash(self, password: str) -> str: - password_bytes = password.encode("utf-8") + password_bytes = self._get_password_bytes(password) hashed = bcrypt.hashpw(password_bytes, bcrypt.gensalt()) return hashed.decode("utf-8") def verify(self, password: str, hashed: str) -> bool: - password_bytes = password.encode("utf-8") + password_bytes = self._get_password_bytes(password) hashed_bytes = hashed.encode("utf-8") return bcrypt.checkpw(password_bytes, hashed_bytes) diff --git a/tests/unit_tests/core/test_security.py b/tests/unit_tests/core/test_security.py index 911e8f369..4898cd1c7 100644 --- a/tests/unit_tests/core/test_security.py +++ b/tests/unit_tests/core/test_security.py @@ -2,21 +2,43 @@ from pytest import MonkeyPatch from mealie.core.config import get_app_settings from mealie.core.security.hasher import BcryptHasher, FakeHasher, get_hasher +from tests.utils.factories import random_string + + +def clear_hasher_cache(): + get_hasher.cache_clear() + get_app_settings.cache_clear() def test_get_hasher(monkeypatch: MonkeyPatch): - hasher = get_hasher() + try: + hasher = get_hasher() + assert isinstance(hasher, FakeHasher) - assert isinstance(hasher, FakeHasher) + monkeypatch.setenv("TESTING", "0") + clear_hasher_cache() - monkeypatch.setenv("TESTING", "0") + hasher = get_hasher() + assert isinstance(hasher, BcryptHasher) + finally: + clear_hasher_cache() - get_hasher.cache_clear() - get_app_settings.cache_clear() - hasher = get_hasher() +def test_hasher_long_password(monkeypatch: MonkeyPatch): + try: + monkeypatch.setenv("TESTING", "0") + clear_hasher_cache() - assert isinstance(hasher, BcryptHasher) + hasher = get_hasher() + assert isinstance(hasher, BcryptHasher) - get_app_settings.cache_clear() - get_hasher.cache_clear() + # Create a very long password which bcrypt doesn't support + password = random_string(256) + assert len(password) > 72 + + # Make sure our hasher still works even though the password is too long + hashed_password = hasher.hash(password) + assert hashed_password + assert hasher.verify(password, hashed_password) + finally: + clear_hasher_cache()