feat(webhooks): non-blocking http:// warning + WH-03 accepted risk
WebhookResponse now carries a `warnings: list[str]` field. When the subscription's URL starts with http://, an `insecure_url` advisory is surfaced on every GET/CREATE without blocking the request. HMAC still detects tampering regardless of transport — only read-confidentiality is lost over plaintext — and test/dev environments without TLS stay usable. Matches the operator-trust posture already established by DA-06 (admin-on-admin protection is out of scope). The alternative — hard rejection at admin time — was considered and declined; warning-plus- visibility is the right shape. THREAT_MODEL WH-03 accepted risk registered; revisit triggers are multi-admin delegation, a regulated customer, or an operator ticket asking for a DECNET_WEBHOOK_REQUIRE_HTTPS enforcement knob.
This commit is contained in:
@@ -173,6 +173,43 @@ async def test_delete_returns_message(
|
||||
assert res2.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_http_url_warns_but_accepts(
|
||||
client: httpx.AsyncClient, auth_token: str
|
||||
):
|
||||
"""Plain http:// is allowed (operator-trust posture per WH-03) but
|
||||
surfaces a non-blocking advisory in the response's warnings list."""
|
||||
res = await client.post(
|
||||
PATH,
|
||||
json={
|
||||
"name": "wh-http",
|
||||
"url": "http://insecure.local/inbound",
|
||||
"topic_patterns": ["system.>"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {auth_token}"},
|
||||
)
|
||||
assert res.status_code == 201, res.text
|
||||
body = res.json()
|
||||
assert any("insecure_url" in w for w in body["warnings"])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_https_url_has_no_warning(
|
||||
client: httpx.AsyncClient, auth_token: str
|
||||
):
|
||||
res = await client.post(
|
||||
PATH,
|
||||
json={
|
||||
"name": "wh-https",
|
||||
"url": "https://secure.example/inbound",
|
||||
"topic_patterns": ["system.>"],
|
||||
},
|
||||
headers={"Authorization": f"Bearer {auth_token}"},
|
||||
)
|
||||
assert res.status_code == 201
|
||||
assert res.json()["warnings"] == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_viewer_forbidden(client: httpx.AsyncClient, viewer_token: str):
|
||||
res = await client.get(
|
||||
|
||||
Reference in New Issue
Block a user