Category
Robustness fix for webdriver-probe-annotate.
Problem
The rule has two delivery paths (see extension/src/rules/webdriver-probe-annotate.ts:19-37):
- Primary —
background.ts registers webdriver-probe.js as a dynamic content script with world: "MAIN" + runAt: "document_start". Extension main-world scripts registered via chrome.scripting are exempt from page CSP, so this path lands on subsequent navigations regardless of CSP.
- Fallback — the rule's
apply() runs at document_idle and creates an inline <script> with textContent = PROBE_SOURCE, then appends it to document.documentElement. Purpose: cover the tab the user was already viewing when they flipped the toggle on, since dynamic registrations only apply to future navigations.
The fallback is blocked on strict script-src origins. Repro: open https://shield-dark-pattern-demo.vercel.app/, toggle webdriver-probe-annotate on. Console:
Refused to execute inline script because it violates the following Content
Security Policy directive 'script-src 'self' 'wasm-unsafe-eval' ...'
Coverage gap: on strict-CSP sites, the current pageview gets no probe. The next navigation is fine.
Proposed solution
Replace the inline-script fallback with a message-to-background that calls chrome.scripting.executeScript:
// content-side
chrome.runtime.sendMessage({ type: "inject-webdriver-probe" });
// background-side
case "inject-webdriver-probe": {
const tabId = sender.tab?.id;
if (tabId == null) return;
await chrome.scripting.executeScript({
target: { tabId, frameIds: sender.frameId == null ? undefined : [sender.frameId] },
world: "MAIN",
func: installProbe,
});
}
executeScript with world: "MAIN" bypasses page CSP the same way the registered content script does — same primitive, just on-demand. The probe's own __abs_webdriver_probe_installed guard makes re-execution a no-op, so dedupe is already handled.
Considered alternatives
- CSP detection then skip. Sniff
<meta http-equiv="Content-Security-Policy"> or attempt a no-op inline first; abandon if it fails. Quieter console but same coverage gap on the current pageview.
- Reload the tab on toggle-on. Heavy-handed; surprises the user.
- Suppress the console violation. Not possible from JS — Chrome emits it before script execution.
Downsides
- One additional content↔background round-trip per first apply on the active tab. Negligible.
- Slightly more background-side surface area: a new message type and frame-aware
executeScript call.
- No new permissions —
scripting is already in use for the primary path.
Out of scope
- Catching early-parse reads on the toggle-on tab. The fallback today already runs at
document_idle; moving to executeScript doesn't change that. Covering early-parse reads on the existing tab would require a tab reload.
🤖 Generated with Claude Code
Category
Robustness fix for
webdriver-probe-annotate.Problem
The rule has two delivery paths (see
extension/src/rules/webdriver-probe-annotate.ts:19-37):background.tsregisterswebdriver-probe.jsas a dynamic content script withworld: "MAIN"+runAt: "document_start". Extension main-world scripts registered viachrome.scriptingare exempt from page CSP, so this path lands on subsequent navigations regardless of CSP.apply()runs atdocument_idleand creates an inline<script>withtextContent = PROBE_SOURCE, then appends it todocument.documentElement. Purpose: cover the tab the user was already viewing when they flipped the toggle on, since dynamic registrations only apply to future navigations.The fallback is blocked on strict
script-srcorigins. Repro: openhttps://shield-dark-pattern-demo.vercel.app/, togglewebdriver-probe-annotateon. Console:Coverage gap: on strict-CSP sites, the current pageview gets no probe. The next navigation is fine.
Proposed solution
Replace the inline-script fallback with a message-to-background that calls
chrome.scripting.executeScript:executeScriptwithworld: "MAIN"bypasses page CSP the same way the registered content script does — same primitive, just on-demand. The probe's own__abs_webdriver_probe_installedguard makes re-execution a no-op, so dedupe is already handled.Considered alternatives
<meta http-equiv="Content-Security-Policy">or attempt a no-op inline first; abandon if it fails. Quieter console but same coverage gap on the current pageview.Downsides
executeScriptcall.scriptingis already in use for the primary path.Out of scope
document_idle; moving toexecuteScriptdoesn't change that. Covering early-parse reads on the existing tab would require a tab reload.🤖 Generated with Claude Code