# tests/test_security_hardening.py — Regression tests for ROADMAP #84
# (BUG-021 → BUG-034): sanitizer, path isolation, password policy, regex
# safety, webhook SSRF, rate limiting, audit IP and share-page escaping.
import time
from pathlib import Path
import pytest
from backend.services.errors import ServiceError
# ═══════════════════════════════════════════════════════════════════
# BUG-021/022 — HTML sanitizer
# ═══════════════════════════════════════════════════════════════════
class TestSanitizer:
def test_strips_event_handlers(self):
from backend.services.sanitizer import sanitize_html
out = sanitize_html('')
assert "onerror" not in out
assert "alert" not in out
def test_drops_script_content(self):
from backend.services.sanitizer import sanitize_html
out = sanitize_html("
ok
tail") assert "">') assert "data:text/html" not in bad def test_heading_ids_preserved(self): from backend.services.sanitizer import sanitize_html out = sanitize_html('x
') assert "style" not in out def test_is_safe_url(self): from backend.services.sanitizer import is_safe_url assert is_safe_url("/relative") assert is_safe_url("https://example.com") assert is_safe_url("#anchor") assert not is_safe_url("javascript:alert(1)") assert not is_safe_url("vbscript:msgbox") assert not is_safe_url("data:text/html,\n\n" "Normal **text**.\n" ) (Path(test_vault_dir) / "xss.md").write_text(payload, encoding="utf-8") resp = client.get("/api/file/TestVault", params={"path": "xss.md"}) assert resp.status_code == 200 html = resp.json()["html"] assert "onerror" not in html assert ""\n' 'author: "