feat(s52): webhook errors never leak the secret-bearing URL
This commit is contained in:
parent
61c4cb2d26
commit
c782c3402c
2 changed files with 36 additions and 2 deletions
|
|
@ -16,7 +16,7 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from portfolio_optimiser.notify import NotifyRefused, WebhookNotifier
|
||||
from portfolio_optimiser.notify import NotifyError, NotifyRefused, WebhookNotifier
|
||||
from portfolio_optimiser.verdicts import ProposalFeatures, Verdict, capture_verdict
|
||||
|
||||
_SRC_DIR = Path(__file__).resolve().parents[1] / "src" / "portfolio_optimiser"
|
||||
|
|
@ -57,6 +57,35 @@ def test_webhook_gate_refuses_without_optin_and_posts_with_optin() -> None:
|
|||
assert len(posts) == 1
|
||||
|
||||
|
||||
# --- LOAD-BEARING: transport failures never leak the secret-bearing URL ---------------------------
|
||||
|
||||
|
||||
def test_webhook_error_never_leaks_url() -> None:
|
||||
"""LOAD-BEARING (secret discipline): a Slack/Teams webhook URL embeds the receiver secret, so
|
||||
the wrapped ``NotifyError`` must NOT contain it (STRICTER than ``ingest.py:314``, which may
|
||||
include its url). Positive control: the url IS delivered to a successful transport — so the
|
||||
negative assertion has teeth. Detach (put the url in the error message) → RED."""
|
||||
url = "https://hooks.example.test/T000/B000/secret-token"
|
||||
|
||||
def failing_post(post_url: str, body: str) -> None:
|
||||
raise OSError("connection refused")
|
||||
|
||||
notifier = WebhookNotifier(url, allow_egress=True, post=failing_post)
|
||||
with pytest.raises(NotifyError) as excinfo:
|
||||
notifier(_verdict())
|
||||
assert url not in str(excinfo.value)
|
||||
assert url not in repr(excinfo.value)
|
||||
|
||||
# Positive control: the same url reaches a working transport verbatim.
|
||||
posts: list[tuple[str, str]] = []
|
||||
|
||||
def recording_post(post_url: str, body: str) -> None:
|
||||
posts.append((post_url, body))
|
||||
|
||||
WebhookNotifier(url, allow_egress=True, post=recording_post)(_verdict())
|
||||
assert posts[0][0] == url
|
||||
|
||||
|
||||
def test_notify_registered_maf_free() -> None:
|
||||
"""Meta: notify.py is registered in the MAF-free guard list, so ``test_okf_is_maf_free``
|
||||
actually scans it — otherwise the MAF-free claim would be green-but-dead (never checked)."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue