mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
fix(security): cap WeCom callback body size before pre-auth XML parse (#54615)
The WeCom callback endpoint (internet-facing, 0.0.0.0) parsed untrusted request bodies before signature verification. defusedxml already guards the entity-expansion class on main, but there was no cap on raw body size, so an unauthenticated POST could still force unbounded read work pre-auth. Set client_max_size=64KB on the aiohttp app (413 at the framework layer) plus an explicit length guard in _handle_callback as defense in depth. WeCom callbacks are small encrypted XML envelopes — media is delivered out-of-band via MediaId, never inline — so 64KB is ample for legitimate traffic. Adds tests for oversized (413) and normal-sized (not 413) bodies. Salvaged from #10192 by @memosr (body-size limit half; defusedxml half already superseded on main).
This commit is contained in:
parent
0b733a8418
commit
74541beb9c
2 changed files with 55 additions and 2 deletions
|
|
@ -54,6 +54,11 @@ logger = logging.getLogger(__name__)
|
|||
DEFAULT_HOST = "0.0.0.0"
|
||||
DEFAULT_PORT = 8645
|
||||
DEFAULT_PATH = "/wecom/callback"
|
||||
# Cap pre-auth request bodies. WeCom callbacks are small encrypted XML
|
||||
# envelopes (media is delivered out-of-band via MediaId, never inline), so
|
||||
# 64 KB is ample for any legitimate message while bounding the work an
|
||||
# unauthenticated POST can force before signature verification.
|
||||
_MAX_BODY = 65_536
|
||||
ACCESS_TOKEN_TTL_SECONDS = 7200
|
||||
MESSAGE_DEDUP_TTL_SECONDS = 300
|
||||
|
||||
|
|
@ -132,7 +137,9 @@ class WecomCallbackAdapter(BasePlatformAdapter):
|
|||
# Tighter keepalive so idle CLOSE_WAIT drains promptly (#18451).
|
||||
from gateway.platforms._http_client_limits import platform_httpx_limits
|
||||
self._http_client = httpx.AsyncClient(timeout=20.0, limits=platform_httpx_limits())
|
||||
self._app = web.Application()
|
||||
# client_max_size rejects oversized bodies at the aiohttp layer
|
||||
# (413) before our handler — and before any signature work — runs.
|
||||
self._app = web.Application(client_max_size=_MAX_BODY)
|
||||
self._app.router.add_get("/health", self._handle_health)
|
||||
self._app.router.add_get(self._path, self._handle_verify)
|
||||
self._app.router.add_post(self._path, self._handle_callback)
|
||||
|
|
@ -273,7 +280,13 @@ class WecomCallbackAdapter(BasePlatformAdapter):
|
|||
msg_signature = request.query.get("msg_signature", "")
|
||||
timestamp = request.query.get("timestamp", "")
|
||||
nonce = request.query.get("nonce", "")
|
||||
body = await request.text()
|
||||
# Explicit guard in addition to client_max_size: rejects oversized
|
||||
# payloads before any XML parse / signature check (DoS, zip bombs).
|
||||
body_bytes = await request.read()
|
||||
if len(body_bytes) > _MAX_BODY:
|
||||
logger.warning("[WecomCallback] Payload too large (%d bytes) — rejected", len(body_bytes))
|
||||
return web.Response(status=413, text="payload too large")
|
||||
body = body_bytes.decode("utf-8", errors="replace")
|
||||
|
||||
for app in self._apps:
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue