forked from w3c/reporting
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.src.html
962 lines (757 loc) · 37.1 KB
/
index.src.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
<h1>Reporting API</h1>
<pre class="metadata">
Status: CG-DRAFT
ED: https://wicg.github.io/reporting/
Shortname: reporting
Group: wicg
Editor: Ilya Grigorik 56102, Google Inc., [email protected]
Editor: Mike West 56384, Google Inc., [email protected]
Abstract:
This document defines a generic reporting framework which allows web
developers to associate a set of named reporting endpoints with an origin.
Various platform features (like Content Security Policy, Network Error
Reporting, and others) will use these endpoints to deliver feature-specific
reports in a consistent manner.
Level: 1
Indent: 2
Version History: https://github.com/wicg/reporting/commits/master/index.src.html
Boilerplate: omit conformance, omit feedback-header
!Participate: <a href="https://github.com/wicg/reporting/issues/new">File an issue</a> (<a href="https://github.com/wicg/reporting/issues">open issues</a>)
Markup Shorthands: css off, markdown on
</pre>
<pre class="anchors">
spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/
type: dfn
text: Content-Security-Policy
text: reports directive; url: directives-reporting
spec: ECMA-262; urlPrefix: http://www.ecma-international.org/ecma-262/6.0/
type: method
text: JSON.stringify(); url: sec-json.stringify
spec: FETCH; urlPrefix: https://fetch.spec.whatwg.org/
type: dfn
text: navigation request
text: response; url: concept-response
text: request; url: concept-request
text: header; url: concept-header
text: header list; url: concept-header-list
text: main fetch
text: fetch; url: concept-fetch
text: wait for a response
text: ok status
type: attribute
for: response
text: url; url: concept-response-url
text: HTTPS state; url: concept-response-https-state
text: header list; url: concept-response-header-list
for: request
text: target browsing context; url: concept-request-target-browsing-context
for: header
text: name; url: concept-header-name
text: value; url: concept-header-value
spec: SECURE-CONTEXTS; urlPrefix: https://w3c.github.io/webappsec-secure-contexts/
type: dfn
text: potentially trustworthy; url: is-origin-trustworthy
spec: URL; urlPrefix: https://url.spec.whatwg.org/
type: dfn
text: origin of a url; url: concept-url-origin
text: URL serializer; url: concept-url-serializer
text: URL parser; url: concept-url-parser
type: interface
text: URL; url: concept-url
type: attribute
for: URL
text: username; url: concept-url-username
text: password; url: concept-url-password
spec: HTML; urlPrefix: https://html.spec.whatwg.org/multipage/
urlPrefix: infrastructure.html
type: dfn
text: ASCII case-insensitive
urlPrefix: webappapis.html
type: dfn
text: global object
text: environment settings object
text: creation URL
text: queue a task
urlPrefix: browsers.html
type: dfn
text: origin
text: top-level browsing context
spec: RFC3986; urlPrefix: https://tools.ietf.org/html/rfc3986
type: grammar
text: absolute-uri; url: section-4.3
spec: RFC6797; urlPrefix: https://tools.ietf.org/html/rfc6797
type: dfn
url: section-8.2
text: superdomain match
text: congruent match
spec: RFC7159; urlPrefix: https://tools.ietf.org/html/rfc7159
type: dfn
text: JSON text; url: section-2
spec: RFC7230; urlPrefix: https://tools.ietf.org/html/rfc7230
type: grammar
text: OWS; url: section-3.2.3
text: BWS; url: section-3.2.3
text: token; url: section-3.2.6
text: quoted-string; url: section-3.2.6
text: #rule; url: section-7
spec: RFC7234; urlPrefix: https://tools.ietf.org/html/rfc7234
type: grammar
text: delta-seconds; url: section-1.2.1
spec: RFC7469; urlPrefix: https://tools.ietf.org/html/rfc7469
type: dfn
text: Public-Key-Pins; url: section-2.1
spec: HTTP-JFV; urlPrefix: https://greenbytes.de/tech/webdav/draft-reschke-http-jfv-02.html
type: grammar
text: json-field-value; url: rfc.section.2
</pre>
<pre class="biblio">
{
"SECURE-CONTEXTS": {
"authors": [ "Mike West", "Yan Zhu" ],
"href": "https://w3c.github.io/webappsec-secure-contexts/",
"title": "Secure Contexts",
"publisher": "W3C"
},
"HTTP-JFV": {
"authors": [ "Julian Reschke" ],
"href": "https://greenbytes.de/tech/webdav/draft-reschke-http-jfv-02.html",
"title": "A JSON Encoding for HTTP Header Field Values"
}
}
</pre>
<section>
<h2 id="intro">Introduction</h2>
[INTRODUCTION GOES HERE]
<h3 id="guarantees">Guarantees</h3>
This specification aims to provide a best-effort report delivery system that
executes out-of-band with website activity. The user agent will be able to do
a better job prioritizing and scheduling delivery of reports, as it has an
overview of cross-origin activity that individual websites do not, and can
deliver reports based on error conditions that would prevent a website from
loading in the first place.
The delivery is not, however, guaranteed in a strict sense. We spell out a
reasonable set of retry rules in the algorithms below, but it's quite possible
for a report to be dropped on the floor if things go badly.
Reporting can generate a good deal of traffic, so we allow developers to set
up groups of <a>endpoints</a> in order to distribute load. Each of these
endpoints will receive a subset of the generated reports which target that
group. The user agent will do its best to deliver a particular report to
<strong>at most one</strong> endpoint in a group. That is, reports will not
fan-out to all the endpoints in a group, but the user agent will attempt
delivery to one endpoint, and fallback to another upon failure.
<h3 id="examples">Examples</h3>
<div class="example">
MegaCorp Inc. wants to collect Content Security Policy and Key Pinning
violation reports. It can do so by delivering the following header to
define a set of reporting endpoints named "`endpoint-1`":
<pre>
<a>Report-To</a>: { "<a>url</a>": "https://example.com/reports",
"<a>group</a>": "endpoint-1",
"<a>max-age</a>": 10886400 },
{ "<a>url</a>": "https://backup.com/reports",
"<a>group</a>": "endpoint-1",
"<a>max-age</a>": 10886400 }
</pre>
And the following headers, which direct CSP and HPKP reports to that group:
<pre>
<a>Content-Security-Policy</a>: ...; <a lt="reports directive">report-to</a>=endpoint-1
<a>Public-Key-Pins</a>: ...; report-to=endpoint-1
</pre>
</div>
<div class="example">
After processing reports for a little while, MegaCorp Inc. decides to split
the processing of these two types of reports out into two distinct endpoints
in order to make the processing scripts simpler. It can do so by delivering
the following header to define two reporting endpoints:
<pre>
<a>Report-To</a>: { "<a>url</a>": "https://example.com/csp-reports",
"<a>group</a>": "csp-endpoint",
"<a>max-age</a>": 10886400 },
{ "<a>url</a>": "https://example.com/hpkp-reports",
"<a>group</a>": "hpkp-endpoint",
"<a>max-age</a>": 10886400 }
</pre>
And the following headers, which direct CSP and HPKP reports to those named
endpoint:
<pre>
<a>Content-Security-Policy</a>: ...; <a lt="reports directive">report-to</a>=csp-endpoint
<a>Public-Key-Pins</a>: ...; report-to=hpkp-endpoint
</pre>
</div>
</section>
<section>
<h2 id="concept">Concepts</h2>
<h3 id="concept-endpoints">Endpoints</h3>
An <dfn export>endpoint</dfn> is location to which <a>reports</a> may be sent.
Each <a>endpoint</a> has a <dfn for="endpoint" export attribute>url</dfn>,
which is a {{URL}}.
Each <a>endpoint</a> has a
<dfn for="endpoint" export attribute>failures</dfn>, which is a non-negative
integer representing the number of consecutive times this endpoint has failed
to respond to a request.
Each <a>endpoint</a> has a
<dfn for="endpoint" export attribute>retry-after</dfn>, which is either
`null`, or a timestamp after which delivery should be retried.
Each <a>endpoint</a> has a <dfn for="endpoint" export attribute>clients</dfn>
list, which is a list of <a>clients</a>.
An <a>endpoint</a> is <dfn for="endpoint" id="endpoint-expired">expired</dfn>
if every <a>client</a> in its list of {{endpoint/clients}} is
<a for="client">expired</a>.
An <a>endpoint</a> is <dfn for="endpoint">pending</dfn> if its
{{endpoint/retry-after}} is not `null`, and represents a time in the future.
<h3 id="concept-client">Clients</h3>
A <dfn export>client</dfn> represents a particular origin's relationship to
an <a>endpoint</a>.
Each <a>client</a> has an <dfn for="client" export attribute>origin</dfn>,
which is an <a>origin</a>.
Each <a>client</a> has a <dfn for="client" export attribute>subdomains</dfn>
flag (which is either "`include`" or "`exclude`").
Each <a>client</a> has a <dfn for="client" export attribute>group</dfn> which
is an ASCII string.
Each <a>client</a> has a <dfn for="client" export attribute>ttl</dfn>
representing the the number of seconds the client remains valid for an
<a>endpoint</a>.
Each <a>client</a> has a <dfn for="client" export attribute>creation</dfn>
which is the timestamp at which the client was added to an <a>endpoint</a>.
A <a>client</a> is <dfn for="client" id="client-expired">expired</dfn> if its
{{client/creation}} plus its {{client/ttl}} represents a time in the past.
<h3 id="concept-reports">Reports</h3>
A <dfn export>report</dfn> is a collection of arbitrary data which the user
agent is expected to deliver to a specified endpoint.
Each <a>report</a> has a <dfn for="report" export attribute>body</dfn>,
which is either `null` or an object which can be serialized into a <a>JSON
text</a>.
Each <a>report</a> has an <dfn for="report" export attribute>url</dfn>,
which is the address of the `Document` or `Worker` from which the report was
generated.
Note: We strip the username, password, and fragment from this serialized URL.
See [[#capability-urls]].
Each <a>report</a> has an <dfn for="report" export attribute>origin</dfn>,
which is an <a>origin</a> representing the report's initiator.
Each <a>report</a> has an <dfn for="report" export attribute>group</dfn>,
which is a string representing the {{client/group}} to which this report
will be sent.
Each <a>report</a> has a <dfn for="report" export attribute>type</dfn>,
which is a non-empty string specifying the type of data the report contains.
Each <a>report</a> has a <dfn for="report" export attribute>timestamp</dfn>,
which records the time at which the report was generated, in milliseconds
since the unix epoch.
Each <a>report</a> has a <dfn for="report" export attribute>attempts</dfn>,
which is a non-negative integer representing the number of times the user
agent attempted to deliver the report.
<h3 id="concept-storage">Storage</h3>
A conformant user agent MUST provide a <dfn>reporting cache</dfn>, which
is a storage mechanism that maintains a set of <a>endpoints</a> that websites
have instructed the user agent to associate with their <a>origins</a>, and a
set of <a>reports</a> which are queued for delivery.
This storage mechanism is opaque, vendor-specific, and not exposed to the
web, but it MUST provide the following methods which will be used in the
algorithms this document defines:
1. Insert, update, and remove <a>endpoints</a>.
2. Enqueue and dequeue <a>reports</a> for delivery.
3. Retrieve a list of <a>endpoint</a> objects.
4. Retrieve a list of queued <a>report</a> objects.
5. Clear the cache.
</section>
<section>
<h2 id="endpoint-delivery">Endpoint Delivery</h2>
A server MAY define a set of reporting endpoints for an origin it controls
via the <a>`Report-To`</a> HTTP response header field. This mechanism
is defined in [[#header]], and its processing in [[#process-header]].
<h3 id="header">The `Report-To` HTTP Response Header Field</h3>
The <dfn export>`Report-To`</dfn> HTTP response header field instructs the
user agent to store reporting endpoints for an origin. The header is
represented by the following ABNF grammar [[!RFC5234]]:
<pre class="abnf" link-type="grammar" dfn-type="grammar">
Report-To = <a>json-field-value</a>
; See Section 2 of [[HTTP-JFV]], and Section 2 of [[RFC7159]]
</pre>
The header's value is interpreted as an array of JSON objects, as described in
Section 4 of [[HTTP-JFV]].
Each object in the array defines an <a>endpoint</a> to which reports may be
delivered, and will be parsed as defined in [[#parse-header]].
The following subsections defined the initial set of known members in each
JSON object the header's value defines. Future versions of this document may
define additional such members, and user agents MUST ignore unknown members
when parsing the header.
<h4 id="url-member">The `url` member</h4>
The REQUIRED <dfn>`url`</dfn> member is a string that defines the location
of a reporting endpoint. The member's value MUST be a string; any other type
will result in a parse error.
Moreover, the URL that the member's value represents MUST be <a>potentially
trustworthy</a> [[!SECURE-CONTEXTS]]. Non-secure endpoints will be ignored.
<h4 id="id-member">The `group` member</h4>
The OPTIONAL <dfn>`group`</dfn> member is a string that associates a name with
the reporting endpoint. The member's value MUST be a string; any other type
will result in a parse error. If no member named "`group`" is present in the
object, the <a>endpoint</a> will be associated with a group named "`default`".
The group name is not unique and multiple endpoints may use the same name to
create a set of reporting endpoints that can be used for backup and failover
purposes. If the member is omitted, the endpoint MUST be associated with
the "default" group name.
Note: If a group resolves to multiple <a>endpoints</a>, the user agent will
deliver a particular <a>report</a> to <strong>at most one</strong>
<a>endpoint</a> in that group on a best-effort basis.
<h4 id="includesubdomains-member">The `includeSubdomains` member</h4>
The OPTIONAL <dfn>`includeSubdomains`</dfn> member is a boolean that enables
an endpoint for all subdomains of the current <a>origin</a>'s {{URL/host}}.
If no member named "`includeSubdomains`" is present in the object, or its
value is not "`true`", the endpoint will not be enabled for subdomains.
<h4 id="max-age-member">The `max-age` member</h4>
The REQUIRED <dfn export>`max-age`</dfn> member defines the reporting endpoint's
lifetime, as a non-negative integer number of seconds. The member's value MUST
be a number; any other type will result in a parse error.
A value of "`0`" will cause the <a>endpoint</a> to be removed from the user
agent's <a>reporting cache</a>.
<h3 id="process-header" algorithm>
Process reporting endpoints for |response| to |request|
</h3>
Given a <a>response</a> (|response|) and a <a>request</a> (|request|), this
algorithm extracts a list of <a>endpoint</a> objects, and updates the
<a>reporting cache</a> accordingly.
Note: This algorithm is called from around step 13 of <a>main fetch</a>
[[FETCH]], and only updates the <a>reporting cache</a> if the |response|
has been delivered securely.
ISSUE: Fetch monkey patching. Talk to Anne.
1. Abort these steps if any of the following conditions are true:
1. |response|'s <a for="response" attribute>HTTPS state</a> is not
"`modern`", and the <a lt="origin of a url">origin</a> of |response|'s
<a for="response" attribute>url</a> is not <a>potentially
trustworthy</a>.
2. |response|'s <a for="response" attribute>header list</a> does not
contain a <a>header</a> whose <a for="header" attribute>name</a> is
"<a>`Report-To`</a>".
2. Let |header| be the <a for="header" attribute>value</a> of the
<a>header</a> in |response|'s <a for="response" attribute>header list</a>
whose name is "<a>`Report-To`</a>".
3. Let |origin| be the <a lt="origin of a url">origin</a> of |response|'s
<a for="response" attribute>url</a>.
4. Let |tuples| be the result of executing [[#parse-header]] on |header|.
5. For each |tuple| in |tuples|:
1. If there exists an <a>endpoint</a> (|endpoint|) in the <a>reporting
cache</a> whose {{endpoint/url}} is |tuple|'s {{URL}}:
1. For each |client| in |endpoint|'s {{endpoint/clients}}, delete
|client| if |origin| is the same as |client|'s {{client/origin}}.
2. If |tuple|'s {{client/ttl}} is equal to 0, skip to the next tuple.
3. If there does not exist an <a>endpoint</a> (|endpoint|) in the
<a>reporting cache</a> whose {{endpoint/url}} is |tuple|'s {{URL}}:
1. Let |endpoint| be a new <a>endpoint</a> whose properties are set
as follows:
: {{endpoint/url}}
:: |tuple|'s {{URL}}
: {{endpoint/failures}}
:: 0
: {{endpoint/retry-after}}
:: `null`
: {{endpoint/clients}}
:: an empty list
2. Insert |endpoint| into the <a>reporting cache</a> for |origin|.
4. Let |client| be a new <a>client</a> whose properties are set as follows:
: {{client/origin}}
:: |origin|
: {{client/subdomains}}
:: |tuple|'s {{client/subdomains}}
: {{client/group}}
:: |tuple|'s {{client/group}}
: {{client/ttl}}
:: |tuple|'s {{client/ttl}}
: {{client/creation}}
:: The current timestamp
5. Append |client| to |endpoint|'s {{endpoint/clients}} list.
<h3 id="parse-header" algorithm>
Parse reporting endpoint tuples from |value|
</h3>
Given a string (|value|) this algorithm will return a list of tuples
containing a {{URL}}, {{client/subdomains}} flag, {{client/ttl}}, and a
{{client/group}}. This list will be empty if no valid endpoints could be
parsed.
1. Let |list| be the result of executing the algorithm defined in Section 4
of [[HTTP-JFV]]. If that algorithm results in an error, abort these steps.
2. Let |tuples| be an empty list.
3. For each |item| in |list|:
1. If |item| has no member named "<a>`url`</a>", or that member's value
is not a string, skip to the next |item|.
2. If |item| has no member named "<a>`max-age`</a>", or that member's
value is not a number, skip to the next |item|.
3. Let |tuple| be a new tuple containing the following:
: "`url`"
:: The result of executing the <a>URL parser</a> on |item|'s
"<a>`url`</a>" member's value.
: "`subdomains`"
:: "`Include`" if |item| has a member named
"<a>`includeSubdomains`</a>" whose value is `true`, "`Exclude`"
otherwise.
: "`ttl`"
:: |item|'s "<a>`max-age`</a>" member's value.
: "`group`"
:: |item|'s "<a>`group`</a>" member's value if present, and
"`default`" otherwise.
4. If |tuple|'s "`url`" is not <a>potentially trustworthy</a>, or
|tuple|'s "`ttl`" is a negative number, skip to the next |item|.
Note: User agents SHOULD raise some sort of developer-visible parse
error in this case.
5. Append |tuple| to |tuples|.
4. Return |tuples|.
</section>
<!-- Big Text: Reporting -->
<section>
<h2 id="report-delivery">Report Delivery</h2>
Over time, various features will queue up a list of <a>reports</a> in the
user agent's <a>reporting cache</a>. The user agent will periodically grab
the list of currently pending reports, and deliver them to the associated
endpoints. This document does not define a schedule for the user agent to
follow, and assumes that the user agent will have enough contextual
information to deliver reports in a timely manner, balanced against impacting
a user's experience.
That said, a user agent SHOULD make a effort to deliver reports as soon as
possible after queuing, as a report's data might be significantly more useful
in the period directly after its generation than it would be a day or a week
later.
<h3 id="queue-report" algorithm>
Queue |data| as |type| for |endpoint group| on |settings|
</h3>
Given a serializable object (|data|), a string (|type|), another string
(|endpoint group|), and an <a>environment settings object</a> (|settings|),
the following algorithm will create a <a>report</a>, and add it to
<a>reporting cache</a>'s queue for future delivery.
1. Let |report| be a new <a>report</a> object with its values initialized as
follows:
: {{report/body}}
:: |data|
: {{report/origin}}
:: |settings|'s <a>origin</a>
: {{report/group}}
:: |endpoint group|
: {{report/type}}
:: |type|
: {{report/timestamp}}
:: The current timestamp.
: {{report/attempts}}
:: 0
2. Let |url| be |settings|'s <a>creation URL</a>.
3. Set |url|'s {{URL/username}} to the empty string, and its {{URL/password}}
to `null`.
4. Set |report|'s {{report/url}} to the result of executing the <a>URL
serializer</a> on |url| with the <em>exclude fragment flag</em> set.
5. Add |report| to the <a>reporting cache</a>.
Note: We strip the username, password, and fragment from the serialized URL
in the report. See [[#capability-urls]].
Note: The user agent MAY reject reports for any reason. This API does not
guarantee delivery of arbitrary amounts of data, for instance.
<h3 id="does-endpoint-match-report">
Does |endpoint| match |report|?
</h3>
Given an <a>endpoint</a> (|endpoint|) and a <a>report</a> (|report|), this
algorithm returns "`Match`" if |report| should be sent to |endpoint|, and
"`Does Not Match`" otherwise:
1. For each |client| in |endpoint|'s {{endpoint/clients}}:
1. If |client| is <a for="client">expired</a>, skip to the next |client|.
Note: In this case, the user agent MAY remove |client| from
|endpoint|, or it may wait and collect garbage <i lang="fr">en
masse</i> at some point in the future as described in [[#gc]].
2. Return "`Match`" if each of the following criteria is met:
1. |report|'s {{report/group}} is an <a>ASCII case-insensitive</a>
match for |client|'s {{client/group}}
2. If |client|'s {{client/subdomains}} is "`exclude`", |report|'s
{{report/origin}} is the same as |client|'s {{client/origin}}
Otherwise, |client|'s {{client/origin}}'s {{URL/host}} is either
a <a>superdomain match</a> or a <a>congruent match</a> for
|report|'s {{report/origin}}'s {{URL/host}} [[!RFC6797]]
2. Return "`Does Not Match`".
<h3 id="send-reports" algorithm>
Send reports
</h3>
A user agent sends reports by executing the following steps:
1. Let |reports| be a copy of the list of queued <a>report</a> objects in
<a>reporting cache</a>.
2. Let |endpoint map| be an empty map of <a>endpoint</a> objects to lists of
<a>report</a> objects.
3. For each |report| in |reports|:
1. For |endpoint| in the <a>reporting cache</a>:
1. If |endpoint| is <a for="endpoint">expired</a>, skip to the next
|endpoint|.
Note: In this case, the user agent MAY remove |endpoint| from the
<a>reporting cache</a>, or it may wait and collect garbage
<i lang="fr">en masse</i> at some point in the future as described
in [[#gc]].
2. If |endpoint| is <a for="endpoint">pending</a>, skip to the next
|endpoint|.
3. If [[#does-endpoint-match-report]] returns "`Match`" when executed
upon |endpoint| and |report|:
1. Append |report| to |endpoint map|'s list of reports for
|endpoint|.
2. Skip to the next |report|.
Note: This ensures that each report is assigned to a single
endpoint, even if it matches multiple. In order to ensure
an even distribution across endpoints, the user agent SHOULD
randomize the order in which it walks through endpoints.
2. If we reach this step, the |report| did not match any <a>endpoint</a>
and the user agent MAY remove |report| from the <a>reporting cache</a>
directly. Depending on load, the user agent MAY instead wait for
[[#gc]] at some point in the future.
4. For each (|endpoint|, |reports|) pair in |endpoint map|, execute the
following steps asynchronously:
1. Let |result| be the result of executing [[#try-delivery]] on
|endpoint| and |reports|.
2. If |result| is "`Success`":
1. Set |endpoint|'s {{endpoint/failures}} to 0, and its
{{endpoint/retry-after}} to `null`.
2. Remove each <a>report</a> in |reports| from the <a>reporting
cache</a>.
Otherwise, if |result| is "`Remove Endpoint`":
1. Remove |endpoint| from the reporting cache.
Note: |reports| remain in the reporting cache for potential
delivery to other endpoints.
Otherwise (if |result| is "`Failure`"):
1. Increment |endpoint|'s {{endpoint/failures}}.
2. Set |endpoint|'s {{endpoint/retry-after}} to a point in the future
which the user agent chooses.
Note: We don't specify a particular algorithm here, but user
agents are encouraged to employ some sort of exponential backoff
algorithm which increases the retry period with the number of
failures, with the addition of some random jitter to ensure that
temporary failures don't lead to a crush of reports all being
retried on the same schedule.
ISSUE: Add in a reasonable reference describing a good algorithm.
Wikipedia, if nothing else.
Note: User agents MAY decide to attempt delivery for only a subset of the
collected reports or endpoints (because, for example, sending all the reports
at once would consume an unreasonable amount of bandwidth, etc). As reports
are only removed from the cache when they're successfully delivered, skipped
reports will simply be delivered later.
<h3 id="try-delivery" algorithm>
Attempt to deliver |reports| to |endpoint|
</h3>
Given a list of <a>reports</a> (|reports|) and an <a>endpoint</a>
(|endpoint|), this algorithm will construct a <a>request</a>, and attempt to
deliver it to |endpoint|. It returns "`Success`" if that delivery succeeds,
"`Remove Endpoint`" if the endpoint explicitly removes itself as a reporting
endpoint by sending a 410 response, and "`Failure`" otherwise.
1. Let |collection| be a new ECMAScript `Array` object [[!ECMA-262]].
2. For each |report| in |reports|:
1. Let |data| be a new ECMAScript `Object` with the following properties
[[!ECMA-262]]:
: `age`
:: The number of milliseconds between |report|'s {{report/timestamp}}
and the current time.
: `type`
:: |report|'s {{report/type}}
: `url`
:: |report|'s {{report/url}}
: `body`
:: |report|'s {{report/body}}
Note: Client clocks are unreliable and subject to skew. We therefore
deliver an `age` attribute rather than an absolute timestamp. See
also [[#fingerprinting-clock-skew]]
2. Increment |report|'s {{report/attempts}}.
3. Append |data| to |collection|.
3. Let |request| be a new <a>request</a> with the following properties
[[FETCH]]:
: `url`
:: |endpoint|'s {{endpoint/url}}
: `header list`
:: A new <a>header list</a> containing a <a>header</a> named
"`Content-Type`" whose value is "`application/report`"
: `client`
:: `null`
: `window`
:: "`no-window`"
: `skip-service-worker` flag
:: Set.
: `initiator`
:: ""
: `type`
:: "`report`"
: `destination`
:: ""
: `mode`
:: "`cors`"
: `credentials`
:: "`include`"
: `body`
:: The string resulting from executing the {{JSON.stringify()}} algorithm
on |collection| [[!ECMA-262]]
ISSUE: The "`report`" type does not exist in Fetch. Talk to Anne.
4. <a>Queue a task</a> to <a>fetch</a> |request|.
5. <a>Wait for a response</a> (|response|).
6. If |response|'s `status` is an <a>OK status</a> (200-299), return
"`Success`".
7. If |response|'s `status` is `410 Gone` [[!RFC7231]], return "`Remove
Endpoint`".
8. Return "`Failure`".
</section>
<section>
<h2 id="implementation">Implementation Considerations</h2>
<h3 id="delivery">Delivery</h3>
The user agent SHOULD attempt to deliver reports as soon as possible to
provide feedback to developers as quickly as possible. However, when this
desire is balanced against the impact on the user, the user wins. With that
in mind, the user agent MAY delay delivery of reports based on its knowledge
of the user's activities and context.
For instance, the user agent SHOULD prioritize the transmission of reporting
data lower than other network traffic. The user's explicit activities on a
website should preempt reporting traffic.
The user agent MAY choose to withhold report delivery entirely until the user
is on a fast, cheap network in order to prevent unnecessary data cost.
The user agent MAY choose to prioritize reports from particular origins over
others (perhaps those that the user visits most often?)
<h3 id="gc">Garbage Collection</h3>
Periodically, the user agent SHOULD walk through the cached <a>reports</a>
and <a>endpoints</a>, and discard those that are no longer relevant. These
include:
* <a>endpoints</a> which are <a for="endpoint">expired</a>
* <a>endpoints</a> which have not been used in some arbitrary period of time
(perhaps a ~week?)
* <a>endpoints</a> whose {{endpoint/failures}} exceed
some user-agent-defined threshold (~5 seems reasonable)
* <a>reports</a> whose {{report/attempts}} exceed
some user-agent-defined threshold (~5 seems reasonable)
* <a>reports</a> which have not been delivered in some arbitrary period of
time (perhaps ~2 days?)
</section>
<section>
<h2 id="sample-reports">Sample Reports</h2>
<div class="example">
<pre>
POST / HTTP/1.1
Host: example.com
...
Content-Type: application/report
[{
"type": "csp",
"age": 10,
"url": "https://example.com/vulnerable-page/",
"body": {
"blocked": "https://evil.com/evil.js",
"directive": "script-src",
"policy": "script-src 'self'; object-src 'none'",
"status": 200,
"referrer": "https://evil.com/"
}
}, {
"type": "hpkp",
"age": 32,
"url": "https://www.example.com/",
"body": {
"date-time": "2014-04-06T13:00:50Z",
"hostname": "www.example.com",
"port": 443,
"effective-expiration-date": "2014-05-01T12:40:50Z"
"include-subdomains": false,
"served-certificate-chain": [
"-----BEGIN CERTIFICATE-----\n
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\n
...
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto\n
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6\n
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx\n
-----END CERTIFICATE-----",
...
]
}
}, {
"type": "nel",
"age": 29,
"url": "https://example.com/thing.js",
"body": {
"referrer": "https://www.example.com/",
"server-ip": "234.233.232.231",
"protocol": "",
"status-code": 0,
"elapsed-time": 143,
"age": 0,
"type": "http.dns.name_not_resolved"
}
}]
</pre>
</div>
</section>
<section>
<h2 id="security">Security Considerations</h2>
<h3 id="capability-urls">Capability URLs</h3>
Some URLs are valuable in and of themselves. To mitigate the possibility
that such URLs will be leaked via this reporting mechanism, we strip out
credential information and fragment data from the URL we store as a
<a>report</a>'s originator. It is still possible, however, for a feature
to unintentionally leak such data via a report's {{report/body}}. Implementers
SHOULD ensure that URLs contained in a report's body are similarly stripped.
</section>
<section>
<h2 id="privacy">Privacy Considerations</h2>
<h3 id="network-leakage">Network Leakage</h3>
Because this reporting mechanism is out-of-band, and doesn't rely on a page
being open, it's entirely possible for a report generated while a user is on
one network to be sent while the user is on another network, even if they
don't explicitly open the page from which the report was sent.
ISSUE(WICG/BackgroundSync#107): Consider mitigations. For example, we could
drop reports if we change from one network to another.
<h3 id="fingerprinting-clock-skew">Clock Skew</h3>
Each report is delivered along with an `age` property, rather than the
timestamp at which it was generated. We do this because each user's local
clock will be skewed from the clock on the server by an arbitrary amount.
The difference between the time the report was generated and the time it
was sent will be stable, regardless of clock skew, and we can avoid the
fingerprinting risk of exposing the clock skew via this API.
<h3 id="correlation">Cross-origin correlation</h3>
If multiple origins all use the same reporting endpoint, that endpoint may
learn that a particular user has interacted with a certain set of websites,
as it will receive origin-tagged reports from each. This doesn't seem worse
than the status quo ability to track the same information from cooperative
origins, and doesn't grant any new tracking ability above and beyond what's
possible with `<img>` today.
<h3 id="subdomains">Subdomains</h3>
This specification allows any resource on a host to declare a set of reporting
endpoints for that host and each of its subdomains. This doesn't have privacy
implications in and of itself (beyond those noted in [[#clear-cache]]), as the
reporting endpoints themselves don't take any real action, as features will
need to opt-into using these reporting endpoints explicitly. Those features
certainly will have privacy implications, and should carefully consider
whether they should be enabled across origin boundaries.
<h3 id="clear-cache">Clearing the reporting cache</h3>
A user agent's <a>reporting cache</a> contains data about a user's activity
on the web, and user agents ought to handle this data carefully. In
particular, if a user agent gives users the ability to clear their site data,
browsing history, browsing cache, or similar, the user agent MUST also clear
the <a>reporting cache</a>. Note that this includes both the pending reports
themselves, as well as the endpoints to which they would be sent. Both MUST
be cleared.
<h3 id="disable">Disabling Reporting</h3>
Reporting is, to some extent, a question of commons. In the aggregate, it
seems useful for everyone for reports to be delivered. There is direct benefit
to developers, as they can fix bugs, which means there's indirect benefit to
users, as the sites they enjoy will be more stable and enjoyable. As a
concrete example, Content Security Policy grants something like herd immunity
to cross-site scripting attacks by alerting developers about potential holes
in their sites' defenses. Fixing those bugs helps every user, even those whose
user agents don't support Content Security Policy.
The calculus, of course, depends on the nature of data that's being delivered,
and the relative maliciousness of the reporting endpoints, but that's the
value proposition in broad strokes.
That said, it can't be the case that this general benefit be allowed to take
priority over the ability of a user to individually opt-out of such a system.
Sending reports costs bandwidth, and potentially could reveal some small
amount of additional information above and beyond what a website can obtain
in-band ([[NETWORK-ERROR-LOGGING]], for instance). User agents MUST allow
users to disable reporting with some reasonable amount of granularity in order
to maintain the priority of constituencies espoused in
[[HTML-DESIGN-PRINCIPLES]].
</section>
<!-- Big Text: IANA -->
<section>
<h2 id="iana-considerations">IANA Considerations</h2>
The permanent message header field registry should be updated
with the following registration: [[!RFC3864]]
<h3 id="iana-report-to">
Report-To
</h3>
: Header field name
:: `Report-To`
: Applicable protocol
:: http
: Status
:: standard
: Author/Change controller
:: W3C
: Specification document
:: This specification (see [[#header]])
</section>