View | Details | Raw Unified | Return to bug 38365
Collapse All | Expand All

(-)a/Koha/ContentSecurityPolicy.pm (-4 / +8 lines)
Lines 183-197 sub is_enabled { Link Here
183
183
184
sub nonce {
184
sub nonce {
185
    my ( $self, $nonce ) = @_;
185
    my ( $self, $nonce ) = @_;
186
    my $cache = Koha::Cache::Memory::Lite->new;
187
186
187
    #Koha::Middleware::ContentSecurityPolicy sets an environmental variable
188
    #We cannot use the L1 cache since it is cleared after the middleware is applied
189
    my $env_value = $ENV{CSP_NONCE};
190
191
    #NOTE: This is just used for testing
188
    if ($nonce) {
192
    if ($nonce) {
189
        $cache->set_in_cache( 'CSP-NONCE', $nonce );
193
        $ENV{CSP_NONCE} = $nonce;
190
        $self->{nonce} = $nonce;
194
        $self->{nonce} = $nonce;
191
        return $self->{nonce};
195
        return $self->{nonce};
192
    }
196
    }
193
197
194
    if ( $nonce = $cache->get_from_cache('CSP-NONCE') ) {
198
    if ( $nonce = $env_value ) {
195
        $self->{nonce} = $nonce;
199
        $self->{nonce} = $nonce;
196
        return $self->{nonce};
200
        return $self->{nonce};
197
    }
201
    }
Lines 201-207 sub nonce { Link Here
201
        $self->{nonce} = $nonce;
205
        $self->{nonce} = $nonce;
202
    }
206
    }
203
207
204
    $cache->set_in_cache( 'CSP-NONCE', $self->{nonce} );
208
    $ENV{CSP_NONCE} = $self->{nonce};
205
209
206
    return $self->{nonce};
210
    return $self->{nonce};
207
}
211
}
(-)a/Koha/FrameworkPlugin.pm (-2 / +9 lines)
Lines 117-122 use Cwd qw//; Link Here
117
use base qw(Class::Accessor);
117
use base qw(Class::Accessor);
118
118
119
use C4::Context;
119
use C4::Context;
120
use Koha::ContentSecurityPolicy;
120
121
121
__PACKAGE__->mk_ro_accessors(
122
__PACKAGE__->mk_ro_accessors(
122
    qw|
123
    qw|
Lines 137-143 __PACKAGE__->mk_ro_accessors( Link Here
137
138
138
sub new {
139
sub new {
139
    my ( $class, $params ) = @_;
140
    my ( $class, $params ) = @_;
140
    my $self = $class->SUPER::new();
141
    my $self      = $class->SUPER::new();
142
    my $csp_nonce = Koha::ContentSecurityPolicy->new->nonce;
143
    if ($csp_nonce) {
144
        $self->{csp_nonce} = $csp_nonce;
145
    }
141
    if ( ref($params) eq 'HASH' ) {
146
    if ( ref($params) eq 'HASH' ) {
142
        foreach ( 'name', 'path', 'item_style' ) {
147
        foreach ( 'name', 'path', 'item_style' ) {
143
            $self->{$_} = $params->{$_};
148
            $self->{$_} = $params->{$_};
Lines 356-361 sub _generate_js { Link Here
356
sub _process_javascript {
361
sub _process_javascript {
357
    my ( $self, $params, $script ) = @_;
362
    my ( $self, $params, $script ) = @_;
358
363
364
    my $csp_nonce = $self->{csp_nonce};
365
359
    #remove the script tags; we add them again later
366
    #remove the script tags; we add them again later
360
    $script =~ s/\<script[^>]*\>\s*(\/\/\<!\[CDATA\[)?\s*//s;
367
    $script =~ s/\<script[^>]*\>\s*(\/\/\<!\[CDATA\[)?\s*//s;
361
    $script =~ s/(\/\/\]\]\>\s*)?\<\/script\>//s;
368
    $script =~ s/(\/\/\]\]\>\s*)?\<\/script\>//s;
Lines 372-378 sub _process_javascript { Link Here
372
    }
379
    }
373
    $self->{noclick}    = !$clickfound;
380
    $self->{noclick}    = !$clickfound;
374
    $self->{javascript} = <<JS;
381
    $self->{javascript} = <<JS;
375
<script>
382
<script nonce="$csp_nonce">
376
\$(document).ready(function () {
383
\$(document).ready(function () {
377
$script
384
$script
378
});
385
});
(-)a/t/Koha/ContentSecurityPolicy.t (-7 / +3 lines)
Lines 140-149 subtest 'header_value tests' => sub { Link Here
140
        $conf_csp_section,
140
        $conf_csp_section,
141
        { opac => { csp_header_value => 'begin nonce-_CSP_NONCE_ end' } }
141
        { opac => { csp_header_value => 'begin nonce-_CSP_NONCE_ end' } }
142
    );
142
    );
143
    my $cache = Koha::Cache::Memory::Lite->get_instance();
143
    $csp->nonce('cached value');
144
    $cache->set_in_cache( 'CSP-NONCE', 'cached value' );
145
    is( $csp->header_value, 'begin nonce-cached value end', 'Cached csp_header_value' );
144
    is( $csp->header_value, 'begin nonce-cached value end', 'Cached csp_header_value' );
146
    $cache->flush;
147
145
148
    $csp = Koha::ContentSecurityPolicy->new( { nonce => 'forced value' } );
146
    $csp = Koha::ContentSecurityPolicy->new( { nonce => 'forced value' } );
149
    is( $csp->header_value, 'begin nonce-forced value end', 'Forced csp_header_value' );
147
    is( $csp->header_value, 'begin nonce-forced value end', 'Forced csp_header_value' );
Lines 163-177 subtest 'nonce tests' => sub { Link Here
163
    $csp = Koha::ContentSecurityPolicy->new( { nonce => 'forced value' } );
161
    $csp = Koha::ContentSecurityPolicy->new( { nonce => 'forced value' } );
164
    is( $csp->nonce, 'forced value', 'nonce is not re-generated as was cached in memory' );
162
    is( $csp->nonce, 'forced value', 'nonce is not re-generated as was cached in memory' );
165
163
166
    my $cache = Koha::Cache::Memory::Lite->get_instance();
164
    delete $ENV{CSP_NONCE};
167
    $cache->flush;
168
165
169
    $csp = Koha::ContentSecurityPolicy->new();
166
    $csp = Koha::ContentSecurityPolicy->new();
170
    my $nonce = $csp->nonce;
167
    my $nonce = $csp->nonce;
171
    like( $nonce, qr/\w{22}/, 'nonce is a random string of 22 characters (128+ bits entropy)' );
168
    like( $nonce, qr/\w{22}/, 'nonce is a random string of 22 characters (128+ bits entropy)' );
172
    is( $nonce, $csp->nonce, 're-calling nonce() returns the same randomly generated cached value' );
169
    is( $nonce, $csp->nonce, 're-calling nonce() returns the same randomly generated cached value' );
173
170
174
    $cache->set_in_cache( 'CSP-NONCE', 'cached value' );
171
    $csp->nonce('cached value');
175
    is( $csp->nonce, 'cached value', 'nonce is not re-generated as it was previously cached' );
172
    is( $csp->nonce, 'cached value', 'nonce is not re-generated as it was previously cached' );
176
};
173
};
177
174
178
- 

Return to bug 38365