feat: harden notification stack and switch logging selectors to icon grid
Notifications: - Add shared http_base, redact, and SSRF hardening modules - Refactor dispatcher, queue, receiver and per-provider clients (telegram, discord, email, matrix, ntfy, slack, webhook) to use the shared base, with bounded queue and redacted error logs - Tests for ssrf, redact, http_base, queue bounds, dispatcher aggregation, telegram media partition, email and matrix clients Frontend: - Settings: log level / log format selectors now use IconGridSelect with per-option icons and i18n descriptions - Minor providers page and entity-cache store updates Tooling: - Document code-review-graph MCP usage in CLAUDE.md - Ignore .code-review-graph/, register .mcp.json
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
"""Secret-redaction helper regression tests.
|
||||
|
||||
Locks in the patterns that surface from real provider error paths:
|
||||
Telegram bot URLs in aiohttp.ClientError messages, Authorization Bearer
|
||||
tokens in Matrix/ntfy responses, Discord/Slack webhook tokens, URL
|
||||
userinfo, and common ?token= query params.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from notify_bridge_core.notifications.redact import redact, redact_exc
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"raw,expected_substr,not_in",
|
||||
[
|
||||
(
|
||||
"Cannot connect to host api.telegram.org/bot1234567:AABBCC-secret-token/sendMessage",
|
||||
"api.telegram.org/bot***",
|
||||
"AABBCC-secret-token",
|
||||
),
|
||||
(
|
||||
"Authorization: Bearer ey.JhbGciOiJIUzI1NiJ9.payload.sig",
|
||||
"Bearer ***",
|
||||
"ey.JhbGciOiJIUzI1NiJ9",
|
||||
),
|
||||
(
|
||||
"POST https://discord.com/api/webhooks/12345/abcdefg-token failed",
|
||||
"discord.com/api/webhooks/12345/***",
|
||||
"abcdefg-token",
|
||||
),
|
||||
(
|
||||
"POST https://hooks.slack.com/services/T01/B02/zzzzz failed",
|
||||
"hooks.slack.com/services/T01/B02/***",
|
||||
"zzzzz",
|
||||
),
|
||||
(
|
||||
"fetch http://user:supersecret@example.com/foo",
|
||||
"http://***@example.com/foo",
|
||||
"supersecret",
|
||||
),
|
||||
(
|
||||
"https://api.example.com/x?token=mytoken123&extra=ok",
|
||||
"token=***",
|
||||
"mytoken123",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_redact_known_secrets(raw: str, expected_substr: str, not_in: str) -> None:
|
||||
out = redact(raw)
|
||||
assert expected_substr in out
|
||||
assert not_in not in out
|
||||
|
||||
|
||||
def test_redact_idempotent() -> None:
|
||||
once = redact("Bearer abcdefghij1234567890")
|
||||
twice = redact(once)
|
||||
assert once == twice
|
||||
|
||||
|
||||
def test_redact_exc_returns_str() -> None:
|
||||
err = RuntimeError("Bearer abcdefghij1234567890")
|
||||
out = redact_exc(err)
|
||||
assert isinstance(out, str)
|
||||
assert "Bearer ***" in out
|
||||
assert "abcdefghij1234567890" not in out
|
||||
|
||||
|
||||
def test_redact_handles_non_string() -> None:
|
||||
# Coercion path should not raise.
|
||||
out = redact(12345) # type: ignore[arg-type]
|
||||
assert out == "12345"
|
||||
Reference in New Issue
Block a user