← All issues

[3] HTMLPlugInElement skips content extensions for embed/object loads

Severity: Medium | Component: WebCore HTML plugin elements | 7d28e39

Rated Medium because the diff fixes a deterministic content-blocker bypass reachable from any page that can author <embed>/<object> markup; impact is policy-evasion (tracker/malware/ad loads escaping user-installed rules), not memory corruption.

Plugin content loaded via <embed> and <object> elements was not checked against content extension rules. The fix adds a content-extension check in HTMLPlugInElement::canLoadURL so blocked URLs are rejected before the wouldLoadAsPlugIn deferral in updateWidget.

Source/WebCore/html/HTMLPlugInElement.cpp

+#if ENABLE(CONTENT_EXTENSIONS)
+#include "ContentExtensionsBackend.h"
+#include "ResourceLoadInfo.h"
+#include "UserContentProvider.h"
+#endif
...
bool HTMLPlugInElement::canLoadURL(const URL& completeURL) const
{
+#if ENABLE(CONTENT_EXTENSIONS)
+ if (completeURL.isValid()) {
+ RefPtr page = document().page();
+ RefPtr frame = document().frame();
+ RefPtr documentLoader = frame ? frame->loader().documentLoader() : nullptr;
+ RefPtr userContentProvider = frame ? frame->userContentProvider() : nullptr;
+ if (page && documentLoader && userContentProvider) {
+ auto results = userContentProvider->processContentRuleListsForLoad(*page, completeURL, ContentExtensions::ResourceType::Other, *documentLoader);
+ if (results.shouldBlock())
+ return false;
+ }
+ }
+#endif
return !isProhibitedSelfReference(completeURL);
}

LayoutTests/http/tests/contentextensions/block-embed-element.html

+<embed src="http://127.0.0.1:8000/contentextensions/resources/should-load.html?should-be-blocked" type="text/html" width="100" height="100"></embed>
+<embed src="http://127.0.0.1:8000/media/resources/test.pdf?should-be-blocked" type="application/pdf" width="100" height="100"></embed>

When ENABLE(CONTENT_EXTENSIONS) is on and the URL is valid, canLoadURL now obtains the document's Page, DocumentLoader, and UserContentProvider, then calls UserContentProvider::processContentRuleListsForLoad(page, completeURL, ContentExtensions::ResourceType::Other, documentLoader). If the aggregated result's shouldBlock() is true, canLoadURL returns false so the caller skips loading the plugin resource. Regression tests load <embed> and <object> resources matching a .*should-be-blocked URL filter and assert the standard 'Content blocker prevented frame...' console message fires.

Missing content-policy consultation on a resource-load path, allowing the plugin element class to bypass a filter applied uniformly to other element types.

Content Extensions (also called content blockers) expose a declarative rule-list API to WebKit extensions and managed configurations. Each rule pairs a trigger (URL pattern, resource type, load context) with an action (block, block-cookies, css-display-none, make-https). UserContentProvider::processContentRuleListsForLoad is the engine entry point that evaluates all active rule lists for a load and returns aggregated actions; callers inspect results.shouldBlock() to decide whether to abort the load. HTMLPlugInElement is the C++ base class for <embed>, <object>, and legacy <applet>; its canLoadURL is the single funnel both subclasses consult before updateWidget asks the SubframeLoader to instantiate the plugin or subframe widget. Content-extension hooks on <iframe>, <img>, <script>, and XHR/fetch loads live in their respective loader paths.

HTMLPlugInElement::canLoadURL validated the URL against self-reference checks but never consulted the content-extension engine. The rule-list evaluation path that exists for <img>, <script>, <iframe>, XHR, and other resource types was simply not wired into the plugin element class.

🔒

How content-policy enforcement is distributed across WebKit's resource-loading classes, and what this commit reveals about coverage gaps in user-installed filters

Subscribe to read more

🔒

Several reusable audit patterns identified for finding sibling bypasses in other element and loader classes, with concrete starting points

Subscribe to read more