fix: WeasyPrint URL fetcher + attachment improvements + webhook error propagation + WebSocket conversation check + RLS disabled on system tables (bootstrap fix)
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
This commit is contained in:
@@ -515,7 +515,23 @@ async def websocket_endpoint(
|
||||
elif msg_type == "subscribe":
|
||||
conv_id = msg.get("conversation_id")
|
||||
if conv_id:
|
||||
ws_manager.subscribe(conv_id, user_id)
|
||||
# P1.9 fix: Check if user is a participant of this conversation
|
||||
from app.core.db import async_session_maker
|
||||
from sqlalchemy import text as sql_text
|
||||
try:
|
||||
async with async_session_maker() as db:
|
||||
await db.execute(sql_text("SELECT set_config('app.current_tenant_id', :tid, true)"), {"tid": tenant_id})
|
||||
result = await db.execute(
|
||||
sql_text("SELECT 1 FROM conversation_participants WHERE conversation_id = :cid AND user_id = :uid"),
|
||||
{"cid": conv_id, "uid": user_id},
|
||||
)
|
||||
if result.first():
|
||||
ws_manager.subscribe(conv_id, user_id)
|
||||
else:
|
||||
await ws_manager.send_to_user(user_id, {"type": "error", "message": "Not a participant of this conversation"})
|
||||
except Exception:
|
||||
logger.warning("Failed to check conversation participation for %s in %s", user_id, conv_id)
|
||||
await ws_manager.send_to_user(user_id, {"type": "error", "message": "Cannot verify participation"})
|
||||
elif msg_type == "unsubscribe":
|
||||
conv_id = msg.get("conversation_id")
|
||||
if conv_id:
|
||||
|
||||
@@ -130,9 +130,23 @@ def render_template_string(template_content: str, data: dict[str, Any]) -> str:
|
||||
return template.render(**data)
|
||||
|
||||
|
||||
def _safe_url_fetcher(url: str, timeout: int = 10) -> dict:
|
||||
"""URL fetcher that only allows data: URIs and blocks external resources.
|
||||
|
||||
Prevents SSRF and local file access via WeasyPrint.
|
||||
"""
|
||||
if url.startswith('data:'):
|
||||
from weasyprint import default_url_fetcher
|
||||
return default_url_fetcher(url, timeout)
|
||||
# Block all external URLs (http, https, file, etc.)
|
||||
raise ValueError(f"External resource blocked by URL fetcher: {url}")
|
||||
|
||||
|
||||
def generate_pdf(html_content: str) -> bytes:
|
||||
"""Generate a PDF from HTML content using WeasyPrint.
|
||||
|
||||
Uses a safe URL fetcher that blocks external resources (SSRF protection).
|
||||
|
||||
Args:
|
||||
html_content: Valid HTML string
|
||||
|
||||
@@ -141,7 +155,7 @@ def generate_pdf(html_content: str) -> bytes:
|
||||
"""
|
||||
from weasyprint import HTML
|
||||
|
||||
pdf = HTML(string=html_content).write_pdf()
|
||||
pdf = HTML(string=html_content, url_fetcher=_safe_url_fetcher).write_pdf()
|
||||
return pdf
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user