Python 88.3%
TypeScript 7.6%
Shell 4.1%
1"""Offline webhook signature verification tests (no network, no key): SDK `webhooks.unwrap` vs manual HMAC.2Scheme: HMAC-SHA256(base64decode(secret sans 'whsec_'), f"{webhook-id}.{webhook-timestamp}.{body}") -> 'v1,<b64>'.3"""4from __future__ import annotations56import base647import json8import sys9import time10from pathlib import Path1112import pytest1314ROOT = Path(__file__).resolve().parents[2]15sys.path.insert(0, str(ROOT / "examples/openai/webhooks"))16from verify_manual import InvalidSignature, sign, verify # noqa: E4021718openai = pytest.importorskip("openai")19from openai import InvalidWebhookSignatureError, OpenAI # noqa: E4022021SECRET = "whsec_" + base64.b64encode(b"atlas-fake-secret-32-bytes-long!!").decode()222324@pytest.fixture25def signed():26 body = json.dumps({"id": "evt_t1", "object": "event", "type": "batch.completed", "created_at": 1, "data": {"id": "batch_t1"}})27 ts = int(time.time())28 headers = {"webhook-id": "wh_t1", "webhook-timestamp": str(ts), "webhook-signature": sign(SECRET, "wh_t1", ts, body)}29 return body, headers, ts303132def test_sdk_and_manual_agree(signed):33 body, headers, _ = signed34 client = OpenAI(api_key="sk-offline-dummy", webhook_secret=SECRET)35 ev = client.webhooks.unwrap(body, headers)36 assert ev.type == "batch.completed" and ev.data.id == "batch_t1"37 assert verify(body, headers, SECRET)["id"] == "evt_t1"383940def test_known_vector():41 """Deterministic vector: fixed secret/id/timestamp/body -> fixed signature (regression guard for the scheme)."""42 secret = "whsec_" + base64.b64encode(bytes(range(32))).decode()43 sig = sign(secret, "wh_fixed", 1750287078, '{"a":1}')44 assert sig.startswith("v1,") and len(base64.b64decode(sig[3:])) == 3245 # same inputs through the SDK (bypassing the timestamp window by passing a huge tolerance)46 client = OpenAI(api_key="sk-offline-dummy")47 client.webhooks.verify_signature('{"a":1}', {"webhook-id": "wh_fixed", "webhook-timestamp": "1750287078", "webhook-signature": sig},48 secret=secret, tolerance=10**10)495051def test_tampered_body_rejected(signed):52 body, headers, _ = signed53 bad = body.replace("batch_t1", "batch_evil")54 with pytest.raises(InvalidWebhookSignatureError):55 OpenAI(api_key="sk-offline-dummy").webhooks.unwrap(bad, headers, secret=SECRET)56 with pytest.raises(InvalidSignature):57 verify(bad, headers, SECRET)585960def test_stale_timestamp_rejected(signed):61 body, headers, ts = signed62 old = ts - 30163 stale = dict(headers, **{"webhook-timestamp": str(old), "webhook-signature": sign(SECRET, "wh_t1", old, body)})64 with pytest.raises(InvalidWebhookSignatureError):65 OpenAI(api_key="sk-offline-dummy").webhooks.unwrap(body, stale, secret=SECRET)66 with pytest.raises(InvalidSignature):67 verify(body, stale, SECRET)68 assert verify(body, stale, SECRET, tolerance=600)["id"] == "evt_t1" # wider window accepts697071def test_multiple_signatures_rotation(signed):72 body, headers, ts = signed73 other = "whsec_" + base64.b64encode(b"rotated-secret-32-bytes-long!!!!").decode()74 headers = dict(headers, **{"webhook-signature": sign(other, "wh_t1", ts, body) + " " + headers["webhook-signature"]})75 assert verify(body, headers, SECRET)["id"] == "evt_t1"76 assert verify(body, headers, other)["id"] == "evt_t1"77 OpenAI(api_key="sk-offline-dummy").webhooks.unwrap(body, headers, secret=other)787980def test_missing_header_rejected(signed):81 body, headers, _ = signed82 with pytest.raises((InvalidWebhookSignatureError, ValueError, InvalidSignature, Exception)):83 OpenAI(api_key="sk-offline-dummy").webhooks.unwrap(body, {k: v for k, v in headers.items() if k != "webhook-signature"}, secret=SECRET)84