Skip to content

Webhooks: unbounded threads, retries permanent 4xx, unsigned-when-no-secret, replayable signatures #21

Description

@codenamekt

Summary

The webhook dispatcher has resource, retry-policy, and signature-security weaknesses, and the whole feature is undocumented.

Findings

Unbounded thread-per-event (medium)

hexus/webhook/dispatcher.py:108-114 + mcp_server/tools.py:190-204, 340-353dispatch_webhook starts a new daemon thread per event, and memory_retain calls it inside the per-content loop.

  • Scenario: a 5,000-item memory_retain spawns up to 5,000 concurrent webhook threads, each retrying with backoff — thread exhaustion. No queue/worker pool.

Retries permanent 4xx (medium)

dispatcher.py:59-70 — all non-2xx responses are retried, including 401/404/410. A dead endpoint gets 4 attempts × (5s timeout + 1+2+4s backoff) per event; combined with thread-per-event, a write burst piles up dozens of ~27s threads.

Unsigned when secret unset — silent (medium)

tools.py:196, 346secret=os.environ.get("HEXUS_WEBHOOK_SECRET"); if unset, sign_payload is skipped and the payload (including full memory content) is POSTed unsigned with no warning. No TLS/scheme enforcement on HEXUS_WEBHOOK_URL.

Weak signature format (low)

dispatcher.py:46 — the signature header has no scheme prefix (e.g. sha256=) and signs no timestamp, so receivers can't do GitHub-style verification and payloads are replayable.

Undocumented feature (low → see docs issue)

Webhooks (HEXUS_WEBHOOK_URL/HEXUS_WEBHOOK_SECRET, payload schema, signature-verification recipe) are documented nowhere except a one-line ROADMAP mention.

Suggested direction

Use a bounded worker pool / reuse the async writer for dispatch; retry only 5xx/timeouts (and 429 with Retry-After); warn loudly (or refuse) when a URL is set without a secret; add a sha256= prefix + signed timestamp; document the feature and a verification snippet.

Filed from the 2026-07 codebase review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: webhooksArea: webhooksbugSomething isn't workingcode-reviewFiled from the 2026-07 codebase reviewpriority: mediumShould fix; hardening/robustness

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions