diff --git a/scripts/odysseus-webhook b/scripts/odysseus-webhook index 5c173b7..f3f162f 100755 --- a/scripts/odysseus-webhook +++ b/scripts/odysseus-webhook @@ -30,6 +30,17 @@ except ModuleNotFoundError as e: sys.exit(2) +def _mask_token(token: str, reveal: bool = False) -> str: + token = token or "" + if reveal: + return token + if not token: + return "" + if len(token) <= 10: + return "***" + return token[:6] + "…" + token[-4:] + + def _summary(t: "ScheduledTask", reveal: bool = False) -> dict: tok = t.webhook_token or "" return { @@ -37,7 +48,7 @@ def _summary(t: "ScheduledTask", reveal: bool = False) -> dict: "name": t.name, "status": t.status, "task_type": t.task_type, - "webhook_token": tok if reveal else (tok[:6] + "…" + tok[-4:]) if tok else "", + "webhook_token": _mask_token(tok, reveal), "has_token": bool(tok), } diff --git a/tests/test_webhook_cli_mask.py b/tests/test_webhook_cli_mask.py new file mode 100644 index 0000000..8dde3f3 --- /dev/null +++ b/tests/test_webhook_cli_mask.py @@ -0,0 +1,31 @@ +import importlib.machinery +import importlib.util +import sys +import types +from pathlib import Path +from unittest.mock import MagicMock + + +ROOT = Path(__file__).resolve().parents[1] + + +def _load_cli(monkeypatch): + db = types.ModuleType("core.database") + db.SessionLocal = MagicMock() + db.ScheduledTask = MagicMock() + monkeypatch.setitem(sys.modules, "core.database", db) + path = ROOT / "scripts" / "odysseus-webhook" + loader = importlib.machinery.SourceFileLoader("odysseus_webhook_cli", str(path)) + spec = importlib.util.spec_from_loader(loader.name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + return module + + +def test_mask_token_handles_short_values(monkeypatch): + cli = _load_cli(monkeypatch) + + assert cli._mask_token("") == "" + assert cli._mask_token("short") == "***" + assert cli._mask_token("abcdef1234567890") == "abcdef…7890" + assert cli._mask_token("short", reveal=True) == "short"