From 7283556048b92b375098bec8437d20890e7ca375 Mon Sep 17 00:00:00 2001 From: chengyongru Date: Thu, 25 Jun 2026 10:00:59 +0800 Subject: [PATCH] fix: cap rendered webhook prompts maintainer edit: prevent custom webhook templates from enqueueing the full request body into the agent/session path. Reuses the existing prompt cap and adds regression coverage for {{ body }}. --- nanobot/webhooks.py | 2 +- tests/test_webhooks.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/nanobot/webhooks.py b/nanobot/webhooks.py index 4fb0bb0fb..d9c6229ea 100644 --- a/nanobot/webhooks.py +++ b/nanobot/webhooks.py @@ -363,7 +363,7 @@ def _render_prompt(route: WebhookRouteConfig, context: dict[str, Any]) -> str: raise WebhookError(400, f"webhook prompt template failed: {exc}") from exc if not rendered.strip(): raise WebhookError(400, "webhook prompt template rendered empty content") - return rendered + return truncate_text(rendered, _DEFAULT_PROMPT_MAX_CHARS) def _render_thread(route: WebhookRouteConfig, context: dict[str, Any]) -> str: diff --git a/tests/test_webhooks.py b/tests/test_webhooks.py index 0a4118c35..ef2d9f0ef 100644 --- a/tests/test_webhooks.py +++ b/tests/test_webhooks.py @@ -117,6 +117,37 @@ async def test_generic_webhook_accepts_hmac_signature() -> None: assert "untrusted external data" in msg.content +@pytest.mark.asyncio +async def test_custom_prompt_is_truncated_after_rendering() -> None: + bus = MessageBus() + router = WebhookRouter( + WebhooksConfig( + routes={ + "big": WebhookRouteConfig( + auth="none", + to="websocket:ops", + prompt="{{ body }}", + ) + } + ), + bus, + ) + body = b"a" * 1_048_576 + + response = await router.handle( + method="POST", + path="/webhooks/big", + headers={}, + body=body, + ) + + assert response is not None + assert response.status == 202 + msg = await bus.consume_inbound() + assert len(msg.content) < len(body) + assert msg.content.endswith("\n... (truncated)") + + @pytest.mark.asyncio async def test_webhook_without_delivery_id_is_not_deduped() -> None: bus = MessageBus()