import pytest from fastapi import status from fastapi.testclient import TestClient from monday_com_orca_backend.api.main import app from monday_com_orca_backend.api.routers.data.endpoints import router from monday_com_orca_backend.enums import HttpHeader, HttpMethod client = TestClient(app) ROUTE_PREFIX: str = "/api/data/" def get_api_routes(router_): """Extracts all API routes from the FastAPI router.""" routes = [] for route in router_.routes: if hasattr(route, "path") and hasattr(route, "methods"): routes.append((route.path, list(route.methods))) assert routes, "No API routes to test found in the router" return routes class TestApiEndpointProtection: # Ignore testing for OPTIONS method, as it is typically used for CORS preflight # requests and does not require authentication. _ignored_methods: set[HttpMethod] = { HttpMethod.OPTIONS, } @classmethod def assert_routes_protected(cls, path, methods, header_func, error_msg): test_methods = set(methods) - cls._ignored_methods assert test_methods, f"No testable methods found for route {path}" for method in test_methods: response = client.request( method, f"{ROUTE_PREFIX.rstrip("/")}{path}", headers=header_func() ) # Assert that instead of a 401 Unauthorized, we get a 404 Not Found, # so that the endpoint is hidden from unauthorized users # (including potential attackers). assert response.status_code == status.HTTP_404_NOT_FOUND, error_msg.format( method=method, path=path ) @pytest.mark.parametrize("path,methods", get_api_routes(router)) def test_all_api_endpoints_are_protected_from_non_authenticated_requests( self, path, methods ): self.assert_routes_protected( path, methods, header_func=lambda: {}, # No headers for unauthenticated requests error_msg="Route {method} {path} is not protected!", ) @pytest.mark.parametrize("auth_string", ["", " ", "Bearer"]) @pytest.mark.parametrize("path,methods", get_api_routes(router)) def test_all_api_endpoints_are_protected_from_empty_token( self, auth_string, path, methods ): self.assert_routes_protected( path, methods, header_func=lambda: {HttpHeader.AUTHORIZATION: auth_string}, error_msg="Route {method} {path} is not protected from empty token!", ) @pytest.mark.parametrize("path,methods", get_api_routes(router)) def test_all_api_endpoints_are_protected_from_bad_token(self, path, methods): self.assert_routes_protected( path, methods, header_func=lambda: { HttpHeader.AUTHORIZATION: "Bearer invalid.token.value" }, error_msg="Route {method} {path} is not protected from bad token!", )