HTML encode the link in the 302 redirect page to prevent XSS.
Colin Newell [Wed, 23 Jan 2013 19:31:34 +0000 (19:31 +0000)]
lib/Catalyst.pm
t/lib/TestApp/Controller/Root.pm
t/live_redirect_body.t

index 18a0f35..0b2c012 100644 (file)
@@ -23,6 +23,7 @@ use Path::Class::File ();
 use URI ();
 use URI::http;
 use URI::https;
+use HTML::Entities;
 use Tree::Simple qw/use_weak_refs/;
 use Tree::Simple::Visitor::FindByUID;
 use Class::C3::Adopt::NEXT;
@@ -1869,6 +1870,7 @@ sub finalize_headers {
 
         if ( !$response->has_body ) {
             # Add a default body if none is already present
+            my $encoded_location = encode_entities($location);
             $response->body(<<"EOF");
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml"> 
@@ -1876,7 +1878,7 @@ sub finalize_headers {
     <title>Moved</title>
   </head>
   <body>
-     <p>This item has moved <a href="$location">here</a>.</p>
+     <p>This item has moved <a href="$encoded_location">here</a>.</p>
   </body>
 </html>
 EOF
index c1bd6d5..e5137e0 100644 (file)
@@ -91,6 +91,13 @@ sub test_redirect :Global {
     $c->res->redirect('/go_here');
 }
 
+sub test_redirect_uri_for :Global {
+    my ($self, $c) = @_;
+    # Don't set content_type
+    # Don't set body
+    $c->res->redirect($c->uri_for('/go_here'));
+}
+
 sub test_redirect_with_contenttype :Global {
     my ($self, $c) = @_;
     # set content_type but don't set body
index b6d4c96..913f0e9 100644 (file)
@@ -44,5 +44,20 @@ use Test::More;
     like( $response->content, qr/kind sir/, 'Content contains content set by the Controller' );
 }
 
+# test redirect with dodgy host
+{
+    local $Catalyst::Test::default_host = "-->\">'>'\"<sfi000003v407412>";
+    my $request  =
+      HTTP::Request->new( GET => 'http://localhost:3000/test_redirect_uri_for');
+
+    ok( my $response = request($request), 'Request' );
+    is( $response->code, 302, 'Response Code' );
+
+    # When no body and no content_type has been set, redirecting should set both.
+    is( $response->header( 'Content-Type' ), 'text/html; charset=utf-8', 'Content Type' );
+    like( $response->content, qr/<body>/, 'Content contains HTML body' );
+    like( $response->content, qr/href="[^"]+">here<\/a>/, 'link doesn\'t have xss' );
+}
+
 done_testing;