126 lines
5.0 KiB
Python
126 lines
5.0 KiB
Python
import sys
|
|
import os
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
from app.main import app
|
|
|
|
client = TestClient(app)
|
|
|
|
def test_cors_allowed_origin_preflight():
|
|
"""Test browser OPTIONS preflight for allowed origin (http://localhost:3000)."""
|
|
response = client.options(
|
|
"/v1/onboarding/start",
|
|
headers={
|
|
"Origin": "http://localhost:3000",
|
|
"Access-Control-Request-Method": "POST",
|
|
"Access-Control-Request-Headers": "authorization, content-type, x-api-key",
|
|
},
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.headers.get("access-control-allow-origin") == "http://localhost:3000"
|
|
assert response.headers.get("access-control-allow-credentials") == "true"
|
|
assert "POST" in response.headers.get("access-control-allow-methods", "").upper()
|
|
|
|
def test_cors_allowed_origin_actual_request():
|
|
"""Test actual POST request from allowed origin (http://localhost:5173)."""
|
|
response = client.post(
|
|
"/v1/onboarding/start",
|
|
json={
|
|
"patient": {"id": "PAT-CORS-1", "name": "CORS Test", "age": 30, "gender": "female"},
|
|
"category": {"type": "general", "code": "general_medicine", "name": "General Medicine"},
|
|
"language": "en"
|
|
},
|
|
headers={
|
|
"Origin": "http://localhost:5173",
|
|
"Authorization": "Bearer test_token_PAT-CORS-1"
|
|
}
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.headers.get("access-control-allow-origin") == "http://localhost:5173"
|
|
assert response.headers.get("access-control-allow-credentials") == "true"
|
|
|
|
def test_cors_unauthorized_request_returns_cors_headers():
|
|
"""Test that 401 Unauthorized responses STILL return CORS headers to allowed origin."""
|
|
response = client.post(
|
|
"/v1/onboarding/start",
|
|
json={"patient": {"id": "PAT-NOAUTH"}},
|
|
headers={"Origin": "http://localhost:3000"}
|
|
)
|
|
assert response.status_code == 401
|
|
assert response.headers.get("access-control-allow-origin") == "http://localhost:3000"
|
|
assert response.headers.get("access-control-allow-credentials") == "true"
|
|
|
|
def test_cors_forbidden_request_returns_cors_headers():
|
|
"""Test that 403 Forbidden responses STILL return CORS headers to allowed origin."""
|
|
# 1. Create session for PAT-CORS-OWNER
|
|
res_start = client.post(
|
|
"/v1/onboarding/start",
|
|
json={
|
|
"patient": {"id": "PAT-CORS-OWNER", "name": "Owner", "age": 28, "gender": "male"},
|
|
"category": {"type": "general", "code": "general_medicine", "name": "General Medicine"}
|
|
},
|
|
headers={"Authorization": "Bearer test_token_PAT-CORS-OWNER"}
|
|
)
|
|
session_id = res_start.json()["session_id"]
|
|
|
|
# 2. Access with different patient PAT-CORS-ATTACKER
|
|
res_idor = client.post(
|
|
f"/v1/onboarding/{session_id}/message",
|
|
json={"message": "IDOR attempt"},
|
|
headers={
|
|
"Origin": "http://localhost:3000",
|
|
"Authorization": "Bearer test_token_PAT-CORS-ATTACKER"
|
|
}
|
|
)
|
|
assert res_idor.status_code == 403
|
|
assert res_idor.headers.get("access-control-allow-origin") == "http://localhost:3000"
|
|
assert res_idor.headers.get("access-control-allow-credentials") == "true"
|
|
|
|
def test_cors_disallowed_origin_rejected():
|
|
"""Test that disallowed origin (https://malicious-example.com) is NOT allowed by CORS headers."""
|
|
response = client.options(
|
|
"/v1/onboarding/start",
|
|
headers={
|
|
"Origin": "https://malicious-example.com",
|
|
"Access-Control-Request-Method": "POST",
|
|
"Access-Control-Request-Headers": "authorization, content-type",
|
|
},
|
|
)
|
|
# Browsers block preflight if access-control-allow-origin does not match
|
|
assert response.headers.get("access-control-allow-origin") != "https://malicious-example.com"
|
|
|
|
def test_cors_voice_endpoints():
|
|
"""Test preflight and actual POST on voice onboarding endpoints."""
|
|
# Preflight voice start
|
|
res_opt = client.options(
|
|
"/v1/voice-onboarding/start",
|
|
headers={
|
|
"Origin": "http://localhost:5173",
|
|
"Access-Control-Request-Method": "POST",
|
|
"Access-Control-Request-Headers": "authorization, content-type"
|
|
}
|
|
)
|
|
assert res_opt.status_code == 200
|
|
assert res_opt.headers.get("access-control-allow-origin") == "http://localhost:5173"
|
|
|
|
# Actual voice start
|
|
res_post = client.post(
|
|
"/v1/voice-onboarding/start",
|
|
json={
|
|
"patient": {"id": "PAT-CORS-VOICE", "name": "Voice Test", "age": 40, "gender": "female"},
|
|
"language": "en-IN"
|
|
},
|
|
headers={
|
|
"Origin": "http://localhost:5173",
|
|
"Authorization": "Bearer test_token_PAT-CORS-VOICE"
|
|
}
|
|
)
|
|
assert res_post.status_code == 200
|
|
assert res_post.headers.get("access-control-allow-origin") == "http://localhost:5173"
|
|
|
|
if __name__ == '__main__':
|
|
pytest.main(["-v", __file__])
|