@@ -187,6 +187,9 @@ impl HostConfig {
187187 bail ! ( "Invalid allowed host {host}: wildcards are allowed only as subdomains" ) ;
188188 }
189189
190+ // Remove trailing slashes
191+ host = host. trim_end_matches ( '/' ) ;
192+
190193 Ok ( Self :: List ( vec ! [ host. into( ) ] ) )
191194 }
192195
@@ -502,6 +505,37 @@ mod test {
502505 use super :: * ;
503506 use std:: net:: { Ipv4Addr , Ipv6Addr } ;
504507
508+ #[ test]
509+ fn test_allowed_hosts_accepts_url_without_port ( ) {
510+ assert_eq ! (
511+ AllowedHostConfig :: new(
512+ SchemeConfig :: new( "http" ) ,
513+ HostConfig :: new( "spin.fermyon.dev" ) ,
514+ PortConfig :: new( 80 )
515+ ) ,
516+ AllowedHostConfig :: parse( "http://spin.fermyon.dev" ) . unwrap( )
517+ ) ;
518+
519+ assert_eq ! (
520+ AllowedHostConfig :: new(
521+ SchemeConfig :: new( "http" ) ,
522+ // Trailing slash is removed
523+ HostConfig :: new( "spin.fermyon.dev" ) ,
524+ PortConfig :: new( 80 )
525+ ) ,
526+ AllowedHostConfig :: parse( "http://spin.fermyon.dev/" ) . unwrap( )
527+ ) ;
528+
529+ assert_eq ! (
530+ AllowedHostConfig :: new(
531+ SchemeConfig :: new( "https" ) ,
532+ HostConfig :: new( "spin.fermyon.dev" ) ,
533+ PortConfig :: new( 443 )
534+ ) ,
535+ AllowedHostConfig :: parse( "https://spin.fermyon.dev" ) . unwrap( )
536+ ) ;
537+ }
538+
505539 #[ test]
506540 fn test_allowed_hosts_accepts_url_with_port ( ) {
507541 assert_eq ! (
@@ -667,6 +701,9 @@ mod test {
667701
668702 #[ test]
669703 fn test_allowed_hosts_rejects_path ( ) {
704+ // An empty path is allowed
705+ assert ! ( AllowedHostConfig :: parse( "http://spin.fermyon.dev/" ) . is_ok( ) ) ;
706+ // All other paths are not allowed
670707 assert ! ( AllowedHostConfig :: parse( "http://spin.fermyon.dev/a" ) . is_err( ) ) ;
671708 assert ! ( AllowedHostConfig :: parse( "http://spin.fermyon.dev:6666/a/b" ) . is_err( ) ) ;
672709 assert ! ( AllowedHostConfig :: parse( "http://*.fermyon.dev/a" ) . is_err( ) ) ;
@@ -700,13 +737,23 @@ mod test {
700737 assert ! (
701738 allowed. allows( & OutboundUrl :: parse( "http://example.com:8383/foo/bar" , "http" ) . unwrap( ) )
702739 ) ;
740+ // Allow urls with and without a trailing slash
741+ assert ! ( allowed. allows( & OutboundUrl :: parse( "https://spin.fermyon.dev" , "https" ) . unwrap( ) ) ) ;
703742 assert ! ( allowed. allows( & OutboundUrl :: parse( "https://spin.fermyon.dev/" , "https" ) . unwrap( ) ) ) ;
704743 assert ! ( !allowed. allows( & OutboundUrl :: parse( "http://example.com/" , "http" ) . unwrap( ) ) ) ;
705744 assert ! ( !allowed. allows( & OutboundUrl :: parse( "http://google.com/" , "http" ) . unwrap( ) ) ) ;
706745 assert ! ( allowed. allows( & OutboundUrl :: parse( "spin.fermyon.dev:443" , "https" ) . unwrap( ) ) ) ;
707746 assert ! ( allowed. allows( & OutboundUrl :: parse( "example.com:8383" , "http" ) . unwrap( ) ) ) ;
708747 }
709748
749+ #[ test]
750+ fn test_allowed_hosts_with_trailing_slash ( ) {
751+ let allowed =
752+ AllowedHostsConfig :: parse ( & [ "https://my.api.com/" ] , & dummy_resolver ( ) ) . unwrap ( ) ;
753+ assert ! ( allowed. allows( & OutboundUrl :: parse( "https://my.api.com" , "https" ) . unwrap( ) ) ) ;
754+ assert ! ( allowed. allows( & OutboundUrl :: parse( "https://my.api.com/" , "https" ) . unwrap( ) ) ) ;
755+ }
756+
710757 #[ test]
711758 fn test_allowed_hosts_can_be_subdomain_wildcards ( ) {
712759 let allowed = AllowedHostsConfig :: parse (
0 commit comments