CVE-2026-54635: pytonapi Webhook Custom Path Authentication Bypass
pytonapi's webhook dispatcher skips bearer-token verification entirely when a handler is registered with a custom path, letting any unauthenticated attacker fire victim-defined business logic with…

The problem
TonapiWebhookDispatcher in pytonapi 2.x stores bearer tokens only under default suffix paths (e.g., /hook/account-tx) during setup(). When a handler is registered with the documented path= argument, the custom path is never inserted into the token map.
When a request arrives at the custom path, self._tokens.get(path) returns None. The guard reads if expected_token is not None and ...: raise, so with None the entire check is skipped (fail-open). An unauthenticated remote attacker can POST arbitrary payloads to any custom webhook endpoint and trigger victim handlers with fully attacker-controlled data, enabling fake payment events, account state manipulation, or notification spam.
Proof of concept
A working proof-of-concept for CVE-2026-54635 in pytonapi, with the exact payload below.
POST /hook/custom HTTP/1.1
Host: victim.example
Content-Type: application/json
{"event_type":"account_tx","account_id":"0:victim","lt":1,"tx_hash":"FORGED_TX_HASH"}The root cause is a fail-open None check in dispatcher.py. setup() writes self._tokens[self._path + suffix] using only the DEFAULT_SUFFIXES key, so a handler registered with path='/hook/custom' leaves that path absent from the map. At request time, self._tokens.get('/hook/custom') returns None, and the condition expected_token is not None evaluates False before the inequality is even tested, so the raise is never reached.
The patch (commit 854222b) fixes both sides. On the write side, setup() now iterates all unique local_paths collected from registered handlers and calls _endpoint_for_path() to derive the correct endpoint for each, then stores the token under that real path. On the read side, _build_path_map() is updated to enumerate every (account_filter, fn, path) triple instead of only reading handlers[0][2], closing a secondary gap where multiple handlers on different paths would have been mapped incorrectly.
CWE-287 (Improper Authentication).
The fix
Upgrade pytonapi to 2.2.1 (patched in commit 854222b7ee68d3fb7b4d6d899d200f388483bd86). No configuration changes are required. If upgrading immediately is not possible, avoid using the path= argument in account_tx decorators and rely only on the default suffix paths until the patch is applied.
Reported by EQSTLab.
Related research
- high · 8CVE-2026-59224CVE-2026-59224: open-webui Terminal Proxy Authentication Bypass via session_id Query Injection
- criticalCVE-2026-61740CVE-2026-61740: LightRAG Authentication Bypass via Hardcoded JWT Secret and Guest Token Short-Circuit
- high · 7.4CVE-2026-54547CVE-2026-54547: meta-ads-mcp Authentication Bypass via X-Pipeboard-Token Header
- high · 7CVE-2026-16584CVE-2026-16584: AWS API MCP Server Security Policy Bypass via Startup Initialization Failure