Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow some fenced frames to inherit permissions #140

Merged
merged 10 commits into from
Dec 11, 2024
177 changes: 106 additions & 71 deletions spec.bs
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,9 @@ following [=struct/items=]:
:: a [=string=]
</dl>

A <dfn for=fencedframetype>permissions policy behavior</dfn> is either "<dfn for="permissions policy
behavior">`fixed`</dfn>" or "<dfn for="permissions policy behavior">`flexible`</dfn>".

The <dfn export for=fencedframetype>default fenced frame effective sandboxing flags</dfn> are a
[=sandboxing flag set=] with the following flags:

Expand Down Expand Up @@ -1300,6 +1303,9 @@ A <dfn export>fenced frame config instance</dfn> is a [=struct=] with the follow
: <dfn>effective sandboxing flags</dfn>
:: null, or a [=sandboxing flag set=]

: <dfn>permissions policy behavior</dfn>
:: a [=fencedframetype/permissions policy behavior=]

: <dfn>effective enabled permissions</dfn>
:: null, or a [=list=] of [=policy-controlled features=]

Expand Down Expand Up @@ -1354,6 +1360,10 @@ A <dfn export>fenced frame config instance</dfn> is a [=struct=] with the follow
:: |config|'s [=fenced frame config/effective sandboxing flags=] if null, otherwise |config|'s
[=fenced frame config/effective sandboxing flags=]'s [=effective sandboxing flags/value=]

: [=fenced frame config instance/permissions policy behavior=]
:: [=permissions policy behavior/flexible=] if |config|'s [=fenced frame config/effective
enabled permissions=] is null, [=permissions policy behavior/fixed=] otherwise.

: [=fenced frame config instance/effective enabled permissions=]
:: |config|'s [=fenced frame config/effective enabled permissions=] if null, otherwise
|config|'s [=fenced frame config/effective enabled permissions=]'s [=effective enabled
Expand Down Expand Up @@ -3615,22 +3625,32 @@ directive wouldn't give web sites enough control over their CSP rules. Introduce

*This introductory sub-section is non-normative.*

The [=policy-controlled features=] available to {{Document}}s inside of a <{fencedframe}> are
determined exclusively by the [=fenced frame config=] that the <{fencedframe}> navigates to.
Specifically, the [=fenced frame 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 [=fenced frame config=] [=instantiate a config|instantiates=] a [=browsing
context/fenced frame config instance=] that is stored on the [=browsing context=] in the [=fenced
navigable container/fenced navigable=]. This browsing context's [=browsing context/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.
The [=policy-controlled features=] available to {{Document}}s inside of a <{fencedframe}>, as well
as the manner in which they are calculated, vary depending on how the [=fenced frame config=] that
the <{fencedframe}> navigates to is constructed.

A [=fenced frame config instance=] created via the {{FencedFrameConfig}} constructor on the web
platform will have a [=permissions policy behavior/flexible=] [=fencedframetype/permissions policy
behavior=], and the inner {{Document}} of the <{fencedframe}> it navigates will be allowed to
inherit permissions as long as they are part of the [=fenced frame allowed permissions=] list. All
other [=policy-controlled features=] will be disabled.

A [=fenced frame config instance=] created via a config-generating API that sets its [=fenced frame
config/effective enabled permissions=] will have a [=permissions policy behavior/fixed=]
[=fencedframetype/permissions policy behavior=], and the inner {{Document}} of the <{fencedframe}>
it navigates to will have the [=fenced frame config/effective enabled permissions=] be the exclusive
list of [=policy-controlled features=] that will be enabled in the {{Document}} (all others will be
disabled).

During a <{fencedframe}> navigation to a [=fenced frame config instance=] with a [=permissions
policy behavior/fixed=] [=fencedframetype/permissions policy behavior=], it compares the [=fenced
frame config instance/effective enabled permissions=] of the [=fenced frame config instance=] being
navigated to against the resulting {{Document}}'s [=Document/permissions policy=]'s [=permissions
policy/inherited policy=]. The navigation only succeeds if each inherited feature whose [=inherited
policy for a feature|inherited policy value=] is "`Enabled`" also appears in the [=fenced frame
config instance/effective enabled permissions=] [=fenced frame config instance=]. 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
Expand All @@ -3640,7 +3660,8 @@ consideration of whether its [=navigation params/origin=] is [=same origin=] wit
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:
Considering all of the above, we get the following interesting implications for [=permissions policy
behavior/fixed=] [=fencedframetype/permissions policy behavior=] navigations:

* 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
Expand Down Expand Up @@ -3690,6 +3711,11 @@ Considering all of the above, we get the following interesting implications:
The patches in the below section "fence" the appropriate [[PERMISSIONS-POLICY]] and [[HTML]]
algorithms to achieve the outcomes described in the above explanatory content.

<h4 id=permissions-policy-definitions>Definitions</h4>

The <dfn>fenced frame allowed permissions</dfn> are either "`private-aggregation`",
"`shared-storage`", or "`shared-storage-select-url`".

<h4 id=permissions-policy-patches>Algorithm patches</h4>

<div id=allow-attribute-fenced-frame algorithm=allow-attribute-fenced-frame>
Expand All @@ -3711,47 +3737,56 @@ algorithms to achieve the outcomes described in the above explanatory content.
</div>

<div algorithm>
Create a new algorithm, called <dfn>Derive a permissions policy directly from a fenced frame
config instance</dfn>.

Given null or an [=element=] (|container|), this algorithm returns a new [=permissions policy=].
Create a new algorithm, called <dfn>Create a permissions policy for a fenced navigable</dfn>.

1. Let |inherited policy| be a new [=ordered map=].
Given a [=fenced navigable container=] (|container|) and an [=origin=] (|origin|), this algorithm
returns a new [=Permissions Policy=].

1. Let |effective permissions| be an empty [=list=].

1. Let |fenced frame config| be |container|'s [=Node/node document=]'s [=navigable/active browsing
1. Let |fencedFrameConfig| be |container|'s [=Node/node document=]'s [=navigable/active browsing
context=]'s [=browsing context/fenced frame config instance=].

1. If |fenced frame config| is not null, and |fenced frame config|'s [=fenced frame config/
effective enabled permissions=] are not null, set |effective permissions| to
|fenced frame config|'s [=fenced frame config/effective enabled permissions=].
1. Let |inheritedPolicy| be a new [=ordered map=].

1. If |fencedFrameConfig| is not null and |fencedFrameConfig|'s [=fenced frame config
instance/permissions policy behavior=] is [=permissions policy behavior/fixed=], then:

1. [=list/For each=] |feature| [=supported features|supported=]:
1. [=list/For each=] |feature| [=supported features|supported=]:

1. If |effective permissions| [=list/contains=] |feature|, then set |inherited policy|[feature]
to "`Enabled`".

Otherwise, set |inherited policy|[feature] to "`Disabled`".
1. If |fencedFrameConfig|'s [=fenced frame config instance/effective enabled permissions=]
[=list/contains=] |feature|, then set |inheritedPolicy|[feature] to "`Enabled`".

Otherwise, set |inheritedPolicy|[feature] to "`Disabled`".

Note: While this doesn't take the <{fencedframe/allow}> attribute into consideration, it
will have already been checked by the time this is called because of [=Should navigation
response to navigation request be blocked by Permissions Policy?=]. Any policy specified in
<{fencedframe/allow}> that is too restrictive would have cause the fenced frame to not load,
and any policy that is more permissive than what is specified in the [=fenced frame
config/effective enabled permissions=] will be ignored.

1. Otherwise:

1. [=list/For each=] |feature| [=supported features|supported=]:

1. If |feature| matches one of the [=fenced frame allowed permissions=], then set
|inheritedPolicy|[feature] to the result of running [$Define an inherited policy for
feature in container at origin$] given |feature|, |container|, and |origin|.

Otherwise, set |inheritedPolicy|[feature] to "`Disabled`".

1. Let |policy| be a new [=permissions policy=], with [=permissions policy/inherited policy=]
|inherited policy| and [=permissions policy/declared policy=] a new [=ordered map=].
|inheritedPolicy| and [=permissions policy/declared policy=] initialized to two new [=ordered
maps=].

1. Return |policy|.

Note: While this algorithm doesn't take the <{fencedframe/allow}> attribute into consideration, it
will have already been checked by the time this is called because of [=Should navigation response
to navigation request be blocked by Permissions Policy?=]. Any policy specified in
<{fencedframe/allow}> that is too restrictive would have cause the fenced frame to not load, and
any policy that is more permissive than what is specified in the [=fenced frame config/effective
enabled permissions=] will be ignored.
</div>

<div algorithm=create-permissions-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=].
|matchAll| that defaults to false, this algorithm returns a new [=permissions policy=].

Rewrite step 1 to read:

Expand All @@ -3763,7 +3798,7 @@ algorithms to achieve the outcomes described in the above explanatory content.
4. [=list/For each=] |feature| [=supported features|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|.
container at origin$] on |feature|, |container|, |origin|, and |matchAll|.

1. Set <var ignore>inherited policy</var>[|feature|] to |isInherited|.
</div>
Expand All @@ -3778,7 +3813,7 @@ algorithms to achieve the outcomes described in the above explanatory content.
Rewrite step 1 to read:

1. If |container| is a [=fenced navigable container=], then let |policy| be the result of running
[=derive a permissions policy directly from a fenced frame config instance=] given |container|.
[=create a permissions policy for a fenced navigable=] given |container| and |origin|.

Otherwise, Let |policy| be the result of running [$Create a Permissions Policy for a
navigable$] given |container| and |origin|.
Expand Down Expand Up @@ -3828,28 +3863,28 @@ algorithms to achieve the outcomes described in the above explanatory content.
has not yet been assigned to the browsing context. We should consider storing the instance
inside |navigationParams| and reference it from here instead.

1. Let |permissions policy| be the result of [$Create a Permissions Policy for a navigable|
1. Let |permissionsPolicy| be the result of [$Create a Permissions Policy for a navigable|
creating a permissions policy$] given |navigable|'s [=fenced navigable container=], |origin|,
and <var ignore>fenced</var> set to true.

Note: This is almost identical to the [=permissions policy=] that will be [=derive a
permissions policy directly from a fenced frame config instance|created=] when the navigation
constructs the ultimate {{Document}} for this pending navigation. The difference is that this
algorithm, just like when it is called on iframes, will include all of the permissions
specified in the <{fencedframe/allow}> attribute, even if that permission isn't specified in
the [=fenced frame config=]'s [=fenced frame config/effective enabled permissions=]. 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 and
unconditionally installed on the {{Document}}. However, the recreation will not include any
additional enabled permissions that are not included in the [=fenced frame config/effective
enabled permissions=], effectively locking the enabled permissions to only what is specified in
[=fenced frame config/effective enabled permissions=].

1. Let |inherited policy| be |permissions policy|'s [=permissions policy/inherited policy=].
and true.

Note: This is almost identical to the [=permissions policy=] that will be [=create a
permissions policy for a fenced navigable|created=] when the navigation constructs the ultimate
{{Document}} for this pending navigation. The difference is that this algorithm, just like when
it is called on iframes, will include all of the permissions specified in the
<{fencedframe/allow}> attribute, even if that permission isn't specified in the [=fenced frame
config=]'s [=fenced frame config/effective enabled permissions=]. 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 and unconditionally installed
on the {{Document}}. However, the recreation will not include any additional enabled
permissions that are not included in the [=fenced frame config/effective enabled permissions=],
effectively locking the enabled permissions to only what is specified in [=fenced frame
config/effective enabled permissions=].

1. Let |inheritedPolicy| be |permissionsPolicy|'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. If |inheritedPolicy|[|effective permission|] is "Disabled", return "`Blocked`".

1. Return "`Allowed`."
</div>
Expand Down Expand Up @@ -3886,22 +3921,22 @@ algorithms to achieve the outcomes described in the above explanatory content.
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
document in that container (|origin|), and an optional [=boolean=] |matchAll| 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
|container|'s [=Node/node document=], |origin|, and |matchAll| 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
|matchAll| [=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
7. If |matchAll| 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"`.
</div>
Expand All @@ -3910,25 +3945,25 @@ algorithms to achieve the outcomes described in the above explanatory content.
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
an optional [=boolean=] |matchAll| 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 [=permissions policy/declared policy=],

1. If |fenced| is false, and the [=allowlist=] for |feature| in |policy|'s [=permissions policy/
declared policy=] [=permissions/matches=] |origin|, then return "`Enabled`".
1. If |matchAll| is false, and the [=allowlist=] for |feature| in |policy|'s [=permissions
policy/ declared policy=] [=permissions/matches=] |origin|, then return "`Enabled`".

1. Otherwise, if |fenced| is true, and the [=allowlist=] for |feature| in |policy|'s
1. Otherwise, if |matchAll| is true, and the [=allowlist=] for |feature| in |policy|'s
[=permissions policy/declared policy=] is [=the special value *=], then return "`Enabled`".

1. Otherwise, return "`Disabled`".

Rewrite step 5 to read:

5. If |fenced| is false, |feature|'s [=policy-controlled feature/default allowlist=] is `'self'`,
and |origin| is [=same origin=] with |document|'s origin, return "Enabled".
5. If |matchAll| is false, |feature|'s [=policy-controlled feature/default allowlist=] is
`'self'`, and |origin| is [=same origin=] with |document|'s origin, return "Enabled".
</div>

<div algorithm=declared-origin-patch>
Expand Down
Loading