@@ -5,7 +5,7 @@ use std::result::Result;
55use std:: sync:: Arc ;
66use std:: task:: { Context , Poll } ;
77
8- use http:: { Method , Request , Response } ;
8+ use http:: { Method , Request , Response , Uri } ;
99use tower:: { BoxError , Layer , Service } ;
1010use url:: Url ;
1111
@@ -33,6 +33,30 @@ pub enum ProtectionError {
3333 SecFetchSiteUnexpectedValue ( String ) ,
3434}
3535
36+ struct Bypass < T : Fn ( & Method , & Uri ) -> bool > ( T ) ;
37+
38+ impl < T : Fn ( & Method , & Uri ) -> bool > std:: fmt:: Debug for Bypass < T > {
39+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
40+ f. debug_struct ( "<fn>" ) . finish ( )
41+ }
42+ }
43+
44+ trait Filter : std:: fmt:: Debug + Send + Sync {
45+ fn is_bypassed ( & self , method : & Method , uri : & Uri ) -> bool ;
46+ }
47+
48+ impl < T : Fn ( & Method , & Uri ) -> bool > Filter for Option < Bypass < T > >
49+ where
50+ T : Send + Sync ,
51+ {
52+ fn is_bypassed ( & self , method : & Method , uri : & Uri ) -> bool {
53+ match self {
54+ Some ( ref p) => p. 0 ( method, uri) ,
55+ None => false ,
56+ }
57+ }
58+ }
59+
3660#[ derive( Clone , Debug , Default ) ]
3761struct Origins ( Arc < HashSet < String > > ) ;
3862
@@ -46,11 +70,21 @@ impl Origins {
4670 }
4771}
4872
49- #[ derive( Clone , Debug , Default ) ]
73+ #[ derive( Clone , Debug ) ]
5074pub struct CrossOriginProtectionLayer {
75+ insecure_bypass : Arc < dyn Filter > ,
5176 trusted_origins : Origins ,
5277}
5378
79+ impl Default for CrossOriginProtectionLayer {
80+ fn default ( ) -> Self {
81+ CrossOriginProtectionLayer {
82+ insecure_bypass : Arc :: new ( Option :: < Bypass < fn ( & Method , & Uri ) -> bool > > :: default ( ) ) ,
83+ trusted_origins : Origins :: default ( ) ,
84+ }
85+ }
86+ }
87+
5488impl CrossOriginProtectionLayer {
5589 pub fn add_trusted_origin < S : Into < String > > ( mut self , origin : S ) -> Result < Self , ConfigError > {
5690 let origin = origin. into ( ) ;
@@ -66,27 +100,47 @@ impl CrossOriginProtectionLayer {
66100
67101 Ok ( self )
68102 }
103+
104+ pub fn with_insecure_bypass < F > ( self , predicate : F ) -> CrossOriginProtectionLayer
105+ where
106+ F : Fn ( & Method , & Uri ) -> bool + Send + Sync + ' static ,
107+ {
108+ CrossOriginProtectionLayer {
109+ insecure_bypass : Arc :: new ( Some ( Bypass ( predicate) ) ) ,
110+ trusted_origins : self . trusted_origins ,
111+ }
112+ }
69113}
70114
71115impl < S > Layer < S > for CrossOriginProtectionLayer {
72116 type Service = CrossOriginProtectionMiddleware < S > ;
73117
74118 fn layer ( & self , inner : S ) -> Self :: Service {
75- let trusted_origins = self . trusted_origins . clone ( ) ;
76-
77119 CrossOriginProtectionMiddleware {
78120 inner,
79- trusted_origins,
121+ insecure_bypass : self . insecure_bypass . clone ( ) ,
122+ trusted_origins : self . trusted_origins . clone ( ) ,
80123 }
81124 }
82125}
83126
84- #[ derive( Clone , Debug , Default ) ]
127+ #[ derive( Clone , Debug ) ]
85128pub struct CrossOriginProtectionMiddleware < S > {
86129 inner : S ,
130+ insecure_bypass : Arc < dyn Filter > ,
87131 trusted_origins : Origins ,
88132}
89133
134+ impl < S : Default > Default for CrossOriginProtectionMiddleware < S > {
135+ fn default ( ) -> Self {
136+ Self {
137+ inner : S :: default ( ) ,
138+ insecure_bypass : Arc :: new ( Option :: < Bypass < fn ( & Method , & Uri ) -> bool > > :: default ( ) ) ,
139+ trusted_origins : Origins :: default ( ) ,
140+ }
141+ }
142+ }
143+
90144impl < S , ReqBody , ResBody > Service < Request < ReqBody > > for CrossOriginProtectionMiddleware < S >
91145where
92146 S : Service < Request < ReqBody > , Response = Response < ResBody > > + Clone + Send + ' static ,
@@ -116,6 +170,10 @@ where
116170
117171impl < S > CrossOriginProtectionMiddleware < S > {
118172 fn verify < Body > ( & self , req : & Request < Body > ) -> Result < ( ) , ProtectionError > {
173+ if self . insecure_bypass . is_bypassed ( req. method ( ) , req. uri ( ) ) {
174+ return Ok ( ( ) ) ;
175+ }
176+
119177 if matches ! ( * req. method( ) , Method :: GET | Method :: HEAD | Method :: OPTIONS ) {
120178 return Ok ( ( ) ) ;
121179 }
@@ -172,6 +230,27 @@ mod tests {
172230 use super :: * ;
173231 use http:: Request ;
174232
233+ #[ test]
234+ fn test_middleware_debug_trait ( ) {
235+ let layer = CrossOriginProtectionLayer :: default ( ) ;
236+ let middleware = layer
237+ . clone ( )
238+ . with_insecure_bypass ( |method, uri| method == Method :: POST && uri. path ( ) == "/bypass" )
239+ . layer ( ( ) ) ;
240+
241+ assert_eq ! (
242+ format!( "{:?}" , middleware) ,
243+ "CrossOriginProtectionMiddleware { inner: (), insecure_bypass: Some(<fn>), trusted_origins: Origins({}) }"
244+ ) ;
245+
246+ let middleware = layer. layer ( ( ) ) ;
247+
248+ assert_eq ! (
249+ format!( "{:?}" , middleware) ,
250+ "CrossOriginProtectionMiddleware { inner: (), insecure_bypass: None, trusted_origins: Origins({}) }"
251+ ) ;
252+ }
253+
175254 #[ test]
176255 fn test_add_trusted_origin ( ) {
177256 assert ! ( matches!(
@@ -306,6 +385,24 @@ mod tests {
306385 assert ! ( middleware. verify( & request) . is_err( ) ) ;
307386 }
308387
388+ #[ test]
389+ fn test_origin_mismatch_host_bypassed ( ) {
390+ let layer = CrossOriginProtectionLayer :: default ( ) ;
391+ let middleware = layer
392+ . with_insecure_bypass ( |method, uri| method == Method :: POST && uri. path ( ) == "/bypass" )
393+ . layer ( ( ) ) ;
394+
395+ let request = Request :: builder ( )
396+ . method ( "POST" )
397+ . uri ( "/bypass" )
398+ . header ( "origin" , "https://evil.com" )
399+ . header ( "host" , "example.com" )
400+ . body ( ( ) )
401+ . unwrap ( ) ;
402+
403+ assert ! ( middleware. verify( & request) . is_ok( ) ) ;
404+ }
405+
309406 #[ test]
310407 fn test_no_origin_no_sec_fetch_site_allowed ( ) {
311408 let middleware: CrossOriginProtectionMiddleware < ( ) > = Default :: default ( ) ;
0 commit comments