@@ -5,20 +5,23 @@ use actix_web::HttpResponseBuilder;
5
5
use awc:: http:: header:: InvalidHeaderValue ;
6
6
use rand:: random;
7
7
use std:: fmt:: { Display , Formatter } ;
8
+ use std:: sync:: Arc ;
9
+
10
+ use crate :: AppState ;
8
11
9
12
pub const DEFAULT_CONTENT_SECURITY_POLICY : & str = "script-src 'self' 'nonce-{NONCE}'" ;
10
13
11
14
#[ derive( Debug , Clone ) ]
12
15
pub struct ContentSecurityPolicy {
13
16
pub nonce : u64 ,
14
- policy : String ,
17
+ app_state : Arc < AppState > ,
15
18
}
16
19
17
20
impl ContentSecurityPolicy {
18
- pub fn new < S : Into < String > > ( policy : S ) -> Self {
21
+ pub fn new ( app_state : Arc < AppState > ) -> Self {
19
22
Self {
20
23
nonce : random ( ) ,
21
- policy : policy . into ( ) ,
24
+ app_state ,
22
25
}
23
26
}
24
27
@@ -28,21 +31,23 @@ impl ContentSecurityPolicy {
28
31
}
29
32
}
30
33
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
33
36
}
34
37
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 ( )
38
40
}
39
41
}
40
42
41
43
impl Display for ContentSecurityPolicy {
42
44
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
+ }
46
51
}
47
52
}
48
53
@@ -52,50 +57,7 @@ impl TryIntoHeaderPair for &ContentSecurityPolicy {
52
57
fn try_into_pair ( self ) -> Result < ( HeaderName , HeaderValue ) , Self :: Error > {
53
58
Ok ( (
54
59
CONTENT_SECURITY_POLICY ,
55
- HeaderValue :: from_str ( & self . to_string ( ) ) ?,
60
+ HeaderValue :: from_maybe_shared ( self . to_string ( ) ) ?,
56
61
) )
57
62
}
58
63
}
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