File Coverage

blib/lib/HTTP/CSPHeader.pm
Criterion Covered Total %
statement 59 61 96.7
branch 10 16 62.5
condition n/a
subroutine 14 14 100.0
pod 2 2 100.0
total 85 93 91.4


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   275507 use v5.14;
  1         5  
6              
7 1     1   753 use Moo;
  1         11439  
  1         6  
8              
9 1     1   2102 use Fcntl qw/ O_NONBLOCK O_RDONLY /;
  1         2  
  1         76  
10 1     1   9 use List::Util 1.29 qw/ pairmap pairs /;
  1         33  
  1         84  
11 1     1   722 use Session::Token;
  1         13627  
  1         66  
12 1     1   562 use Types::Common 2.000000 qw/ ArrayRef is_ArrayRef Bool HashRef IntRange Str /;
  1         365725  
  1         14  
13              
14             # RECOMMEND PREREQ: Ref::Util::XS
15             # RECOMMEND PREREQ: Type::Tiny::XS
16              
17 1     1   6623 use namespace::autoclean;
  1         27352  
  1         5  
18              
19             our $VERSION = 'v0.4.1';
20              
21              
22             has _base_policy => (
23             is => 'ro',
24             isa => HashRef,
25             required => 1,
26             init_arg => 'policy',
27             );
28              
29             has policy => (
30             is => 'lazy',
31             isa => HashRef,
32             clearer => '_clear_policy',
33             init_arg => undef,
34             );
35              
36             sub _build_policy {
37 4     4   54 my ($self) = @_;
38 4         10 my %policy = %{ $self->_base_policy };
  4         29  
39 4 100       12 if ( my @dirs = @{ $self->nonces_for } ) {
  4         103  
40 2         68 my $nonce = "'nonce-" . $self->nonce . "'";
41 2         21 for my $dir (@dirs) {
42 4 50       13 if ( defined $policy{$dir} ) {
43 4         16 $policy{$dir} .= " " . $nonce;
44             }
45             else {
46 0         0 $policy{$dir} = $nonce;
47             }
48             }
49 2         50 $self->_changed(1);
50             }
51 4         212 return \%policy;
52             }
53              
54             has _changed => (
55             is => 'rw',
56             isa => Bool,
57             lazy => 1,
58             default => 0,
59             init_arg => undef,
60             );
61              
62              
63             has nonces_for => (
64             is => 'lazy',
65             isa => ArrayRef [Str],
66 1     1   27 builder => sub { return [] },
67             coerce => sub { my $val = is_ArrayRef( $_[0] ) ? $_[0] : [ $_[0] ] },
68             );
69              
70              
71             has nonce => (
72             is => 'lazy',
73             isa => Str,
74             clearer => '_clear_nonce',
75             unit_arg => undef,
76             );
77              
78             sub _build_nonce {
79 2     2   766 my ($self) = @_;
80 2         14 state $rng = Session::Token->new;
81 2         291 return $rng->get;
82             }
83              
84              
85             has header => (
86             is => 'lazy',
87             isa => Str,
88             clearer => '_clear_header',
89             init_arg => undef,
90             );
91              
92             sub _build_header {
93 8     8   366212 my ($self) = @_;
94 8         199 my $policy = $self->policy;
95 8     27   224 return join( "; ", pairmap { $a . " " . $b } %$policy );
  27         328  
96             }
97              
98              
99             sub reset {
100 2     2 1 2467 my ($self) = @_;
101 2 50       99 return unless $self->_changed;
102 2         78 $self->_clear_nonce;
103 2         63 $self->_clear_policy;
104 2         59 $self->_clear_header;
105 2         54 $self->_changed(0);
106             }
107              
108              
109             sub amend {
110 4     4 1 5090 my ($self, @args) = @_;
111 4         142 my $policy = $self->policy;
112              
113 4 50       48 if (@args) {
114              
115 4         77 for my $pol ( pairs @args ) {
116              
117 4         24 my ( $dir, $val ) = @$pol;
118              
119 4 100       26 if ( $dir =~ s/^\+// ) { # append to directive
120 1 50       6 if ( exists $policy->{$dir} ) {
    0          
121 1         6 $policy->{$dir} .= " " . $val;
122             }
123             elsif ( defined $val ) {
124 0         0 $policy->{$dir} = $val;
125             }
126              
127             }
128             else {
129 3 100       10 if ( defined $val ) {
130 2         7 $policy->{$dir} = $val;
131             }
132             else {
133 1         5 delete $policy->{$dir};
134             }
135             }
136             }
137              
138 4         126 $self->_clear_header;
139 4         136 $self->_changed(1);
140             }
141              
142 4         145 return $policy;
143             }
144              
145             1;
146              
147             __END__