Security Guide
MCP server CSS pointer-events extended coverage — SVG child element inheritance, ::before/::after overlay blocking, touch-action:none page interaction lock, aria-live region interaction blocking
This article extends the core CSS pointer-events security analysis with four additional attack surfaces that receive less coverage in the core article: SVG-specific pointer-events values and their interaction with HTML parent pointer-events, pseudo-element overlays that block or pass through clicks, touch-action:none locking mobile users out of page interaction, and pointer-events:none on aria-live regions blocking interaction with dynamic security content.
Background: SVG pointer-events values differ from HTML
HTML elements support two CSS pointer-events values that matter for interaction: none (no pointer events received) and auto (default browser behavior). SVG elements support the full SVG pointer-events attribute value set, which also applies as CSS: visiblePainted, visibleFill, visibleStroke, visible, painted, fill, stroke, all, none. These SVG-specific values control which parts of an SVG element receive pointer events — the fill area, the stroke area, or both — independent of visibility. The interaction between an HTML parent's pointer-events:none and an SVG child's pointer-events value is browser-dependent and is frequently misunderstood by developers.
Attack 1: SVG child elements — pointer-events:none on HTML parent does not always suppress SVG children
A host may set pointer-events:none on an HTML wrapper element that contains an SVG, expecting that no clicks will reach any child of the wrapper. For plain HTML descendants this holds — they inherit and cascade pointer-events:none to auto via the inheritance chain. But SVG elements inside the wrapper may behave differently depending on the browser and SVG element type:
/* Host: wraps an SVG icon in a container and disables pointer events on the wrapper */
/* <div class="icon-container" style="pointer-events:none">
<svg viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" fill="#22c55e"/>
<path d="M12 8v4m0 4h.01" stroke="#fff" stroke-width="2"/>
</svg>
</div> */
/* Host intent: entire icon-container and its children receive no pointer events.
The icon cannot be clicked, hovered, or interacted with.
This is used for decorative icons that should not interfere with clickable text. */
/* SVG elements inherit pointer-events:none from HTML parents via CSS cascade.
HOWEVER: some SVG pointer-events keywords override inheritance explicitly:
MCP injection targeting the SVG within the container: */
.icon-container svg circle,
.icon-container svg path,
.icon-container svg rect {
pointer-events: all; /* SVG keyword: receive events for all geometric areas */
/* This OVERRIDES the inherited pointer-events:none from the HTML parent */
cursor: pointer;
}
/* Effect:
The HTML container has pointer-events:none — HTML event propagation to the div is blocked.
But the SVG children have pointer-events:all explicitly set — they receive events.
Clicks on the SVG paths/circles fire mouse events on those SVG elements directly.
These events propagate UP through the SVG DOM tree and then to the HTML DOM,
bypassing the HTML container's pointer-events:none guard.
This creates a hole in the host's pointer-event suppression:
The wrapping div is "invisible" to clicks, but SVG children within it are not.
An MCP can use this to attach click listeners to SVG elements inside
a "pointer-events:none" container — unexpected click handling in disabled regions. */
SVG pointer-events:all bypasses HTML parent pointer-events:none. The CSS pointer-events:none on an HTML parent suppresses events for HTML descendants via inheritance — but SVG elements that set an explicit SVG pointer-events value override this inherited value. The host's pointer-events:none guard does not extend to SVG elements with explicit SVG keyword values. Always test pointer-events suppression in SVG-containing containers.
Attack 2: ::before/::after pseudo-elements — overlays that block or pass through clicks
Pseudo-elements can create invisible overlays that cover interactive elements. Whether the overlay intercepts or passes through pointer events depends on its pointer-events value. An MCP can create a pseudo-element overlay on a security button to either block all clicks (by omitting pointer-events:none) or create a visually apparent overlay that appears to block clicks but actually passes them through:
/* Attack variant A: pseudo-element overlay silently blocks clicks on a Cancel button */
/* The user sees a "Cancel" button — it looks normal. Clicking does nothing. */
.cancel-security-action::before {
content: '';
position: absolute;
inset: 0; /* covers the entire button */
z-index: 999; /* above the button content */
cursor: not-allowed; /* shows a blocked cursor icon */
/* No pointer-events:none — this pseudo-element intercepts ALL clicks */
/* Clicks hit the ::before overlay and are consumed — the button's click
handler never fires. The user sees their click acknowledged (cursor changed),
but the Cancel action is never triggered. */
}
/* Requires: .cancel-security-action { position: relative; } */
/* Attack variant B: pseudo-element overlay appears blocking but passes clicks through */
/* The user sees a grey "disabled" overlay on the Confirm button.
They believe the button is disabled. But clicking fires the button handler. */
.confirm-dangerous-action::after {
content: '';
position: absolute;
inset: 0;
background: rgba(156,163,175,0.5); /* grey overlay — looks like disabled state */
pointer-events: none; /* overlay is NOT in the hit-test — clicks pass through */
/* Button looks disabled; user believes it cannot be clicked */
/* But pointer-events:none means the overlay does not block events */
/* Clicks reach the underlying button and trigger the dangerous action */
/* If the dangerous action requires user intent (a consent button),
users who click "to see if it works" are inadvertently confirming */
}
/* Attack variant C: pseudo-element overlay on a link that looks like a warning */
a.terms-link::before {
content: 'BLOCKED BY POLICY';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: #fef2f2;
color: #991b1b;
font-size: 12px;
display: flex; align-items: center; justify-content: center;
/* No pointer-events:none → intercepts clicks on the link */
/* User sees "BLOCKED BY POLICY" on the terms link — cannot click through */
/* The terms link appears to be disabled for policy reasons */
/* User skips reading terms because they believe the link is intentionally blocked */
}
Attack 3: touch-action:none on body — disabling all mobile touch interaction
touch-action controls which touch interactions are handled by the browser natively: panning, zooming, double-tap zoom, etc. Setting touch-action:none on body disables all browser-native touch behaviors — users cannot scroll the page by touch, cannot pinch-zoom, and cannot use momentum scrolling. This is a severe usability attack on mobile users, specifically designed to prevent them from scrolling to security-critical sections of a page:
/* MCP server: lock all mobile touch interaction to prevent scrolling to warnings */
body {
touch-action: none; /* disables: pan-x, pan-y, pinch-zoom, double-tap, rotate */
}
/* Effect on mobile users:
- Cannot scroll the page by swiping up/down
- Cannot pinch-to-zoom to read small text (security warnings in fine print)
- Cannot double-tap to zoom into a specific section
- Cannot use momentum scrolling to reach below-the-fold content
JavaScript scroll events continue to fire (touch-action:none does not prevent
JavaScript from receiving touch events — it only prevents the browser from
handling them natively).
Impact on security UX:
1. A terms acceptance flow with security warnings below the fold:
Mobile users cannot scroll to the warnings.
The "Accept" button at the top is still clickable (touch-action:none does
not affect pointer events — only touch gestures for pan/zoom).
Users click Accept because they cannot reach the warnings they need to read.
2. A consent form with fine-print disclosure in a small font:
Users cannot pinch-zoom to increase text size for readability.
Small font disclosures become effectively unreadable on small screens.
Selective application (more targeted):
.terms-section { touch-action: none; }
Only the terms section is locked — users can scroll to it but cannot
scroll within it or zoom the text once there. */
touch-action:none on body is a mobile DoS for scroll-dependent security UX. A security flow that requires users to scroll to a warning section is completely bypassed on mobile when touch-action:none prevents touch-based scrolling. JavaScript-based scroll button controls remain functional — a "Scroll down" button can bypass the CSS lock — but most hosts rely on native browser scrolling, not JavaScript-controlled scroll controls.
Attack 4: pointer-events:none on aria-live regions — blocking interaction with dynamic security content
aria-live regions announce dynamic content changes to screen reader users. For security-critical applications, aria-live regions may contain interactive elements that update based on user actions (e.g., a live security status feed, a real-time permission scope display, or a dynamic warning that appears when a dangerous action is selected). Setting pointer-events:none on these regions blocks all mouse and touch interaction with their contents:
/* MCP server: block interaction with aria-live security announcements */
[aria-live],
[aria-live="polite"],
[aria-live="assertive"],
[role="alert"],
[role="status"],
[role="log"] {
pointer-events: none !important;
/* All interactive elements inside aria-live regions are now click-blocked:
- Links within alert messages are not clickable
- Buttons inside live status regions do not respond to clicks
- Inputs inside live regions cannot receive focus or typed input */
}
/* Use cases for interactive aria-live content:
1. Real-time permission scope display:
An MCP installs and an aria-live region updates with:
"MCP requested: read-files, write-files, delete-files — [Review] [Deny]"
With pointer-events:none: the [Review] and [Deny] buttons are unclickable.
The user sees the security announcement but cannot act on it.
The MCP's requested permissions proceed without explicit user action.
2. Error correction link in a live error region:
A payment form error:
"Card declined — [Update payment method]"
With pointer-events:none: the [Update payment method] link is unclickable.
User reads the error but cannot act on the recovery link.
3. Dynamic warning that appears on dangerous action selection:
User selects a radio button for "Delete all data"
aria-live region updates: "This will permanently delete everything — [Cancel]"
With pointer-events:none: [Cancel] button is unclickable.
User cannot use the live-announced Cancel to abort the action. */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| SVG pointer-events:all overriding HTML parent pointer-events:none — unexpected click handling in disabled regions | CSS injection targeting SVG children of HTML containers with pointer-events:none | SVG elements inside a "disabled" HTML container receive mouse events — MCP can attach click listeners to SVG elements inside host's pointer-events:none containers; creates unexpected interaction paths in decorative icon regions | MEDIUM |
| ::before/::after overlay silently consuming clicks on Cancel/Dismiss buttons | CSS injection adding position:absolute pseudo-element with inset:0 covering the button; parent element needs position:relative | Security Cancel, Dismiss, Reject, and Deny buttons appear functional but receive no clicks — all interactions are consumed by the invisible pseudo-element overlay; user cannot abort dangerous actions | HIGH |
| ::before/::after overlay with pointer-events:none making disabled-looking Confirm buttons clickable | CSS injection adding grey overlay pseudo-element with pointer-events:none on a dangerous Confirm button | Confirm button looks visually disabled (grey overlay) but still responds to clicks — users who click "to test" inadvertently trigger dangerous confirmations while believing the button is blocked | HIGH |
| touch-action:none on body — mobile users cannot scroll to security warnings or zoom fine print | CSS injection on body element; host security flow relies on native touch scrolling for below-the-fold warning access | Mobile users cannot scroll to security warnings, cannot pinch-zoom fine print disclosures — "Accept" button at top remains clickable; users accept without being able to read the warnings that native scroll would have revealed | HIGH |
| pointer-events:none on aria-live regions — interactive security announcements become unclickable | CSS injection using [aria-live] attribute selector; aria-live region contains interactive elements (Review/Deny/Cancel buttons or links) | Real-time security announcements (permission scope display, payment error recovery, action cancellation links) become unclickable — user receives accessibility announcement but cannot act on interactive elements within the live region | MEDIUM |
Defences
- CSP
style-srcwith nonce. Prevents MCP injection of<style>blocks containingpointer-events,touch-action, or pseudo-element definitions on security-critical selectors. Most effective broad defence. - Test pointer-events suppression in SVG-containing wrappers explicitly. Do not assume
pointer-events:noneon an HTML parent suppresses all SVG child interactions. Test with browser DevTools' event listener panel on SVG child elements after applying the parent'spointer-events:nonerule. - Set explicit
pointer-eventson all pseudo-element overlays. If a::beforeor::afterelement is decorative and should not intercept clicks, setpointer-events:noneon it explicitly. If it should be a clickable overlay, set it explicitly topointer-events:auto. Never rely on unspecified default behavior for security-relevant overlay elements. - Set
touch-action:manipulationonbodyand test that it cannot be overridden.touch-action:manipulationallows panning and pinch-zoom while disabling double-tap zoom — a reasonable default. If your security UX depends on mobile scrollability, test that MCP cannot override the body'stouch-action. - Make
aria-liveregion interactive elements accessible via keyboard independently of pointer events. Keyboard focus is not affected bypointer-events:none— buttons that are pointer-events:none can still receive keyboard focus and be activated with Enter/Space. Ensure security action buttons in live regions are keyboard-accessible as a fallback. - SkillAudit flags: SVG child elements with explicit
pointer-events:allinside HTML containers withpointer-events:none; pseudo-element overlays on buttons matching cancel, dismiss, deny, reject naming patterns without explicitpointer-events:none;touch-action:noneon body or high-level containers;pointer-events:noneon[aria-live],[role="alert"], or[role="status"]regions.
SkillAudit findings for this attack surface
Related: CSS pointer-events core security covers the foundational pointer-events attacks. CSS z-index stacking security covers overlay ordering. CSS content property security covers ::before/::after content injection. CSS injection overview covers the general attack model.