Coverage for src/lexigram/web/security/config/headers.py: 83%
35 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""Security headers, HSTS, and cross-origin configuration."""
3from __future__ import annotations
5from typing import ClassVar
7from lexigram.config.base import BaseConfig
8from lexigram.contracts.core.config import ConfigIssue, Environment
9from lexigram.validation import ConfigDict, Field
12class SecurityHeadersConfig(BaseConfig):
13 """Configuration for security response headers.
15 Attributes:
16 hsts_max_age: Strict-Transport-Security max age in seconds.
17 hsts_include_subdomains: Whether HSTS applies to subdomains.
18 content_type_nosniff: Sets X-Content-Type-Options to 'nosniff'.
19 frame_options: X-Frame-Options value ('DENY', 'SAMEORIGIN').
20 xss_protection: X-XSS-Protection value.
21 referrer_policy: Referrer-Policy value.
22 csp: Content-Security-Policy header string.
23 permissions_policy: Permissions-Policy header string.
24 """
26 model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
28 hsts_max_age: int = Field(default=31536000)
29 hsts_include_subdomains: bool = Field(default=True)
30 content_type_nosniff: bool = Field(default=True)
31 frame_options: str = Field(default="DENY")
32 xss_protection: str = Field(default="1; mode=block")
33 referrer_policy: str = Field(default="strict-origin-when-cross-origin")
34 csp: str | None = Field(default=None)
35 permissions_policy: str | None = Field(default=None)
37 def validate_for_environment(
38 self, env: Environment | None = None
39 ) -> list[ConfigIssue]:
40 """Validate security headers for production."""
41 resolved = env or self.environment
42 issues: list[ConfigIssue] = []
44 if resolved == Environment.PRODUCTION:
45 if self.hsts_max_age < 31536000:
46 issues.append(
47 ConfigIssue(
48 field="headers.hsts_max_age",
49 message="HSTS max_age should be at least 1 year (31536000 seconds) in production",
50 severity="warning",
51 suggestion="Increase hsts_max_age to 31536000 or higher",
52 )
53 )
55 return issues
58class HSTSConfig(BaseConfig):
59 """HTTP Strict Transport Security configuration.
61 Attributes:
62 enabled: Emit the ``Strict-Transport-Security`` header.
63 max_age: ``max-age`` directive in seconds.
64 include_subdomains: Append the ``includeSubDomains`` directive.
65 preload: Append the ``preload`` directive.
66 """
68 model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
70 enabled: bool = Field(
71 default=False, description="Emit the Strict-Transport-Security header"
72 )
73 max_age: int = Field(
74 default=31536000, description="HSTS max-age in seconds (default 1 year)"
75 )
76 include_subdomains: bool = Field(
77 default=True, description="Apply HSTS to all subdomains"
78 )
79 preload: bool = Field(
80 default=False, description="Include site in HSTS preload list"
81 )
84class CrossOriginConfig(BaseConfig):
85 """Cross-Origin policy configuration.
87 Controls whether and with what values the three cross-origin
88 isolation headers are sent:
90 * ``Cross-Origin-Embedder-Policy``
91 * ``Cross-Origin-Opener-Policy``
92 * ``Cross-Origin-Resource-Policy``
94 Attributes:
95 enabled: Emit the cross-origin isolation headers.
96 embedder_policy: ``COEP`` value (e.g. ``'require-corp'``).
97 opener_policy: ``COOP`` value (e.g. ``'same-origin'``).
98 resource_policy: ``CORP`` value (e.g. ``'same-origin'``).
99 """
101 model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
103 enabled: bool = Field(
104 default=False,
105 description="Emit cross-origin isolation headers",
106 )
107 embedder_policy: str = Field(
108 default="require-corp",
109 description="Cross-Origin-Embedder-Policy header value",
110 )
111 opener_policy: str = Field(
112 default="same-origin",
113 description="Cross-Origin-Opener-Policy header value",
114 )
115 resource_policy: str = Field(
116 default="same-origin",
117 description="Cross-Origin-Resource-Policy header value",
118 )
121__all__ = [
122 "CrossOriginConfig",
123 "HSTSConfig",
124 "SecurityHeadersConfig",
125]