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

(-)a/t/db_dependent/api/v1/public/csp_reports.t (-29 / +93 lines)
Lines 57-90 subtest 'add() tests' => sub { Link Here
57
                    'blocked-uri'         => 'inline',
57
                    'blocked-uri'         => 'inline',
58
                    'line-number'         => 42,
58
                    'line-number'         => 42,
59
                    'column-number'       => 10,
59
                    'column-number'       => 10,
60
                    'script-sample'       => 'console.log("hi");',
60
                    'source-file'         => 'https://library.example.org/cgi-bin/koah/opac-main.pl',
61
                    'source-file'         => 'https://library.example.org/cgi-bin/koah/opac-main.pl',
61
                    'status-code'         => 200,
62
                    'status-code'         => 200,
62
                }
63
                }
63
            };
64
            };
64
            if ( $csp_report_type eq 'report-to' ) {
65
                $csp_report->{'body'}       = delete $csp_report->{'csp-report'};
66
                $csp_report->{'age'}        = 1;
67
                $csp_report->{'type'}       = 'csp-violation';
68
                $csp_report->{'url'}        = 'https://library.example.org/cgi-bin/koha/opac-main.pl';
69
                $csp_report->{'user_agent'} = 'Test::Mojo';
70
            }
71
65
66
            # as the parameters between report-to and report-uri differ, the following hashref maintains
67
            # the proper parameter keys for both types
68
            my $csp_param_names = { map { $_ => $_ } keys %{ $csp_report->{'csp-report'} } };
69
            _convert_report_uri_to_report_to( $csp_param_names, $csp_report ) if $csp_report_type eq 'report-to';
72
            my $csp_report_body_key = $csp_report_type eq 'report-uri' ? 'csp-report' : 'body';
70
            my $csp_report_body_key = $csp_report_type eq 'report-uri' ? 'csp-report' : 'body';
73
71
74
            $csp_report->{$csp_report_body_key}->{'line-number'} = 99999999999999999;
72
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = 99999999999999999;
75
73
76
            # Too large integers should be rejected
74
            # Too large integers should be rejected
77
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
75
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
78
                ->status_is( 400, 'CSP report rejected (400) because of line-number exceeding maximum value' );
76
                ->status_is( 400, 'CSP report rejected (400) because of line-number exceeding maximum value' );
79
77
80
            $csp_report->{$csp_report_body_key}->{'line-number'} = -1;
78
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = -1;
81
79
82
            # Too small integers should be rejected
80
            # Too small integers should be rejected
83
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
81
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
84
                ->status_is( 400, 'CSP report rejected (400) because of line-number not reaching minimum value' );
82
                ->status_is( 400, 'CSP report rejected (400) because of line-number not reaching minimum value' );
85
            $csp_report->{$csp_report_body_key}->{'line-number'} = 42;
83
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = 42;
86
84
87
            $csp_report->{$csp_report_body_key}->{'disposition'} = 'this is not okay';
85
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'disposition'} } = 'this is not okay';
88
86
89
            # Enum values should be confirmed
87
            # Enum values should be confirmed
90
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
88
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
Lines 92-106 subtest 'add() tests' => sub { Link Here
92
                400,
90
                400,
93
                'CSP report rejected (400) because of disposition is not either "enforce" nor "report"'
91
                'CSP report rejected (400) because of disposition is not either "enforce" nor "report"'
94
                );
92
                );
95
            $csp_report->{$csp_report_body_key}->{'disposition'} = 'enforce';
93
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'disposition'} } = 'enforce';
96
94
97
            $csp_report->{$csp_report_body_key}->{'script-sample'} =
95
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'script-sample'} } =
98
                'this is way too long script sample. a maximum of only 40 characters is allowed';
96
                'this is way too long script sample. a maximum of only 40 characters is allowed';
99
97
100
            # Too long strings should be rejected
98
            # Too long strings should be rejected
101
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
99
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
102
                ->status_is( 400, 'CSP report rejected (400) because of script-sample exceeding maximum length' );
100
                ->status_is( 400, 'CSP report rejected (400) because of script-sample exceeding maximum length' );
103
            $csp_report->{$csp_report_body_key}->{'script-sample'} = 'console.log("hi");';
101
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'script-sample'} } = 'console.log("hi");';
104
102
105
            # Anonymous request should work (browsers send these without auth)
103
            # Anonymous request should work (browsers send these without auth)
106
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
104
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
Lines 118-137 subtest 'add() tests' => sub { Link Here
118
                    'blocked-uri'        => 'https://evil.example.com/script.js',
116
                    'blocked-uri'        => 'https://evil.example.com/script.js',
119
                }
117
                }
120
            };
118
            };
121
            if ( $csp_report_type eq 'report-to' ) {
119
            _convert_report_uri_to_report_to( $csp_param_names, $minimal_report ) if $csp_report_type eq 'report-to';
122
                $minimal_report->{'body'}       = delete $minimal_report->{'csp-report'};
123
                $minimal_report->{'age'}        = 1;
124
                $minimal_report->{'type'}       = 'csp-violation';
125
                $minimal_report->{'url'}        = 'https://library.example.org/cgi-bin/koha/opac-main.pl';
126
                $minimal_report->{'user_agent'} = 'Test::Mojo';
127
            }
128
120
129
            $t->post_ok(
121
            $t->post_ok(
130
                '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $minimal_report )
122
                '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $minimal_report )
131
                ->status_is( 204, 'Minimal CSP report accepted' );
123
                ->status_is( 204, 'Minimal CSP report accepted' );
132
124
133
            subtest 'make sure log entries are being written' => sub {
125
            subtest 'make sure log entries are being written' => sub {
134
                plan tests => 17;
126
                plan tests => 18;
135
127
136
                my $log4perl_conf_file = C4::Context->config('intranetdir') . '/etc/log4perl.conf';
128
                my $log4perl_conf_file = C4::Context->config('intranetdir') . '/etc/log4perl.conf';
137
                open my $fh, '<:encoding(UTF-8)', $log4perl_conf_file or do {
129
                open my $fh, '<:encoding(UTF-8)', $log4perl_conf_file or do {
Lines 173-185 subtest 'add() tests' => sub { Link Here
173
165
174
                my $expected_log_entry = sprintf(
166
                my $expected_log_entry = sprintf(
175
                    "CSP violation: '%s' blocked '%s' on page '%s'%s",
167
                    "CSP violation: '%s' blocked '%s' on page '%s'%s",
176
                    $csp_report->{$csp_report_body_key}->{'violated-directive'},
168
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'violated-directive'} },
177
                    $csp_report->{$csp_report_body_key}->{'blocked-uri'},
169
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'blocked-uri'} },
178
                    $csp_report->{$csp_report_body_key}->{'document-uri'},
170
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'document-uri'} },
179
                    ' at '
171
                    ' at '
180
                        . $csp_report->{$csp_report_body_key}->{'source-file'} . ':'
172
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'source-file'} } . ':'
181
                        . $csp_report->{$csp_report_body_key}->{'line-number'} . ':'
173
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } . ':'
182
                        . $csp_report->{$csp_report_body_key}->{'column-number'}
174
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'column-number'} }
183
                );
175
                );
184
176
185
                like(
177
                like(
Lines 189-194 subtest 'add() tests' => sub { Link Here
189
                is( $appenderplack->buffer, '', 'Nothing in plack log buffer yet' );
181
                is( $appenderplack->buffer, '', 'Nothing in plack log buffer yet' );
190
                $appender->clear();
182
                $appender->clear();
191
183
184
                # clear buffers
185
                $appender->clear();
186
                $appenderplack->clear();
187
188
                subtest 'test array as input' => sub {
189
                    plan tests => 7;
190
191
                    my $csp_report_copy = {%$csp_report};    # make a shallow copy
192
                    $csp_report_copy->{'body'} = { %{ $csp_report->{'body'} } };
193
194
                    # for testing multiple reports being logged correctly, differentiate the two reports
195
                    # by violatedDir
196
                    $csp_report_copy->{'body'}->{'effectiveDirective'} = 'style-src';
197
                    ( my $expected_log_entry_copy = $expected_log_entry ) =~ s/script-src/style-src/;
198
199
                    $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json =>
200
                            [ $csp_report, $csp_report_copy ] )->status_is( 204, 'Multiple CSP reports accepted' );
201
                    my @log_entries = split( /\n/, $appender->buffer );
202
                    is(
203
                        scalar @log_entries, 2,
204
                        'There are two log entries because we sent two reports simultaneously.'
205
                    );
206
                    like( $expected_log_entry, qr/: 'script-src' blocked/, 'Verify correct directive' );
207
                    like(
208
                        $log_entries[0], qr/^\[\d+.*?\] $expected_log_entry$/,
209
                        'First entry is the expected log entry'
210
                    );
211
                    like(
212
                        $expected_log_entry_copy, qr/: 'style-src' blocked/,
213
                        'Verify correct directive for the second entry'
214
                    );
215
                    like(
216
                        $log_entries[1], qr/^\[\d+.*?\] $expected_log_entry_copy$/,
217
                        'Second entry is the expected log entry'
218
                    );
219
                    $appender->clear();
220
                    }
221
                    if $csp_report_type eq 'report-to';
222
                ok(
223
                    1,
224
                    'report-uri does not support array format. this test is just a placeholder for counting planned tests'
225
                ) if $csp_report_type eq 'report-uri';
226
192
                $ENV{'plack.is.enabled.for.this.test'} = 1;    # tricking C4::Context->psgi_env
227
                $ENV{'plack.is.enabled.for.this.test'} = 1;    # tricking C4::Context->psgi_env
193
                $t->post_ok(
228
                $t->post_ok(
194
                    '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
229
                    '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
Lines 226-229 HERE Link Here
226
    $schema->storage->txn_rollback;
261
    $schema->storage->txn_rollback;
227
};
262
};
228
263
264
sub _convert_report_uri_to_report_to {
265
    my ( $csp_param_names, $csp_report ) = @_;
266
267
    foreach my $key ( keys %{ $csp_report->{'csp-report'} } ) {
268
        my $orig_key = $key;
269
        $key =~ s/uri/URL/g;    # uri is replaced with URL in the camelCase version
270
        my @parts      = split( /-/, $key );
271
        my $camelcased = shift(@parts);
272
        foreach my $part (@parts) {
273
            $camelcased .= ucfirst($part);
274
        }
275
        $csp_report->{'csp-report'}->{$camelcased} = delete $csp_report->{'csp-report'}->{$orig_key};
276
        $csp_param_names->{$orig_key} = $camelcased;
277
    }
278
279
    $csp_report->{'body'}       = delete $csp_report->{'csp-report'};
280
    $csp_report->{'age'}        = 1;
281
    $csp_report->{'type'}       = 'csp-violation';
282
    $csp_report->{'url'}        = 'https://library.example.org/cgi-bin/koha/opac-main.pl';
283
    $csp_report->{'user_agent'} = 'Test::Mojo';
284
285
    # script-sample (report-uri) => sample (report-to)
286
    $csp_report->{'body'}->{'sample'} = delete $csp_report->{'body'}->{'scriptSample'}
287
        if exists $csp_report->{'body'}->{'scriptSample'};
288
    $csp_param_names->{'script-sample'} = 'sample';
289
290
    # violated-directive does not exist in report-to
291
    delete $csp_report->{'body'}->{'violatedDir'};
292
}
293
229
1;
294
1;
230
- 

Return to bug 38365