Coverage for src / lexigram / contracts / security / protocols.py: 0%
37 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""Security protocol definitions for the Lexigram Framework.
3Protocols for guard chains, input sanitization, and security header management.
4All security implementations must satisfy these structural interfaces.
5"""
7from __future__ import annotations
9from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
11if TYPE_CHECKING:
12 from lexigram.contracts.web.guard import GuardProtocol
15@runtime_checkable
16class HasherProtocol(Protocol):
17 """General-purpose hashing protocol (non-password-specific)."""
19 @property
20 def algorithm(self) -> str:
21 """Hash algorithm name (for example ``sha256`` or ``blake2b``)."""
22 ...
24 def digest(self, data: str | bytes) -> str:
25 """Hash input data and return an encoded digest.
27 Args:
28 data: Input string or bytes to hash.
30 Returns:
31 Encoded hash string.
32 """
33 ...
35 def verify_digest(self, data: str | bytes, expected: str) -> bool:
36 """Constant-time verification against an expected digest.
38 Args:
39 data: Input string or bytes to hash.
40 expected: Expected encoded digest.
42 Returns:
43 True when the digest matches, False otherwise.
44 """
45 ...
47 async def hash(self, value: str) -> str:
48 """Backward-compatible async alias for :meth:`digest`."""
49 ...
51 async def verify(self, value: str, hashed_value: str) -> bool:
52 """Backward-compatible async alias for :meth:`verify_digest`."""
53 ...
56@runtime_checkable
57class KeyDerivationProtocol(Protocol):
58 """Protocol for key derivation services."""
60 async def derive(self, secret: str, *, salt: bytes | None = None) -> str:
61 """Derive an encoded key from a secret.
63 Args:
64 secret: Input secret to derive from.
65 salt: Optional salt. When omitted, implementations generate one.
67 Returns:
68 Stable encoded key derivation string.
69 """
70 ...
72 async def verify(self, secret: str, encoded: str) -> bool:
73 """Verify a secret against a derived key payload.
75 Args:
76 secret: Input secret to verify.
77 encoded: Stable encoded key derivation string.
79 Returns:
80 True when the secret matches the encoded payload, False otherwise.
81 """
82 ...
84 async def hash(self, secret: str, *, salt: bytes | None = None) -> str:
85 """Backward-compatible async alias for :meth:`derive`."""
86 ...
89@runtime_checkable
90class GuardChainProtocol(Protocol):
91 """Executes a sequence of guards; short-circuits on first denial.
93 Implementations must raise ``GuardDeniedError`` from
94 ``lexigram.contracts.exceptions.security`` when any guard denies.
95 """
97 def add(self, guard: GuardProtocol) -> GuardChainProtocol:
98 """Add a guard to the chain.
100 Args:
101 guard: The guard to append.
103 Returns:
104 Self, for fluent chaining.
105 """
106 ...
108 async def execute(self, context: dict[str, Any]) -> None:
109 """Execute all guards in order, raising on first denial.
111 Args:
112 context: Arbitrary request context forwarded to each guard.
114 Raises:
115 GuardDeniedError: If any guard returns False from ``can_activate``.
116 """
117 ...
120@runtime_checkable
121class InputSanitizerProtocol(Protocol):
122 """Sanitizes raw input strings against injection vectors.
124 Implementations should strip XSS payloads, HTML entities, and
125 dangerous character sequences without raising on clean input.
126 """
128 def sanitize(self, value: str) -> str:
129 """Sanitize a single string value.
131 Args:
132 value: Raw input string.
134 Returns:
135 The sanitized string.
136 """
137 ...
139 def sanitize_dict(self, data: dict[str, Any]) -> dict[str, Any]:
140 """Recursively sanitize all string values in a mapping.
142 Args:
143 data: Dictionary whose string leaf values will be sanitized.
145 Returns:
146 A new dictionary with all string values sanitized.
147 """
148 ...
150 def sanitize_header_value(self, value: str) -> str:
151 """Strip CRLF characters from an HTTP header value to prevent header injection.
153 Args:
154 value: Raw header value string.
156 Returns:
157 The value with CR and LF characters removed or replaced.
158 """
159 ...
161 def is_safe_url_for_request(self, url: str) -> bool:
162 """Return False if the URL targets a private or reserved IP range (SSRF guard).
164 Args:
165 url: Fully-qualified URL to evaluate.
167 Returns:
168 True if the URL is considered safe to request, False otherwise.
169 """
170 ...
173@runtime_checkable
174class SecurityHeadersProtocol(Protocol):
175 """Applies security-related HTTP response headers.
177 Implementations must support HSTS, X-Frame-Options, CSP, and
178 X-Content-Type-Options at minimum.
179 """
181 def apply(self, headers: dict[str, str]) -> dict[str, str]:
182 """Apply security headers to an existing headers mapping.
184 Args:
185 headers: Mutable mapping of header names to values.
187 Returns:
188 The headers mapping with security headers merged in.
189 """
190 ...
193@runtime_checkable
194class EncryptionProtocol(Protocol):
195 """Protocol for symmetric/asymmetric encryption providers."""
197 async def encrypt(self, plaintext: bytes, key_id: str | None = None) -> bytes: ...
198 async def decrypt(self, ciphertext: bytes, key_id: str | None = None) -> bytes: ...
199 async def rotate_key(self, key_id: str) -> str: ...
202@runtime_checkable
203class CORSProtocol(Protocol):
204 """Protocol for Cross-Origin Resource Sharing policy enforcement."""
206 def is_allowed_origin(self, origin: str) -> bool: ...
207 def get_allowed_headers(self) -> list[str]: ...
208 def get_allowed_methods(self) -> list[str]: ...
209 def get_max_age(self) -> int: ...
212@runtime_checkable
213class CSPProtocol(Protocol):
214 """Protocol for Content Security Policy header generation."""
216 def build_header(self) -> str: ...
217 def add_directive(self, directive: str, value: str) -> None: ...
220@runtime_checkable
221class CSRFProtocol(Protocol):
222 """Protocol for CSRF token generation and validation."""
224 def generate_token(self, session_id: str) -> str: ...
225 def validate_token(self, session_id: str, token: str) -> bool: ...
226 def invalidate_token(self, session_id: str) -> None: ...
229__all__ = [
230 "CORSProtocol",
231 "CSPProtocol",
232 "CSRFProtocol",
233 "EncryptionProtocol",
234 "GuardChainProtocol",
235 "HasherProtocol",
236 "InputSanitizerProtocol",
237 "KeyDerivationProtocol",
238 "SecurityHeadersProtocol",
239]