File Coverage

blib/lib/Plack/Middleware/SignedCookies.pm
Criterion Covered Total %
statement 43 43 100.0
branch 18 20 90.0
condition 7 9 77.7
subroutine 11 11 100.0
pod 2 2 100.0
total 81 85 95.2


line stmt bran cond sub pod time code
1 1     1   113756 use 5.006; use strict; use warnings;
  1     1   4  
  1     1   6  
  1         2  
  1         23  
  1         4  
  1         2  
  1         76  
2              
3             package Plack::Middleware::SignedCookies;
4             $Plack::Middleware::SignedCookies::VERSION = '1.202';
5             # ABSTRACT: accept only server-minted cookies
6              
7 1     1   11 use parent 'Plack::Middleware';
  1         6  
  1         8  
8              
9 1     1   13120 use Plack::Util ();
  1         2  
  1         21  
10 1     1   5 use Plack::Util::Accessor qw( secret secure httponly );
  1         3  
  1         4  
11 1     1   584 use Digest::SHA ();
  1         3165  
  1         699  
12              
13 65     65   724 sub _hmac { y{+/}{-~}, return $_ for &Digest::SHA::hmac_sha256_base64 }
14              
15             my $length = length _hmac 'something', 'something';
16              
17             sub call {
18 9     9 1 55090 my ( $self, $env ) = ( shift, @_ );
19              
20 9         32 my $secret = $self->secret;
21              
22             local $env->{'HTTP_COOKIE'} =
23             join '; ',
24 21 100       84 grep { s/[ \t]*=[ \t]*/=/; s/[ \t]*([-~A-Za-z0-9]{$length})\z//o and $1 eq _hmac $_, $secret }
  21         193  
25 9 100 66     138 map { defined && /\A[ \t]*(.*[^ \t])/s ? split /[ \t]*;[ \t]*/, "$1" : () }
26 9         61 $env->{'HTTP_COOKIE'};
27              
28 9 100       35 delete $env->{'HTTP_COOKIE'} if '' eq $env->{'HTTP_COOKIE'};
29              
30             return Plack::Util::response_cb( $self->app->( $env ), sub {
31 9     9   1291 my $do_sign;
32 9         14 for ( @{ $_[0][1] } ) {
  9         27  
33 90 100       178 if ( $do_sign ) {
34 45 100       123 my $flags = s/(;.*)//s ? $1 : '';
35 45         323 s/\A[ \t]+//, s/[ \t]+\z//, s/[ \t]*=[ \t]*|\z/=/; # normalise
36 45         108 $_ .= ' ' . _hmac( $_, $secret ) . $flags;
37 45 100 100     126 $_ .= '; secure' if $self->secure and $flags !~ /;[ \t]* secure [ \t]* (?![^;])/ix;
38 45 100 66     289 $_ .= '; HTTPonly' if $self->httponly and $flags !~ /;[ \t]* httponly [ \t]* (?![^;])/ix;
39             }
40 90 100       397 $do_sign = defined $do_sign ? undef : 'set-cookie' eq lc;
41             }
42 9         34 } );
43             }
44              
45             sub prepare_app {
46 1     1 1 131 my $self = shift;
47 1 50       7 defined $self->httponly or $self->httponly( 1 );
48 1 50       58 defined $self->secret or $self->secret ( join '', map { chr int rand 256 } 1..17 );
  17         86  
49             }
50              
51             1;
52              
53             __END__