/
index.src.html
1687 lines (1230 loc) · 67.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
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<h1>Content Security Policy: Embedded Enforcement</h1>
<pre class="metadata">
Status: ED
ED: https://w3c.github.io/webappsec-cspee/
TR: https://www.w3.org/TR/csp-embedded-enforcement/
Previous Version: https://www.w3.org/TR/2015/WD-csp-embedded-enforcement-20160909/
Shortname: csp-embedded-enforcement
Level: None
Editor: Mike West 56384, Google Inc., mkwst@google.com
Editor: Antonio Sartori 124875, Google Inc., antoniosartori@google.com
Group: webappsec
Abstract:
This document defines a mechanism by which a web page can embed a nested
browsing context if and only if it agrees to enforce a particular set of
restrictions upon itself.
Indent: 2
Version History: https://github.com/w3c/webappsec-csp/commits/master/embedded/index.src.html
Boilerplate: omit conformance, omit feedback-header
!Participate: <a href="https://github.com/w3c/webappsec-cspee/issues/new">File an issue</a> (<a href="https://github.com/w3c/webappsec-cspee/issues">open issues</a>)
!Tests: <a href=https://github.com/web-platform-tests/wpt/tree/master/content-security-policy/embedded-enforcement>web-platform-tests content-security-policy/embedded-enforcement/</a> (<a href=https://github.com/web-platform-tests/wpt/labels/content-security-policy>ongoing work</a>)
Markup Shorthands: css off, markdown on
</pre>
<pre class="link-defaults">
spec: html
type: dfn; for: /
text: browsing context
text: case-sensitive
text: content attribute
text: navigate
spec:dom; type:interface; text:Document
spec:dom; type:dfn; for:/; text:element
spec:fetch
type: dfn
text:request; for: /
text: response; for: /
spec:infra
type:dfn
text:string; for: /
text:list; for: /
text:set; for: /
text:append; for: set
text:empty; for: set
spec: csp
type: dfn
text: report-to
text: allows all inline behavior; for:source list
</pre>
<pre class="anchors">
spec: FETCH; urlPrefix: https://fetch.spec.whatwg.org/
type: grammar
text: origin-or-null; url: http-origin
text: wildcard; url: http-new-header-syntax
spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/
type:dfn;
text: parse a serialized csp as disposition; url: parse-serialized-policy
text: scheme-part match
text: host-part match
text: port-part match
text: path-part match
text: CSP list
</pre>
<!--
████ ██ ██ ████████ ████████ ███████
██ ███ ██ ██ ██ ██ ██ ██
██ ████ ██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ ████████ ██ ██
██ ██ ████ ██ ██ ██ ██ ██
██ ██ ███ ██ ██ ██ ██ ██
████ ██ ██ ██ ██ ██ ███████
-->
Introduction {#intro}
============
<em>This section is not normative.</em>
Content Security Policy is a great defense against cross-site scripting
attacks, allowing developers to harden their own sites against injection of
malicious script, style, and other resource types. It does not, however,
give developers the ability to apply restrictions to third-party content
loaded in via <{iframe}>. Allowing CSP to apply directly to these third-party
contexts would be dangerous; CSP gives quite granular control over resource
loading, and it's very possible to introduce vulnerabilities into an otherwise
secure page by denying it access to particular scripts. We've seen these kinds
of issues in past features such as `X-XSS-Protection`, so we must be careful
to avoid reintroducing them in a new form.
That said, it would be quite useful to be able to place restrictions upon
widgets, advertisements, and other kinds of third-party content. This document
proposes a mechanism which relies on an explicit opt-in from the embedded
content, which ought to make it possible for widgets to cooperate with their
embedders to negotiate a reasonable set of restrictions.
In short, the embedder proposes a Content Security Policy by setting an attribute on an <{iframe}>
element. This policy is transmitted along with the HTTP request for the framed content in an
HTTP request header (<a http-header>`Sec-Required-CSP`</a>). If the embedded content can accept that
policy, it can enforce it by returning a <a http-header>`Content-Security-Policy`</a> or
<a http-header>`Allow-CSP-From`</a> header along with the response.
If the response contains a policy at least as strict as the policy which the embedder requested,
or accepts the embedder-provided policy, then the user agent will render the embedded content. If
no such assertion is present, the response will be blocked.
Examples {#examples}
--------
<div class="example" id="example1">
MegaCorp Inc. wishes to ensure that the advertisements that run on its
various publications are locked down to include script from trusted origins
that have been audited for safety. They can do so by including the
advertisement via an <{iframe}> element with a <{iframe/csp}> attribute:
<pre>
<iframe src="https://advertisements-r-us.example.com/ad1.cfm"
<a for="iframe" element-attr>csp</a>="script-src https://trusted-cdn.example.com/">
</iframe>
</pre>
This will generate a request to `advertisements-r-us.example.com` that has
a <a http-header>`Sec-Required-CSP`</a> header, as follows:
<pre>
GET / HTTP/1.1
Host: advertisements-r-us.example.com
...
<a http-header>Sec-Required-CSP</a>: script-src https://trusted-cdn.example.com/
...
</pre>
The advertisement server parses this request header, decides that it's acceptable, and adds a
header to the response, informing the user agent that it will adhere to the restrictions imposed
by its embedder (`https://example.com`):
<pre>
HTTP/1.1 200 OK
...
<a http-header>Allow-CSP-From</a>: https://example.com
</pre>
</div>
<div class="example" id="example2">
The advertisement server in the example above could also accept the restrictions by emitting its
own <a http-header>`Content-Security-Policy`</a> header that's at least as strong as the policy
which the embedder requires. For example, it might wish to ensure that no plugins are loaded,
regardless of what the embedder allows. It can do so by emitting a policy that includes the
embedder's restrictions, and adds more on top:
<pre>
HTTP/1.1 200 OK
...
<a http-header>Content-Security-Policy</a>: script-src https://trusted-cdn.example.com/; object-src 'none'
</pre>
Since the policy asserted by the response allows strictly fewer requests than the policy
required by the request, the frame loads successfully.
Note that the server could also deliver two policies, one which mirrors the restrictions of the
embedder exactly, another which tightens them:
<pre>
HTTP/1.1 200 OK
...
<a http-header>Content-Security-Policy</a>: script-src https://trusted-cdn.example.com/, object-src 'none'
</pre>
The "`,`" in the <a http-header>`Content-Security-Policy`</a> header's value splits the
string into two serialized policies, each of which is enforced. The user
agent verifies that one of the policies delivered with the response matches
the requirement, and since additional policies can only make the
<em>effective</em> policy for the page more restrictive, allows the frame
to load successfully.
</div>
</section>
<section>
Framework {#framework}
=========
At a high level, this document describes a mechanism by which an embedee can opt-into a set of
restrictions specified by its embedder. The mechanism involves a few steps:
1. The embedder specifies a required policy via a <{iframe/csp}> attribute on an <{iframe}>
element. This is described in more detail in [[#csp-attribute]].
2. That attribute's value will be sent along with any <a>navigation request</a> that targets
the <{iframe}>'s <a>nested browsing context</a> in a <a http-header>`Sec-Required-CSP`</a>
HTTP request header. This header is described in more detail in [[#required-csp-header]].
3. The server can examine the <a http-header>`Sec-Required-CSP`</a> header to determine whether
it wishes to accept the required policy.If so, it can implicitly opt-in by sending a
<a http-header>`Content-Security-Policy`</a> header in the response that contains a policy
which is at least as strong as the required policy, or explicitly opt-in by sending an
<a http-header>`Allow-CSP-From`</a> header in the response that enables the embedding origin
to set whatever policy it wishes. The explicit mechanism is straightforward, described in
[[#allow-csp-from-header]]. The implicit mechanism is quite complicated, and comprises the
entire [[#implicit-opt-in]] section.
If the server doesn't wish to accept the required policy, it can return an explicit error,
or simply return the usual data without either a matching
<a http-header>`Content-Security-Policy`</a> header or an <a http-header>`Allow-CSP-From`</a>
header. In this case, the user agent will block the response. This integration with HTML's
<a>navigate</a> algorithm is described in [[#html-integration]], and the blocking mechanism
is spelled out in [[#process-response]].
`<iframe>`'s `csp` attribute {#csp-attribute}
----------------------------
<{iframe}> elements have a <dfn element-attr for="iframe">csp</dfn> attribute, which specifies
the policy that an embedded document must agree to enforce upon itself. For example, the following
HTML would load `https://embedee.example.com/`, and ensure that `object-src 'none'` was enforced
upon it:
<div class="example">
<pre>
<iframe src="https://embedee.example.com/" <a for="iframe" element-attr>csp</a>="object-src 'none'">
</iframe>
</pre>
</div>
A <a>string</a> (|value|) is a <dfn for="iframe/csp">valid attribute value</dfn> for a given
<a>element</a> (|element|)'s <{iframe/csp}> <a>content attribute</a> if all of the
following statements are true:
1. |value| is not the empty string.
2. |value| matches the <a grammar>serialized-policy</a> ABNF grammar defined in [[!CSP]].
3. One of the following statements is true:
1. |element|'s [=node document=]'s [=Document/browsing context=]'s
[=browsing context/required CSP=] is `null`.
2. The result of [=parse a serialized csp as disposition|parsing=] |value| as "`enforce`" is
[=policy/subsumed by=] |element|'s [=node document=]'s [=Document/browsing context=]'s
[=browsing context/required CSP=].
4. The result of [=parse a serialized csp as disposition|parsing=] |value| as "`enforce`" has a
[=policy/directive set=] that does not [=list/contain=] any of the following directives:
* [=report-uri=]
* [=report-to=]
<div class="example">
The following strings are valid values for the <{iframe/csp}> attribute, as they're valid CSP
grammar:
* `script-src 'none'`
* `script-src 'self'; object-src 'none'; sandbox`
* `not-a-directive https://whatever.not-a-tld`
Note: We consider the last item valid even though it doesn't express a meaningful policy in
order to remain forward-compatible with future CSP syntax.
The following, on the other hand, do not match the CSP syntax, and would not be considered valid
attribute values:
* `script-src *\nInjected-Header: XSS!`
* `💩`
Note: We need to be careful about the values we allow in the <{iframe/csp}> attribute, as its
contents will end up reflected as an HTTP request header. This concern is discussed in a little
more detail in [[#header-injection]].
</div>
<{iframe}>'s <{iframe/csp}> <a>content attribute</a> has a corresponding IDL attribute, defined
by the following WebIDL grammar [[!WEBIDL]]:
<pre class="idl">
partial interface HTMLIFrameElement {
[CEReactions] attribute DOMString csp;
};
</pre>
The {{HTMLIFrameElement/csp}} IDL attribute must [=reflect=] the element's <{iframe/csp}>
[=content attribute=].
ISSUE: Upstream this to all the HTMLs.
The `Sec-Required-CSP` HTTP Request Header {#required-csp-header}
---------------------------------------
In order to ensure that the embedded resource can decide whether or not it is willing to adhere to
the embedder's requirements, the policy expressed in an <{iframe}>'s <{iframe/csp}> attribute is
communicated along with affected <a>navigation requests</a> via a
"<dfn export http-header>`Sec-Required-CSP`</dfn>" HTTP request header. The header's value is
represented by the following ABNF [[!RFC5234]]:
<pre>
Sec-Required-CSP = <a grammar>serialized-policy</a>
</pre>
A user agent MUST NOT send more than one HTTP response header field named "`Sec-Required-CSP`", and
any such header MUST NOT contain more than one <a grammar>serialized-policy</a>.
Servers MUST process only the first policy in the first such header received. As discussed in
[[#header-reflection]], servers SHOULD also carefully consider the implications of simply
reflecting a policy back to a client. If the server wishes to simply accept an embedder's
requirements, the <a http-header>`Allow-CSP-From`</a> header is a safer choice.
This header is set as part of HTML's <a>navigate</a> algorithm (see [[#html-integration]] for
details on the hook that calls the following algorithm):
<div algorithm="set a request's required-csp header">
To <dfn abstract-op local-lt="set-required-csp">set the `Sec-Required-CSP` header</dfn> for a
given <a>request</a> (|request|), run the following steps:
1. If |request| is not a <a>navigation request</a>, return.
2. Let |requirement| be |request|'s <a for="request">client</a>'s
<a for="environment settings object">responsible browsing context</a>'s
<a for="browsing context">required CSP</a>.
3. If |requirement| is `null`, return.
4. Assert: |requirement| is a <a>serialized CSP</a>, matching the
<a grammar>serialized-policy</a> grammar defined in [[!CSP]].
5. <a for="header list">Append</a> a header named "<a http-header>`Sec-Required-CSP`</a>" with a
value of |requirement| to |request|'s <a for="request">header list</a>.
</div>
The `Allow-CSP-From` HTTP Response Header {#allow-csp-from-header}
-------------------------------------------
An embedee can opt-into accepting a policy specified by an embedder by responding with a
"<dfn export http-header>`Allow-CSP-From`</dfn>" HTTP response header. The header's value is
represented by the following ABNF [[!RFC5234]]:
<pre>
Allow-CSP-From = <a grammar>origin-or-null</a> / <a grammar>wildcard</a>
</pre>
Integration with HTML {#html-integration}
---------------------
1. <{iframe}> elements have a <{iframe/csp}> attribute, defined in [[#csp-attribute]].
2. Each <a>browsing context</a> has a <a for="browsing context">required CSP</a>, defined in
[[#required-csp]].
3. Add the following after step 10 of HTML's <a>navigate</a> algorithm:
11. <a lt="set the required CSP" abstract-op>Set</a> `browsingContext`'s
<a for="browsing context">required CSP</a>.
ISSUE: Upstream this to WHATWG's HTML.
ISSUE(w3c/html#584): W3C's HTML's navigation algorithm is wildly divergent from WHATWG's at
this point. Upstream something to that document once things are reconciled.
4. Add the following to the list of error conditions in step 1 of HTML's
<a>process a navigate response</a> algorithm:
* The [[#process-response]] algorithm returns "`Blocked`" when executed
upon `response`, `request`, and `browsingContext`.
ISSUE: Upstream this to WHATWG's HTML.
ISSUE(w3c/html#584): W3C's HTML is not based on Fetch, and does not have
a <a>process a navigate response</a> algorithm into which to hook.
5. Add the following after step 5 of HTML's <a>process a navigate fetch</a>
algorithm:
6. <a lt="set-required-csp" abstract-op>Set</a> `request`'s
"<a http-header>`Sec-Required-CSP`</a>" header.
ISSUE: Upstream this to WHATWG's HTML.
ISSUE(w3c/html#584): W3C's HTML is not based on Fetch, and does not have
a <a>process a navigate fetch</a> algorithm into which to hook.
### Browsing Context's Required CSP ### {#required-csp}
Each <a>browsing context</a> has a <dfn for="browsing context">required CSP</dfn>, which is either
`null` or a <a>serialized CSP</a>. The value is set during the <a>navigate</a> algorithm, and will
not change until the <a>browsing context</a>'s <a>active document</a> changes.
The following algorithm will execute at around step 10 of the current <a>navigate</a> algorithm:
<div algorithm="browsing context's required CSP">
To <dfn abstract-op>set the required CSP</dfn> for a given <a>browsing context</a> (|context|),
run the following steps:
1. If |context| is a <a>nested browsing context</a>:
1. If |context|'s <a>browsing context container</a> has an <{iframe/csp}> <a>content
attribute</a> with a <a for="iframe/csp">valid attribute value</a> (|value|), set
|context|'s <a for="browsing context">required CSP</a> to |value| and return.
2. Set |context|'s <a for="browsing context">required CSP</a> to the value of |context|'s
<a>parent browsing context</a>'s <a for="browsing context">required CSP</a>.
3. Return.
2. Set |context|'s <a for="browsing context">required CSP</a> to `null`.
</div>
ISSUE: Upstream a hook to HTML.
</section>
<section>
Implicit Policy Acceptance {#implicit-opt-in}
==========================
An embedee can explicitly accept a policy requirement specified by its embedder by returning an
<a http-header>`Allow-CSP-From`</a> header along with a response. The requirement can also be
implicitly accepted by delivering a <a http-header>`Content-Security-Policy`</a> header that
contains a policy (or set of policies) whose net effect is at least as strict as the policy
required by the embedder.
"At least as strict", however, isn't very precise. Simple cases are straightforward: if an
embedder requires `object-src https://cdn.example.com`, the embedee can respond with `object-src
'none'`. Since every possible resource that would be blocked by the former would also be blocked
by the latter (because it allows no objects at all), we wouldn't block the embedding. CSP's
syntactical complexity makes this a little bit difficult to reason about for more complicated
cases. For instance, given `script-src 'unsafe-inline' http: 'sha256-abc...def'`, it might appear
that `script-src 'unsafe-inline'` would be a subset of the required policy. The presence of the
<a grammar>hash-source</a> expression, however means that `'unsafe-inline'` is ignored in the
required policy, so the latter policy would actually allow more than the former, despite
appearances.
To formalize the concept a bit, we need a few terms, and more than a few algorithms:
* A <a>policy</a> (|A|) is said to <dfn for="policy" export>subsume</dfn> another <a>policy</a>
(|B|) if |B| is at least as strict as |A|. In this case |B| could also be said to be
<dfn for="policy" export>subsumed by</dfn> |A|. The details of determining "at least as
strict"ness are spelled out in [[#subsumption]].
* When multiple policies are present, they have a combined effect which is described in
[[CSP3#multiple-policies|"The effect of multiple policies"]]. Here, we'll talk about the
combined effect of a [=/CSP list=] as their [=CSP list/intersection=]. The details of
determining that are spelled out in [[#intersection]].
* A <a>policy</a> (|A|) is said to <a>subsume</a> a [=/CSP list=] if |A| <a>subsumes</a> their
[=CSP list/intersection=].
Intersection {#intersection}
------------
<h4 algorithm id="intersection-policy-list">CSP List intersection</h4>
The <dfn export for="CSP list">intersection</dfn> of a [=/CSP list=] (|list|) for an [=/origin=]
(|origin|) is a single [=Content Security Policy object=] representing their net effect,
produced by the following algorithm:
Note: It isn't always possible to represent the [=CSP list/intersection=] of multiple policies as
a single policy. Consider `script-src 'unsafe-inline'` and `script-src 'nonce-abc'`, for instance:
the former allows only inline script, the latter allows only inline or externalized script with a
particular token. The net effect (only inline script with a particular token) cannot be created
with a single policy. Dealing with such policies is, for the moment, left as an exercise for the
reader.
ISSUE: We shouldn't make the reader do this exercise.
<ol class="algorithm">
1. Let |result| be a <a for="/">policy</a> object with an empty <a>directive set</a>
and a disposition of "`enforce`".
2. For each |policy| in |list|:
1. If |policy|'s <a for="policy">disposition</a> is "`report`",
<a for="iteration">continue</a>.
2. Set |result| to the [=content security policy object/intersection=] of |result|
and |policy| for |origin|.
3. Return |result|.
</ol>
<div class="example">
The intersection of the policies created by parsing each item in the following list of
<a>serialized CSPs</a>:
<pre>
«
"default-src 'self' http://example.com http://example.net;
connect-src 'none';",
"connect-src http://example.com/;
script-src http://example.com/",
"style-src 'self';
script-src http://example.com/ http://example.net",
»
</pre>
is the policy created by parsing the following <a>serialized CSP</a>:
<pre>
"default-src 'self' http://example.com http://example.net;
connect-src 'none';
script-src http://example.com/;
style-src 'self'"
</pre>
Each policy specified in the initial list <a for="policy">subsumes</a> the intersection.
</div>
### Policy Intersection ### {#intersection-policy}
The <dfn for="content security policy object">intersection</dfn> of two [=Content Security Policy
objects=] (|A| and |B|) for an [=/origin=] (|origin|) is a single [=Content Security Policy
object=] representing their combined effect, produced by the following algorithm:
<ol class="algorithm">
1. Assert: |A| and |B| both have a <a for="policy">disposition</a> of "`enforce`".
2. If |A|'s <a>directive set</a> <a for="list">is empty</a>, return |B|.
3. If |B|'s <a>directive set</a> <a for="list">is empty</a>, return |A|.
4. Let |policy| be a new <a for="/">policy</a> with an empty
<a for="policy">directive set</a>, and a <a for="policy">disposition</a>
"`enforce`".
5. Let |directive names| be an empty <a>set</a>.
6. For each |directive| in |A|:
1. <a for="set">Append</a> |directive|'s <a for="directive">name</a> to |directive names|.
7. For each |directive| in |B|:
1. <a for="set">Append</a> |directive|'s <a for="directive">name</a> to |directive names|.
8. For each |directive name| in |directive names|:
1. If |directive name| is "`report-uri`", "`report-to`", <a for="iteration">continue</a>.
2. Let |directive A| be the <a>effective directive value</a> for |directive name| and |A|.
3. Let |directive B| be the <a>effective directive value</a> for |directive name| and |B|.
4. Assert: |directive A| and |directive B| are not both `null`, and either both of their
<a for="directive">values</a> are <a>source lists</a>, or neither of their
<a for="directive">values</a> are <a>source lists</a>.
5. If either |directive A| or |directive B| has a <a for="directive">value</a> which is
not a <a>source list</a>, <a for="iteration">continue</a>.
ISSUE: We need to extend this definition to handle things that are not source lists.
Also, we should be more precise about this, perhaps by defining a term like "source list
directive" that we could check against |directive name|.
6. If |directive A| is `null`:
1. Let |directive| be a new <a>directive</a> with the following properties:
: <a for="directive">name</a>
:: |directive name|
: <a for="directive">value</a>
:: |directive B|'s <a for="directive">value</a>
2. <a for="set">append</a> a |directive| to |policy|'s <a for="policy">directive set</a>.
3. <a for="iteration">Continue</a>.
7. If |directive B| is `null`:
1. Let |directive| be a new <a>directive</a> with the following properties:
: <a for="directive">name</a>
:: |directive name|
: <a for="directive">value</a>
:: |directive A|'s <a for="directive">value</a>
2. <a for="set">append</a> a |directive| to |policy|'s <a for="policy">directive set</a>.
3. <a for="iteration">Continue</a>.
8. Let |directive value| be the [=source list/intersection=] of |directive A|'s
<a for="directive">value</a>, |directive B|'s <a for="directive">value</a>, |directive
name|, and |origin|.
9. Let |directive| be a new <a>directive</a> with he following properties:
: <a for="directive">name</a>
:: |directive name|
: <a for="directive">value</a>
:: |directive value|
10. <a for="set">Append</a> |directive| to |policy|'s <a>directive set</a>.
9. Return |policy|.
</ol>
<div class="example">
The intersection of the policies obtained by parsing the following <a>serialized CSPs</a>:
<pre>
"default-src 'self' http://example.com http://example.net;
connect-src 'none';"
and
"connect-src http://example.com/;
script-src http://example.com/"
</pre>
is the policy obtained by parsing the following <a>serialized CSP</a>:
<pre>
"default-src 'self' http://example.com http://example.net;
connect-src 'none';
script-src http://example.com/;
</pre>
Both of the given policies <a>subsume</a> the <a for="CSP list">intersection</a>. For example,
the <a for="CSP list">intersection</a>'s "`script-src http://example.com/`" is
<a for="policy">subsumed by</a> the first policy's "`default-src 'self' http://example.com
http://example.net`" and the second policy's "`script-src http://example.com/`".
</div>
### Source List Intersection ### {#intersection-source-list}
The <dfn for="source list">intersection</dfn> of two [=source lists=] for a
[=directive/name|directive name=] (|name|) and an [=/origin=] (|origin|) is a [=source list=]
representing their net effect. If no such [=source list=] exists (for example,
`https://example.com/` in |A| and `https://not-example.com` in |B|), then the intersection will
be the list « <a grammar>'none'</a> ».
<ol class="algorithm">
1. Let |effective A| be the <a>effective source list</a> for |A|, |name|, and |origin|.
2. Let |effective B| be the <a>effective source list</a> for |B|, |name|, and |origin|.
3. If either |effective A| or |effective B| is « <a grammar>`'none'`</a> »,
return « <a grammar>`'none'`</a> ».
4. If |effective A| is empty, return |effective B|.
5. If |effective B| is empty, return |effective A|.
6 Let |schemes| be an empty <a>set</a>.
7. Let |intersection| be an empty <a>source list</a>.
8. For each |expression B| in |effective B|:
1. If |expression B| matches the <a grammar>`scheme-source`</a> grammar and |expression B|
is <a for="set">contained</a> in |effective A|, then <a for="set">append</a>
|expression B| to |schemes|.
Note: Getting the <a>effective source list</a> above means that tokens matching the
<a grammar>`scheme-source`</a> grammar have already been normalized such that
"`http:`"/"`ws:`" never appears without "`https:`"/"`ws:`" also appearing.
9. For each |expression| in |schemes|:
1. If |expression| does not match "`https:`" or |schemes| does not
<a for="list">contain</a> "`http:`":
1. If |expression| does not match "`wss:`" or |schemes| does not
<a for="list">contain</a> "`ws:`", <a for="set">append</a>
|expression| to |intersection|.
10. For each |expression A| in |effective A|:
1. If |expression A| matches <a grammar>`scheme-source`</a> grammar
and |schemes| <a for="list">contains</a> |expression A|,
<a for="iteration">continue</a>.
2. For each |expression B| in |effective B|:
1. If at least one of |expression A| and |expression B| does not
match <a grammar>`scheme-source`</a> or
<a grammar>`host-source`</a> grammar:
1. If |expression A| matches <a grammar>`keyword-source`</a>
grammar and is an <a>ASCII case-insensitive</a> match for
|expression B|, <a for="set">append</a> |expression A| to
|intersection|.
2. If |expression A| matches <a grammar>`nonce-source`</a> or
<a grammar>`hash-source`</a> grammar and is a
<a>case-sensitive</a> match for |expression B|,
<a for="set">append</a> |expression A| to |intersection|.
3. <a for="iteration">Continue</a> to the next |expression B|.
2. If |expression B|'s <a grammar>`scheme-part`</a> matches one of
the elements in |schemes|, <a for="iteration">continue</a> to the
next |expression B|.
3. If the result of executing [[#intersection-source-expressions]] is
not `null` given |expression A| and |expression B|, <a for="set">append</a>
the result to |intersection|.
11. Return |intersection|.
</ol>
<div class="example">
In these cases, `intersection` is an
<a for="source expression">intersection</a> for |A| and |B|.
<pre>
A = wss: http://example.com
B = https: wss: 'none'
intersection = wss: https://example.com
</pre>
The expression "wss:" is present in both policies, so it is present in
their intersection. Similarly, "http://example.com" is present in the
intersection because it is the only expression subsumed by both
"http://example.com" and "https:". Note that "'none'"" is ignored, as it is
not the only token in |B|.
<pre>
A = http://*.a.com http://*.b.com
B = https://a.com:* http://*.c.com
intersection = https://a.com
</pre>
Only two sources are similar: "http://*.a.com" in |A| is similar to
"https://a.com:*" in |B| so the intersection of the two source lists is
"https://a.com".
<pre>
A = 'unsafe-inline' http://example.com:443/page1/html 'nonce-abc'
B = 'unsafe-inline' https://example.com:443/ 'strict-dynamic' 'nonce-abc'
intersection = 'nonce-abc'
</pre>
Since "`strict-dynamic`" honors only <a grammar>`nonce-source`</a> and
<a grammar>`hash-source`</a> expressions, |B| is effectively
"'strict-dynamic' 'nonce-abc'". That is why the intersection is
"'nonce-abc'".
</div>
### Intersection Helpers ### {#intersection-helpers}
#### Effective Directive Value #### {#effective-directive}
<div algorithm="effective directive">
Given a <a>string</a> (|name|) and a <a>policy</a> (|policy|), the
<dfn lt="effective directive value" export>effective directive value for |name| and
|policy|</dfn> is the <a for="directive">value</a> resulting from running the following steps:
1. Switch on |name| and execute the associated steps:
: "`child-src`"
: "`connect-src`"
: "`font-src`"
: "`img-src`"
: "`manifest-src`"
: "`media-src`"
: "`object-src`"
: "`script-src`"
: "`style-src`"
::
1. If |policy|'s <a>directive set</a> <a for="set">contains</a> a <a>directive</a>
whose <a for="directive">name</a> is |name|, return that <a>directive</a>'s
<a for="directive">value</a>.
2. If |policy|'s <a>directive set</a> <a for="set">contains</a> a <a>directive</a>
whose <a for="directive">name</a> is "`default-src`", return that <a>directive</a>'s
<a for="directive">value</a>.
3. Return `null`.
: "`frame-src`"
: "`worker-src`"
::
1. If |policy|'s <a>directive set</a> <a for="set">contains</a> a <a>directive</a>
whose <a for="directive">name</a> is |name|, return that <a>directive</a>'s
<a for="directive">value</a>.
2. If |policy|'s <a>directive set</a> <a for="set">contains</a> a <a>directive</a>
whose <a for="directive">name</a> is "`child-src`", return that <a>directive</a>'s
<a for="directive">value</a>.
3. If |policy|'s <a>directive set</a> <a for="set">contains</a> a <a>directive</a>
whose <a for="directive">name</a> is "`default-src`", return that <a>directive</a>'s
<a for="directive">value</a>.
4. Return `null`.
: "`base-uri`"
: "`block-all-mixed-content`"
: "`default-src`"
: "`frame-ancestors`"
: "`form-action`"
: "`plugin-types`"
: "`report-uri`"
: "`require-sri-for`"
: "`sandbox`"
: "`upgrade-insecure-requests`"
::
1. If |policy|'s <a>directive set</a> <a for="set">contains</a> a <a>directive</a>
whose <a for="directive">name</a> is |name|, return that <a>directive</a>'s
<a for="directive">value</a>.
2. Return `null`.
2. Return `null`.
</div>
#### Effective Source List #### {#the-effective-source-list}
<div algorithm="effective source list">
Given a <a>source list</a> (|list|), a <a>string</a> (|name|), and an <a for="/">origin</a>
(|origin|), the <dfn lt="effective source list">effective source list for |list|, |name|, and
|origin|</dfn> is a simplification of |list| that expands complex tokens like
<a grammar>`'self'`</a> and <a grammar>`*`</a>, and removes ineffective, obviated, or invalid
tokens (for instance, <a grammar>`'unsafe-inline'`</a> in the presence of a nonce). The result
of running the following steps will generally be more verbose than |list|, but will be
significantly simpler to compare:
1. If |list| <a for="set">is empty</a> or « 'none' », return « 'none' ».
2. Let |result| be an empty <a>source list</a>.
3. For each |expression| in |list|:
1. If |expression| is "<a grammar>`'self'`</a>":
1. <a for="set">Append</a> the result of executing [[#rewrite-self]] given |origin| to
|result|.
2. <a for="iteration">Continue</a>.
2. If |expression| matches the <a grammar>`keyword-source`</a> grammar, and |name| is not
"`script-src`" or "`style-src`", <a for="iteration">continue</a>.
2. <a for="iteration">Continue</a> if any of the following statements are true:
* |expression| is "<a grammar>`'none'`</a>"
* |expression| is "<a grammar>`'strict-dynamic'`</a>" and |name| is not "`script-src`"
* |expression| matches either the <a grammar>`nonce-source`</a> or
<a grammar>`hash-source`</a> grammar, and |name| is not "`script-src`" or
"`style-src`"
* |expression| is "<a grammar>`'unsafe-inline'`</a>", |name| is "`script-src`, and
|list| <a for="set">contains</a> one or more tokens that match one of the
<a grammar>`nonce-source`</a> grammar, the <a grammar>`hash-source`</a> grammar,
or "<a grammar>`'strict-dynamic'`</a>"
* |expression| matches either the <a grammar>`host-source`</a> or
<a grammar>`scheme-source`</a> grammar, |name| is "`script-src`", amd |list|
<a for="set">contains</a> the token "<a grammar>`'strict-dynamic'`</a>"
* |name| is "`plugin-types`", and |expression| does not match the
<a grammar>`media-type`</a> grammar
3. If |expression| is the U+002A ASTERISK character (`*`):
1. For each |scheme| in « "`ftp:`", "`http:`", "`https:`", "`ws:`", "`wss:`" »:
1. Append |scheme| to |result|.
2. Append the concatenation of |origin|'s <a for="origin">scheme</a> and "`:`" to
|result|.
3. <a for="iteration">Continue</a>.
4. If |expression| matches the <a grammar>scheme-source</a> grammar:
1. If |expression| is "`http:`", <a for="set">append</a> "`https:`" to
|result|.
2. If |expression| is "`ws:`", <a for="set">append</a> "`wss:`" to |result|.
5. If |expression| matches the <a grammar>host-source</a> grammar:
1. If |expression|'s <a grammar>scheme-part</a> is "http", append the result of
concatenating "https://", |expression|'s <a grammar>host-part</a>, |expression|'s
<a grammar>port-part</a>, and |expression|'s <a grammar>path-part</a> to |result|.
2. If |expression|'s <a grammar>scheme-part</a> is "ws", append the result of
concatenating "wss://", |expression|'s <a grammar>host-part</a>, |expression|'s
<a grammar>port-part</a>, and |expression|'s <a grammar>path-part</a> to |result|.
6. <a for="set">Append</a> |expression| to |result|.
4. If |result| <a for="set">is empty</a> or « 'strict-dynamic' », return « 'none' ».
5. Return |result|.
</div>
<div class="example">
For any directive with origin `https://example.test/`:
<pre>
https: wss: 'none' 'self'
</pre>
The effective source list is "http: wss: https://example.test/". Note that
"'none'" is not part of the effective source list because it has no effect
when it is not the only source.
For "`style-src`":
<pre>
http://example.com 'strict-dynamic' 'nonce-abc'
</pre>
The effective source list is "http://example.com 'nonce-abc'" since
"'strict-dynamic'" is ignored in non-"`script-src`" directives.
For "`script-src`":
<pre>
http://example.com 'strict-dynamic' 'nonce-abc'
</pre>
The effective source list is "'strict-dynamic' 'nonce-abc'" since
"'strict-dynamic'" in "`script-src`" case does not honor host and scheme
source expressions.
</div>
#### Source Expression Similarity #### {#similarity-source-expressions}
A [=source expression=] (|A|) is said to be <dfn export>source-expression similar</dfn> to
another [=source expression=] (|B|) if the two expressions are <a>case-sensitive</a>
matches, or if the relevant parts of their grammar match (for example, in the case of
<a grammar>`scheme-source`</a> expressions, the respective <a grammar>`scheme-part`</a>s
must <a>`scheme-part` match</a> in one direction or the other.
Note: This property is symmetric. That is if |A| is <a>source-expression similar</a> to |B|,
then |B| will be <a>source-expression similar</a> to |A|.
A <a>source expression</a> has a
<dfn for="source expression">wildcard host</dfn> if the first character of the
<a>source expression</a>'s <a grammar>`host-part`</a> is an U+002A ASTERISK
character (`*`).
A <a>source expression</a> has a
<dfn for="source expression">wildcard port</dfn> if the
<a grammar>`port-part`</a> of the <a>source expression</a> is an U+002A
ASTERISK character (`*`).
<ol class="algorithm">
1. If |A|'s grammar does not match |B|'s grammar, return "`Not Similar`".
2. If |A| matches the <a grammar>`keyword-source`</a>, <a grammar>`nonce-source`</a>,
or <a grammar>`hash-source`</a> grammar:
1. If |A| is a <a>case-sensitive</a> match to |B|, return "`Similar`".
2. Return "`Not Similar`".
2. Let |scheme A| be |A|'s <a grammar>`scheme-part`</a>, if present, and
`null` otherwise.
3. Let |scheme B| be |B|'s <a grammar>`scheme-part`</a>, if present, and
`null` otherwise.
4. If the |scheme A| does not <a>scheme-part match</a> |scheme B|, and |scheme B| does not
<a>scheme-part match</a> |scheme A|, return "`Not Similar`".
5. If |A| or |B| matches <a grammar>`scheme-source`</a> grammar, return "`Similar`".
6. Let |host A| be |A|'s <a grammar>`host-part`</a>, if present, and `null` otherwise.
7. Let |host B| be |B|'s <a grammar>`host-part`</a>, if present, and `null` otherwise.
8. Let |port A| be |A|'s <a grammar>`port-part`</a>, if present, and `null` otherwise.
9. Let |port B| be |B|'s <a grammar>`port-part`</a>, if present, and `null` otherwise.
10. Let |path A| be |A|'s <a grammar>`path-part`</a>, if present, and `null` otherwise.
11. Let |path B| be |B|'s <a grammar>`path-part`</a>, if present, and `null` otherwise.
12. Return "`Not Similar`" if any of the following is true:
1. Both |A| and |B| have a <a for="source expression">wildcard host</a>, but |host A| is
not an <a>ASCII case-insensitive</a> match to |host B|.
2. At most one of |A| and |B| has a <a for="source expression">wildcard host</a>, |host A|
does not <a>`host-part` match</a> |host B|, and |host B| does not <a>`host-part`
match</a> |host A|.
3. Neither |A| nor |B| has a <a for="source expression">wildcard port</a>, |port A|
does not <a>`port-part` match</a> |port B|, and |port B| does not <a>`port-part`
match</a> |port A|.
4. |path A| does not <a>`path-part` match</a> |path B|, and |path B| does not
<a>`path-part` match</a> |path A|.
13. Return "`Similar`".
</ol>
<div class="example">
<pre>
A = 'nonce-ch4hvvbHDpv7xCSvXCs3BrNggHdTzxUA'
B = 'nonce-ch4hvvbHDpv7xCSvXCs3BrNggHdTzxUA'
</pre>
Since both |A| and |B| match the <a grammar>`nonce-source`</a> grammar and |A|
is a <a>case-sensitive</a> match for |B|, |A| is similar to |B|.
<pre>
A = https://inner.example.com/foo/
B = http://*.example.com/foo/bar/
</pre>
Since |A| has a <a for="source expression">wildcard host</a>, it matches any
subdomain which in this case is "inner" so that |A| is similar to |B|.
<pre>
A = http://*.example.com
B = https://example.com:*
</pre>