mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-24 21:38:09 +02:00
fix open redirect, error leaking, unused imports, state validation
This commit is contained in:
parent
e676ebfabe
commit
940889c291
6 changed files with 13 additions and 24 deletions
|
|
@ -1,7 +1,5 @@
|
||||||
"""Shared auth helper for Discord agent tools (REST API, not gateway bot)."""
|
"""Shared auth helper for Discord agent tools (REST API, not gateway bot)."""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.future import select
|
from sqlalchemy.future import select
|
||||||
|
|
||||||
|
|
@ -9,8 +7,6 @@ from app.config import config
|
||||||
from app.db import SearchSourceConnector, SearchSourceConnectorType
|
from app.db import SearchSourceConnector, SearchSourceConnectorType
|
||||||
from app.utils.oauth_security import TokenEncryption
|
from app.utils.oauth_security import TokenEncryption
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
DISCORD_API = "https://discord.com/api/v10"
|
DISCORD_API = "https://discord.com/api/v10"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
"""Shared auth helper for Luma agent tools."""
|
"""Shared auth helper for Luma agent tools."""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.future import select
|
from sqlalchemy.future import select
|
||||||
|
|
||||||
from app.db import SearchSourceConnector, SearchSourceConnectorType
|
from app.db import SearchSourceConnector, SearchSourceConnectorType
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
LUMA_API = "https://public-api.luma.com/v1"
|
LUMA_API = "https://public-api.luma.com/v1"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
"""Shared auth helper for Teams agent tools (Microsoft Graph REST API)."""
|
"""Shared auth helper for Teams agent tools (Microsoft Graph REST API)."""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.future import select
|
from sqlalchemy.future import select
|
||||||
|
|
||||||
from app.config import config
|
|
||||||
from app.db import SearchSourceConnector, SearchSourceConnectorType
|
from app.db import SearchSourceConnector, SearchSourceConnectorType
|
||||||
from app.utils.oauth_security import TokenEncryption
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
GRAPH_API = "https://graph.microsoft.com/v1.0"
|
GRAPH_API = "https://graph.microsoft.com/v1.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ router.include_router(logs_router)
|
||||||
router.include_router(circleback_webhook_router) # Circleback meeting webhooks
|
router.include_router(circleback_webhook_router) # Circleback meeting webhooks
|
||||||
router.include_router(surfsense_docs_router) # Surfsense documentation for citations
|
router.include_router(surfsense_docs_router) # Surfsense documentation for citations
|
||||||
router.include_router(notifications_router) # Notifications with Zero sync
|
router.include_router(notifications_router) # Notifications with Zero sync
|
||||||
router.include_router(mcp_oauth_router) # MCP OAuth 2.1 for Linear, Jira, ClickUp
|
router.include_router(mcp_oauth_router) # MCP OAuth 2.1 for Linear, Jira, ClickUp, Slack, Airtable
|
||||||
router.include_router(composio_router) # Composio OAuth and toolkit management
|
router.include_router(composio_router) # Composio OAuth and toolkit management
|
||||||
router.include_router(public_chat_router) # Public chat sharing and cloning
|
router.include_router(public_chat_router) # Public chat sharing and cloning
|
||||||
router.include_router(incentive_tasks_router) # Incentive tasks for earning free pages
|
router.include_router(incentive_tasks_router) # Incentive tasks for earning free pages
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ async def connect_mcp_service(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Failed to initiate %s MCP OAuth: %s", service, e, exc_info=True)
|
logger.error("Failed to initiate %s MCP OAuth: %s", service, e, exc_info=True)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=500, detail=f"Failed to initiate {service} MCP OAuth: {e!s}",
|
status_code=500, detail=f"Failed to initiate {service} MCP OAuth.",
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -221,6 +221,9 @@ async def mcp_oauth_callback(
|
||||||
space_id = data["space_id"]
|
space_id = data["space_id"]
|
||||||
svc_key = data.get("service", service)
|
svc_key = data.get("service", service)
|
||||||
|
|
||||||
|
if svc_key != service:
|
||||||
|
raise HTTPException(status_code=400, detail="State/path service mismatch")
|
||||||
|
|
||||||
from app.services.mcp_oauth.registry import get_service
|
from app.services.mcp_oauth.registry import get_service
|
||||||
|
|
||||||
svc = get_service(svc_key)
|
svc = get_service(svc_key)
|
||||||
|
|
@ -315,7 +318,7 @@ async def mcp_oauth_callback(
|
||||||
svc.name, db_connector.id, user_id,
|
svc.name, db_connector.id, user_id,
|
||||||
)
|
)
|
||||||
reauth_return_url = data.get("return_url")
|
reauth_return_url = data.get("return_url")
|
||||||
if reauth_return_url and reauth_return_url.startswith("/"):
|
if reauth_return_url and reauth_return_url.startswith("/") and not reauth_return_url.startswith("//"):
|
||||||
return RedirectResponse(
|
return RedirectResponse(
|
||||||
url=f"{config.NEXT_FRONTEND_URL}{reauth_return_url}"
|
url=f"{config.NEXT_FRONTEND_URL}{reauth_return_url}"
|
||||||
)
|
)
|
||||||
|
|
@ -347,7 +350,7 @@ async def mcp_oauth_callback(
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=409, detail=f"Database integrity error: {e!s}",
|
status_code=409, detail="A connector for this service already exists.",
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
_invalidate_cache(space_id)
|
_invalidate_cache(space_id)
|
||||||
|
|
@ -368,7 +371,7 @@ async def mcp_oauth_callback(
|
||||||
)
|
)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=500,
|
status_code=500,
|
||||||
detail=f"Failed to complete {service} MCP OAuth: {e!s}",
|
detail=f"Failed to complete {service} MCP OAuth.",
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -495,7 +498,7 @@ async def reauth_mcp_service(
|
||||||
)
|
)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=500,
|
status_code=500,
|
||||||
detail=f"Failed to initiate {service} MCP re-auth: {e!s}",
|
detail=f"Failed to initiate {service} MCP re-auth.",
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ class OAuthConnectorRoute:
|
||||||
state_mgr = oauth._get_state_manager()
|
state_mgr = oauth._get_state_manager()
|
||||||
|
|
||||||
extra: dict[str, Any] = {"connector_id": connector_id}
|
extra: dict[str, Any] = {"connector_id": connector_id}
|
||||||
if return_url and return_url.startswith("/"):
|
if return_url and return_url.startswith("/") and not return_url.startswith("//"):
|
||||||
extra["return_url"] = return_url
|
extra["return_url"] = return_url
|
||||||
|
|
||||||
auth_params: dict[str, str] = {
|
auth_params: dict[str, str] = {
|
||||||
|
|
@ -498,7 +498,7 @@ class OAuthConnectorRoute:
|
||||||
data = state_mgr.validate_state(state)
|
data = state_mgr.validate_state(state)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400, detail=f"Invalid state parameter: {e!s}"
|
status_code=400, detail="Invalid or expired state parameter."
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
user_id = UUID(data["user_id"])
|
user_id = UUID(data["user_id"])
|
||||||
|
|
@ -552,7 +552,7 @@ class OAuthConnectorRoute:
|
||||||
db_connector.id,
|
db_connector.id,
|
||||||
user_id,
|
user_id,
|
||||||
)
|
)
|
||||||
if reauth_return_url and reauth_return_url.startswith("/"):
|
if reauth_return_url and reauth_return_url.startswith("/") and not reauth_return_url.startswith("//"):
|
||||||
return RedirectResponse(
|
return RedirectResponse(
|
||||||
url=f"{config.NEXT_FRONTEND_URL}{reauth_return_url}"
|
url=f"{config.NEXT_FRONTEND_URL}{reauth_return_url}"
|
||||||
)
|
)
|
||||||
|
|
@ -603,7 +603,7 @@ class OAuthConnectorRoute:
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
await session.rollback()
|
await session.rollback()
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=409, detail=f"Database integrity error: {e!s}"
|
status_code=409, detail="A connector for this service already exists."
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue