CVE-2011-1589: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: Directory traversal vulnerability in Path.pm in Mojolicious before 1.16 allows remote attackers to read arbitrary files via a %2f..%2f (encoded slash dot dot slash) in a URI.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Published: 2011-04-29T22:55Z
Repository: https://github.com/kraih/mojo
Language: Perl
Files changed: Changes, lib/Mojo/Path.pm, lib/Mojolicious.pm, t/mojo/path.t
Commit date: 2011-04-15 10:45:23 +0200
Commit message: fixed critical security issue that can expose files on your system and prepared emergency release

Diff: diff --git a/Changes b/Changes
index fe32a07f..bd74b020 100644
--- a/Changes
+++ b/Changes
@@ -1,8 +1,8 @@
 This file documents the revision history for Perl extension Mojolicious.
 
-1.16 2011-03-19 00:00:00
+1.17 2011-04-15 00:00:00
         - Deprecated Mojolicious process method in favor of the on_process
           attribute.
         - Added Failraptor.
         - Added support for MOJO_CERT_FILE and MOJO_KEY_FILE environment
           variables.
@@ -27,10 +27,14 @@ This file documents the revision history for Perl extension Mojolicious.
         - Fixed small route pattern escaping bug.
         - Fixed small reload bug.
         - Fixed small perldoc browser bug. (kberov)
         - Fixed cookbook recipe. (moritz)
 
+1.16 2011-04-15 00:00:00
+        - Emergency release for a critical security issue that can expose
+          files on your system, everybody should update!
+
 1.15 2011-03-18 00:00:00
         - Changed default log level in "production" mode from "error" to
           "info".
         - Improved lookup method in Mojo::IOLoop.
         - Fixed small Mojo::Util bug.
diff --git a/lib/Mojo/Path.pm b/lib/Mojo/Path.pm
index 31a474f4..d0b99af8 100644
--- a/lib/Mojo/Path.pm
+++ b/lib/Mojo/Path.pm
@@ -78,10 +78,13 @@ sub parse {
   # Meta
   $path = '' unless defined $path;
   $path =~ /^\// ? $self->leading_slash(1)  : $self->leading_slash(0);
   $path =~ /\/$/ ? $self->trailing_slash(1) : $self->trailing_slash(0);
 
+  # Unescape
+  url_unescape $path;
+
   # Parse
   my @parts;
   for my $part (split '/', $path) {
 
     # Empty parts before the first are garbage
@@ -89,11 +92,10 @@ sub parse {
 
     # Empty parts behind the first are ok
     $part = '' unless defined $part;
 
     # Store
-    url_unescape $part;
     push @parts, $part;
   }
 
   $self->parts(\@parts);
 
diff --git a/lib/Mojolicious.pm b/lib/Mojolicious.pm
index 104996f7..15cb0b70 100644
--- a/lib/Mojolicious.pm
+++ b/lib/Mojolicious.pm
@@ -40,11 +40,11 @@ has secret   => sub {
 has sessions => sub { Mojolicious::Sessions->new };
 has static   => sub { Mojolicious::Static->new };
 has types    => sub { Mojolicious::Types->new };
 
 our $CODENAME = 'Smiling Cat Face With Heart-Shaped Eyes';
-our $VERSION  = '1.16';
+our $VERSION  = '1.17';
 
 # "These old doomsday devices are dangerously unstable.
 #  I'll rest easier not knowing where they are."
 sub AUTOLOAD {
   my $self = shift;
diff --git a/t/mojo/path.t b/t/mojo/path.t
index ca7913b3..fdfea5c0 100644
--- a/t/mojo/path.t
+++ b/t/mojo/path.t
@@ -1,14 +1,29 @@
 #!/usr/bin/env perl
 
 use strict;
 use warnings;
 
-use Test::More tests => 3;
+use Test::More tests => 11;
 
 # "This is the greatest case of false advertising I’ve seen since I sued the
 #  movie 'The Never Ending Story.'"
 use_ok 'Mojo::Path';
 
 my $path = Mojo::Path->new;
 is $path->parse('/path')->to_string,   '/path',   'right path';
 is $path->parse('/path/0')->to_string, '/path/0', 'right path';
+
+# Canonicalizing
+$path = Mojo::Path->new(
+  '/%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd');
+is "$path", '/../../../../../../../../../../etc/passwd', 'rigth result';
+is $path->parts->[0], '..', 'right part';
+is $path->canonicalize, '/../../../../../../../../../../etc/passwd',
+  'rigth result';
+is $path->parts->[0], '..', 'right part';
+$path = Mojo::Path->new(
+  '/%2ftest%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd');
+is "$path", '/test/../../../../../../../../../etc/passwd', 'rigth result';
+is $path->parts->[0], 'test', 'right part';
+is $path->canonicalize, '/../../../../../../../../etc/passwd', 'rigth result';
+is $path->parts->[0], '..', 'right part';

Vulnerable code: 1.16 2011-03-19 00:00:00
    url_unescape $part;
our $VERSION  = '1.16';
use Test::More tests => 3;
Fixed code: 1.17 2011-04-15 00:00:00
1.16 2011-04-15 00:00:00
        - Emergency release for a critical security issue that can expose
          files on your system, everybody should update!

  # Unescape
  url_unescape $path;

our $VERSION  = '1.17';
use Test::More tests => 11;

# Canonicalizing
$path = Mojo::Path->new(
  '/%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd');
is "$path", '/../../../../../../../../../../etc/passwd', 'rigth result';
is $path->parts->[0], '..', 'right part';
is $path->canonicalize, '/../../../../../../../../../../etc/passwd',
  'rigth result';
is $path->parts->[0], '..', 'right part';
$path = Mojo::Path->new(
  '/%2ftest%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd');
is "$path", '/test/../../../../../../../../../etc/passwd', 'rigth result';
is $path->parts->[0], 'test', 'right part';
is $path->canonicalize, '/../../../../../../../../etc/passwd', 'rigth result';
is $path->parts->[0], '..', 'right part';