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

(-)a/C4/Auth.pm (+2 lines)
Lines 49-54 use C4::Auth_with_shibboleth qw( shib_ok get_login_shib login_shib_url logout_sh Link Here
49
use Net::CIDR;
49
use Net::CIDR;
50
use C4::Log qw( logaction );
50
use C4::Log qw( logaction );
51
use Koha::CookieManager;
51
use Koha::CookieManager;
52
use Koha::Token;
52
53
53
# use utf8;
54
# use utf8;
54
55
Lines 299-304 sub get_template_and_user { Link Here
299
        $template->param( loggedinusernumber => $borrowernumber ); # FIXME Should be replaced with logged_in_user.borrowernumber
300
        $template->param( loggedinusernumber => $borrowernumber ); # FIXME Should be replaced with logged_in_user.borrowernumber
300
        $template->param( logged_in_user     => $patron );
301
        $template->param( logged_in_user     => $patron );
301
        $template->param( sessionID          => $sessionID );
302
        $template->param( sessionID          => $sessionID );
303
        $template->param( csrf_token => Koha::Token->new->generate_csrf({ session_id => scalar $sessionID }));
302
304
303
        if ( $in->{'type'} eq 'opac' ) {
305
        if ( $in->{'type'} eq 'opac' ) {
304
            require Koha::Virtualshelves;
306
            require Koha::Virtualshelves;
(-)a/C4/Output.pm (-6 / +27 lines)
Lines 34-39 use URI::Escape; Link Here
34
use C4::Auth qw( get_template_and_user );
34
use C4::Auth qw( get_template_and_user );
35
use C4::Context;
35
use C4::Context;
36
use C4::Templates;
36
use C4::Templates;
37
use Koha::Token;
37
38
38
our (@ISA, @EXPORT_OK);
39
our (@ISA, @EXPORT_OK);
39
40
Lines 314-322 sub is_ajax { Link Here
314
To executed at the beginning of scripts to stop the script at this point if
315
To executed at the beginning of scripts to stop the script at this point if
315
some errors are found.
316
some errors are found.
316
317
317
Tests for module 'members':
318
A series of tests can be run for a given module, or a specific check.
318
* patron is not defined (we are looking for a patron that does no longer exist/never existed)
319
Params "module" and "check" are mutually exclusive.
319
* The logged in user cannot see patron's infos (feature 'cannot_see_patron_infos')
320
321
Tests for modules:
322
* members:
323
    - Patron is not defined (we are looking for a patron that does no longer exist/never existed)
324
    - The logged in user cannot see patron's infos (feature 'cannot_see_patron_infos')
325
326
Tests for specific check:
327
* csrf_token
328
    will test if the csrf_token CGI param is valid
320
329
321
Others will be added here depending on the needs (for instance biblio does not exist will be useful).
330
Others will be added here depending on the needs (for instance biblio does not exist will be useful).
322
331
Lines 332-347 sub output_and_exit_if_error { Link Here
332
            if ( not $current_patron ) {
341
            if ( not $current_patron ) {
333
                $error = 'unknown_patron';
342
                $error = 'unknown_patron';
334
            }
343
            }
335
            elsif( not $logged_in_user->can_see_patron_infos( $current_patron ) ) {
344
            elsif ( not $logged_in_user->can_see_patron_infos($current_patron) )
345
            {
336
                $error = 'cannot_see_patron_infos';
346
                $error = 'cannot_see_patron_infos';
337
            }
347
            }
338
        } elsif ( $params->{module} eq 'cataloguing' ) {
348
        }
349
        elsif ( $params->{module} eq 'cataloguing' ) {
339
            # We are testing the record to avoid additem to fetch the Koha::Biblio
350
            # We are testing the record to avoid additem to fetch the Koha::Biblio
340
            # But in the long term we will want to get a biblio in parameter
351
            # But in the long term we will want to get a biblio in parameter
341
            $error = 'unknown_biblio' unless $params->{record};
352
            $error = 'unknown_biblio' unless $params->{record};
342
        }
353
        }
343
    }
354
    }
344
355
    elsif ( $params and exists $params->{check} ) {
356
        if ( $params->{check} eq 'csrf_token' ) {
357
            $error = 'wrong_csrf_token'
358
              unless Koha::Token->new->check_csrf(
359
                {
360
                    session_id => scalar $query->cookie('CGISESSID'),
361
                    token      => scalar $query->param('csrf_token'),
362
                }
363
              );
364
        }
365
    }
345
    output_and_exit( $query, $cookie, $template, $error ) if $error;
366
    output_and_exit( $query, $cookie, $template, $error ) if $error;
346
    return;
367
    return;
347
}
368
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/csrf-token.inc (+1 lines)
Line 0 Link Here
1
<input type="hidden" name="csrf_token" value="[% csrf_token | html %]" />
(-)a/t/Output.t (-2 / +39 lines)
Lines 17-32 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 7;
20
use Test::More tests => 8;
21
use Test::Warn;
21
use Test::Warn;
22
use Test::MockModule;
23
24
use File::Temp qw/tempfile/;
22
use CGI qw ( -utf8 );
25
use CGI qw ( -utf8 );
23
26
27
use C4::Auth qw( get_template_and_user );
28
24
use t::lib::Mocks;
29
use t::lib::Mocks;
25
30
26
BEGIN {
31
BEGIN {
27
    use_ok('C4::Output', qw( output_html_with_http_headers parametrized_url  ));
32
    use_ok('C4::Output', qw( output_html_with_http_headers output_and_exit_if_error parametrized_url ));
28
}
33
}
29
34
35
our $output_module = Test::MockModule->new('C4::Output');
36
30
my $query = CGI->new();
37
my $query = CGI->new();
31
my $cookie;
38
my $cookie;
32
my $output = 'foobarbaz';
39
my $output = 'foobarbaz';
Lines 93-95 subtest 'output_with_http_headers() tests' => sub { Link Here
93
    like($stdout, qr/Access-control-allow-origin: https:\/\/koha-community\.org/, 'Header set to https://koha-community.org');
100
    like($stdout, qr/Access-control-allow-origin: https:\/\/koha-community\.org/, 'Header set to https://koha-community.org');
94
    close STDOUT;
101
    close STDOUT;
95
};
102
};
103
104
subtest 'output_and_exit_if_error() tests' => sub {
105
    plan tests => 1;
106
107
    $output_module->mock(
108
        'output_and_exit',
109
        sub {
110
            my ( $query, $cookie, $template, $error ) = @_;
111
            is( $error, 'wrong_csrf_token', 'Got right error message' );
112
        }
113
    );
114
115
    t::lib::Mocks::mock_config( 'pluginsdir', [ C4::Context::temporary_directory ] );
116
    my ( $fh, $fn ) = tempfile( SUFFIX => '.tt', UNLINK => 1, DIR => C4::Context::temporary_directory );
117
    print $fh qq|[% blocking_error %]|;
118
    close $fh;
119
120
    my $query = CGI->new();
121
    $query->param('csrf_token','');
122
    my ( $template, $loggedinuser, $cookies ) = get_template_and_user(
123
        {
124
            template_name   => $fn,
125
            query           => $query,
126
            type            => "intranet",
127
            authnotrequired => 1,
128
        }
129
    );
130
    # Next call triggers test in the mocked sub
131
    output_and_exit_if_error($query, $cookie, $template, { check => 'csrf_token' });
132
};
(-)a/t/db_dependent/Auth.t (+1 lines)
Lines 388-393 subtest 'get_template_and_user' => sub { # Tests for the language URL paramete Link Here
388
    );
388
    );
389
    is($template->{VARS}->{'opac_name'}, "multibranch-19", "Opac name was set correctly");
389
    is($template->{VARS}->{'opac_name'}, "multibranch-19", "Opac name was set correctly");
390
    is($template->{VARS}->{'opac_search_limit'}, "branch:multibranch-19", "Search limit was set correctly");
390
    is($template->{VARS}->{'opac_search_limit'}, "branch:multibranch-19", "Search limit was set correctly");
391
    ok(defined($template->{VARS}->{'csrf_token'}), "CSRF token returned");
391
392
392
    delete $ENV{"HTTP_COOKIE"};
393
    delete $ENV{"HTTP_COOKIE"};
393
};
394
};
(-)a/xt/find-missing-csrf.t (-1 / +82 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2021 Koha development team
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Test::More tests => 1;
22
use File::Find;
23
use File::Slurp;
24
use Data::Dumper;
25
26
my @themes;
27
28
# OPAC themes
29
my $opac_dir  = 'koha-tmpl/opac-tmpl';
30
opendir ( my $dh, $opac_dir ) or die "can't opendir $opac_dir: $!";
31
for my $theme ( grep { not /^\.|lib|js|xslt/ } readdir($dh) ) {
32
    push @themes, "$opac_dir/$theme/en";
33
}
34
close $dh;
35
36
# STAFF themes
37
my $staff_dir = 'koha-tmpl/intranet-tmpl';
38
opendir ( $dh, $staff_dir ) or die "can't opendir $staff_dir: $!";
39
for my $theme ( grep { not /^\.|lib|js/ } readdir($dh) ) {
40
    push @themes, "$staff_dir/$theme/en";
41
}
42
close $dh;
43
44
my @files;
45
sub wanted {
46
    my $name = $File::Find::name;
47
    push @files, $name
48
        if $name =~ m[\.(tt|inc)$] and -f $name;
49
}
50
51
find({ wanted => \&wanted, no_chdir => 1 }, @themes );
52
53
my @errors;
54
for my $file ( @files ) {
55
    my @e = check_csrf_in_forms($file);
56
    push @errors, { file => $file, errors => \@e } if @e;
57
}
58
59
is( @errors, 0, "Template variables should be correctly escaped" )
60
    or diag(Dumper @errors);
61
62
sub check_csrf_in_forms {
63
    my ( $file ) = @_;
64
65
    my @lines = read_file($file);
66
    my @errors;
67
    return @errors unless grep { $_ =~ m|<form| } @lines;
68
    my ( $open, $found ) = ( 0, 0 );
69
    my $line = 0 ;
70
    for my $l (@lines) {
71
        $line++;
72
        $open = $line if ( $l =~ m{<form} && !( $l =~ m{method=('|")get('|")} ) );
73
        $found++  if ( $l =~ m|csrf\-token\.inc| && $open );
74
        if ( $open && $l =~ m|</form| ) {
75
            push @errors,
76
                "The <form> starting on line $open is missing it's corresponding csrf_token include (see bug 22990)"
77
                if !$found;
78
            $found = 0;
79
        }
80
    }
81
    return @errors;
82
}

Return to bug 22990