from __future__ import annotations import hashlib import hmac from yuxi.channel.domain.port.external.channel_request_verifier_port import ( VerifyResult, ) class HooksRequestVerifier: @property def channel_type(self) -> str: return "hooks" @property def enabled(self) -> bool: return True async def verify(self, body: bytes, headers: dict[str, str], *, secret: str | None = None) -> VerifyResult: if secret is None: return VerifyResult(passed=False, method="hooks_none", reason="secret not configured, request rejected") if not secret: return VerifyResult(passed=True, method="hooks_none", reason="no secret configured") auth_header = headers.get("authorization", "") if auth_header.startswith("Bearer "): token = auth_header[7:] if hmac.compare_digest(token, secret): return VerifyResult(passed=True, method="hooks_bearer") return VerifyResult(passed=False, method="hooks_bearer", reason="invalid bearer token") signature = headers.get("x-signature-256") or headers.get("x-hub-signature-256") if signature: sig = signature[7:] if signature.startswith("sha256=") else signature expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() if hmac.compare_digest(sig, expected): return VerifyResult(passed=True, method="hooks_hmac") return VerifyResult(passed=False, method="hooks_hmac", reason="invalid signature") return VerifyResult(passed=False, method="hooks_none", reason="authentication required")