mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-04 08:28:36 +00:00
fix: cap rendered webhook thread keys
This commit is contained in:
parent
f67506dadf
commit
806ab0ce33
@ -2009,7 +2009,7 @@ Common template variables:
|
|||||||
| `webhooks.routes.<name>.to` | empty | Required target address in `channel:chat` format. |
|
| `webhooks.routes.<name>.to` | empty | Required target address in `channel:chat` format. |
|
||||||
| `webhooks.routes.<name>.events` | `[]` | Optional provider event-name allowlist. For GitHub this matches `X-GitHub-Event`, such as `pull_request`. |
|
| `webhooks.routes.<name>.events` | `[]` | Optional provider event-name allowlist. For GitHub this matches `X-GitHub-Event`, such as `pull_request`. |
|
||||||
| `webhooks.routes.<name>.actions` | `[]` | Optional JSON payload `action` allowlist, such as `opened` or `synchronize`. |
|
| `webhooks.routes.<name>.actions` | `[]` | Optional JSON payload `action` allowlist, such as `opened` or `synchronize`. |
|
||||||
| `webhooks.routes.<name>.thread` | empty | Optional Jinja template for the session key. Defaults to `to`. |
|
| `webhooks.routes.<name>.thread` | empty | Optional Jinja template for the session key. Defaults to `to`; rendered values are capped at 512 characters. |
|
||||||
| `webhooks.routes.<name>.prompt` | empty | Optional Jinja template for the inbound agent message. |
|
| `webhooks.routes.<name>.prompt` | empty | Optional Jinja template for the inbound agent message. |
|
||||||
| `webhooks.routes.<name>.sender` | `webhook` | Sender ID placed on the inbound message. |
|
| `webhooks.routes.<name>.sender` | `webhook` | Sender ID placed on the inbound message. |
|
||||||
| `webhooks.routes.<name>.maxBodyBytes` | `1048576` | Maximum request body size, from 1 KiB to 10 MiB. |
|
| `webhooks.routes.<name>.maxBodyBytes` | `1048576` | Maximum request body size, from 1 KiB to 10 MiB. |
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from nanobot.utils.helpers import truncate_text
|
|||||||
|
|
||||||
_HMAC_PREFIX = "sha256="
|
_HMAC_PREFIX = "sha256="
|
||||||
_DEFAULT_PROMPT_MAX_CHARS = 24_000
|
_DEFAULT_PROMPT_MAX_CHARS = 24_000
|
||||||
|
_DEFAULT_THREAD_MAX_CHARS = 512
|
||||||
_REDACTED_HEADERS = {
|
_REDACTED_HEADERS = {
|
||||||
"authorization",
|
"authorization",
|
||||||
"cookie",
|
"cookie",
|
||||||
@ -494,7 +495,10 @@ def _render_thread(route: WebhookRouteConfig, context: dict[str, Any]) -> str:
|
|||||||
rendered = _jinja().from_string(template).render(**context)
|
rendered = _jinja().from_string(template).render(**context)
|
||||||
except TemplateError as exc:
|
except TemplateError as exc:
|
||||||
raise WebhookError(400, f"webhook thread template failed: {exc}") from exc
|
raise WebhookError(400, f"webhook thread template failed: {exc}") from exc
|
||||||
return rendered.strip()
|
rendered = rendered.strip()
|
||||||
|
if len(rendered) > _DEFAULT_THREAD_MAX_CHARS:
|
||||||
|
raise WebhookError(400, "webhook thread template rendered too long")
|
||||||
|
return rendered
|
||||||
|
|
||||||
|
|
||||||
def _jinja() -> Environment:
|
def _jinja() -> Environment:
|
||||||
|
|||||||
@ -218,6 +218,47 @@ async def test_webhook_custom_path_and_thread_template() -> None:
|
|||||||
assert msg.session_key_override == "deploy:api:delivery-2"
|
assert msg.session_key_override == "deploy:api:delivery-2"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_webhook_rejects_overlong_thread_template_without_dedupe() -> None:
|
||||||
|
bus = MessageBus()
|
||||||
|
router = WebhookRouter(
|
||||||
|
WebhooksConfig(
|
||||||
|
routes={
|
||||||
|
"deploy": WebhookRouteConfig(
|
||||||
|
auth="none",
|
||||||
|
to="websocket:ops",
|
||||||
|
thread="deploy:{{ event.thread }}",
|
||||||
|
prompt="Deploy {{ event.service }}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
bus,
|
||||||
|
)
|
||||||
|
|
||||||
|
failed = await router.handle(
|
||||||
|
method="POST",
|
||||||
|
path="/webhooks/deploy",
|
||||||
|
headers={"X-Nanobot-Delivery": "delivery-3"},
|
||||||
|
body=json.dumps({"service": "api", "thread": "x" * 600}).encode(),
|
||||||
|
)
|
||||||
|
accepted = await router.handle(
|
||||||
|
method="POST",
|
||||||
|
path="/webhooks/deploy",
|
||||||
|
headers={"X-Nanobot-Delivery": "delivery-3"},
|
||||||
|
body=json.dumps({"service": "api", "thread": "release"}).encode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert failed is not None
|
||||||
|
assert failed.status == 400
|
||||||
|
assert "thread template rendered too long" in failed.body["error"]
|
||||||
|
assert accepted is not None
|
||||||
|
assert accepted.status == 202
|
||||||
|
assert accepted.body["queued"] is True
|
||||||
|
assert bus.inbound_size == 1
|
||||||
|
msg = await bus.consume_inbound()
|
||||||
|
assert msg.session_key_override == "deploy:release"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_github_webhook_validates_signature_and_dedupes_delivery() -> None:
|
async def test_github_webhook_validates_signature_and_dedupes_delivery() -> None:
|
||||||
bus = MessageBus()
|
bus = MessageBus()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user