diff --git a/tests/test_app_path_translation.py b/tests/test_app_path_translation.py deleted file mode 100644 index 948c2d6..0000000 --- a/tests/test_app_path_translation.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -Test /app/ to ./ path translation for standalone mode. - -Tests that internal application paths work in both Docker and standalone modes. -""" - -import os -import tempfile -from unittest.mock import patch - -from utils.file_utils import translate_path_for_environment - - -class TestAppPathTranslation: - """Test translation of /app/ paths for different environments.""" - - def test_app_path_translation_in_standalone_mode(self): - """Test that /app/ paths are translated to ./ in standalone mode.""" - - # Mock standalone environment (no Docker) - with patch("utils.file_utils.CONTAINER_WORKSPACE") as mock_container_workspace: - mock_container_workspace.exists.return_value = False - - # Clear WORKSPACE_ROOT to simulate standalone mode - with patch.dict(os.environ, {}, clear=True): - - # Test translation of internal app paths - test_cases = [ - ("/app/conf/custom_models.json", "./conf/custom_models.json"), - ("/app/conf/other_config.json", "./conf/other_config.json"), - ("/app/logs/app.log", "./logs/app.log"), - ("/app/data/file.txt", "./data/file.txt"), - ] - - for input_path, expected_output in test_cases: - result = translate_path_for_environment(input_path) - assert result == expected_output, f"Expected {expected_output}, got {result}" - - def test_allowed_app_path_unchanged_in_docker_mode(self): - """Test that allowed /app/ paths remain unchanged in Docker mode.""" - - with tempfile.TemporaryDirectory() as tmpdir: - # Mock Docker environment - with patch("utils.file_utils.CONTAINER_WORKSPACE") as mock_container_workspace: - mock_container_workspace.exists.return_value = True - mock_container_workspace.__str__.return_value = "/workspace" - - # Set WORKSPACE_ROOT to simulate Docker environment - with patch.dict(os.environ, {"WORKSPACE_ROOT": tmpdir}): - - # Only specifically allowed internal app paths should remain unchanged in Docker - allowed_path = "/app/conf/custom_models.json" - result = translate_path_for_environment(allowed_path) - assert ( - result == allowed_path - ), f"Docker mode should preserve allowed path {allowed_path}, got {result}" - - def test_non_allowed_app_paths_blocked_in_docker_mode(self): - """Test that non-allowed /app/ paths are blocked in Docker mode.""" - - with tempfile.TemporaryDirectory() as tmpdir: - # Mock Docker environment - with patch("utils.file_utils.CONTAINER_WORKSPACE") as mock_container_workspace: - mock_container_workspace.exists.return_value = True - mock_container_workspace.__str__.return_value = "/workspace" - - # Set WORKSPACE_ROOT to simulate Docker environment - with patch.dict(os.environ, {"WORKSPACE_ROOT": tmpdir}): - - # Non-allowed internal app paths should be blocked in Docker for security - blocked_paths = [ - "/app/conf/other_config.json", - "/app/logs/app.log", - "/app/server.py", - ] - - for blocked_path in blocked_paths: - result = translate_path_for_environment(blocked_path) - assert result.startswith( - "/inaccessible/" - ), f"Docker mode should block non-allowed path {blocked_path}, got {result}" - - def test_non_app_paths_unchanged_in_standalone(self): - """Test that non-/app/ paths are unchanged in standalone mode.""" - - # Mock standalone environment - with patch("utils.file_utils.CONTAINER_WORKSPACE") as mock_container_workspace: - mock_container_workspace.exists.return_value = False - - with patch.dict(os.environ, {}, clear=True): - - # Non-app paths should be unchanged - test_cases = [ - "/home/user/file.py", - "/etc/config.conf", - "./local/file.txt", - "relative/path.py", - "/workspace/file.py", - ] - - for input_path in test_cases: - result = translate_path_for_environment(input_path) - assert result == input_path, f"Non-app path {input_path} should be unchanged, got {result}" - - def test_edge_cases_in_app_translation(self): - """Test edge cases in /app/ path translation.""" - - # Mock standalone environment - with patch("utils.file_utils.CONTAINER_WORKSPACE") as mock_container_workspace: - mock_container_workspace.exists.return_value = False - - with patch.dict(os.environ, {}, clear=True): - - # Test edge cases - test_cases = [ - ("/app/", "./"), # Root app directory - ("/app", "/app"), # Exact match without trailing slash - not translated - ("/app/file", "./file"), # File directly in app - ("/app//double/slash", "./double/slash"), # Handle double slashes - ] - - for input_path, expected_output in test_cases: - result = translate_path_for_environment(input_path) - assert ( - result == expected_output - ), f"Edge case {input_path}: expected {expected_output}, got {result}" diff --git a/tests/test_internal_config_file_access.py b/tests/test_internal_config_file_access.py deleted file mode 100644 index 38f1e6f..0000000 --- a/tests/test_internal_config_file_access.py +++ /dev/null @@ -1,290 +0,0 @@ -""" -Integration tests for internal application configuration file access. - -These tests verify that: -1. Specific internal config files are accessible (exact path matching) -2. Path variations and traversal attempts are blocked (security) -3. The OpenRouter model configuration loads properly -4. Normal workspace file operations continue to work - -This follows the established testing patterns from test_docker_path_integration.py -by using actual file operations and module reloading instead of mocks. -""" - -import importlib -import os -import tempfile -from pathlib import Path -from unittest.mock import patch - -import pytest - -from utils.file_utils import translate_path_for_environment - - -class TestInternalConfigFileAccess: - """Test access to internal application configuration files.""" - - def test_allowed_internal_config_file_access(self): - """Test that the specific internal config file is accessible.""" - - with tempfile.TemporaryDirectory() as tmpdir: - # Set up Docker-like environment - host_workspace = Path(tmpdir) / "host_workspace" - host_workspace.mkdir() - container_workspace = Path(tmpdir) / "container_workspace" - container_workspace.mkdir() - - original_env = os.environ.copy() - try: - os.environ["WORKSPACE_ROOT"] = str(host_workspace) - - # Reload modules to pick up environment - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - # Test with Docker environment simulation - with patch("utils.file_utils.CONTAINER_WORKSPACE", container_workspace): - # The exact allowed path should pass through unchanged - result = translate_path_for_environment("/app/conf/custom_models.json") - assert result == "/app/conf/custom_models.json" - - finally: - # Restore environment - os.environ.clear() - os.environ.update(original_env) - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - def test_blocked_config_file_variations(self): - """Test that variations of the config file path are blocked.""" - - with tempfile.TemporaryDirectory() as tmpdir: - host_workspace = Path(tmpdir) / "host_workspace" - host_workspace.mkdir() - container_workspace = Path(tmpdir) / "container_workspace" - container_workspace.mkdir() - - original_env = os.environ.copy() - try: - os.environ["WORKSPACE_ROOT"] = str(host_workspace) - - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - with patch("utils.file_utils.CONTAINER_WORKSPACE", container_workspace): - # Test blocked variations - these should return inaccessible paths - blocked_paths = [ - "/app/conf/", # Directory - "/app/conf/other_file.json", # Different file - "/app/conf/custom_models.json.backup", # Extra extension - "/app/conf/custom_models.txt", # Different extension - "/app/conf/../server.py", # Path traversal - "/app/server.py", # Application code - "/etc/passwd", # System file - ] - - for path in blocked_paths: - result = translate_path_for_environment(path) - assert result.startswith("/inaccessible/"), f"Path {path} should be blocked but got: {result}" - - finally: - os.environ.clear() - os.environ.update(original_env) - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - def test_workspace_files_continue_to_work(self): - """Test that normal workspace file operations are unaffected.""" - - with tempfile.TemporaryDirectory() as tmpdir: - host_workspace = Path(tmpdir) / "host_workspace" - host_workspace.mkdir() - container_workspace = Path(tmpdir) / "container_workspace" - container_workspace.mkdir() - - # Create a test file in the workspace - test_file = host_workspace / "src" / "test.py" - test_file.parent.mkdir(parents=True) - test_file.write_text("# test file") - - original_env = os.environ.copy() - try: - os.environ["WORKSPACE_ROOT"] = str(host_workspace) - - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - with patch("utils.file_utils.CONTAINER_WORKSPACE", container_workspace): - # Normal workspace file should translate correctly - result = translate_path_for_environment(str(test_file)) - expected = str(container_workspace / "src" / "test.py") - assert result == expected - - finally: - os.environ.clear() - os.environ.update(original_env) - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - def test_openrouter_config_loading_real_world(self): - """Test that OpenRouter configuration loading works in real container environment.""" - - # This test validates that our fix works in the actual Docker environment - # by checking that the translate_path_for_environment function handles - # the exact internal config path correctly - - with tempfile.TemporaryDirectory() as tmpdir: - host_workspace = Path(tmpdir) / "host_workspace" - host_workspace.mkdir() - container_workspace = Path(tmpdir) / "container_workspace" - container_workspace.mkdir() - - original_env = os.environ.copy() - try: - os.environ["WORKSPACE_ROOT"] = str(host_workspace) - - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - with patch("utils.file_utils.CONTAINER_WORKSPACE", container_workspace): - # Test that the function correctly handles the config path - result = translate_path_for_environment("/app/conf/custom_models.json") - - # The path should pass through unchanged (not be blocked) - assert result == "/app/conf/custom_models.json" - - # Verify it's not marked as inaccessible - assert not result.startswith("/inaccessible/") - - finally: - os.environ.clear() - os.environ.update(original_env) - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - def test_security_boundary_comprehensive(self): - """Comprehensive test of all security boundaries in Docker environment.""" - - with tempfile.TemporaryDirectory() as tmpdir: - host_workspace = Path(tmpdir) / "host_workspace" - host_workspace.mkdir() - container_workspace = Path(tmpdir) / "container_workspace" - container_workspace.mkdir() - - # Create a workspace file for testing - workspace_file = host_workspace / "project" / "main.py" - workspace_file.parent.mkdir(parents=True) - workspace_file.write_text("# workspace file") - - original_env = os.environ.copy() - try: - os.environ["WORKSPACE_ROOT"] = str(host_workspace) - - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - with patch("utils.file_utils.CONTAINER_WORKSPACE", container_workspace): - # Test cases: (path, should_be_allowed, description) - test_cases = [ - # Allowed cases - ("/app/conf/custom_models.json", True, "Exact allowed internal config"), - (str(workspace_file), True, "Workspace file"), - (str(container_workspace / "existing.py"), True, "Container path"), - # Blocked cases - ("/app/conf/", False, "Directory access"), - ("/app/conf/other.json", False, "Different config file"), - ("/app/conf/custom_models.json.backup", False, "Config with extra extension"), - ("/app/server.py", False, "Application source"), - ("/etc/passwd", False, "System file"), - ("../../../etc/passwd", False, "Relative path traversal"), - ("/app/conf/../server.py", False, "Path traversal through config dir"), - ] - - for path, should_be_allowed, description in test_cases: - result = translate_path_for_environment(path) - - if should_be_allowed: - # Should either pass through unchanged or translate to container path - assert not result.startswith( - "/inaccessible/" - ), f"{description}: {path} should be allowed but was blocked" - else: - # Should be blocked with inaccessible path - assert result.startswith( - "/inaccessible/" - ), f"{description}: {path} should be blocked but got: {result}" - - finally: - os.environ.clear() - os.environ.update(original_env) - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - def test_exact_path_matching_prevents_wildcards(self): - """Test that using exact path matching prevents any wildcard-like behavior.""" - - with tempfile.TemporaryDirectory() as tmpdir: - host_workspace = Path(tmpdir) / "host_workspace" - host_workspace.mkdir() - container_workspace = Path(tmpdir) / "container_workspace" - container_workspace.mkdir() - - original_env = os.environ.copy() - try: - os.environ["WORKSPACE_ROOT"] = str(host_workspace) - - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - with patch("utils.file_utils.CONTAINER_WORKSPACE", container_workspace): - # Even subtle variations should be blocked - subtle_variations = [ - "/app/conf/custom_models.jsonx", # Extra char - "/app/conf/custom_models.jso", # Missing char - "/app/conf/custom_models.JSON", # Different case - "/app/conf/custom_models.json ", # Trailing space - " /app/conf/custom_models.json", # Leading space - "/app/conf/./custom_models.json", # Current dir reference - "/app/conf/subdir/../custom_models.json", # Up and down - ] - - for variation in subtle_variations: - result = translate_path_for_environment(variation) - assert result.startswith( - "/inaccessible/" - ), f"Variation {variation} should be blocked but got: {result}" - - finally: - os.environ.clear() - os.environ.update(original_env) - import utils.security_config - - importlib.reload(utils.security_config) - importlib.reload(utils.file_utils) - - -if __name__ == "__main__": - pytest.main([__file__, "-v"])