When a Chrome extension is updated or disabled while the user has a tab open, the content script context is invalidated but any DOM elements it injected remain. Without cleanup logic, updating the extension leaves orphaned buttons, sidebars, or overlays on long-lived pages — a support and reliability failure that manifests as 'ghost UI' that does nothing when clicked. ISO 25010 reliability requirements apply: a component that cannot clean up its own side effects on context invalidation is not reliably bounded. In enterprise environments where tabs stay open for days, uncleaned DOM from invalidated extension contexts accumulates and causes user-visible breakage.
Low because orphaned DOM elements are a reliability failure rather than a security vulnerability, but they directly surface to users as broken UI after every extension update.
Connect a port in your content script and listen for onDisconnect to remove injected elements when the extension context is invalidated.
// src/content.js
const port = chrome.runtime.connect();
port.onDisconnect.addListener(() => {
host.remove(); // Clean up injected UI
});
Alternatively, assign a unique data-extension-id attribute to all injected elements and remove them all via querySelectorAll in a MutationObserver callback that detects context invalidation.
ID: extension-permissions-security.content-script-isolation.dom-cleanup
Severity: low
What to look for: Search content scripts for cleanup handlers: runtime.onDisconnect, disconnect event listeners, MutationObserver teardown, or element removal logic. Count the number of injected elements vs cleanup handlers.
Pass criteria: At least 1 cleanup mechanism exists to remove orphan UI elements when the extension is updated, disabled, or when the content script disconnects. Logic handles runtime.onDisconnect or equivalent.
Fail criteria: No cleanup logic found in content scripts for injected UI.
Skip (N/A) when: No UI injection in content scripts.
Detail on fail: "No cleanup logic for injected UI. Updates may leave 'ghost' buttons or duplicate UIs on long-lived pages."
Remediation: Listen for the runtime.onDisconnect event in your content script or use a MutationObserver pattern to detect if your extension context has been invalidated, and clean up your DOM elements.
// src/content.js
const port = chrome.runtime.connect();
port.onDisconnect.addListener(() => { host.remove(); });