CVE-2025-13107
Overview
Background
- `<permission>` element (PEPC)
- the Page-Embedded Permission Control, a browser-rendered permission button implemented in
third_party/blink/renderer/core/html/html_permission_element.cc, whose click is treated as a trusted user permission decision. - PEPC style locking
- Blink allows only a list of CSS properties on
<permission>and clamps or resets others, so that a page cannot make the control illegible or misleading. - Internal permission text span
- the shadow-tree element that holds the control’s label text; it inherits computed style from the
<permission>host.
Root Cause Analysis
The <permission> style-adjustment code in html_permission_element.cc (the enclosing function’s name is not in the hunk; the hunk ends just before HTMLPermissionElement::DidRecalcStyle) enforced an allowlist of CSS properties for the element. As the commit message and the new comment explain, -webkit-text-stroke-width, -webkit-text-stroke-color and -webkit-text-fill-color are not on that allowlist, but they are inherited by default. So values from the page, set on the element or an ancestor, still flowed into the computed style and reached the internal label span. The invariant that the label is painted only with locked styling was therefore broken.
The fix tightens enforcement by calling builder.ResetTextStrokeWidth(), builder.ResetTextFillColor() and builder.ResetTextStrokeColor() at the end of the adjustment, so those properties fall back to their initial values before the inner text inherits them. The reftest text-stroke-and-fill.tentative.html, which sets a 10px blue stroke and red fill on both permission and its parent div, must now match an unstyled <permission>. The advisory files this under “Compositing”, but the fix itself is in Blink style resolution for PEPC; the commit’s Fixed: 429440615 matches the advisory’s bug ID.
-webkit-text-* paint properties until they were explicitly reset on the builder.Attack Path
- Page embeds a permission control
A site places
<permission type="camera">in its document. - Page sets inherited text paint properties
It applies
-webkit-text-stroke-width,-webkit-text-stroke-colorand/or-webkit-text-fill-colorto the element or an ancestor, as the reftest does. - Values bypass the allowlist via inheritance Before the fix, these inherited values reached the internal label span and changed how the browser-controlled text was painted.
- Label is obscured or disguised
A thick stroke, or a fill colour that differs from the contrast-checked
color, could make the label illegible or misleading and so trick the user into granting a permission (inferred; the patch demonstrates only the visual change).
Impact Assessment
<permission> control’s label is painted in its own renderer, weakening PEPC’s anti-clickjacking style guarantees. This is a UI-deception issue (inferred), not a memory-safety or sandbox bug. It is reachable by ordinary page CSS wherever the <permission> element is available to the page. That requires the PEPC feature to be enabled (flag or origin trial at the time; inferred, since the diff does not show the gating), plus a user click on the altered control. The vendor severity is low, and the supplied metadata does not report in-the-wild exploitation.Files Changed
third_party/blink/renderer/core/html/html_permission_element.ccthird_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.htmlthird_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html
Audit Directions
- Inherited properties outside the allowlistEnumerate every inherited CSS property, especially prefixed or paint-only ones such as
-webkit-text-security,text-shadow,paint-order,-webkit-text-emphasis,font-paletteandcaret/accentcolours, and confirm that PEPC either allows and clamps each one or explicitly resets it. - Contrast checks vs. actual paint colourPEPC validates
coloragainst the background, so check whether other properties (fill colour, stroke,background-clip: text, filters on ancestors) can make the painted colours differ from the checked ones. - Shadow-tree inheritance in locked controlsFor other browser-rendered trusted UI built from shadow DOM, check that host-level property restrictions are re-applied to, or reset for, the internal elements that inherit from the host.
Patch
From c5539496a8fd440a8f9ad62031184a109307efd7 Mon Sep 17 00:00:00 2001 From: Andy Paicu <[email protected]> Date: Tue, 08 Jul 2025 01:46:38 -0700 Subject: [PATCH] [PEPC] Prevent -webkit-text-stroke/-webkit-text-fill on the permission element Even though these properties are not allowed for the permission element, it seems they get inherited to the inner text anyways. Therefore reset them manually. Fixed: 429440615 Change-Id: Ie17b82de959c92ba808d3c966385f31527ef2ea1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6705900 Reviewed-by: Mason Freed <[email protected]> Commit-Queue: Andy Paicu <[email protected]> Cr-Commit-Position: refs/heads/main@{#1483611} --- diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc index 28d8b733..ac35dd0 100644 --- a/third_party/blink/renderer/core/html/html_permission_element.cc +++ b/third_party/blink/renderer/core/html/html_permission_element.cc @@ -981,6 +981,15 @@ } } } + + // These webkit-prefixed properties are not supported by the permission + // element. But since they are inherited by default, they are passed through + // to the internal permission text span, even if they're not on the list of + // allowed CSS properties. + // Reset them here to avoid any side effects. + builder.ResetTextStrokeWidth(); + builder.ResetTextFillColor(); + builder.ResetTextStrokeColor(); } void HTMLPermissionElement::DidRecalcStyle(const StyleRecalcChange change) { diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.html new file mode 100644 index 0000000..c420d08 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<body> + <div> + <permission type="camera"></permission> + </div> +</body> +</html> \ No newline at end of file diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html new file mode 100644 index 0000000..05b49508 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="match" href="text-stroke-and-fill-ref.html"> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<body> + <style> + permission, div { + -webkit-text-stroke-width: 10px; + -webkit-text-stroke-color: blue; + -webkit-text-fill-color: red; + } + </style> + <div> + <permission type="camera"></permission> + </div> +</body> +</html> \ No newline at end of file
Regression Test / PoC
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.html
new file mode 100644
index 0000000..c420d08
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<body>
+ <div>
+ <permission type="camera"></permission>
+ </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html
new file mode 100644
index 0000000..05b49508
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/text-stroke-and-fill.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<link rel="match" href="text-stroke-and-fill-ref.html">
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style">
+<body>
+ <style>
+ permission, div {
+ -webkit-text-stroke-width: 10px;
+ -webkit-text-stroke-color: blue;
+ -webkit-text-fill-color: red;
+ }
+ </style>
+ <div>
+ <permission type="camera"></permission>
+ </div>
+</body>
+</html>
\ No newline at end of file
Original Bug Report
Permission element inner div with style -webkit-text-stroke-width: thick;font-weight: bolder; can be abused if no element in the parent chain has any -webkit-text-stroke-width: thick;font-weight: bolder; are set.
This bug is similar to https://issues.chromium.org/issues/398803201, https://issues.chromium.org/issues/423670839, https://issues.chromium.org/issues/428455319, https://issues.chromium.org/issues/429270814
when -webkit-text-stroke-width: thick;font-weight: bolder; are embedded in a tag above the <permission> tag, for example a div or something else, this affects the <permission> tag, thus covering the text in the <permission> tag. if style -webkit-text-stroke-width: thick;font-weight: bolder; embedded directly in the <permission> tag are not affected. The impact is that sites can potentially trick users to trigger permission prompts even if the permission is currently in a DENY state for that origin. (It does not however bypass the permission prompt in any way)
references for some css that can and cannot be embedded in the <permission> tag: https://developer.chrome.com/blog/permission-element-origin-trial?hl=en