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

(-)a/Koha/REST/V1/CSPReports.pm (+7 lines)
Lines 81-86 sub add { Link Here
81
        my $line_number   = $csp_report->{'lineNumber'}   // $csp_report->{'line-number'}   // '';
81
        my $line_number   = $csp_report->{'lineNumber'}   // $csp_report->{'line-number'}   // '';
82
        my $column_number = $csp_report->{'columnNumber'} // $csp_report->{'column-number'} // '';
82
        my $column_number = $csp_report->{'columnNumber'} // $csp_report->{'column-number'} // '';
83
83
84
        $document_uri  = length($document_uri) > 200 ? substr( $document_uri,  0, 200 ) . "..." : $document_uri;
85
        $violated_dir  = length($violated_dir) > 100 ? substr( $violated_dir,  0, 100 ) . "..." : $violated_dir;
86
        $blocked_uri   = length($blocked_uri) > 200  ? substr( $blocked_uri,   0, 200 ) . "..." : $blocked_uri;
87
        $source_file   = length($source_file) > 200  ? substr( $source_file,   0, 200 ) . "..." : $source_file;
88
        $line_number   = length($line_number) > 7    ? substr( $line_number,   0, 7 ) . "..."   : $line_number;
89
        $column_number = length($column_number) > 7  ? substr( $column_number, 0, 7 ) . "..."   : $column_number;
90
84
        # Build location string if available
91
        # Build location string if available
85
        my $location = '';
92
        my $location = '';
86
        if ($source_file) {
93
        if ($source_file) {
(-)a/api/v1/swagger/definitions/csp_report.yaml (-60 lines)
Lines 1-60 Link Here
1
---
2
type: object
3
description: |
4
  A Content-Security-Policy violation report as sent by browsers.
5
  See https://www.w3.org/TR/CSP3/#violation-reports for specification.
6
properties:
7
  document-uri:
8
    type: string
9
    description: The URI of the document where the violation occurred
10
    maxLength: 200
11
  referrer:
12
    type: string
13
    description: The referrer of the document where the violation occurred
14
    maxLength: 200
15
  violated-directive:
16
    type: string
17
    description: The directive that was violated (e.g., "script-src 'self'")
18
    maxLength: 100
19
  effective-directive:
20
    type: string
21
    description: The effective directive that was violated
22
    maxLength: 100
23
  original-policy:
24
    type: string
25
    description: The original CSP policy as specified in the header
26
    maxLength: 400
27
  disposition:
28
    type: string
29
    description: Either "enforce" or "report" depending on CSP mode
30
    enum:
31
      - enforce
32
      - report
33
  blocked-uri:
34
    type: string
35
    description: The URI of the resource that was blocked
36
    maxLength: 200
37
  line-number:
38
    type: integer
39
    description: Line number in the document where the violation occurred
40
    minimum: 0
41
    maximum: 999999
42
  column-number:
43
    type: integer
44
    description: Column number where the violation occurred
45
    minimum: 0
46
    maximum: 999999
47
  source-file:
48
    type: string
49
    description: The URI of the script where the violation occurred
50
    maxLength: 200
51
  status-code:
52
    type: integer
53
    description: HTTP status code of the document
54
    minimum: 0
55
    maximum: 999
56
  script-sample:
57
    type: string
58
    description: First 40 characters of the inline script that caused the violation
59
    maxLength: 41
60
additionalProperties: true
(-)a/api/v1/swagger/definitions/csp_report_body.yaml (-56 lines)
Lines 1-56 Link Here
1
---
2
type: object
3
description: |
4
  A Content-Security-Policy violation report as sent by browsers.
5
  See https://www.w3.org/TR/CSP3/#violation-reports for specification.
6
properties:
7
  documentURL:
8
    type: string
9
    description: The URI of the document where the violation occurred
10
    maxLength: 200
11
  referrer:
12
    type: string
13
    description: The referrer of the document where the violation occurred
14
    maxLength: 200
15
  effectiveDirective:
16
    type: string
17
    description: The effective directive that was violated
18
    maxLength: 100
19
  originalPolicy:
20
    type: string
21
    description: The original CSP policy as specified in the header
22
    maxLength: 400
23
  disposition:
24
    type: string
25
    description: Either "enforce" or "report" depending on CSP mode
26
    enum:
27
      - enforce
28
      - report
29
  blockedURL:
30
    type: string
31
    description: The URI of the resource that was blocked
32
    maxLength: 200
33
  lineNumber:
34
    type: integer
35
    description: Line number in the document where the violation occurred
36
    minimum: 0
37
    maximum: 999999
38
  columnNumber:
39
    type: integer
40
    description: Column number where the violation occurred
41
    maximum: 999999
42
    minimum: 0
43
  sample:
44
    type: string
45
    description: First 40 characters of the inline script that caused the violation
46
    maxLength: 41
47
  sourceFile:
48
    type: string
49
    description: The URI of the script where the violation occurred
50
    maxLength: 200
51
  statusCode:
52
    type: integer
53
    description: HTTP status code of the document
54
    minimum: 0
55
    maximum: 999
56
additionalProperties: true
(-)a/api/v1/swagger/definitions/csp_report_uri_to.yaml (-27 lines)
Lines 1-27 Link Here
1
---
2
type:
3
  - array
4
  - object
5
description: |
6
  A Content-Security-Policy violation report as sent by browsers.
7
  See https://www.w3.org/TR/CSP3/#violation-reports for specification.
8
properties:
9
  csp-report:
10
    $ref: "csp_report.yaml"
11
  age:
12
    type: integer
13
    minimum: 0
14
    maximum: 9999999999
15
  body:
16
    $ref: "csp_report_body.yaml"
17
  user_agent:
18
    type: string
19
    maxLength: 300
20
  url:
21
    type: string
22
    maxLength: 200
23
  type:
24
    type: string
25
    enum:
26
      - "csp-violation"
27
additionalProperties: true
(-)a/api/v1/swagger/paths/public_csp_reports.yaml (-7 lines)
Lines 20-32 Link Here
20
      - application/reports+json
20
      - application/reports+json
21
    produces:
21
    produces:
22
      - application/json
22
      - application/json
23
    parameters:
24
      - name: body
25
        in: body
26
        description: CSP violation report
27
        required: true
28
        schema:
29
          $ref: "../swagger.yaml#/definitions/csp_report_uri_to"
30
    responses:
23
    responses:
31
      "204":
24
      "204":
32
        description: Report received successfully
25
        description: Report received successfully
(-)a/api/v1/swagger/swagger.yaml (-4 lines)
Lines 42-51 definitions: Link Here
42
    $ref: ./definitions/circ-rule-kind.yaml
42
    $ref: ./definitions/circ-rule-kind.yaml
43
  city:
43
  city:
44
    $ref: ./definitions/city.yaml
44
    $ref: ./definitions/city.yaml
45
  csp_report:
46
    $ref: ./definitions/csp_report.yaml
47
  csp_report_uri_to:
48
    $ref: ./definitions/csp_report_uri_to.yaml
49
  credit:
45
  credit:
50
    $ref: ./definitions/credit.yaml
46
    $ref: ./definitions/credit.yaml
51
  debit:
47
  debit:
(-)a/t/db_dependent/api/v1/public/csp_reports.t (-34 / +69 lines)
Lines 42-48 subtest 'add() tests' => sub { Link Here
42
42
43
    foreach my $csp_report_type ( 'report-uri', 'report-to' ) {
43
    foreach my $csp_report_type ( 'report-uri', 'report-to' ) {
44
        subtest "Test $csp_report_type" => sub {
44
        subtest "Test $csp_report_type" => sub {
45
            plan tests => 17;
45
            plan tests => 9;
46
            my $content_type = $csp_report_type eq 'report-to' ? 'application/csp-report' : 'application/reports+json';
46
            my $content_type = $csp_report_type eq 'report-to' ? 'application/csp-report' : 'application/reports+json';
47
47
48
            # Test with standard CSP report-uri format
48
            # Test with standard CSP report-uri format
Lines 69-105 subtest 'add() tests' => sub { Link Here
69
            _convert_report_uri_to_report_to( $csp_param_names, $csp_report ) if $csp_report_type eq 'report-to';
69
            _convert_report_uri_to_report_to( $csp_param_names, $csp_report ) if $csp_report_type eq 'report-to';
70
            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';
71
71
72
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = 99999999999999999;
73
74
            # Too large integers should be rejected
75
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
76
                ->status_is( 400, 'CSP report rejected (400) because of line-number exceeding maximum value' );
77
78
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = -1;
79
80
            # Too small integers should be rejected
81
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
82
                ->status_is( 400, 'CSP report rejected (400) because of line-number not reaching minimum value' );
83
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = 42;
84
85
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'disposition'} } = 'this is not okay';
86
87
            # Enum values should be confirmed
88
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
89
                ->status_is(
90
                400,
91
                'CSP report rejected (400) because of disposition is not either "enforce" nor "report"'
92
                );
93
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'disposition'} } = 'enforce';
94
95
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'script-sample'} } =
96
                'this is way too long script sample. a maximum of only 40 characters is allowed';
97
98
            # Too long strings should be rejected
99
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
100
                ->status_is( 400, 'CSP report rejected (400) because of script-sample exceeding maximum length' );
101
            $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'script-sample'} } = 'console.log("hi");';
102
103
            # Anonymous request should work (browsers send these without auth)
72
            # Anonymous request should work (browsers send these without auth)
104
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
73
            $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
105
                ->status_is( 204, 'CSP report accepted' );
74
                ->status_is( 204, 'CSP report accepted' );
Lines 135-141 subtest 'add() tests' => sub { Link Here
135
                ->status_is( 204, 'Minimal CSP report accepted' );
104
                ->status_is( 204, 'Minimal CSP report accepted' );
136
105
137
            subtest 'make sure log entries are being written' => sub {
106
            subtest 'make sure log entries are being written' => sub {
138
                plan tests => 22;
107
                plan tests => 30;
139
108
140
                my $log4perl_conf_file = C4::Context->config('intranetdir') . '/etc/log4perl.conf';
109
                my $log4perl_conf_file = C4::Context->config('intranetdir') . '/etc/log4perl.conf';
141
                open my $fh, '<:encoding(UTF-8)', $log4perl_conf_file or do {
110
                open my $fh, '<:encoding(UTF-8)', $log4perl_conf_file or do {
Lines 213-218 subtest 'add() tests' => sub { Link Here
213
                $appender->clear();
182
                $appender->clear();
214
                $appenderplack->clear();
183
                $appenderplack->clear();
215
184
185
                # Do not crash if input is not JSON
186
                $t->post_ok( '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => "not json" )
187
                    ->status_is( 204, 'CSP report accepted' );
188
189
                # Too large integers should be truncated
190
                $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = 99999999999999999;
191
                $t->post_ok(
192
                    '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
193
                    ->status_is(204);
194
                $expected_log_entry = sprintf(
195
                    "CSP violation: '%s' blocked '%s' on page '%s'%s",
196
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'violated-directive'} },
197
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'blocked-uri'} },
198
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'document-uri'} },
199
                    ' at '
200
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'source-file'} } . ':'
201
                        . '9999999...:'
202
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'column-number'} }
203
                );
204
205
                like(
206
                    $appender->buffer, qr/$expected_log_entry/
207
                    ,                  'Too long line number was truncated'
208
                );
209
                $appender->clear();
210
211
                $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } = 42;
212
213
                # Too long strings should be rejected
214
                $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'effective-directive'} } =
215
                    'this is an example of a way too long effective-directive. a maximum of only 100 characters is allowed. so this should be truncated';
216
                $t->post_ok(
217
                    '/api/v1/public/csp-reports' => { 'Content-Type' => $content_type } => json => $csp_report )
218
                    ->status_is(204);
219
220
                $expected_log_entry = sprintf(
221
                    "CSP violation: '%s' blocked '%s' on page '%s'%s",
222
                    substr(
223
                        $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'effective-directive'} }, 0, 100
224
                        )
225
                        . '...',
226
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'blocked-uri'} },
227
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'document-uri'} },
228
                    ' at '
229
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'source-file'} } . ':'
230
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } . ':'
231
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'column-number'} }
232
                );
233
234
                like(
235
                    $appender->buffer, qr/$expected_log_entry/
236
                    ,                  'Too long line number was truncated'
237
                );
238
                $appender->clear();
239
                $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'effective-directive'} } = 'script-src';
240
241
                $expected_log_entry = sprintf(
242
                    "CSP violation: '%s' blocked '%s' on page '%s'%s",
243
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'violated-directive'} },
244
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'blocked-uri'} },
245
                    $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'document-uri'} },
246
                    ' at '
247
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'source-file'} } . ':'
248
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'line-number'} } . ':'
249
                        . $csp_report->{$csp_report_body_key}->{ $csp_param_names->{'column-number'} }
250
                );
251
216
                subtest 'test array as input' => sub {
252
                subtest 'test array as input' => sub {
217
                    plan tests => 7;
253
                    plan tests => 7;
218
254
219
- 

Return to bug 38365