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

1"""Security headers, HSTS, and cross-origin configuration.""" 

2 

3from __future__ import annotations 

4 

5from typing import ClassVar 

6 

7from lexigram.config.base import BaseConfig 

8from lexigram.contracts.core.config import ConfigIssue, Environment 

9from lexigram.validation import ConfigDict, Field 

10 

11 

12class SecurityHeadersConfig(BaseConfig): 

13 """Configuration for security response headers. 

14 

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 """ 

25 

26 model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore") 

27 

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) 

36 

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] = [] 

43 

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 ) 

54 

55 return issues 

56 

57 

58class HSTSConfig(BaseConfig): 

59 """HTTP Strict Transport Security configuration. 

60 

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 """ 

67 

68 model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore") 

69 

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 ) 

82 

83 

84class CrossOriginConfig(BaseConfig): 

85 """Cross-Origin policy configuration. 

86 

87 Controls whether and with what values the three cross-origin 

88 isolation headers are sent: 

89 

90 * ``Cross-Origin-Embedder-Policy`` 

91 * ``Cross-Origin-Opener-Policy`` 

92 * ``Cross-Origin-Resource-Policy`` 

93 

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 """ 

100 

101 model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore") 

102 

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 ) 

119 

120 

121__all__ = [ 

122 "CrossOriginConfig", 

123 "HSTSConfig", 

124 "SecurityHeadersConfig", 

125]