SPB Git forge

spb/doc-api

Public
2commits 1branches 0releases
15.7 MBsize
maindefault branch
13 days agolast push
Python 88.3% TypeScript 7.6% Shell 4.1%
3.7 KB · 84 lines python
Raw Blame History
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