Bug 38365: Move default Content-Security-Policy value into Perl module

By defining a default policy in the Perl module, we can easily update
it over time. Using koha-conf.xml will just be for overrides, so people
can manage their own there.

Signed-off-by: Lari Taskula <lari.taskula@hypernova.fi>
Signed-off-by: David Cook <dcook@prosentient.com.au>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
This commit is contained in:
David Cook 2026-02-17 05:31:35 +00:00 committed by Jonathan Druart
parent b14528e2f8
commit da52fd9c6f
No known key found for this signature in database
GPG Key ID: A085E712BEF0E0F0
4 changed files with 81 additions and 43 deletions

View File

@ -88,11 +88,10 @@ sub header_name {
my $conf_csp = $self->{config}->get('content_security_policy');
Koha::Exceptions::Config::MissingEntry->throw( error => 'Missing request content_security_policy csp_header_value' )
unless exists $conf_csp->{$interface}->{csp_mode};
return 'Content-Security-Policy-Report-Only' if $conf_csp->{$interface}->{csp_mode} eq 'report-only';
return 'Content-Security-Policy' if $conf_csp->{$interface}->{csp_mode} eq 'enabled';
if ( $conf_csp && $conf_csp->{$interface} && $conf_csp->{$interface}->{csp_mode} ) {
return 'Content-Security-Policy-Report-Only' if $conf_csp->{$interface}->{csp_mode} eq 'report-only';
return 'Content-Security-Policy' if $conf_csp->{$interface}->{csp_mode} eq 'enabled';
}
Koha::Exceptions::Config::MissingEntry->throw(
error => 'Content Security Policy is disabled. Header name should only be retrieved when CSP is enabled.' );
@ -112,8 +111,6 @@ sub header_name {
Returns content_security_policy.[opac|staff].csp_header_value
Throws Koha::Exceptions::Config::MissingEntry is CSP property "csp_header_value" is missing in KOHA_CONF
=cut
sub header_value {
@ -121,15 +118,29 @@ sub header_value {
my $interface = $args->{interface} || C4::Context->interface;
my @default_policy_lines = (
q#default-src 'self'#,
q# script-src 'self' 'nonce-_CSP_NONCE_'#,
q# style-src 'self' 'nonce-_CSP_NONCE_'#,
q# style-src-attr 'unsafe-inline'#,
q# img-src 'self' data:#,
q# font-src 'self'#,
q# object-src 'none'#,
);
my $default_policy = join( ';', @default_policy_lines );
my $conf_csp = $self->{config}->get('content_security_policy');
Koha::Exceptions::Config::MissingEntry->throw( error => 'Missing request content_security_policy csp_header_value' )
unless exists $conf_csp->{$interface}->{csp_header_value};
my $user_policy = $conf_csp->{$interface}->{csp_header_value};
my $csp_header_value = $conf_csp->{$interface}->{csp_header_value};
my $csp_policy = ($user_policy) ? $user_policy : $default_policy;
my $csp_header_value = $csp_policy;
my $csp_nonce = $self->get_nonce;
$csp_header_value =~ s/_CSP_NONCE_/$csp_nonce/g;
if ($csp_nonce) {
$csp_header_value =~ s/_CSP_NONCE_/$csp_nonce/g;
}
return $csp_header_value;
}

View File

@ -514,21 +514,39 @@ __END_SRU_PUBLICSERVER__
csp_header_value: The CSP policy directives. Special placeholders:
- _CSP_NONCE_: Replaced with a unique nonce for each request
Recommended workflow:
1. Start with report-only mode to identify violations
2. Review logs and fix any legitimate violations
3. Switch to enabled mode once violations are resolved
NOTE: Koha comes with a hard-coded csp_header_value. You only need to define your own
csp_header_value if you want or need a different Content-Security-Policy value.
To enable violation reporting, create your own csp_header_value including
'report-to' pointing to csp-violations and add 'report-uri'.
NOTE: You must be using HTTPS to receive violation reports.
To enable violation reporting, add report-to to your policy pointing to csp-violations.
Example with reporting:
<csp_header_value>default-src 'self'; script-src 'self' 'nonce-_CSP_NONCE_'; style-src 'self' 'nonce-_CSP_NONCE_'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; report-uri /api/v1/public/csp-reports; report-to csp-violations</csp_header_value>
<content_security_policy>
<opac>
<csp_mode>report-only</csp_mode>
<csp_header_value>default-src 'self'; script-src 'self' 'nonce-_CSP_NONCE_'; style-src 'self' 'nonce-_CSP_NONCE_'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; report-uri /api/v1/public/csp-reports; report-to csp-violations</csp_header_value>
</opac>
<intranet>
<csp_mode>report-only</csp_mode>
<csp_header_value>default-src 'self'; script-src 'self' 'nonce-_CSP_NONCE_'; style-src 'self' 'nonce-_CSP_NONCE_'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; report-uri /api/v1/public/csp-reports; report-to csp-violations</csp_header_value>
</intranet>
</content_security_policy>
Reports are logged via Koha's logging system. Configure log4perl to capture them:
log4perl.logger.api.csp = WARN, CSP
log4perl.appender.CSP = Log::Log4perl::Appender::File
log4perl.appender.CSP.filename = /var/log/koha/__KOHASITE__/csp-violations.log
log4perl.appender.CSP.layout = PatternLayout
log4perl.appender.CSP.layout.ConversionPattern = [%d] %m%n
log4perl.logger.plack-csp = WARN, PLACKCSP
log4perl.appender.PLACKCSP=Log::Log4perl::Appender::File
log4perl.appender.PLACKCSP.filename=/var/log/koha/<kohasite>/plack-csp-violations.log
log4perl.appender.PLACKCSP.mode=append
log4perl.appender.PLACKCSP.layout=PatternLayout
log4perl.appender.PLACKCSP.layout.ConversionPattern=[%d] %m%n
log4perl.appender.PLACKCSP.utf8=1
Recommended workflow:
1. Start with report-only mode to identify violations
2. Review logs and fix any legitimate violations (by fixing the code or by modifying your csp_header_value)
3. Switch to enabled mode once violations are resolved
-->
<content_security_policy>
<opac>

View File

@ -325,21 +325,39 @@
csp_header_value: The CSP policy directives. Special placeholders:
- _CSP_NONCE_: Replaced with a unique nonce for each request
Recommended workflow:
1. Start with report-only mode to identify violations
2. Review logs and fix any legitimate violations
3. Switch to enabled mode once violations are resolved
NOTE: Koha comes with a hard-coded csp_header_value. You only need to define your own
csp_header_value if you want or need a different Content-Security-Policy value.
To enable violation reporting, create your own csp_header_value including
'report-to' pointing to csp-violations and add 'report-uri'.
NOTE: You must be using HTTPS to receive violation reports.
To enable violation reporting, add report-to to your policy pointing csp-violations.
Example with reporting:
<csp_header_value>default-src 'self'; script-src 'self' 'nonce-_CSP_NONCE_'; style-src 'self' 'nonce-_CSP_NONCE_'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; report-uri /api/v1/public/csp-reports; report-to csp-violations</csp_header_value>
<content_security_policy>
<opac>
<csp_mode>report-only</csp_mode>
<csp_header_value>default-src 'self'; script-src 'self' 'nonce-_CSP_NONCE_'; style-src 'self' 'nonce-_CSP_NONCE_'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; report-uri /api/v1/public/csp-reports; report-to csp-violations</csp_header_value>
</opac>
<intranet>
<csp_mode>report-only</csp_mode>
<csp_header_value>default-src 'self'; script-src 'self' 'nonce-_CSP_NONCE_'; style-src 'self' 'nonce-_CSP_NONCE_'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; report-uri /api/v1/public/csp-reports; report-to csp-violations</csp_header_value>
</intranet>
</content_security_policy>
Reports are logged via Koha's logging system. Configure log4perl to capture them:
log4perl.logger.api.csp = WARN, CSP
log4perl.appender.CSP = Log::Log4perl::Appender::File
log4perl.appender.CSP.filename = /var/log/koha/csp-violations.log
log4perl.appender.CSP.layout = PatternLayout
log4perl.appender.CSP.layout.ConversionPattern = [%d] %m%n
log4perl.logger.plack-csp = WARN, PLACKCSP
log4perl.appender.PLACKCSP=Log::Log4perl::Appender::File
log4perl.appender.PLACKCSP.filename=/var/log/koha/<kohasite>/plack-csp-violations.log
log4perl.appender.PLACKCSP.mode=append
log4perl.appender.PLACKCSP.layout=PatternLayout
log4perl.appender.PLACKCSP.layout.ConversionPattern=[%d] %m%n
log4perl.appender.PLACKCSP.utf8=1
Recommended workflow:
1. Start with report-only mode to identify violations
2. Review logs and fix any legitimate violations (by fixing the code or by modifying your csp_header_value)
3. Switch to enabled mode once violations are resolved
-->
<content_security_policy>
<opac>

View File

@ -117,22 +117,13 @@ subtest 'header_name tests' => sub {
};
subtest 'header_value tests' => sub {
plan tests => 6;
plan tests => 3;
t::lib::Mocks::mock_config( $conf_csp_section, {} );
C4::Context->interface('opac');
my $csp = Koha::ContentSecurityPolicy->new;
throws_ok { $csp->header_value } 'Koha::Exceptions::Config::MissingEntry',
'Exception thrown when missing csp_header_value';
t::lib::Mocks::mock_config( $conf_csp_section, { opac => {} } );
throws_ok { $csp->header_value } 'Koha::Exceptions::Config::MissingEntry',
'Exception thrown when missing csp_header_value';
t::lib::Mocks::mock_config( $conf_csp_section, { opac => { csp_header_value => '' } } );
is( $csp->header_value, '', 'Even an empty csp_header_value is retrieved form koha-conf.xml' );
t::lib::Mocks::mock_config( $conf_csp_section, { opac => { csp_header_value => 'some value' } } );
is( $csp->header_value, 'some value', 'csp_header_value is retrieved from koha-conf.xml' );