Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented allow_encoded_slashes aka handling of 2%F as part of the PAT... #1060

Open
wants to merge 2 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ A huge thank you to all of them!
Marc Chantreux <[email protected]>
Mark Allen <[email protected]>
Mark Stosberg <[email protected]>
Markus Jansen <[email protected]>
Matthew Horsfall (alh) <[email protected]>
Maurice <[email protected]>
Max Maischein <[email protected]>
Expand Down
34 changes: 21 additions & 13 deletions lib/Dancer/Config.pm
Original file line number Diff line number Diff line change
Expand Up @@ -239,19 +239,20 @@ sub load_settings_from_yaml {
}

sub load_default_settings {
$SETTINGS->{server} ||= $ENV{DANCER_SERVER} || '0.0.0.0';
$SETTINGS->{port} ||= $ENV{DANCER_PORT} || '3000';
$SETTINGS->{content_type} ||= $ENV{DANCER_CONTENT_TYPE} || 'text/html';
$SETTINGS->{charset} ||= $ENV{DANCER_CHARSET} || '';
$SETTINGS->{startup_info} ||= !$ENV{DANCER_NO_STARTUP_INFO};
$SETTINGS->{daemon} ||= $ENV{DANCER_DAEMON} || 0;
$SETTINGS->{apphandler} ||= $ENV{DANCER_APPHANDLER} || 'Standalone';
$SETTINGS->{warnings} ||= $ENV{DANCER_WARNINGS} || 0;
$SETTINGS->{auto_reload} ||= $ENV{DANCER_AUTO_RELOAD} || 0;
$SETTINGS->{traces} ||= $ENV{DANCER_TRACES} || 0;
$SETTINGS->{server_tokens} ||= !$ENV{DANCER_NO_SERVER_TOKENS};
$SETTINGS->{logger} ||= $ENV{DANCER_LOGGER} || 'file';
$SETTINGS->{environment} ||=
$SETTINGS->{server} ||= $ENV{DANCER_SERVER} || '0.0.0.0';
$SETTINGS->{port} ||= $ENV{DANCER_PORT} || '3000';
$SETTINGS->{content_type} ||= $ENV{DANCER_CONTENT_TYPE} || 'text/html';
$SETTINGS->{charset} ||= $ENV{DANCER_CHARSET} || '';
$SETTINGS->{startup_info} ||= !$ENV{DANCER_NO_STARTUP_INFO};
$SETTINGS->{daemon} ||= $ENV{DANCER_DAEMON} || 0;
$SETTINGS->{apphandler} ||= $ENV{DANCER_APPHANDLER} || 'Standalone';
$SETTINGS->{warnings} ||= $ENV{DANCER_WARNINGS} || 0;
$SETTINGS->{auto_reload} ||= $ENV{DANCER_AUTO_RELOAD} || 0;
$SETTINGS->{traces} ||= $ENV{DANCER_TRACES} || 0;
$SETTINGS->{server_tokens} ||= !$ENV{DANCER_NO_SERVER_TOKENS};
$SETTINGS->{logger} ||= $ENV{DANCER_LOGGER} || 'file';
$SETTINGS->{allow_encoded_slashes} ||= $ENV{DANCER_ALLOW_ENCODED_SLASHES} || 'Off';
$SETTINGS->{environment} ||=
$ENV{DANCER_ENVIRONMENT}
|| $ENV{PLACK_ENV}
|| 'development';
Expand Down Expand Up @@ -670,6 +671,13 @@ Maximum size of route cache (e.g. 1024, 2M). Defaults to 10M (10MB) - see L<Danc

Maximum number of routes to cache. Defaults to 600 - see L<Dancer::Route::Cache>

=head2 Route handling

=head3 allow_encoded_slashes (string)

Possible values are 'On', 'Off', and 'NoDecode'.
If not set to 'Off', forward slashes encoded as %2F contained in the route will not be considered for the pattern matching.
Encoded slashes as parameter values will be decoded if the value is 'On'. Default is 'Off'.

=head2 DANCER_CONFDIR and DANCER_ENVDIR

Expand Down
43 changes: 38 additions & 5 deletions lib/Dancer/Request.pm
Original file line number Diff line number Diff line change
Expand Up @@ -444,13 +444,40 @@ sub _build_path {
my ($self) = @_;
my $path = "";

# prevent decoding encoded slashes if appropriate
# which is a MAY condition according to RFC 3875
my $allow_encoded_slashes = setting('allow_encoded_slashes') || 'Off';

my $unencode_slashes = $allow_encoded_slashes eq 'Off' ? 1 : 0;

$path .= $self->script_name if defined $self->script_name;
$path .= $self->env->{PATH_INFO} if defined $self->env->{PATH_INFO};

# CGI.pm rigorously unescapes PATH_INFO, so we cannot use that
# ... unless $request_uri is not defined
my $request_uri = $self->request_uri;
$unencode_slashes = 1 if (! defined $request_uri);

if ($unencode_slashes) {
$path .= $self->env->{PATH_INFO} if defined $self->env->{PATH_INFO};
}

# fallback to REQUEST_URI if nothing found
# we have to decode it, according to PSGI specs.
if (defined $self->request_uri) {
$path ||= $self->_url_decode($self->request_uri);
# however, we should cut off the trailing query_string

if ($path eq '' && defined $request_uri) {
# $request_uri =~ s/\?.*$//;
my ( $request_uri_path, $query_string ) =
$request_uri =~ /^([^?]+)(\?.*)$/ ? ( $1, $2 ) : ( $request_uri, '' );
$path = $self->_url_decode($request_uri_path, !$unencode_slashes);

# change PATH_INFO in retrospect if we can safely do so
my $path_info = $self->env->{PATH_INFO} || '';
my $unencoded_request_uri_path = $self->_url_decode($request_uri_path);
if ( $unencoded_request_uri_path ne $path &&
$unencoded_request_uri_path eq $path_info ) {
$self->env->{PATH_INFO} = $path;
}
}

raise core_request => "Cannot resolve path" if not $path;
Expand Down Expand Up @@ -478,10 +505,16 @@ sub _build_method {
}

sub _url_decode {
my ($self, $encoded) = @_;
my ($self, $encoded, $allow_encoded_slashes) = @_;
my $clean = $encoded;
$clean =~ tr/\+/ /;
$clean =~ s/%([a-fA-F0-9]{2})/pack "H2", $1/eg;
if ($allow_encoded_slashes) {
# don't pack %2F
$clean =~ s/%([a-fA-F013-9][a-fA-F0-9]|2[a-eA-E0-9])/pack "H2", $1/eg;
}
else {
$clean =~ s/%([a-fA-F0-9]{2})/pack "H2", $1/eg;
}
return $clean;
}

Expand Down
9 changes: 9 additions & 0 deletions lib/Dancer/Route.pm
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ sub match {
# IT WILL MOVE VERY SOON
$request->{_route_pattern} = $self->pattern;

# decode encoded slashes if appropriate
my $allow_encoded_slashes = setting('allow_encoded_slashes') || 'Off';
if ( $allow_encoded_slashes eq 'On' )
{
foreach my $i ( 0 .. $#values ) {
$values[$i] =~ s,%2[Ff],/,g;
}
}

# named tokens
my @tokens = @{$self->{_params} || []};

Expand Down
33 changes: 33 additions & 0 deletions t/02_request/19_encoded_slashes.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use Test::More;

plan tests => 2;

{
use Dancer ':tests';
diag setting('allow_encoded_slashes');

get '/:foo/baz' => sub {
param 'foo';
};

get '/:foo/:bar/baz' => sub {
param 'foo';
};
}

use Dancer::Test;
use Dancer::Config 'setting';

# CGI.pm used by HTTP::Server::Simple is the real culprit, so we cannot test the 'Off' mode here
#
# # set allow_encoded_slashes => 'Off';
# setting('allow_encoded_slashes' => 'Off');
# response_content_is [GET => '/42%2F42/baz'], '42';

# set allow_encoded_slashes => 'On';
setting('allow_encoded_slashes' => 'On');
response_content_is [GET => '/42%2F42/baz'], '42/42';

# set allow_encoded_slashes => 'NoDecode';
setting('allow_encoded_slashes' => 'NoDecode');
response_content_is [GET => '/42%2F42/baz'], '42%2F42';