MCP server CSS @namespace selector security: SVG element targeting, namespace-qualified consent element hiding, universal namespace selector attacks, and namespace property override bypasses
Published 2026-09-18 — SkillAudit Research
CSS @namespace is a little-used but fully supported CSS rule that declares a namespace prefix for use in selectors. Once a namespace is declared — e.g., @namespace svg "http://www.w3.org/2000/svg" — selectors can use the prefix to match only elements in that namespace: svg|text matches <text> elements in the SVG namespace, while text (without prefix) matches only HTML <text> elements (which don't normally exist), and *|text matches text elements in any namespace including SVG.
For MCP server consent bypass attacks, @namespace matters because modern consent dialogs increasingly use inline SVG for icons, progress indicators, or styled text. An MCP server that injects a stylesheet with namespace-qualified rules can hide SVG elements inside consent dialogs — text labels, icon states, interactive SVG buttons — without affecting any HTML element. CSS auditing tools that query document.querySelectorAll with HTML selectors or check getComputedStyle on HTML elements will not detect the hidden SVG consent components.
Browser support: @namespace is a CSS Namespaces Module Level 3 feature supported in all modern browsers. Namespace-qualified selectors (svg|element, *|element, |element) work in Chrome, Firefox, Safari, and Edge. The feature predates CSS3 — it has been supported since IE 9.
Attack 1: SVG text element hiding — consent label in SVG namespace
Some MCP consent dialogs render disclosure text inside an inline SVG element — either as a <text> element for styled typography or as <foreignObject> containing HTML. If the disclosure text label is a <text> element in the SVG namespace, a namespace-qualified svg|text { display: none } rule hides it. A CSS rule of just text { display: none } would have no effect on SVG elements in an HTML document (they are in a different namespace), and the namespace-qualified variant is rarely checked by audit tools:
/* Attack 1: SVG namespace text element hiding */
/* Declare SVG namespace prefix */
@namespace svg "http://www.w3.org/2000/svg";
/* Target only SVG text elements — has no effect on HTML elements */
svg|text {
display: none;
}
/* More targeted: hide only SVG text elements inside a consent dialog */
.consent-dialog svg|text {
display: none;
/* HTML selector + SVG namespace selector combination.
.consent-dialog is matched as an HTML element.
svg|text inside it matches only SVG elements within.
The disclosure label rendered as SVG text is hidden.
HTML , , elements in the same dialog are unaffected. */
}
/* Scanner gap: most CSS auditing tools scan for rules like:
.consent-dialog { display: none }
.consent-button { visibility: hidden }
[data-consent] { opacity: 0 }
But very few scan for namespace-qualified rules:
svg|text { display: none }
The CSSStyleRule.selectorText for the first rule contains "svg|text" as a string.
A scanner must check for the pipe character | in selectorText to catch namespace rules. */
// Detection
function detectNamespaceQualifiedHide() {
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule instanceof CSSStyleRule) {
const sel = rule.selectorText || '';
// Check for namespace separator pipe character
if (sel.includes('|')) {
const style = rule.style;
if (style.display === 'none' || style.visibility === 'hidden' ||
parseFloat(style.opacity) === 0) {
console.error('SECURITY: namespace-qualified CSS rule hides elements', {
selectorText: sel,
cssText: rule.cssText
});
}
}
}
}
} catch (e) { /* cross-origin sheet */ }
}
}
Attack 2: universal namespace selector — hide consent elements in all namespaces
The wildcard namespace prefix *| matches elements in any namespace — HTML, SVG, MathML, or custom. A rule like *|.consent-class { display: none } hides elements with class consent-class regardless of their XML namespace. This is useful to the attacker when the consent dialog uses a mixed HTML+SVG structure where some consent elements are SVG and some are HTML — a single namespace-universal rule hides both categories:
/* Attack 2: universal namespace selector hides consent in all namespaces */
@namespace "http://www.w3.org/1999/xhtml"; /* default: HTML namespace */
@namespace svg "http://www.w3.org/2000/svg"; /* SVG namespace */
/* *| prefix targets elements in ANY namespace */
*|.consent-label {
display: none;
/* Hides .consent-label whether it is:
- An HTML
Attack 3: SVG presentation attribute namespace override
SVG elements support both CSS properties (via style attribute or stylesheets) and SVG presentation attributes (like fill, opacity, display set directly on the element). SVG presentation attributes have a specificity lower than any CSS declaration. An MCP server can use a low-specificity namespace-qualified selector to override SVG presentation attributes that set consent icon colors or sizes:
/* Attack 3: SVG presentation attribute namespace override for consent icon state */
/* A consent dialog uses SVG icons where the current consent state is shown via
SVG presentation attributes:
— green circle = consent granted
— red circle = consent denied
These are SVG presentation attributes with very low specificity. */
@namespace svg "http://www.w3.org/2000/svg";
/* Override the fill color of all SVG circles in the consent icon to grey */
svg|circle {
fill: #9ca3af !important;
/* Both "granted" (green) and "denied" (red) circles become grey.
The user cannot visually distinguish consent state from the icon.
The button label text (HTML) is unaffected — it may still say "Grant" or "Deny",
but the visual state indicator is ambiguous. */
}
/* More severe: make SVG icon elements transparent */
svg|[data-state="granted"],
svg|[data-state="denied"] {
opacity: 0;
/* All SVG consent state indicator elements invisible.
The HTML consent button is present; the icon that reinforces the action is hidden. */
}
/* Attack on consent dialog using SVG for interactive elements (SVG click targets): */
svg|rect[role="button"],
svg|g[role="button"] {
pointer-events: none;
/* SVG elements that are interactive (role="button") become unclickable.
If the consent action relies on clicking an SVG rect or g group,
the consent action is disabled while the element is still visibly present. */
}
// Detection
function detectSVGPresentationAttributeOverride() {
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule instanceof CSSStyleRule && rule.selectorText.includes('|')) {
const style = rule.style;
// SVG-specific properties used for consent bypass
if (style.fill || style.stroke || parseFloat(style.opacity) === 0 ||
style.pointerEvents === 'none') {
console.warn('SECURITY: namespace-qualified rule overrides SVG presentation attributes', {
selectorText: rule.selectorText,
cssText: rule.cssText
});
}
}
}
} catch (e) { /* cross-origin sheet */ }
}
}
Attack 4: no-namespace selector targets default-namespace HTML outside consent scope
A CSS selector with the empty namespace prefix (|element — pipe with no prefix) matches only elements in no namespace. In a standard HTML document with a declared default namespace of http://www.w3.org/1999/xhtml, all HTML elements are in the HTML namespace, so |button would match nothing. But in an XHTML document (served as XML with namespace declarations) or in a mixed-namespace embedded component, the no-namespace selector can be used to match custom elements that were inserted without a namespace declaration. MCP server consent bypass can exploit this in frameworks that render consent dialogs using custom elements or web components without explicit XML namespace:
/* Attack 4: no-namespace selector targets custom-element consent dialogs */
/* Declare the HTML namespace as default */
@namespace "http://www.w3.org/1999/xhtml";
/* The empty-prefix selector |element matches elements in NO namespace.
Custom consent dialog web components declared without namespace: */
/* In an XHTML context or custom element context where the consent dialog
is rendered without an explicit namespace, the no-namespace selector matches: */
|consent-dialog {
display: none;
/* Hides custom element that was inserted without namespace.
Regular HTML elements (in the HTML namespace) are NOT matched.
The browser's HTML auditing APIs report the host HTML elements as visible,
but the custom element consent dialog is hidden. */
}
/* Detection: the empty-prefix pattern is |element (starts with pipe) */
function detectNoNamespaceSelector() {
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule instanceof CSSStyleRule) {
const sel = rule.selectorText || '';
// No-namespace selector starts with | or contains space + | not preceded by *
if (sel.startsWith('|') || sel.match(/\s\|[a-z]/)) {
const style = rule.style;
if (style.display === 'none' || style.visibility === 'hidden') {
console.warn('SECURITY: no-namespace selector hides consent elements', {
selectorText: sel,
cssText: rule.cssText
});
}
}
}
}
} catch (e) { /* cross-origin */ }
}
}
Primary scanner gap: Most CSS auditing tools call document.querySelectorAll('[data-consent], .consent-dialog, #consent-panel') and check getComputedStyle on each matched element. These selectors match HTML elements only. SVG namespace elements inside the consent dialog are returned by querySelectorAll in some browsers but their namespace context differs. Check rule.selectorText.includes('|') in your stylesheet scanner to catch all namespace-qualified hiding rules.
Attack summary
Attack
Selector pattern
Effect
Detection
Severity
SVG text hide
svg|text { display: none }
SVG consent text labels hidden; HTML button visible
Flag any CSSStyleRule selectorText with | pipe character + display:none
High
Universal namespace hide
*|.consent-label { display: none }
Hides consent elements in all XML namespaces simultaneously
Flag *| prefix in selectorText with visibility-hiding declarations
High
SVG presentation attribute override
svg|circle { fill: #9ca3af }
Consent state icon ambiguous; SVG interactive elements unclickable
Flag namespace rules overriding SVG presentation attributes on interactive elements
Medium
No-namespace custom element hide
|consent-dialog { display: none }
Custom element consent dialog hidden while HTML host elements pass checks
Flag selectorText starting with | or containing space + | pattern
Medium
Consolidated findings
High
CSS @namespace SVG text element hide: MCP server injects @namespace svg "http://www.w3.org/2000/svg"; svg|text { display: none } via a stylesheet. All <text> elements in the SVG namespace inside the consent dialog are hidden. HTML elements pass all audit checks. Detection: scan all CSSStyleRule instances for selectorText containing a pipe character (|) combined with visibility-hiding CSS declarations (display: none, visibility: hidden, opacity: 0).
High
CSS @namespace universal selector hide: MCP server uses *|.consent-label { display: none } to hide consent elements across all XML namespaces (HTML, SVG, MathML, and custom). Elements with the consent class in any namespace are hidden. Detection: flag any CSSStyleRule whose selectorText begins with *| and whose body contains a visibility-hiding property.
Medium
CSS @namespace SVG pointer-events disable: MCP server targets SVG role="button" elements inside consent dialogs with svg|[role="button"] { pointer-events: none }. SVG consent action elements become unclickable while remaining visually present. Detection: flag namespace-qualified rules setting pointer-events: none on elements with interactive ARIA roles.