import logging from io import StringIO import pytest from openhands.core.config import AppConfig, LLMConfig from openhands.core.logger import openhands_logger as openhands_logger @pytest.fixture def test_handler(): stream = StringIO() handler = logging.StreamHandler(stream) handler.setLevel(logging.INFO) formatter = logging.Formatter('%(message)s') handler.setFormatter(formatter) openhands_logger.addHandler(handler) yield openhands_logger, stream openhands_logger.removeHandler(handler) def test_openai_api_key_masking(test_handler): logger, stream = test_handler api_key = 'sk-1234567890abcdef' message = f"OpenAI API key: api_key='{api_key}'and there's some stuff here" logger.info(message) log_output = stream.getvalue() assert "api_key='******'" in log_output assert api_key not in log_output def test_azure_api_key_masking(test_handler): logger, stream = test_handler api_key = '1a2b3c4d5e6f7g8h9i0j' message = f"Azure API key: api_key='{api_key}' and chatty chat with ' and \" and '" logger.info(message) log_output = stream.getvalue() assert "api_key='******'" in log_output assert api_key not in log_output def test_google_vertex_api_key_masking(test_handler): logger, stream = test_handler api_key = 'AIzaSyA1B2C3D4E5F6G7H8I9J0' message = f"Google Vertex API key: api_key='{api_key}' or not" logger.info(message) log_output = stream.getvalue() assert "api_key='******'" in log_output assert api_key not in log_output def test_anthropic_api_key_masking(test_handler): logger, stream = test_handler api_key = 'sk-ant-1234567890abcdef-some-more-stuff-here' message = f"Anthropic API key: api_key='{api_key}' and there's some 'stuff' here" logger.info(message) log_output = stream.getvalue() assert "api_key='******'" in log_output assert api_key not in log_output def test_llm_config_attributes_masking(test_handler): logger, stream = test_handler llm_config = LLMConfig( api_key='sk-abc123', aws_access_key_id='AKIAIOSFODNN7EXAMPLE', aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', ) logger.info(f'LLM Config: {llm_config}') log_output = stream.getvalue() assert "api_key='******'" in log_output assert "aws_access_key_id='******'" in log_output assert "aws_secret_access_key='******'" in log_output assert 'sk-abc123' not in log_output assert 'AKIAIOSFODNN7EXAMPLE' not in log_output assert 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' not in log_output def test_app_config_attributes_masking(test_handler): logger, stream = test_handler app_config = AppConfig(e2b_api_key='e2b-xyz789') logger.info(f'App Config: {app_config}') log_output = stream.getvalue() assert "e2b_api_key='******'" in log_output assert 'github_token' not in log_output assert 'e2b-xyz789' not in log_output assert 'ghp_abcdefghijklmnopqrstuvwxyz' not in log_output def test_sensitive_env_vars_masking(test_handler): logger, stream = test_handler sensitive_data = { 'API_KEY': 'API_KEY_VALUE', 'AWS_ACCESS_KEY_ID': 'AWS_ACCESS_KEY_ID_VALUE', 'AWS_SECRET_ACCESS_KEY': 'AWS_SECRET_ACCESS_KEY_VALUE', 'E2B_API_KEY': 'E2B_API_KEY_VALUE', 'GITHUB_TOKEN': 'GITHUB_TOKEN_VALUE', 'JWT_SECRET': 'JWT_SECRET_VALUE', } log_message = ' '.join( f"{attr}='{value}'" for attr, value in sensitive_data.items() ) logger.info(log_message) log_output = stream.getvalue() for attr, value in sensitive_data.items(): assert f"{attr}='******'" in log_output assert value not in log_output def test_special_cases_masking(test_handler): logger, stream = test_handler sensitive_data = { 'LLM_API_KEY': 'LLM_API_KEY_VALUE', 'SANDBOX_ENV_GITHUB_TOKEN': 'SANDBOX_ENV_GITHUB_TOKEN_VALUE', } log_message = ' '.join( f"{attr}={value} with no single quotes' and something" for attr, value in sensitive_data.items() ) logger.info(log_message) log_output = stream.getvalue() for attr, value in sensitive_data.items(): assert f"{attr}='******'" in log_output assert value not in log_output