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

(-)a/C4/Output.pm (+7 lines)
Lines 34-39 use C4::Auth qw( get_template_and_user ); Link Here
34
use C4::Context;
34
use C4::Context;
35
use C4::Templates;
35
use C4::Templates;
36
36
37
use Koha::ContentSecurityPolicy;
38
37
our ( @ISA, @EXPORT_OK );
39
our ( @ISA, @EXPORT_OK );
38
40
39
BEGIN {
41
BEGIN {
Lines 270-275 sub output_with_http_headers { Link Here
270
        $options->{'Content-Script-Type'} = 'text/javascript';
272
        $options->{'Content-Script-Type'} = 'text/javascript';
271
    }
273
    }
272
274
275
    my $csp = Koha::ContentSecurityPolicy->new;
276
    if ( $csp->is_enabled ) {
277
        $options->{ $csp->header_name } = $csp->header_value if $csp->header_value;
278
    }
279
273
    # We can't encode here, that will double encode our templates, and xslt
280
    # We can't encode here, that will double encode our templates, and xslt
274
    # We need to fix the encoding as it comes out of the database, or when we pass the variables to templates
281
    # We need to fix the encoding as it comes out of the database, or when we pass the variables to templates
275
282
(-)a/t/db_dependent/Output.t (-2 / +158 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 3;
20
use Test::More tests => 4;
21
use Test::MockModule;
21
use Test::MockModule;
22
use Test::NoWarnings;
22
use Test::NoWarnings;
23
use Test::Warn;
23
use Test::Warn;
Lines 160-162 subtest 'redirect_if_opac_suppressed() tests' => sub { Link Here
160
160
161
    $schema->storage->txn_rollback;
161
    $schema->storage->txn_rollback;
162
};
162
};
163
- 
163
164
subtest 'Content-Security-Policy tests' => sub {
165
    plan tests => 1;
166
167
    subtest 'OPAC' => sub {
168
169
        plan tests => 3;
170
171
        subtest 'turned off' => sub {
172
173
            plan tests => 2;
174
175
            $schema->storage->txn_begin;
176
177
            t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
178
179
            my $query = CGI->new();
180
181
            {
182
                local *STDOUT;
183
                my $stdout;
184
                open STDOUT, '>', \$stdout;
185
186
                my ( $template, $borrowernumber, $cookie ) = C4::Auth::get_template_and_user(
187
                    {
188
                        template_name   => "opac-main.tt",
189
                        type            => "opac",
190
                        query           => $query,
191
                        authnotrequired => 1,
192
                    }
193
                );
194
195
                C4::Output::output_html_with_http_headers( $query, $cookie, $template->output );
196
197
                unlike( $stdout, qr{Content-Security-Policy:}, 'No header present: Content-Security-Policy' );
198
                unlike(
199
                    $stdout, qr{Content-Security-Policy-Report-Only:},
200
                    'No header present: Content-Security-Policy-Report-Only'
201
                );
202
203
                close STDOUT;
204
            };
205
206
            $schema->storage->txn_rollback;
207
208
        };
209
210
        subtest 'report-only' => sub {
211
212
            plan tests => 3;
213
214
            $schema->storage->txn_begin;
215
216
            t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
217
218
            C4::Context->interface('opac');
219
220
            my $expected_csp_header = 'Content-security-policy-report-only';
221
            my $expected_csp_header_value =
222
                "default-src 'self'; script-src 'self' 'unsafe-eval' _CSP_NONCE_; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'";
223
            t::lib::Mocks::mock_config(
224
                'content_security_policy',
225
                { 'opac' => { csp_mode => 'report-only', csp_header_value => $expected_csp_header_value } }
226
            );
227
            $expected_csp_header_value =~ s/_CSP_NONCE_//g;
228
229
            my $query = CGI->new();
230
231
            {
232
                local *STDOUT;
233
                my $stdout;
234
                open STDOUT, '>', \$stdout;
235
236
                my ( $template, $borrowernumber, $cookie ) = C4::Auth::get_template_and_user(
237
                    {
238
                        template_name   => "opac-main.tt",
239
                        type            => "opac",
240
                        query           => $query,
241
                        authnotrequired => 1,
242
                    }
243
                );
244
245
                C4::Output::output_html_with_http_headers( $query, $cookie, $template->output );
246
247
                $stdout =~ /(Content-Security-Policy[A-Za-z\-]*): (.*)\r/im;
248
                my ( $csp_header, $csp_header_value ) = ( $1, $2 );
249
                isnt(
250
                    $csp_header, 'Content-Security-Policy',
251
                    'No header present: Content-Security-Policy'
252
                );
253
                is( $csp_header, $expected_csp_header, 'Header present: Content-Security-Policy-Report-Only' );
254
                is(
255
                    $csp_header_value, $expected_csp_header_value,
256
                    'Content-Security-Policy-Report-Only value matches expected value'
257
                );
258
259
                close STDOUT;
260
            };
261
262
            $schema->storage->txn_rollback;
263
264
        };
265
266
        subtest 'enabled' => sub {
267
268
            plan tests => 3;
269
270
            $schema->storage->txn_begin;
271
272
            t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
273
274
            C4::Context->interface('opac');
275
276
            my $expected_csp_header = 'Content-security-policy';
277
            my $expected_csp_header_value =
278
                "default-src 'self'; script-src 'self' 'unsafe-eval' _CSP_NONCE_; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'";
279
            t::lib::Mocks::mock_config(
280
                'content_security_policy',
281
                { 'opac' => { csp_mode => 'enabled', csp_header_value => $expected_csp_header_value } }
282
            );
283
            $expected_csp_header_value =~ s/_CSP_NONCE_//g;
284
285
            my $query = CGI->new();
286
287
            {
288
                local *STDOUT;
289
                my $stdout;
290
                open STDOUT, '>', \$stdout;
291
292
                my ( $template, $borrowernumber, $cookie ) = C4::Auth::get_template_and_user(
293
                    {
294
                        template_name   => "opac-main.tt",
295
                        type            => "opac",
296
                        query           => $query,
297
                        authnotrequired => 1,
298
                    }
299
                );
300
301
                C4::Output::output_html_with_http_headers( $query, $cookie, $template->output );
302
303
                $stdout =~ /(Content-Security-Policy[A-Za-z\-]*): (.*)\r/im;
304
                my ( $csp_header, $csp_header_value ) = ( $1, $2 );
305
                isnt(
306
                    $csp_header, 'Content-Security-Policy-Report-Only:',
307
                    'No header present: Content-Security-Policy-Report-Only'
308
                );
309
                is( $csp_header, $expected_csp_header,       'Header present: Content-Security-Policy' );
310
                is( $csp_header_value,  $expected_csp_header_value, 'Content-Security-Policy value matches expected value' );
311
312
                close STDOUT;
313
            };
314
315
            $schema->storage->txn_rollback;
316
317
        };
318
    };
319
};

Return to bug 38365