from __future__ import annotations import pytest from app.security import ( WebhookValidationError, parse_bounded_form, redact, token_matches, validate_webhook, ) def test_contact_webhook_contract(full_settings) -> None: event = validate_webhook( "contact", {"token": "contact-token-value-32-characters", "ID": "42"}, { "document_id[0]": "crm", "document_id[1]": "CCrmDocumentContact", "document_id[2]": "CONTACT_42", "auth[domain]": "portal.example", "auth[member_id]": "member_12345678", "auth[client_endpoint]": "https://attacker.invalid/rest/", }, "203.0.113.10", full_settings, alert_entity_type_id=None, ) assert event.entity_id == "42" def test_webhook_rejects_document_query_mismatch(full_settings) -> None: with pytest.raises(WebhookValidationError, match="mismatch"): validate_webhook( "contact", {"token": "contact-token-value-32-characters", "ID": "41"}, { "document_id[0]": "crm", "document_id[1]": "CCrmDocumentContact", "document_id[2]": "CONTACT_42", "auth[domain]": "portal.example", "auth[member_id]": "member_12345678", }, "203.0.113.10", full_settings, alert_entity_type_id=None, ) def test_bounded_form_and_constant_time_token_helpers() -> None: assert parse_bounded_form(b"a=1&b=2", max_fields=2) == {"a": "1", "b": "2"} with pytest.raises(WebhookValidationError): parse_bounded_form(b"a=1&b=2&c=3", max_fields=2) assert token_matches("old", "new", "old") assert not token_matches("other", "new", "old") def test_redaction_removes_pii_and_secrets() -> None: assert "secret-value" not in redact("token=secret-value") assert redact("user@example.test") == "[PII_REDACTED]" assert redact("+79001234567") == "[PII_REDACTED]"