File Coverage

blib/lib/Plack/Middleware/SignedCookies.pm
Criterion Covered Total %
statement 44 44 100.0
branch 19 22 86.3
condition 7 9 77.7
subroutine 11 11 100.0
pod 2 2 100.0
total 83 88 94.3


line stmt bran cond sub pod time code
1 1     1   133170 use 5.006; use strict; use warnings;
  1     1   6  
  1     1   5  
  1         3  
  1         22  
  1         5  
  1         3  
  1         228  
2              
3             package Plack::Middleware::SignedCookies;
4              
5             our $VERSION = '1.204';
6              
7 1     1   489 BEGIN { require Plack::Middleware; our @ISA = 'Plack::Middleware' }
  1         12166  
8              
9 1     1   10 use Plack::Util ();
  1         3  
  1         21  
10 1     1   6 use Plack::Util::Accessor qw( secret secure httponly );
  1         3  
  1         4  
11 1     1   545 use Digest::SHA ();
  1         2611  
  1         748  
12              
13 65     65   717 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 53595 my ( $self, $env ) = ( shift, @_ );
19              
20 9         36 my $secret = $self->secret;
21              
22 9         58 my $orig = delete $env->{'HTTP_COOKIE'};
23 9 50       33 $env->{'signedcookies.orig'} = $orig if defined $orig;
24              
25             my $cookie =
26             join '; ',
27 21 100       87 grep { s/[ \t]*=[ \t]*/=/; s/[ \t]*([-~A-Za-z0-9]{$length})\z//o and $1 eq _hmac $_, $secret }
  21         170  
28 9 100 66     20 map { defined && /\A[ \t]*(.*[^ \t])/s ? split /[ \t]*;[ \t]*/, "$1" : () }
  9         128  
29             $orig;
30              
31 9 100       30 $env->{'HTTP_COOKIE'} = $cookie if '' ne $cookie;
32              
33             return Plack::Util::response_cb( $self->app->( $env ), sub {
34 9     9   1648 my $do_sign;
35 9         18 for ( @{ $_[0][1] } ) {
  9         25  
36 90 100       184 if ( $do_sign ) {
37 45 100       133 my $flags = s/(;.*)//s ? $1 : '';
38 45         318 s/\A[ \t]+//, s/[ \t]+\z//, s/[ \t]*=[ \t]*|\z/=/; # normalise
39 45         134 $_ .= ' ' . _hmac( $_, $secret ) . $flags;
40 45 100 100     145 $_ .= '; secure' if $self->secure and $flags !~ /;[ \t]* secure [ \t]* (?![^;])/ix;
41 45 100 66     326 $_ .= '; HTTPonly' if $self->httponly and $flags !~ /;[ \t]* httponly [ \t]* (?![^;])/ix;
42             }
43 90 100       403 $do_sign = defined $do_sign ? undef : 'set-cookie' eq lc;
44             }
45 9         37 } );
46             }
47              
48             sub prepare_app {
49 1     1 1 124 my $self = shift;
50 1 50       8 defined $self->httponly or $self->httponly( 1 );
51 1 50       58 defined $self->secret or $self->secret ( join '', map { chr int rand 256 } 1..17 );
  17         82  
52             }
53              
54             1;
55              
56             __END__