File Coverage

blib/lib/Plack/Middleware/SignedCookies.pm
Criterion Covered Total %
statement 43 43 100.0
branch 9 10 90.0
condition 13 17 76.4
subroutine 10 10 100.0
pod 1 1 100.0
total 76 81 93.8


line stmt bran cond sub pod time code
1 1     1   74376 use 5.010;
  1         3  
  1         47  
2 1     1   5 use strict;
  1         2  
  1         31  
3 1     1   5 use warnings;
  1         2  
  1         61  
4              
5             package Plack::Middleware::SignedCookies;
6             $Plack::Middleware::SignedCookies::VERSION = '1.103';
7             # ABSTRACT: accept only server-minted cookies
8              
9 1     1   5 use parent 'Plack::Middleware';
  1         2  
  1         6  
10              
11 1     1   65 use Plack::Util ();
  1         2  
  1         19  
12 1     1   9 use Plack::Util::Accessor qw( secret secure httponly );
  1         2  
  1         8  
13 1     1   2541 use Digest::SHA ();
  1         4031  
  1         441  
14              
15 22     22   329 sub _hmac { y{+/}{-~}, return $_ for Digest::SHA::hmac_sha256_base64( @_[0,1] ) }
16              
17             my $length = length _hmac 'something', 'something';
18              
19             sub call {
20 9     9 1 28764 my $self = shift;
21 9         13 my $env = shift;
22              
23 9   66     22 my $secure = $self->secure // do { $self->secure ( 0 ) };
  1         78  
24 9   66     59 my $httponly = $self->httponly // do { $self->httponly( 1 ) };
  1         7  
25             my $secret = $self->secret
26 9   66     42 // do { $self->secret( join '', map { chr int rand 256 } 1..17 ) };
  1         10  
  17         23  
27              
28 5 100       34 my $cookie =
29             join '; ',
30 9   100     78 grep { s/(.{$length})\z//o and $1 eq _hmac $_, $secret }
31             split /\s*[;,]\s*/,
32             $env->{'HTTP_COOKIE'} // '';
33              
34 9 100       27 length $cookie
35             ? local $env->{'HTTP_COOKIE'} = $cookie
36             : delete local $env->{'HTTP_COOKIE'};
37              
38             return Plack::Util::response_cb( $self->app->( $env ), sub {
39 9     9   3216 my ( $i, $headers ) = ( 0, $_[0][1] );
40 9         29 while ( $i < $#$headers ) {
41 18 50       93 ++$i, next if 'set-cookie' ne lc $headers->[$i++];
42 18         27 for ( $headers->[$i++] ) {
43 18         142 s!\A\s*([^;]+?)\K\s*(?=;|\z)!_hmac $1, $secret!e;
  18         27  
44 18 100 100     62 $_ .= '; secure' if $secure and not /;\s* secure \s* (?:;|\z)/ix;
45 18 100 66     98 $_ .= '; HTTPonly' if $httponly and not /;\s* httponly \s* (?:;|\z)/ix;
46             }
47             }
48 9         25 } );
49             }
50              
51             1;
52              
53             __END__