diff --git a/spec.bs b/spec.bs index 7279c18..4d8a99a 100644 --- a/spec.bs +++ b/spec.bs @@ -80,9 +80,11 @@ spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/ text: historyHandling; url: navigation-hh text: referrerPolicy; url: navigation-referrer-policy text: attempt to populate the history entry's document; url: attempt-to-populate-the-history-entry's-document + text: navigation params; url: navigation-params for: navigation params text: response; url: navigation-params-response text: navigable; url: navigation-params-navigable + text: origin; url: navigation-params-origin for: history handling behavior text: replace; url: hh-replace for: document state @@ -109,6 +111,21 @@ spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/ urlPrefix: nav-history-apis.html for: Window text: navigable; url: window-navigable + urlPrefix: webappapis.html + for: environment + text: target browsing context; url: concept-environment-target-browsing-context + urlPrefix: document-sequences.html + for: browsing context + text: active document; url: active-document +spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/ + type: dfn + text: queue a cross-origin embedder policy CORP violation report; url: queue-a-cross-origin-embedder-policy-corp-violation-report + text: should request be blocked due to a bad port; url: block-bad-port +spec: mixed-content; urlPrefix: https://w3c.github.io/webappsec-mixed-content/ + type: dfn + text: should fetching request be blocked as mixed content; url: should-block-fetch +spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/ + type: dfn urlPrefix: interactive-elements.html text: accesskey attribute command; url: using-the-accesskey-attribute-to-define-a-command-on-other-elements text: previously focused element; url: previously-focused-element @@ -125,6 +142,16 @@ spec: RFC8941; urlPrefix: https://www.rfc-editor.org/rfc/rfc8941.html text: structured header; url: #section-1 for: structured header text: token; url: name-tokens +spec: permissions-policy; urlPrefix: https://w3c.github.io/webappsec-permissions-policy + type: dfn + text: ASCII-serialized policy directive; url: serialized-policy-directive + text: serialized permissions policy; url: serialized-permissions-policy + text: supported features; url: supported-features + text: declared policy; url: declared-policy + text: the special value *; url: the-special-value + text: permissions policy; url: permissions-policy + for: permissions + text: matches; url: matches spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/ type: dfn text: directive value; url: directive-value @@ -258,6 +285,7 @@ dl, dd {
[=Global attributes=]
[=width=] — Horizontal dimension
[=height=] — Vertical dimension
+
<{fencedframe/allow}> — [=Permissions policy=] to be applied to the <{fencedframe}>'s contents
[=Accessibility considerations=]:

TODO

[=DOM interface=]:
@@ -270,6 +298,7 @@ interface HTMLFencedFrameElement : HTMLElement { [CEReactions] attribute FencedFrameConfig? config; [CEReactions] attribute DOMString width; [CEReactions] attribute DOMString height; + [CEReactions] attribute DOMString allow; }; @@ -344,6 +373,14 @@ The config IDL attribute getter 1. TODO +The allow attribute, when specified, determines the +[=container policy=] that will be used when the [=Document/permissions policy=] for a {{Document}} +in the <{fencedframe}>'s [=fenced navigable container/fenced navigable=] is initialized. Its value +must be a [=serialized permissions policy=]. [[!PERMISSIONS-POLICY]] + +The IDL attribute allow must [=reflect=] the +respective content attribute of the same name. +

Dimension attributes

This section details monkeypatches to [[!HTML]]'s creator< document's referrer|referrer=], [=Document/origin=], [=creator base url=], [=Document/policy container=], across the fenced frame boundary. -Issue: Ensure we are doing the right thing for [=Document/permissions policy=]. -

Nested traversables

Introduction

@@ -1731,3 +1766,239 @@ specification is printed below: /fenced-frame/cspee.https.html /fenced-frame/embedder-csp-not-propagate.https.html + +

Permissions Policies

+ +*This introductory sub-section is non-normative.* + +The [=policy-controlled features=] available to {{Document}}s inside of a <{fencedframe}> are +determined exclusively by the {{FencedFrameConfig}} that the <{fencedframe}> navigates to. +Specifically, the {{FencedFrameConfig}}'s [=fencedframeconfig/config=]'s [=fenced frame +config/effective enabled permissions=] defines the exclusive list of [=policy-controlled features=] +that will be enabled in the {{Document}} (all others will be disabled). + +During navigation, the {{FencedFrameConfig}}'s [=fencedframeconfig/config=] [=instantiate a +config|instantiates=] a [=navigable/fenced frame config instance=] that is stored on the [=fenced +navigable container/fenced navigable=]. This navigable's [=navigable/fenced frame config +instance=]'s [=fenced frame config instance/effective enabled permissions=] is consulted [=Should +navigation response to navigation request be blocked by Permissions Policy?|during navigation=]. A +<{fencedframe}> navigation can only succeed if the [=Document/permissions policy=] for the +navigation's resulting {{Document}} has an [=permissions policy/inherited policy=] such that the +[=inherited policy for a feature|inherited policy value=] is "`Enabled`" for each feature in the +[=fenced frame config/effective enabled permissions=]. Otherwise the environment the <{fencedframe}> +is embedded in is deemed unsuitable for the [=fenced frame config=], and the navigation is blocked. + +At the same time, to make sure that a <{fencedframe}>'s embedder does not directly influence content +in the frame based on that navigation's [=navigation params/origin=] (since the origin is derived +from cross-site data), this specification modifies various [[PERMISSIONS-POLICY]] algorithms such +that a <{fencedframe}> {{Document}}'s [=permissions policy/inherited policy=] is computed without +consideration of whether its [=navigation params/origin=] is [=same origin=] with its embedder's. +Therefore a feature can only be enabled inside of a <{fencedframe}> if its embedder *explicitly* +delegates it via [=the special value *=] [=allowlist=]. + +Considering all of the above, we get the following interesting implications: + + * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame + config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of + [=the special value *=], and no \`
`Permissions-Policy`\` header is served on + the <{fencedframe}> embedder, and the <{fencedframe/allow}> attribute is empty, the navigation + inside the <{fencedframe}> will succeed, and the resulting {{Document}} will be [=allowed to + use=] the [=policy-controlled feature|feature=] (i.e., it will be enabled). + + * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame + config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of + `'self'`, and no \``Permissions-Policy`\` + header is served on the <{fencedframe}> embedder, and the <{fencedframe/allow}> attribute is + empty, the navigation inside the <{fencedframe}> will be blocked. + + Note: This is because ordinarily this [=policy-controlled feature|feature=] would only be enabled + if the subframe's {{Document}} was [=same origin=] with its embedder, a check this specification + avoids for fenced frames, since the <{fencedframe}>s {{Document}}'s [=Document/origin=] is + derived from cross-site data. Therefore, we simply "fail close". + + * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame + config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of + `'self'`, and the <{fencedframe}>'s <{fencedframe/allow}> + attribute contains the [=policy-controlled feature|feature=] but no [=allowlist=], the rules + described in The `allow` attribute section, the default + [=allowlist=] for the feature will be `'src'` which is meant to represent the embedder-supplied + navigation [=URL=], for which there is none when navigating a <{fencedframe}>, as the navigation + [=URL=] is determined by the [=fenced frame config=]. The navigation will be blocked. + + * If a [=policy-controlled feature|feature=] that [=list/exists=] in the [=fenced frame + config/effective enabled permissions=] has a [=policy-controlled feature/default allowlist=] of + `'self'` but either the \``Permissions-Policy`\` header served on the <{fencedframe}>'s embedder *OR* the + <{fencedframe/allow}> attribute sets that [=policy-controlled feature|feature=]'s [=allowlist=] + to [=the special value *=], then the navigation inside the <{fencedframe}> will succeed, and the + resulting {{Document}} is [=allowed to use=] the [=policy-controlled feature|feature=]. + + * If a navigation inside a <{fencedframe}> would otherwise succeed, but the [=response=] on the + navigation inside the <{fencedframe}> is served with a \``Permissions-Policy`\` header that sets the [=allowlist=] to "`none`" or an + otherwise incompatible [=origin=] for a feature in the [=fenced frame config/effective enabled + permissions=], the navigation still succeeds, but the {{Document}} in the <{fencedframe}> is + **not** [=allowed to use=] the feature. + + Note: This is OK because it is the <{fencedframe}>'s content *itself* that is making the decision + to disable a particular feature, not its embedder environment. + +The patches in the below section "fence" the appropriate [[PERMISSIONS-POLICY]] algorithms to +achieve the outcomes described in the above explanatory content. + +

Algorithm patches

+ +
+ Rename the The + `allow` attribute of the `iframe` element section to "The `allow` attribute of the `iframe` + and `fencedframe` element", and rewrite the section to read: + + <{iframe}> and <{fencedframe}> elements have an respective `allow` attributes (<{iframe}>: + <{iframe/allow}>; <{fencedframe}>: <{fencedframe/allow}>), which contain an [=ASCII-serialized + policy directive=]. + + The allowlist for the features named in the attribute may be empty; in that case, the default + value for the [=allowlist=] is "`src`", which represents the origin of the URL in the iframe's + <{iframe/src}> attribute, or the fencedframe's [=fenced frame config=]. + + When not empty, the <{iframe}>'s <{iframe/allow}> or <{fencedframe}>'s <{fencedframe/allow}> + attribute will result in adding an [=allowlist=] for each [=supported feature=] to the <{iframe}> + or <{fencedframe}>'s [=container policy=]. +
+ +
+ Modify the [$Create a Permissions Policy for a navigable$] algorithm: + + Given null or an element (|container|), an [=origin=] (|origin|), and an optional [=boolean=] + |fenced| that defaults to false, this algorithm returns a new Permissions Policy. + + Rewrite step 1 to read: + + 1. [=Assert=]: if not null, container is either a [=navigable container=] or a + [=fenced navigable container=]. + + Rewrite step 4 to read: + + 4. For each |feature| supported: + + 1. Let |isInherited| be the result of running [$Define an inherited policy for feature in + container at origin$] on |feature|, |container|, |origin|, and |fenced|. + + 2. Set inherited policy[|feature|] to |isInherited|. +
+ +
+ Modify the [$Create a Permissions Policy for a navigable from response$] algorithm to read: + + Given null, a [=navigable container=], or a [=fenced navigable container=] (|container|), an + [=origin=] (|origin|), and a [=response=] (response), this algorithm returns a + new [=Document/permissions policy=]. + + Add a step before step 1 that reads: + + 1. Let |fenced| be true if |container| is a [=fenced navigable container=], and false otherwise. + + Modify what is *now* step 2 of the algorithm to read: + + 2. Let policy be the result of running [$Create a Permissions Policy + for a navigable$] given |container|, |origin|, and |fenced|. +
+ +
+ Modify [[HTML]]'s [=attempt to populate the history entry's document=] algorithm. Add a step + before the step inside the [=queue a task|queued task=] starting with "If + |failure| is true, then:" that reads: + + 8. Otherwise, if the result of [=Should navigation response to navigation request be blocked by + Permissions Policy?=] given navigationParams is "`Blocked`", then set + |failure| to true. + + Note: If this algorithm returns "`Blocked`", the pre-existing {{Document}} in the <{fencedframe}> + does not stick around; an error page will be loaded. +
+ +
+ Create a new algorithm called Should navigation response to navigation request be blocked by + Permissions Policy? in [[!HTML]]. + + Given a [=navigation params=] (|navigationParams|), this algorithm returns "`Blocked`" or + "`Allowed`": + + 1. Let |navigable| be |navigationParams|'s [=navigation params/navigable=]. + + 1. If |navigable| is not a [=fenced navigable container/fenced navigable=], then return + "`Allowed`". + + 1. Let |origin| be |navigationParams|'s [=navigation params/origin=]. + + 1. Let |effective permissions| be the |navigable|'s [=navigable/fenced frame config instance=]'s + [=fenced frame config instance/effective enabled permissions=]. + + 1. Let |permissions policy| be the result of [$Create a Permissions Policy for a navigable| + creating a permissions policy$] given |navigable|'s [=fenced navigable container=], |origin|, + and fenced set to true. + + Note: This is identical to the [=Document/permissions policy=] that will be created when the + navigation constructs the ultimate {{Document}} for this pending navigation. We create it now + and run tests on it since this is the appropriate time to determine if a navigation will fail, + and then throw it away. If the navigation succeeds, it will be recreated identically and + unconditionally installed on the {{Document}}. + + 1. Let |inherited policy| be |permissions policy|'s [=permissions policy/inherited policy=]. + + 1. [=list/For each=] |effective permission| of |effective permissions|: + + 1. If |inherited policy|[|effective permission|] is "Disabled", return "`Blocked`". + + 1. Return "`Allowed`." +
+ +
+ Modify the [$Define an inherited policy for feature in container at origin$] algorithm to + read: + + Given a feature (|feature|), null or a [=navigable container=] (|container|), an [=origin=] for a + document in that container (|origin|), and an optional [=boolean=] |fenced| that defaults to + false, this algorithm returns the [=permissions policy/inherited policy=] for that feature. + + Rewrite step 3 to read: + + 3. If the result of executing [$Is feature enabled in document for origin?$] on |feature|, + |container|'s [=Node/node document=], |origin|, and |fenced| is "Disabled", return + "Disabled". + + Note: We don't have to rewrite step 2, which also delegates to the same algorithm, to pass in the + |fenced| [=boolean=] because step 2 has to do with checking to see if |feature| is enabled + |container|'s [=Node/node document=], not the {{Document}} hosted *inside* |container|. + + Rewrite step 7 to read: + + 7. If |fenced| is false, |feature|'s [=policy-controlled feature/default allowlist=] is + `'self'`, and |origin| is [=same origin=] with |container|'s [=Node/node document=]'s + origin, return `"Enabled"`. +
+ +
+ Modify the [$Is feature enabled in document for origin?$] algorithm to read: + + Given a feature (|feature|), a {{Document}} object (|document|), an [=url/origin=] (|origin|), and + an optional [=boolean=] |fenced| that defaults to false, this algorithm returns "`Disabled`" if + |feature| should be considered disabled, and "`Enabled`" otherwise. + + Rewrite step 3 to read: + + 3. If |feature| is present in |policy|'s [=declared policy=], + + 1. If |fenced| is set to false, and the [=allowlist=] for |feature| in |policy|'s [=declared + policy=] [=permissions/matches=] |origin|, then return "`Enabled`". + + 2. Otherwise, if |fenced| is set to true, and the [=allowlist=] for |feature| in |policy|'s + [=declared policy=] is [=the special value *=], then return "`Enabled`". + + 3. Otherwise, return "`Disabled`". + + Rewrite step 5 to read: + + 5. If |fenced| is set to false, |feature|'s [=policy-controlled feature/default allowlist=] is + `'self'`, and |origin| is [=same origin=] with |document|'s origin, return "Enabled". +