From 34c910e1317d68f9127e3b8c1d143a031ab7d8fb Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:18:16 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=92=20security:=20move=20IMAP=20cr?= =?UTF-8?q?edentials=20to=20request=20body?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved IMAP credentials from query parameters to the request body for the test-connection endpoint to prevent cleartext logging. Implemented a Pydantic model (IMAPTestRequest) for request body validation. Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- backend/app/api/api_v1/endpoints/imap.py | 36 +++++++++++++----------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/backend/app/api/api_v1/endpoints/imap.py b/backend/app/api/api_v1/endpoints/imap.py index 94940d9..1c8bf9e 100644 --- a/backend/app/api/api_v1/endpoints/imap.py +++ b/backend/app/api/api_v1/endpoints/imap.py @@ -1,39 +1,43 @@ import logging from datetime import datetime -from typing import Any, Dict +from typing import Any, Dict, Optional from app.core.security import require_admin_auth from app.services.imap_client import IMAPClient from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException +from pydantic import BaseModel router = APIRouter() logger = logging.getLogger(__name__) +class IMAPTestRequest(BaseModel): + """IMAP connection test request body""" + + server: Optional[str] = None + port: int = 993 + username: Optional[str] = None + password: Optional[str] = None + ssl: bool = True + + @router.post("/test-connection") async def test_imap_connection( + request: IMAPTestRequest, auth: dict = Depends(require_admin_auth), - server: str = None, - port: int = 993, - username: str = None, - password: str = None, - ssl: bool = True, ) -> Dict[str, Any]: """ Test connection to an IMAP server and gather mailbox statistics Security: Requires authentication (X-API-Key or Bearer token) - Note: Credentials should be passed in request body, not query params + Credentials should be passed in request body """ - # Security: Don't accept credentials in query parameters (they get logged) - if any([server, username, password]): - logger.warning("IMAP credentials passed as query parameters - this is insecure") - raise HTTPException( - status_code=400, - detail="Credentials should be passed in request body, not query parameters", - ) - - imap_client = IMAPClient(server=server, port=port, username=username, password=password) + imap_client = IMAPClient( + server=request.server, + port=request.port, + username=request.username, + password=request.password, + ) success, message, stats = imap_client.test_connection() From 1831c1a079139f13c89314e980f0118d635a042e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:57:40 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=92=20security:=20move=20IMAP=20cr?= =?UTF-8?q?edentials=20to=20request=20body=20and=20fix=20CI/pylint=20issue?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Moved IMAP credentials from query parameters to the request body for the test-connection endpoint. - Created missing `__init__.py` files across `backend/app/` to fix CI `ModuleNotFoundError`. - Reformatted `setup.py` and `user.py` using `black` to pass lint checks. - Addressed Pylint warnings in `imap.py` (unused arguments, lazy logging, exception chaining). Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- backend/app/__init__.py | 0 backend/app/api/__init__.py | 0 backend/app/api/api_v1/__init__.py | 0 backend/app/api/api_v1/endpoints/__init__.py | 0 backend/app/api/api_v1/endpoints/imap.py | 10 +++++----- backend/app/api/api_v1/endpoints/setup.py | 4 ++-- backend/app/core/__init__.py | 0 backend/app/models/__init__.py | 0 backend/app/models/user.py | 4 ++-- backend/app/services/__init__.py | 0 backend/app/tests/__init__.py | 0 11 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 backend/app/__init__.py create mode 100644 backend/app/api/__init__.py create mode 100644 backend/app/api/api_v1/__init__.py create mode 100644 backend/app/api/api_v1/endpoints/__init__.py create mode 100644 backend/app/core/__init__.py create mode 100644 backend/app/models/__init__.py create mode 100644 backend/app/services/__init__.py create mode 100644 backend/app/tests/__init__.py diff --git a/backend/app/__init__.py b/backend/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/api/__init__.py b/backend/app/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/api/api_v1/__init__.py b/backend/app/api/api_v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/api/api_v1/endpoints/__init__.py b/backend/app/api/api_v1/endpoints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/api/api_v1/endpoints/imap.py b/backend/app/api/api_v1/endpoints/imap.py index 1c8bf9e..b66328a 100644 --- a/backend/app/api/api_v1/endpoints/imap.py +++ b/backend/app/api/api_v1/endpoints/imap.py @@ -24,7 +24,7 @@ class IMAPTestRequest(BaseModel): @router.post("/test-connection") async def test_imap_connection( request: IMAPTestRequest, - auth: dict = Depends(require_admin_auth), + _auth: dict = Depends(require_admin_auth), ) -> Dict[str, Any]: """ Test connection to an IMAP server and gather mailbox statistics @@ -55,7 +55,7 @@ async def test_imap_connection( @router.post("/fetch-reports") async def fetch_imap_reports( background_tasks: BackgroundTasks, - auth: dict = Depends(require_admin_auth), + _auth: dict = Depends(require_admin_auth), days: int = 7, delete_emails: bool = False, ) -> Dict[str, Any]: @@ -92,14 +92,14 @@ async def fetch_imap_reports( "timestamp": datetime.now().isoformat(), } except Exception as e: - logger.error(f"Error fetching IMAP reports: {str(e)}") + logger.error("Error fetching IMAP reports: %s", str(e)) raise HTTPException( status_code=500, detail="Failed to fetch reports. Check server logs for details." - ) + ) from e @router.get("/status") -async def get_imap_status(auth: dict = Depends(require_admin_auth)) -> Dict[str, Any]: +async def get_imap_status(_auth: dict = Depends(require_admin_auth)) -> Dict[str, Any]: """ Get the current status of IMAP polling background processes diff --git a/backend/app/api/api_v1/endpoints/setup.py b/backend/app/api/api_v1/endpoints/setup.py index 7a04f85..ca2dca1 100644 --- a/backend/app/api/api_v1/endpoints/setup.py +++ b/backend/app/api/api_v1/endpoints/setup.py @@ -1,4 +1,3 @@ - from fastapi import APIRouter, HTTPException, status from pydantic import BaseModel, EmailStr @@ -38,7 +37,8 @@ class SystemConfigRequest(BaseModel): async def get_setup_status(): """Get the current setup status""" return SetupStatusResponse( - is_setup_complete=setup_status["is_setup_complete"], app_name=setup_status["app_name"] + is_setup_complete=setup_status["is_setup_complete"], + app_name=setup_status["app_name"], ) diff --git a/backend/app/core/__init__.py b/backend/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/models/user.py b/backend/app/models/user.py index e5d0d33..1370a83 100644 --- a/backend/app/models/user.py +++ b/backend/app/models/user.py @@ -1,8 +1,8 @@ - -from app.core.database import Base from sqlalchemy import Boolean, Column, Integer, String from sqlalchemy.orm import relationship +from app.core.database import Base + class User(Base): """User model""" diff --git a/backend/app/services/__init__.py b/backend/app/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/tests/__init__.py b/backend/app/tests/__init__.py new file mode 100644 index 0000000..e69de29