11use actix_web:: http:: header:: {
22 HeaderName , HeaderValue , TryIntoHeaderPair , CONTENT_SECURITY_POLICY ,
33} ;
4+ use actix_web:: HttpResponseBuilder ;
45use awc:: http:: header:: InvalidHeaderValue ;
56use rand:: random;
6- use serde:: Deserialize ;
77use std:: fmt:: { Display , Formatter } ;
88
9- #[ derive( Debug , Deserialize , Clone , PartialEq ) ]
10- #[ serde( from = "String" ) ]
9+ pub const DEFAULT_CONTENT_SECURITY_POLICY : & str = "script-src 'self' 'nonce-{NONCE}'" ;
10+
11+ #[ derive( Debug , Clone ) ]
1112pub struct ContentSecurityPolicy {
1213 pub nonce : u64 ,
13- value : String ,
14+ policy : String ,
1415}
1516
1617impl ContentSecurityPolicy {
17- #[ must_use]
18- pub fn is_enabled ( & self ) -> bool {
19- !self . value . is_empty ( )
20- }
21-
22- fn new < S : Into < String > > ( value : S ) -> Self {
18+ pub fn new < S : Into < String > > ( policy : S ) -> Self {
2319 Self {
2420 nonce : random ( ) ,
25- value : value . into ( ) ,
21+ policy : policy . into ( ) ,
2622 }
2723 }
2824
25+ pub fn apply_to_response ( & self , response : & mut HttpResponseBuilder ) {
26+ if self . is_enabled ( ) {
27+ response. insert_header ( self ) ;
28+ }
29+ }
30+
31+ fn is_enabled ( & self ) -> bool {
32+ !self . policy . is_empty ( )
33+ }
34+
2935 #[ allow( dead_code) ]
3036 fn set_nonce ( & mut self , nonce : u64 ) {
3137 self . nonce = nonce;
3238 }
3339}
3440
35- impl Default for ContentSecurityPolicy {
36- fn default ( ) -> Self {
37- Self :: new ( "script-src 'self' 'nonce-{NONCE}'" )
38- }
39- }
40-
4141impl Display for ContentSecurityPolicy {
4242 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
43- let value = self
44- . value
45- . replace ( "{NONCE}" , self . nonce . to_string ( ) . as_str ( ) ) ;
43+ let value = self . policy . replace ( "{NONCE}" , & self . nonce . to_string ( ) ) ;
4644
4745 write ! ( f, "{value}" )
4846 }
4947}
5048
51- impl From < String > for ContentSecurityPolicy {
52- fn from ( input : String ) -> Self {
53- ContentSecurityPolicy :: new ( input)
54- }
55- }
56-
5749impl TryIntoHeaderPair for & ContentSecurityPolicy {
5850 type Error = InvalidHeaderValue ;
5951
@@ -70,38 +62,40 @@ mod tests {
7062 use super :: * ;
7163
7264 #[ test]
73- fn default_csp_contains_random_nonce ( ) {
74- let mut csp = ContentSecurityPolicy :: default ( ) ;
65+ fn default_csp_response_contains_random_nonce ( ) {
66+ let mut csp = ContentSecurityPolicy :: new ( DEFAULT_CONTENT_SECURITY_POLICY ) ;
7567 csp. set_nonce ( 0 ) ;
7668
77- assert_eq ! ( csp. to_string( ) . as_str( ) , "script-src 'self' 'nonce-0'" ) ;
7869 assert ! ( csp. is_enabled( ) ) ;
70+ assert_eq ! ( & csp. to_string( ) , "script-src 'self' 'nonce-0'" ) ;
7971 }
8072
8173 #[ test]
82- fn custom_csp_without_nonce ( ) {
83- let csp: ContentSecurityPolicy = String :: from ( "object-src 'none';" ) . into ( ) ;
84- assert_eq ! ( "object-src 'none';" , csp . to_string ( ) . as_str ( ) ) ;
74+ fn custom_csp_response_without_nonce ( ) {
75+ let csp = ContentSecurityPolicy :: new ( "object-src 'none';" ) ;
76+
8577 assert ! ( csp. is_enabled( ) ) ;
78+ assert_eq ! ( "object-src 'none';" , & csp. to_string( ) ) ;
8679 }
8780
8881 #[ test]
89- fn blank_csp ( ) {
90- let csp: ContentSecurityPolicy = String :: from ( "" ) . into ( ) ;
91- assert_eq ! ( "" , csp . to_string ( ) . as_str ( ) ) ;
82+ fn blank_csp_response ( ) {
83+ let csp = ContentSecurityPolicy :: new ( "" ) ;
84+
9285 assert ! ( !csp. is_enabled( ) ) ;
86+ assert_eq ! ( "" , & csp. to_string( ) ) ;
9387 }
9488
9589 #[ test]
9690 fn custom_csp_with_nonce ( ) {
97- let mut csp: ContentSecurityPolicy =
98- String :: from ( "script-src 'self' 'nonce-{NONCE}'; object-src 'none';" ) . into ( ) ;
91+ let mut csp =
92+ ContentSecurityPolicy :: new ( "script-src 'self' 'nonce-{NONCE}'; object-src 'none';" ) ;
9993 csp. set_nonce ( 0 ) ;
10094
95+ assert ! ( csp. is_enabled( ) ) ;
10196 assert_eq ! (
10297 "script-src 'self' 'nonce-0'; object-src 'none';" ,
10398 csp. to_string( ) . as_str( )
10499 ) ;
105- assert ! ( csp. is_enabled( ) ) ;
106100 }
107101}
0 commit comments