Mark extension <link> writes before rel-based classification
handleAttributeMutation returned early when resourceTypeForElement
yielded null, which happens for `<link href>` whenever `rel` is not yet
"stylesheet". An extension that called `link.setAttribute("href", ...)`
before setting `rel` therefore skipped extensionElements and
extensionUrls marking; when the rel was filled in later and the browser
fetched the stylesheet, PerformanceObserver -- whose stack carries no
extension frame -- reported it as a page tracker.
Check isExtensionCaller() before classification and tag the element and
URL whenever the element type can ever initiate a load via the given
attribute, using a new couldLoadResource helper. The page-caller path
still uses the strict resourceTypeForElement so non-stylesheet `<link>`
writes do not generate spurious reports.
Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -523,17 +523,54 @@ export class ResourceDetector implements Detector {
|
|||||||
): void {
|
): void {
|
||||||
if (typeof value !== "string" || value === "") return;
|
if (typeof value !== "string" || value === "") return;
|
||||||
|
|
||||||
const rt = this.resourceTypeForElement(el, attrName);
|
// Extension marking runs before classification because the
|
||||||
if (rt === null) return;
|
// current attribute alone is not always enough to know whether a
|
||||||
|
// load will happen. The canonical case is `<link>`: a stylesheet
|
||||||
|
// load is only triggered once both `href` and `rel="stylesheet"`
|
||||||
|
// are set, in any order. If the extension sets `href` first we
|
||||||
|
// would otherwise miss tagging, and the eventual PerformanceObserver
|
||||||
|
// entry -- which has no extension frame on its stack -- would leak
|
||||||
|
// through as a page tracker. The same holds for `<link rel=preload>`
|
||||||
|
// and any future rel value that initiates a load.
|
||||||
if (isExtensionCaller()) {
|
if (isExtensionCaller()) {
|
||||||
this.markExtension(el, this.identifierOf(value));
|
if (this.couldLoadResource(el, attrName)) {
|
||||||
|
this.markExtension(el, this.identifierOf(value));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rt = this.resourceTypeForElement(el, attrName);
|
||||||
|
if (rt === null) return;
|
||||||
|
|
||||||
this.processResource(value, rt);
|
this.processResource(value, rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// couldLoadResource reports whether `el` is an element type that can
|
||||||
|
// ever initiate a network load via `attrName`, regardless of any
|
||||||
|
// other attributes that may or may not be set yet. It is a superset
|
||||||
|
// of resourceTypeForElement: it returns true for `<link href>` even
|
||||||
|
// when `rel` has not been set, because the rel may change later and
|
||||||
|
// turn the href into a real load.
|
||||||
|
private couldLoadResource(el: Element, attrName: string): boolean {
|
||||||
|
if (attrName === "src") {
|
||||||
|
return (
|
||||||
|
el instanceof HTMLScriptElement
|
||||||
|
|| el instanceof HTMLIFrameElement
|
||||||
|
|| el instanceof HTMLImageElement
|
||||||
|
|| (typeof HTMLSourceElement !== "undefined" && el instanceof HTMLSourceElement)
|
||||||
|
|| (typeof HTMLEmbedElement !== "undefined" && el instanceof HTMLEmbedElement)
|
||||||
|
|| (typeof HTMLMediaElement !== "undefined" && el instanceof HTMLMediaElement)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (attrName === "href") {
|
||||||
|
return el instanceof HTMLLinkElement;
|
||||||
|
}
|
||||||
|
if (attrName === "data") {
|
||||||
|
return typeof HTMLObjectElement !== "undefined" && el instanceof HTMLObjectElement;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private resourceTypeForElement(el: Element, attrName: string): ResourceType | null {
|
private resourceTypeForElement(el: Element, attrName: string): ResourceType | null {
|
||||||
if (attrName === "src") {
|
if (attrName === "src") {
|
||||||
if (el instanceof HTMLScriptElement) return "script";
|
if (el instanceof HTMLScriptElement) return "script";
|
||||||
|
|||||||
Reference in New Issue
Block a user