diff --git a/backend/api/cloud.py b/backend/api/cloud.py index 69b970a..dd9f32c 100644 --- a/backend/api/cloud.py +++ b/backend/api/cloud.py @@ -37,6 +37,7 @@ from config import settings from db.models import CloudConnection, User from deps.auth import get_regular_user from deps.db import get_db +from deps.utils import get_client_ip from services.audit import write_audit_log from services.rate_limiting import account_limiter from storage.cloud_utils import encrypt_credentials, decrypt_credentials, validate_cloud_url @@ -626,7 +627,7 @@ async def connect_webdav( conn = await _upsert_cloud_connection(session, current_user.id, body.provider, credentials_enc) await session.flush() - _ip = request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None) + _ip = get_client_ip(request) await write_audit_log( session, event_type="cloud.connected", @@ -763,7 +764,7 @@ async def delete_connection( from services.cloud_cache import invalidate_provider_cache # lazy import invalidate_provider_cache(str(current_user.id), provider) - _ip = request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None) + _ip = get_client_ip(request) await write_audit_log( session, diff --git a/backend/api/documents.py b/backend/api/documents.py index 18628b8..767d0b1 100644 --- a/backend/api/documents.py +++ b/backend/api/documents.py @@ -36,6 +36,7 @@ from config import settings from db.models import CloudConnection, Document, Folder, Quota, Share, User from deps.auth import get_regular_user from deps.db import get_db +from deps.utils import get_client_ip from services import classifier, storage from services.audit import write_audit_log from services.rate_limiting import account_limiter @@ -272,9 +273,7 @@ async def upload_document( ) session.add(doc) - _ip = ( - request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None) - ) if request else None + _ip = get_client_ip(request) if request else None await write_audit_log( session, event_type="document.uploaded", @@ -378,10 +377,7 @@ async def confirm_upload( doc.status = "uploaded" # D-13: document uploaded event — size_bytes + storage_backend only, NO filename, NO extracted_text (T-04-07-02) - # TRUST BOUNDARY: X-Forwarded-For is client-controlled — for audit logging only, - # not for auth/access control. Use a trusted reverse proxy in production to - # overwrite this header with the real remote IP before it reaches FastAPI. - _ip = request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None) + _ip = get_client_ip(request) await write_audit_log( session, event_type="document.uploaded", @@ -664,10 +660,7 @@ async def delete_document( is_cloud = doc.storage_backend != "minio" _doc_size = doc.size_bytes _doc_id = doc.id - # TRUST BOUNDARY: X-Forwarded-For is client-controlled — for audit logging only, - # not for auth/access control. Use a trusted reverse proxy in production to - # overwrite this header with the real remote IP before it reaches FastAPI. - _ip = request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None) + _ip = get_client_ip(request) # Cloud routing: attempt provider delete unless remove_only is set if is_cloud and not remove_only: