Skip to content

fix(webhook): exempt url_verification handshake from strict signature pre-verify - #14

Open
wz-heng wants to merge 2 commits into
larksuite:mainfrom
wz-heng:fix/webhook-url-verification-strict-deadlock
Open

fix(webhook): exempt url_verification handshake from strict signature pre-verify#14
wz-heng wants to merge 2 commits into
larksuite:mainfrom
wz-heng:fix/webhook-url-verification-strict-deadlock

Conversation

@wz-heng

@wz-heng wz-heng commented Aug 14, 2026

Copy link
Copy Markdown

Fixes part 2 of #12.

Bug

The Feishu console's "save request URL" challenge (url_verification)
is encrypted (when an encrypt key is configured) but never signed —
that's platform behavior, not something an integrator controls.

With encrypt_key configured and SecurityConfig(mode="strict"),
EventDispatcherHandler._preverify_encrypted_request (and the
equivalent branch in CardActionHandler) rejects any encrypted payload
that arrives without the three signature headers, before the
url_verification branch further down do() ever gets a chance to run.
An integrator using strict mode + encryption can therefore never
complete the console's first-time URL-verification step — the setup
deadlocks.

Fix

When the signature headers are absent, peek-decrypt the payload only to
check whether it's a url_verification handshake. If it is, exempt it
from the pre-verify signature requirement — its own verification_token
(checked immediately afterward in do(), same as every other event) is
its proof of identity, per Feishu's own design. Any other unsigned
encrypted content — or a payload that fails to decrypt during the peek —
is rejected exactly as before; this does not touch the
allow_unsigned_encrypted_webhook escape hatch or weaken strict mode for
anything other than the handshake itself.

Testing

  • New tests: a genuine unsigned encrypted url_verification handshake in
    strict mode now gets a 200 + echoed challenge (both handlers).
  • Updated two existing tests whose invariant ("signature-missing strict
    requests never reach decrypt") predates this fix and is no longer
    literally true — decrypt is now attempted to check for the handshake
    exemption. Renamed/re-scoped them to assert what actually matters:
    non-handshake unsigned content is still blocked, and a payload that
    fails to decrypt during the peek is still blocked (not silently let
    through).

Full suite: python -m pytest -q → 1102 passed.

🤖 Generated with Claude Code

… pre-verify

The Feishu console's "save request URL" challenge is encrypted but
never signed (platform behavior). With an encrypt_key configured,
strict mode's encrypted pre-verification rejected the unsigned
handshake before the url_verification branch was ever reached, so an
integrator using strict mode + encryption could never complete the
console's first-time URL-verification step.

Peek-decrypt only when signature headers are absent, to check whether
the payload is a url_verification handshake; if so, exempt it from
the pre-verify signature requirement and let the existing
verification_token check downstream (its own proof of identity) and
the challenge-echo branch run as normal. Any other unsigned encrypted
content, or a payload that fails to decrypt during the peek, is still
rejected exactly as before.

Fixes larksuite#12 (bug 2 of 2).
test_strict_event_missing_signature_non_handshake_rejects and its card
counterpart only asserted a 500 status and the expected audit reason,
which a subtly broken exemption (one that records
webhook.signature_missing but still lets the request through) could
satisfy by coincidence via a later "processor not found" error.
Register a processor and assert it is never invoked, so the tests
pin down what actually matters: the request never reaches application
code.

Addresses review feedback from this run's required Snape pass.
@CLAassistant

CLAassistant commented Aug 14, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants