commit | ebae2f032492c6b56206f294343e32d089dcb235 | [log] [tgz] |
---|---|---|
author | Liam Brady <lbrady@google.com> | Wed Aug 16 18:49:20 2023 |
committer | Blink WPT Bot <blink-w3c-test-autoroller@chromium.org> | Wed Aug 16 19:38:56 2023 |
tree | 85ae3339d7101d5b7b9d155ee096b3f742d6ce92 | |
parent | 9c29a68172332c51b84cc99fdc4219d64e93234f [diff] |
Fenced frames: add browser-side focus verification. Fenced frames have checks to make sure that focus cannot be pulled programmatically across a fenced frame boundary. However, because of how user activation works, all of the gating happens on the renderer side. This is problematic because this allows a compromised renderer to pull focus across a fenced frame boundary as much as it wants, which can be used to open a communication channel. This CL adds checks on the browser side to make sure that any renderer that pulls focus across a fenced frame boundary was allowed to do so. This handles every focus case, including tab traversals, clicking to focus, programmatic focus, and accessibility-based focusing. If the browser determines focus was not allowed, the plan is to consider the renderer compromised and kill it. Right now, until we know for sure that there are no false positives with this check, we will instead call DumpWithoutCrashing() to be able to monitor this. Note that the focus check will bad message the renderer in tests, CQ, and ToT builds. This CL introduces a new transient state tracking mechanism that will track if a RFHI passing focus had transient user activation. As focus is moved from one RFHI to the next (through the RenderFrameProxyHost::AdvanceFocus and RenderFrameHostImpl::TakeFocus calls), the transient user activation status will be passed with the focus change. A new transient state tracking mechanism is added to RenderWidgetHostImpl to track if focus was lost between calls to RenderFrameHostImpl::DidFocusFrame(). This is done in order to detect if focus was moved from a page to a UI element (such as the navbar). This is needed because focus moving from a UI element back to the page could be considered a bad focus otherwise. The logic that consumes user activation when crossing a fenced frame boundary is moved from the renderer to the browser as part of this change, since focus verification needs to complete before user activation can be consumed. Examples: MainFrame(FF1): - If the main frame currently has focus and we click on FF1, the focus checks will succeed simply because the click action gave the fenced frame user activation. - If instead we <tab> focus from MainFrame to FF1, the focus transfer into FF1 will succeed specifically because <tab> gives MainFrame user activation (which allows it to transfer focus). FF1 script will observe the focus transfer into it, but user activation will not come with it. - If we move focus from FF1 to another element in FF1, focus will be allowed because the change in focus does not cross into a fenced frame boundary. - If a compromised renderer tries to move focus from MainFrame to FF1 without user gesture, since there is no user gesture on either frames, and since focus is crossing a fenced frame boundary, the browser will reject the focus change and badmessage the renderer. Note that this CL will only badmessage the renderer in tests, CQ, and ToT builds. All other builds will instead call DumpWithoutCrashing() so we can monitor potential missed corner cases. MainFrame(FF1(FF2),FF3) with a focusable element only in MainFrame and FF3: - If we <tab> focus through the focusable elements, we will first focus on the element in MainFrame. Focus is allowed because it is not crossing a fenced frame boundary into a fenced frame. The MainFrame will also get transient user activation as a result of this action. We then wait for MainFrame's transient user activation to expire before performing the next step. - The next <tab> gets more complicated. MainFrame will get transient user activation, and then (1) activate FF1's transient state, (2) deactivate MainFrame's transient state, and (3) pass focus into FF1. This passing of focus into FF1 is allowed because the transient state has been activated as a part of the user-gesture-initiated focus from MainFrame => FF1. However, there are no focusable elements in FF1 so, through RenderFrameProxyHost::AdvanceFocus(), FF1 will (1) activate FF2's transient state, (2) deactivate FF1's transient state, and (3) pass focus to FF2. - Focus is allowed in FF2 due to the transient state, but FF2 still needs to pass focus to the next frame. Through RenderFrameHostImpl::TakeFocus(), FF2 will (1) activate FF1's transient state, (2) deactivate FF2's transient state, and (3) pass focus back up to FF1. FF1 will perform the same actions with MainFrame, which in turn will perform the same actions with FF3 through RenderFrameProxyHost::AdvanceFocus(). - FF3 has an element it can focus, so FF3 will focus that element, and the focus check will pass because its transient state is activated. MainFrame(FF1(FF2),FF3) with a focusable element only in FF3: - If we press <tab> for the first time, MainFrame will get user activation as we start the same traversal process as outlined in the previous example. We will end up with the element in FF3 getting focus. - If we press <tab> again, focus will leave the RenderWidgetHost and go into the UI. RenderWidgetHostImpl::LostFocus() will be called and it will set `has_lost_focus_` to true. - Pressing <tab> one more time will start the traversal process again. However, this time, no frames will have user activation. Instead, MainFrame will see that `has_lost_focus_` is true, and will activate its transient state tracking mechanism due to that. It will then go through the same traversal process, where this time the transient state tracking mechanism will be active due to the RWH losing focus rather than the focus originator having user activation. See design document: https://docs.google.com/document/d/1DH9l2mQJbPBJkSEkyVQPpswLafEwSPvKybMJRfgNuhA/edit?usp=sharing Change-Id: I4c771ac56605f204ef3facb501115ff8ce01b428 Bug: 1458985 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4572517 Reviewed-by: Alex Moshchuk <alexmos@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org> Reviewed-by: Peter Beverloo <peter@chromium.org> Reviewed-by: Jeremy Roman <jbroman@chromium.org> Commit-Queue: Liam Brady <lbrady@google.com> Reviewed-by: Dominic Farolino <dom@chromium.org> Cr-Commit-Position: refs/heads/main@{#1184289}
The web-platform-tests Project is a cross-browser test suite for the Web-platform stack. Writing tests in a way that allows them to be run in all browsers gives browser projects confidence that they are shipping software that is compatible with other implementations, and that later implementations will be compatible with their implementations. This in turn gives Web authors/developers confidence that they can actually rely on the Web platform to deliver on the promise of working across browsers and devices without needing extra layers of abstraction to paper over the gaps left by specification editors and implementors.
The most important sources of information and activity are:
wpt:matrix.org
matrix channel; includes participants located around the world, but busiest during the European working day.If you'd like clarification about anything, don't hesitate to ask in the chat room or on the mailing list.
Clone or otherwise get https://github.com/web-platform-tests/wpt.
Note: because of the frequent creation and deletion of branches in this repo, it is recommended to “prune” stale branches when fetching updates, i.e. use git pull --prune
(or git fetch -p && git merge
).
See the documentation website and in particular the system setup for running tests locally.
The wpt
command provides a frontend to a variety of tools for working with and running web-platform-tests. Some of the most useful commands are:
wpt serve
- For starting the wpt http serverwpt run
- For running tests in a browserwpt lint
- For running the lint against all testswpt manifest
- For updating or generating a MANIFEST.json
test manifestwpt install
- For installing the latest release of a browser or webdriver server on the local machine.wpt serve-wave
- For starting the wpt http server and the WAVE test runner. For more details on how to use the WAVE test runner see the documentation.On Windows wpt
commands must be prefixed with python
or the path to the python binary (if python
is not in your %PATH%
).
python wpt [command]
Alternatively, you may also use Bash on Ubuntu on Windows in the Windows 10 Anniversary Update build, then access your windows partition from there to launch wpt
commands.
Please make sure git and your text editor do not automatically convert line endings, as it will cause lint errors. For git, please set git config core.autocrlf false
in your working tree.
The master branch is automatically synced to wpt.live and w3c-test.org.
Save the Web, Write Some Tests!
Absolutely everyone is welcome to contribute to test development. No test is too small or too simple, especially if it corresponds to something for which you've noted an interoperability bug in a browser.
The way to contribute is just as usual:
git checkout -b topic
../wpt lint
as described above.If you spot an issue with a test and are not comfortable providing a pull request per above to fix it, please file a new issue. Thank you!