fix(s52): scrub every URL-bearing exception path in WebhookNotifier

This commit is contained in:
Kjell Tore Guttormsen 2026-07-17 03:15:57 +02:00
commit c48e2102d0
3 changed files with 38 additions and 8 deletions

View file

@ -18,6 +18,7 @@ from __future__ import annotations
import json
import sys
from collections.abc import Callable
from http.client import HTTPException
from pathlib import Path
from typing import TYPE_CHECKING, Any, Protocol, TextIO, runtime_checkable
from urllib.error import URLError
@ -126,10 +127,16 @@ class WebhookNotifier:
def __call__(self, verdict: Verdict) -> None:
try:
self._post(self._url, json.dumps(_verdict_payload(verdict), sort_keys=True))
except (URLError, OSError) as exc:
# STRICTER than ingest.py:314: the message NEVER carries the url — a Slack/Teams
# webhook URL embeds the receiver secret (the original cause stays chained).
raise NotifyError("webhook POST failed") from exc
except (URLError, OSError, ValueError, HTTPException) as exc:
# STRICTER than ingest.py:314: NO exception type may carry the url past this point —
# a Slack/Teams webhook URL embeds the receiver secret. The tuple covers every
# URL-carrier _urllib_post can raise (ValueError incl. UnicodeError; HTTPException
# incl. InvalidURL; OSError incl. URLError/HTTPError/TimeoutError). The chain is
# severed (`from None`) because __cause__ renders in tracebacks/logging.exception;
# the exception type name in the message is the diagnostic substitute for the lost
# chain. Deliberately NOT a bare `except Exception`: the transport is injectable,
# and a test transport's AssertionError/TypeError (programming errors) must surface.
raise NotifyError(f"webhook POST failed ({type(exc).__name__})") from None
# --- config + factory (fail-fast, egress never config-grantable) ----------------------------------