Support CSP in Matomo/Piwik plugin
Change-Id: Ie80c6ffca714460c310e2b55ad9b3b63f5ae71ad
diff --git a/Changes b/Changes
index 80b1bc0..1c6bb51 100755
--- a/Changes
+++ b/Changes
@@ -17,6 +17,9 @@
- Support CSP in notifications framework.
- Fetch plugin configs from JSON file to be
CSP compliant.
+ - Support CSP in Matomo/Piwik plugin.
+ - Removed deprecated default behaviour
+ of the Piwik/Matomo plugin.
0.40 2020-12-17
- Modernize ES and fix in-loops.
diff --git a/Makefile.PL b/Makefile.PL
index d2dca03..8aaee56 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -33,7 +33,7 @@
'JSON' => 4.02,
# Required for bundled plugins
- 'Mojolicious::Plugin::Piwik' => 0.26,
+ 'Mojolicious::Plugin::Piwik' => 0.28,
# Currently on GitHub only (github.com/akron)
'Mojolicious::Plugin::Localize' => 0.20,
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index 1dbc373..59d2695 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -164,16 +164,17 @@
});
# Establish content security policy
+ # This needs to be defined prior to Kalamar::Plugin::Piwik!
$self->plugin(CSP => {
'default-src' => 'self',
- 'style-src' => ['self','unsafe-inline'],
- 'script-src' => 'self',
+ 'style-src' => ['self','unsafe-inline'],
+ 'script-src' => 'self',
'connect-src' => 'self',
- 'frame-src' => '*',
- 'media-src' => 'none',
- 'object-src' => 'self',
- 'font-src' => 'self',
- 'img-src' => ['self', 'data:'],
+ 'frame-src' => '*',
+ 'media-src' => 'none',
+ 'object-src' => 'self',
+ 'font-src' => 'self',
+ 'img-src' => ['self', 'data:'],
-with_nonce => 1
});
@@ -251,15 +252,6 @@
};
# Deprecated Legacy code
- if ($self->config('Piwik') &&
- none { $_ eq 'Piwik' } @{$conf->{plugins} // []}) {
-
- # 2018-11-12
- deprecated 'Piwik is no longer considered a mandatory plugin';
- $self->plugin('Kalamar::Plugin::Piwik');
- };
-
- # Deprecated Legacy code
if ($self->config('Kalamar')->{auth_support} &&
none { $_ eq 'Auth' } @{$conf->{plugins} // []}) {
diff --git a/lib/Kalamar/Plugin/Piwik.pm b/lib/Kalamar/Plugin/Piwik.pm
index e991250..ca414ff 100644
--- a/lib/Kalamar/Plugin/Piwik.pm
+++ b/lib/Kalamar/Plugin/Piwik.pm
@@ -14,6 +14,28 @@
};
};
+ # Add event handler for korap requests
+ my $piwik_conf = $mojo->config('Piwik');
+ if ($piwik_conf) {
+ $piwik_conf->{append} //= '';
+ }
+ else {
+ $piwik_conf = { append => '' };
+ $mojo->config(Piwik => $piwik_conf);
+ };
+
+ my $url = $piwik_conf->{url};
+
+ $piwik_conf->{append} .= <<APPEND;
+;window.addEventListener('korapRequest', function(e) {
+ let _paq=window._paq=window._paq||[];
+ _paq.push(['setDocumentTitle', e.detail.title]);
+ _paq.push(['setReferrerUrl', location.href]);
+ _paq.push(['setCustomUrl', e.detail.url]);
+ _paq.push(['trackPageView']);
+})
+APPEND
+
# Load Piwik if not yet loaded
unless (exists $mojo->renderer->helpers->{piwik_tag}) {
$mojo->plugin('Piwik');
@@ -37,28 +59,20 @@
}
);
+ # Add tracking code as <script/> instead of inline
+
+ $mojo->csp->add('script-src' => $url);
+ $mojo->csp->add('connect-src' => $url);
+ $mojo->csp->add('img-src' => $url);
+
+ # Set track script for CSP compliant tracking
+ $mojo->routes->any('/js/tracking.js')->piwik('track_script');
+
# Add piwik tag to scripts
$mojo->content_block(scripts => {
- inline => '<%= piwik_tag %>'
+ inline => q!<%= piwik_tag 'as-script' %>!
});
- # Add event handler for korap requests
- $mojo->content_block(scripts => {
- inline => <<'SCRIPT'
-% if (stash('piwik.embed')) {
- %= javascript begin
-window.addEventListener('korapRequest', function(e) {
- _paq.push(['setDocumentTitle', e.detail.title]);
- _paq.push(['setReferrerUrl', location.href]);
- _paq.push(['setCustomUrl', e.detail.url]);
- _paq.push(['trackPageView']);
-});
- % end
-% }
-SCRIPT
- });
-
-
# If all requests should be pinged,
# establish this hook
if ($param->{ping_requests}) {
diff --git a/t/plugin/piwik.t b/t/plugin/piwik.t
index 5559cc6..a2ad05c 100644
--- a/t/plugin/piwik.t
+++ b/t/plugin/piwik.t
@@ -3,12 +3,12 @@
use Test::Mojo;
# Test the documentation
-my $t = Test::Mojo->new('Kalamar');
-
-$t->app->plugin('Piwik' => {
- url => 'https://piwik.korap.ids-mannheim.de/',
- site_id => 1,
- embed => 1
+my $t = Test::Mojo->new('Kalamar' => {
+ 'Piwik' => {
+ url => 'https://piwik.korap.ids-mannheim.de/',
+ site_id => 1,
+ embed => 1
+ }
});
# Load piwik
@@ -18,27 +18,63 @@
->status_is(200)
->text_like('section[name=piwik-opt-out] h3', qr!can I opt-out!)
->element_exists('section[name=piwik-opt-out] iframe')
+ ->content_unlike(qr!var _paq!)
+ ->content_unlike(qr!window\.addEventListener\('korapRequest!)
+ ->content_unlike(qr!setDocumentTitle!)
+ ->content_unlike(qr!setCustomUrl!)
+ ->content_unlike(qr!trackPageView!)
+ ->element_exists('script[src$="/js/tracking.js"]')
+ ;
+
+$t = Test::Mojo->new('Kalamar' => {
+ 'Piwik' => {
+ url => 'https://piwik.korap.ids-mannheim.de/',
+ site_id => 1,
+ embed => 1,
+ append => 'console.log("fun")'
+ }
+});
+
+$t->app->plugin('Kalamar::Plugin::Piwik');
+
+is($t->app->piwik_tag('as-script'), '<script src="/js/tracking.js"></script>' .
+ '<script src="https://piwik.korap.ids-mannheim.de/piwik.js" async defer></script>');
+
+$t->get_ok('/doc/faq')
+ ->status_is(200)
+ ->text_like('section[name=piwik-opt-out] h3', qr!can I opt-out!)
+ ->element_exists('section[name=piwik-opt-out] iframe')
+ ->element_exists('script[src$="/js/tracking.js"]')
+ ->content_unlike(qr!_paq!)
+ ->header_like('Content-Security-Policy',qr!connect-src 'self' [^;]*?https://piwik\.korap\.ids-mannheim\.de/!)
+ ->header_like('Content-Security-Policy',qr!img-src 'self' [^;]*?https://piwik\.korap\.ids-mannheim\.de/!)
+ ->header_like('Content-Security-Policy',qr!script-src 'self' [^;]*?https://piwik.korap.ids-mannheim.de/!)
+ ;
+
+$t->get_ok('/js/tracking.js')
+ ->status_is(200)
->content_like(qr!var _paq!)
- ->content_like(qr!window\.addEventListener\('korapRequest!)
- ->content_like(qr!setDocumentTitle!)
- ->content_like(qr!setCustomUrl!)
- ->content_like(qr!trackPageView!)
+ ->content_like(qr!;console\.log\("fun"\)!)
+ ->content_like(qr!;window\.addEventListener\('korapRequest!)
;
# No embedding
-$t->app->plugin('Piwik' => {
- url => 'https://piwik.korap.ids-mannheim.de/',
- site_id => 1,
- embed => 0
+$t = Test::Mojo->new('Kalamar' => {
+ 'Piwik' => {
+ url => 'https://piwik.korap.ids-mannheim.de/',
+ site_id => 1,
+ embed => 0
+ }
});
+$t->app->plugin('Kalamar::Plugin::Piwik');
$t->get_ok('/doc/faq')
->status_is(200)
->text_like('section[name=piwik-opt-out] h3', qr!can I opt-out!)
->element_exists_not('section[name=piwik-opt-out] iframe')
- ->content_unlike(qr!var _paq!)
+ ->content_unlike(qr!_paq!)
->content_unlike(qr!window\.addEventListener\('korapRequest!)
+ ->element_exists_not('script[src$="/js/tracking.js"]')
;
-
-done_testing();
+done_testing;