File Coverage

blib/lib/HTTP/CSPHeader.pm
Criterion Covered Total %
statement 64 66 96.9
branch 10 16 62.5
condition n/a
subroutine 15 15 100.0
pod 2 2 100.0
total 91 99 91.9


line stmt bran cond sub pod time code
1             package HTTP::CSPHeader;
2              
3             # ABSTRACT: manage dynamic content security policy headers
4              
5 1     1   125094 use v5.14;
  1         15  
6              
7 1     1   536 use Moo;
  1         11606  
  1         6  
8              
9 1     1   1911 use Crypt::URandom 0.37 qw/ urandom_ub /;
  1         5473  
  1         57  
10 1     1   7 use Fcntl qw/ O_NONBLOCK O_RDONLY /;
  1         3  
  1         49  
11 1     1   6 use List::Util 1.29 qw/ pairmap pairs /;
  1         26  
  1         99  
12 1     1   514 use Math::Random::ISAAC;
  1         1176  
  1         41  
13 1     1   457 use Types::Common 2.000000 qw/ ArrayRef is_ArrayRef Bool HashRef IntRange Str /;
  1         272385  
  1         9  
14              
15             # RECOMMEND PREREQ: Math::Random::ISAAC::XS
16             # RECOMMEND PREREQ: Ref::Util::XS
17             # RECOMMEND PREREQ: Type::Tiny::XS
18              
19 1     1   5143 use namespace::autoclean;
  1         13455  
  1         4  
20              
21             our $VERSION = 'v0.3.1';
22              
23              
24             has _base_policy => (
25             is => 'ro',
26             isa => HashRef,
27             required => 1,
28             init_arg => 'policy',
29             );
30              
31             has policy => (
32             is => 'lazy',
33             isa => HashRef,
34             clearer => '_clear_policy',
35             init_arg => undef,
36             );
37              
38             sub _build_policy {
39 4     4   52 my ($self) = @_;
40 4         7 my %policy = %{ $self->_base_policy };
  4         34  
41 4 100       9 if ( my @dirs = @{ $self->nonces_for } ) {
  4         129  
42 2         48 my $nonce = "'nonce-" . $self->nonce . "'";
43 2         18 for my $dir (@dirs) {
44 4 50       11 if ( defined $policy{$dir} ) {
45 4         12 $policy{$dir} .= " " . $nonce;
46             }
47             else {
48 0         0 $policy{$dir} = $nonce;
49             }
50             }
51 2         36 $self->_changed(1);
52             }
53 4         184 return \%policy;
54             }
55              
56             has _changed => (
57             is => 'rw',
58             isa => Bool,
59             lazy => 1,
60             default => 0,
61             init_arg => undef,
62             );
63              
64              
65             has nonces_for => (
66             is => 'lazy',
67             isa => ArrayRef [Str],
68 1     1   36 builder => sub { return [] },
69             coerce => sub { my $val = is_ArrayRef( $_[0] ) ? $_[0] : [ $_[0] ] },
70             );
71              
72              
73             has nonce_seed_size => (
74             is => 'lazy',
75             isa => IntRange[ 16, 256 ],
76             default => 16,
77             );
78              
79              
80             has nonce => (
81             is => 'lazy',
82             isa => Str,
83             clearer => '_clear_nonce',
84             unit_arg => undef,
85             );
86              
87             sub _build_nonce {
88 2     2   518 my ($self) = @_;
89              
90 2         5 state $rng = do {
91 1         20 my $data = urandom_ub( $self->nonce_seed_size );
92 1         11225 Math::Random::ISAAC->new( unpack( "C*", $data ) );
93             };
94              
95 2         41 return sprintf( '%x', $rng->irand ^ $$ );
96             }
97              
98              
99             has header => (
100             is => 'lazy',
101             isa => Str,
102             clearer => '_clear_header',
103             init_arg => undef,
104             );
105              
106             sub _build_header {
107 8     8   7109 my ($self) = @_;
108 8         132 my $policy = $self->policy;
109 8     27   169 return join( "; ", pairmap { $a . " " . $b } %$policy );
  27         203  
110             }
111              
112              
113             sub reset {
114 2     2 1 2403 my ($self) = @_;
115 2 50       49 return unless $self->_changed;
116 2         61 $self->_clear_nonce;
117 2         44 $self->_clear_policy;
118 2         43 $self->_clear_header;
119 2         39 $self->_changed(0);
120             }
121              
122              
123             sub amend {
124 4     4 1 4959 my ($self, @args) = @_;
125 4         96 my $policy = $self->policy;
126              
127 4 50       56 if (@args) {
128              
129 4         34 for my $pol ( pairs @args ) {
130              
131 4         15 my ( $dir, $val ) = @$pol;
132              
133 4 100       17 if ( $dir =~ s/^\+// ) { # append to directive
134 1 50       3 if ( exists $policy->{$dir} ) {
    0          
135 1         6 $policy->{$dir} .= " " . $val;
136             }
137             elsif ( defined $val ) {
138 0         0 $policy->{$dir} = $val;
139             }
140              
141             }
142             else {
143 3 100       8 if ( defined $val ) {
144 2         6 $policy->{$dir} = $val;
145             }
146             else {
147 1         3 delete $policy->{$dir};
148             }
149             }
150             }
151              
152 4         97 $self->_clear_header;
153 4         79 $self->_changed(1);
154             }
155              
156 4         122 return $policy;
157             }
158              
159             1;
160              
161             __END__