fix(webhook): accept Gitea's native bare-hex signature format
verify_signature compared the X-Gitea-Signature header against "sha256=<hex>", which is GitHub's format. Gitea sends a bare 64-character hex digest with no prefix, so a correctly-signed Gitea delivery could never match and was rejected with 403 Invalid signature. Confirmed by capturing a real Gitea delivery against a scratch listener: SIG_PRESENT=True HAS_PREFIX=False SIG_LEN=64 Now strips an optional "sha256=" prefix before comparing, so both Gitea's native format and GitHub-style senders verify. The HMAC computation and the constant-time comparison are unchanged — an absent, empty, or incorrect signature is still rejected exactly as before. This was the second of two faults blocking the webhook gate; the first was the repository webhook pointing at the host's public IP, which Gitea's ALLOWED_HOST_LIST denied before any request left the process. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6d31b9bf2e
commit
29621c8837
@ -29,11 +29,17 @@ SECRET = CONFIG.get("webhook_secret", "").encode()
|
|||||||
|
|
||||||
|
|
||||||
def verify_signature(payload: bytes, signature: str) -> bool:
|
def verify_signature(payload: bytes, signature: str) -> bool:
|
||||||
"""Verify Gitea webhook HMAC signature."""
|
"""Verify Gitea webhook HMAC signature.
|
||||||
|
|
||||||
|
Gitea sends X-Gitea-Signature as a bare hex digest. GitHub-style senders
|
||||||
|
prefix it with "sha256=". Accept either form — the HMAC comparison itself
|
||||||
|
is unchanged, so this does not loosen verification.
|
||||||
|
"""
|
||||||
if not SECRET:
|
if not SECRET:
|
||||||
return True # No secret configured — accept all
|
return True # No secret configured — accept all
|
||||||
expected = hmac.new(SECRET, payload, hashlib.sha256).hexdigest()
|
expected = hmac.new(SECRET, payload, hashlib.sha256).hexdigest()
|
||||||
return hmac.compare_digest(f"sha256={expected}", signature)
|
provided = signature[len("sha256="):] if signature.startswith("sha256=") else signature
|
||||||
|
return hmac.compare_digest(expected, provided)
|
||||||
|
|
||||||
|
|
||||||
class WebhookHandler(BaseHTTPRequestHandler):
|
class WebhookHandler(BaseHTTPRequestHandler):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user