Security fixes: P0-P2 complete (22 fixes)
P0 (7): Auth-bypass removed, migrations fixed, plugin-upload disabled, RLS FORCE+WITH CHECK, plugin double-registration fixed, persistent volume, domain removed P1 (11): User/tenant model, Redis centralized, worker separated, transactional outbox, XSS fixed, DMS chunked streaming, permissions unified, password reset, metrics secured, config/docs fixed, cross-tenant FK P2 (4): Contact model normalized, cross-imports reduced 94%, commands+state machines for contacts/dms/mail/calendar, SPA path-traversal 8 new migrations, 99 unit tests, 13 commands, 8 contracts, 72 files changed
This commit is contained in:
@@ -18,7 +18,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.core.db import create_db_session
|
||||
from app.models.audit import AuditLog
|
||||
from app.models.contact import Contact
|
||||
from app.plugins.builtins.mail.models import Mail
|
||||
from app.plugins.builtins.mail.contracts import Mail
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -101,9 +101,9 @@ async def search_related_handler(arguments: dict[str, Any], context: dict[str, A
|
||||
entity_id = uuid.UUID(arguments["entity_id"])
|
||||
limit = arguments.get("limit", 5)
|
||||
|
||||
from app.plugins.builtins.unified_search.search_engine import (
|
||||
find_similar_all_types,
|
||||
)
|
||||
from app.plugins.builtins.unified_search.contracts import get_contract as get_search_contract
|
||||
_search = get_search_contract()
|
||||
find_similar_all_types = _search.hybrid_search
|
||||
|
||||
similar = await find_similar_all_types(
|
||||
db, entity_type, entity_id, tenant_id, limit=limit
|
||||
@@ -163,10 +163,10 @@ async def get_open_tasks_handler(arguments: dict[str, Any], context: dict[str, A
|
||||
try:
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from app.plugins.builtins.calendar.models import (
|
||||
CalendarEntry,
|
||||
CalendarEntryLink,
|
||||
)
|
||||
from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract
|
||||
_cal = get_calendar_contract()
|
||||
CalendarEntry = _cal.CalendarEntry
|
||||
CalendarEntryLink = _cal.CalendarEntryLink
|
||||
|
||||
db, tenant_id, _ = await _get_db_and_tenant(context)
|
||||
entity_type = arguments["entity_type"]
|
||||
@@ -194,7 +194,9 @@ async def get_open_tasks_handler(arguments: dict[str, Any], context: dict[str, A
|
||||
async def hybrid_search_handler(arguments: dict[str, Any], context: dict[str, Any]) -> str:
|
||||
"""Perform hybrid search via unified_search search_engine."""
|
||||
try:
|
||||
from app.plugins.builtins.unified_search.search_engine import hybrid_search
|
||||
from app.plugins.builtins.unified_search.contracts import get_contract as get_search_contract
|
||||
_search = get_search_contract()
|
||||
hybrid_search = _search.hybrid_search
|
||||
|
||||
db, tenant_id, _ = await _get_db_and_tenant(context)
|
||||
query = arguments["query"]
|
||||
|
||||
@@ -30,7 +30,7 @@ from app.plugins.builtins.ai_proactive.services import (
|
||||
get_user_settings,
|
||||
push_suggestion,
|
||||
)
|
||||
from app.plugins.builtins.mail.models import Mail
|
||||
from app.plugins.builtins.mail.contracts import Mail
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -175,9 +175,10 @@ async def deep_analysis(
|
||||
|
||||
# Similar entities via unified_search
|
||||
try:
|
||||
from app.plugins.builtins.unified_search.search_engine import (
|
||||
find_similar_all_types,
|
||||
)
|
||||
from app.plugins.builtins.unified_search.contracts import get_contract as get_search_contract
|
||||
_search = get_search_contract()
|
||||
hybrid_search = _search.hybrid_search
|
||||
find_similar_all_types = _search.hybrid_search # alias
|
||||
|
||||
extended_context["similar"] = await find_similar_all_types(
|
||||
db, entity_type, eid, tid, limit=5
|
||||
@@ -335,7 +336,7 @@ async def heartbeat(ctx: dict[str, Any], user_id: str, tenant_id: str) -> None:
|
||||
|
||||
try:
|
||||
from app.core.db import create_db_session
|
||||
from app.plugins.builtins.kommunikation.services import (
|
||||
from app.plugins.builtins.kommunikation.contracts import (
|
||||
create_plugin_room,
|
||||
send_message,
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ import uuid
|
||||
from typing import Any
|
||||
|
||||
from app.core.db import create_db_session
|
||||
from app.plugins.builtins.kommunikation.participant_registry import ParticipantHandler
|
||||
from app.plugins.builtins.kommunikation.contracts import ParticipantHandler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -173,7 +173,7 @@ class AIProactiveParticipantHandler(ParticipantHandler):
|
||||
|
||||
# Load conversation
|
||||
try:
|
||||
from app.plugins.builtins.kommunikation.services import get_conversation
|
||||
from app.plugins.builtins.kommunikation.contracts import get_conversation
|
||||
|
||||
async with create_db_session(tenant_id) as db:
|
||||
if not sender_id_str:
|
||||
@@ -188,7 +188,7 @@ class AIProactiveParticipantHandler(ParticipantHandler):
|
||||
return
|
||||
|
||||
# Parse mentions from message content
|
||||
from app.plugins.builtins.kommunikation.services import parse_mentions
|
||||
from app.plugins.builtins.kommunikation.contracts import parse_mentions
|
||||
|
||||
mentions = parse_mentions(message_content)
|
||||
|
||||
@@ -204,7 +204,7 @@ class AIProactiveParticipantHandler(ParticipantHandler):
|
||||
|
||||
# If we got a response, send it to the conversation
|
||||
if response_messages:
|
||||
from app.plugins.builtins.kommunikation.services import send_message
|
||||
from app.plugins.builtins.kommunikation.contracts import send_message
|
||||
|
||||
for resp_msg in response_messages:
|
||||
await send_message(
|
||||
|
||||
@@ -59,7 +59,7 @@ class AIProactivePlugin(BasePlugin):
|
||||
from app.plugins.builtins.ai_proactive.context_tools import (
|
||||
register_context_tools,
|
||||
)
|
||||
from app.plugins.builtins.ai_assistant.tool_registry import (
|
||||
from app.plugins.builtins.ai_assistant.contracts import (
|
||||
get_tool_registry,
|
||||
)
|
||||
|
||||
@@ -73,7 +73,7 @@ class AIProactivePlugin(BasePlugin):
|
||||
from app.plugins.builtins.ai_proactive.participant_handler import (
|
||||
AIProactiveParticipantHandler,
|
||||
)
|
||||
from app.plugins.builtins.kommunikation.participant_registry import (
|
||||
from app.plugins.builtins.kommunikation.contracts import (
|
||||
get_participant_registry,
|
||||
)
|
||||
|
||||
@@ -87,7 +87,7 @@ class AIProactivePlugin(BasePlugin):
|
||||
"""Unregister tools, event listeners, and participant."""
|
||||
# Unregister from participant registry
|
||||
try:
|
||||
from app.plugins.builtins.kommunikation.participant_registry import (
|
||||
from app.plugins.builtins.kommunikation.contracts import (
|
||||
get_participant_registry,
|
||||
)
|
||||
|
||||
@@ -99,7 +99,7 @@ class AIProactivePlugin(BasePlugin):
|
||||
self._proactive_handler = None
|
||||
|
||||
try:
|
||||
from app.plugins.builtins.ai_assistant.tool_registry import (
|
||||
from app.plugins.builtins.ai_assistant.contracts import (
|
||||
get_tool_registry,
|
||||
)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ async def _get_llm_api_key(db: AsyncSession, tenant_id: uuid.UUID) -> tuple[str
|
||||
Returns (api_key, base_url, provider_type).
|
||||
"""
|
||||
try:
|
||||
from app.plugins.builtins.ai_assistant.services import get_default_provider
|
||||
from app.plugins.builtins.ai_assistant.contracts import get_default_provider
|
||||
provider = await get_default_provider(db, tenant_id)
|
||||
if provider and provider.api_key:
|
||||
return provider.api_key, provider.base_url, provider.provider_type
|
||||
@@ -167,7 +167,7 @@ async def gather_context(
|
||||
context["contact"] = _serialize_row(contact) if contact else None
|
||||
|
||||
# Last 10 mails
|
||||
from app.plugins.builtins.mail.models import Mail
|
||||
from app.plugins.builtins.mail.contracts import Mail
|
||||
|
||||
mail_result = await db.execute(
|
||||
select(Mail)
|
||||
@@ -203,7 +203,10 @@ async def gather_context(
|
||||
context["companies"] = companies
|
||||
|
||||
# Upcoming calendar events
|
||||
from app.plugins.builtins.calendar.models import CalendarEntry, CalendarEntryLink
|
||||
from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract
|
||||
_cal = get_calendar_contract()
|
||||
CalendarEntry = _cal.CalendarEntry
|
||||
CalendarEntryLink = _cal.CalendarEntryLink
|
||||
|
||||
now = datetime.now(UTC)
|
||||
event_result = await db.execute(
|
||||
@@ -229,7 +232,7 @@ async def gather_context(
|
||||
context["activities"] = [_serialize_row(a) for a in audit_result.scalars().all()]
|
||||
|
||||
elif entity_type == "mail":
|
||||
from app.plugins.builtins.mail.models import Mail
|
||||
from app.plugins.builtins.mail.contracts import Mail
|
||||
|
||||
result = await db.execute(
|
||||
select(Mail)
|
||||
@@ -303,7 +306,7 @@ async def gather_context(
|
||||
context["contacts"] = contacts
|
||||
|
||||
# Mails for this contact
|
||||
from app.plugins.builtins.mail.models import Mail
|
||||
from app.plugins.builtins.mail.contracts import Mail
|
||||
|
||||
mail_result = await db.execute(
|
||||
select(Mail)
|
||||
@@ -315,7 +318,10 @@ async def gather_context(
|
||||
context["mails"] = [_serialize_row(m) for m in mail_result.scalars().all()]
|
||||
|
||||
# Upcoming events
|
||||
from app.plugins.builtins.calendar.models import CalendarEntry, CalendarEntryLink
|
||||
from app.plugins.builtins.calendar.contracts import get_contract as get_calendar_contract
|
||||
_cal = get_calendar_contract()
|
||||
CalendarEntry = _cal.CalendarEntry
|
||||
CalendarEntryLink = _cal.CalendarEntryLink
|
||||
|
||||
now = datetime.now(UTC)
|
||||
event_result = await db.execute(
|
||||
@@ -356,9 +362,9 @@ async def gather_context(
|
||||
|
||||
# Semantically similar entities via unified_search
|
||||
try:
|
||||
from app.plugins.builtins.unified_search.search_engine import (
|
||||
find_similar_all_types,
|
||||
)
|
||||
from app.plugins.builtins.unified_search.contracts import get_contract as get_search_contract
|
||||
_search = get_search_contract()
|
||||
find_similar_all_types = _search.hybrid_search
|
||||
|
||||
context["similar"] = await find_similar_all_types(
|
||||
db, entity_type, entity_id, tenant_id, limit=3
|
||||
|
||||
Reference in New Issue
Block a user