From e9807e0a8bef83b015828818a07f2e67feaad7fe Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:45:06 +0000 Subject: [PATCH] Fix sensitive error detail disclosure in login_required decorator Prevent leaking raw exception details (str(e)) in 401 responses when authentication verification fails. Co-authored-by: Pmaster-dev <293764797+Pmaster-dev@users.noreply.github.com> --- auth/utils.py | 4 +-- tests/test_auth_utils.py | 65 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/test_auth_utils.py diff --git a/auth/utils.py b/auth/utils.py index 4bded89..a060433 100644 --- a/auth/utils.py +++ b/auth/utils.py @@ -128,8 +128,8 @@ def decorated_function(*args, **kwargs): g.user_id = user_id g.user = user_data return f(*args, **kwargs) - except Exception as e: - return jsonify({'error': 'Unauthorized', 'details': str(e)}), 401 + except Exception: + return jsonify({'error': 'Unauthorized'}), 401 return decorated_function diff --git a/tests/test_auth_utils.py b/tests/test_auth_utils.py new file mode 100644 index 0000000..6f0e5df --- /dev/null +++ b/tests/test_auth_utils.py @@ -0,0 +1,65 @@ +import sys +from unittest.mock import MagicMock + +# Mock external cache_db module before importing auth.utils +mock_cache_db = MagicMock() +mock_redis = MagicMock() +mock_user = MagicMock() +mock_refresh_token = MagicMock() + +mock_cache_db.redis_client.redis_client = mock_redis +mock_cache_db.models.User = mock_user +mock_cache_db.models.RefreshToken = mock_refresh_token + +sys.modules['cache_db'] = mock_cache_db +sys.modules['cache_db.redis_client'] = mock_cache_db.redis_client +sys.modules['cache_db.models'] = mock_cache_db.models + +import pytest +from flask import Flask +from auth.utils import login_required, PasswordUtils, JWTUtils + + +@pytest.fixture +def app(): + app_inst = Flask(__name__) + app_inst.config['SECRET_KEY'] = 'test-secret' + app_inst.config['JWT_SECRET_KEY'] = 'test-secret' + + @app_inst.route('/protected') + @login_required + def protected_route(): + return {'message': 'success'} + + return app_inst + + +def test_login_required_unauthorized_does_not_leak_details(app, monkeypatch): + sensitive_error_msg = "Database connection string or internal trace details" + + def mock_verify(): + raise Exception(sensitive_error_msg) + + monkeypatch.setattr('auth.utils.verify_jwt_in_request', mock_verify) + + client = app.test_client() + response = client.get('/protected') + + assert response.status_code == 401 + json_data = response.get_json() + assert json_data == {'error': 'Unauthorized'} + assert sensitive_error_msg not in str(json_data) + + +def test_password_utils(): + hashed = PasswordUtils.hash_password("securepassword123") + assert PasswordUtils.verify_password("securepassword123", hashed) is True + assert PasswordUtils.verify_password("wrongpassword", hashed) is False + + +def test_jwt_utils(): + access_token, _ = JWTUtils.create_tokens("123", "testuser") + decoded = JWTUtils.decode_token(access_token) + assert decoded is not None + assert decoded['user_id'] == "123" + assert decoded['username'] == "testuser"