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

May accidentally match a route if %+ was already populated #1187

Open
bigpresh opened this issue Jun 13, 2018 · 1 comment
Open

May accidentally match a route if %+ was already populated #1187

bigpresh opened this issue Jun 13, 2018 · 1 comment
Labels

Comments

@bigpresh
Copy link
Member

bigpresh commented Jun 13, 2018

In Dancer::Route->match, if %+ has any keys in it, then we will consider the route a match:

sub match {
    my ($self, $request) = @_;

    my $method = lc($request->method);

    my $path   = $request->path_info;

    Dancer::Logger::core(
        sprintf "Trying to match '%s %s' against /%s/ (generated from '%s')",
            $request->method, $path, $self->{_compiled_regexp}, $self->pattern
    );

    my @values = $path =~ $self->{_compiled_regexp};

    Dancer::Logger::core("  --> got ".
        map { defined $_ ? $_ : 'undef' } @values)
        if @values;

    # if some named captures found, return captures
    # no warnings is for perl < 5.10
    if (my %captures =
        do { no warnings; %+ }
      )
    {
        Dancer::Logger::core(
            "  --> captures are: " . join(", ", keys(%captures)))
          if keys %captures;
        return $self->save_match_data($request, {captures => \%captures});
    }

    return unless @values;

    # save the route pattern that matched

Note that, if %+ had keys, then we'll call return $self->save_match_data($request, {captures => \%captures}); before checking if the route actually matched.

But, %+ only gets cleared after a successful match, so it's not safe to rely on it without first checking that the regex did match.

It may be as simple as moving the return unless @values; up a little so it's checked before looking at %+.

We should also confirm whether this is present in Dancer2.

Found by a colleague, @skington.

@bigpresh
Copy link
Member Author

I've looked at Dancer2, and it won't have this problem, because in Dancer2::Core::Route->match we check for the match first:

sub match {
    my ( $self, $request ) = @_; 

    if ( $self->has_options ) { 
        return unless $self->validate_options($request);
    }   

    my @values = $request->path =~ $self->regexp;

    return unless @values;

    # if some named captures are found, return captures
    # no warnings is for perl < 5.10
    # - Note no @values implies no named captures
    if (my %captures =
        do { no warnings; %+ }
      )   
    {   
        return $self->_match_data( { captures => \%captures } );
    } 
...

So, that backs up the "just return unless @values before looking at %+" idea.

@bigpresh bigpresh added the Bug label Jun 13, 2018
bigpresh added a commit that referenced this issue Jun 13, 2018
This is for Issue #1187.

It's not safe to look at `%+` without first having checked whether the regex
matched successfully or not.  Otherwise, we might see keys from a previous regex
match, because `%+` is only cleared after a successful match.

(perldoc perlvar confirms, "Note: "%-" and "%+" are tied views into a common
internal hash associated with the last successful regular expression.")

Dancer2 doesn't have problems here, as it already returns immediately if the
regex didn't match, the same way this change does.

Note: I think this would only happen via certain deployment methods, and if
there was an earlier route which used named captures.

In particular, we never had any problems in our large API app's comprehensive
test suite when we used Dancer::Test.  We've re-worked our testing framework to
use Plack::Test, partially in readyness to switch to Dancer2, and started
getting weird failures which were tracked down to incorrect route matching, and
this was the cause.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant