@@ -5,20 +5,23 @@ use actix_web::HttpResponseBuilder;
55use awc:: http:: header:: InvalidHeaderValue ;
66use rand:: random;
77use std:: fmt:: { Display , Formatter } ;
8+ use std:: sync:: Arc ;
9+
10+ use crate :: AppState ;
811
912pub const DEFAULT_CONTENT_SECURITY_POLICY : & str = "script-src 'self' 'nonce-{NONCE}'" ;
1013
1114#[ derive( Debug , Clone ) ]
1215pub struct ContentSecurityPolicy {
1316 pub nonce : u64 ,
14- policy : String ,
17+ app_state : Arc < AppState > ,
1518}
1619
1720impl ContentSecurityPolicy {
18- pub fn new < S : Into < String > > ( policy : S ) -> Self {
21+ pub fn new ( app_state : Arc < AppState > ) -> Self {
1922 Self {
2023 nonce : random ( ) ,
21- policy : policy . into ( ) ,
24+ app_state ,
2225 }
2326 }
2427
@@ -28,21 +31,23 @@ impl ContentSecurityPolicy {
2831 }
2932 }
3033
31- fn is_enabled ( & self ) -> bool {
32- ! self . policy . is_empty ( )
34+ fn template_string ( & self ) -> & str {
35+ & self . app_state . config . content_security_policy
3336 }
3437
35- #[ allow( dead_code) ]
36- fn set_nonce ( & mut self , nonce : u64 ) {
37- self . nonce = nonce;
38+ fn is_enabled ( & self ) -> bool {
39+ !self . app_state . config . content_security_policy . is_empty ( )
3840 }
3941}
4042
4143impl Display for ContentSecurityPolicy {
4244 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
43- let value = self . policy . replace ( "{NONCE}" , & self . nonce . to_string ( ) ) ;
44-
45- write ! ( f, "{value}" )
45+ let template = self . template_string ( ) ;
46+ if let Some ( ( before, after) ) = template. split_once ( "{NONCE}" ) {
47+ write ! ( f, "{before}{nonce}{after}" , nonce = self . nonce)
48+ } else {
49+ write ! ( f, "{}" , template)
50+ }
4651 }
4752}
4853
@@ -52,50 +57,7 @@ impl TryIntoHeaderPair for &ContentSecurityPolicy {
5257 fn try_into_pair ( self ) -> Result < ( HeaderName , HeaderValue ) , Self :: Error > {
5358 Ok ( (
5459 CONTENT_SECURITY_POLICY ,
55- HeaderValue :: from_str ( & self . to_string ( ) ) ?,
60+ HeaderValue :: from_maybe_shared ( self . to_string ( ) ) ?,
5661 ) )
5762 }
5863}
59-
60- #[ cfg( test) ]
61- mod tests {
62- use super :: * ;
63-
64- #[ test]
65- fn default_csp_response_contains_random_nonce ( ) {
66- let mut csp = ContentSecurityPolicy :: new ( DEFAULT_CONTENT_SECURITY_POLICY ) ;
67- csp. set_nonce ( 0 ) ;
68-
69- assert ! ( csp. is_enabled( ) ) ;
70- assert_eq ! ( & csp. to_string( ) , "script-src 'self' 'nonce-0'" ) ;
71- }
72-
73- #[ test]
74- fn custom_csp_response_without_nonce ( ) {
75- let csp = ContentSecurityPolicy :: new ( "object-src 'none';" ) ;
76-
77- assert ! ( csp. is_enabled( ) ) ;
78- assert_eq ! ( "object-src 'none';" , & csp. to_string( ) ) ;
79- }
80-
81- #[ test]
82- fn blank_csp_response ( ) {
83- let csp = ContentSecurityPolicy :: new ( "" ) ;
84-
85- assert ! ( !csp. is_enabled( ) ) ;
86- assert_eq ! ( "" , & csp. to_string( ) ) ;
87- }
88-
89- #[ test]
90- fn custom_csp_with_nonce ( ) {
91- let mut csp =
92- ContentSecurityPolicy :: new ( "script-src 'self' 'nonce-{NONCE}'; object-src 'none';" ) ;
93- csp. set_nonce ( 0 ) ;
94-
95- assert ! ( csp. is_enabled( ) ) ;
96- assert_eq ! (
97- "script-src 'self' 'nonce-0'; object-src 'none';" ,
98- csp. to_string( ) . as_str( )
99- ) ;
100- }
101- }
0 commit comments