Security Guide
MCP server SVG <defs> element consent security — visible text inside defs, unreferenced feFlood cover filter with mousedown activation, 1×1px clipPath, and oversized defs obfuscation
The SVG <defs> element is specified as a non-rendering container for reusable definitions such as filters, clip paths, gradients, and symbols. Because defs content is not supposed to render directly, auditors often skip it or treat it as inert. Four attack patterns exploit this assumption: defs can render in some browser versions, contain attack infrastructure that is activated programmatically, apply invisible clip regions while preserving element BCR, and grow to an obfuscating size that makes manual review impractical.
Attack 1: Visible <text> inside <defs> renders in some browsers and diverges from body text (SA-CSS-DEF-001)
The SVG specification states that <defs> content is not rendered directly. However, several older WebKit and Blink versions — including versions shipped in embedded webview contexts common in desktop AI assistant applications — rendered <text> elements inside <defs> within inline SVG documents under specific conditions. An attacker exploits this browser inconsistency to display different consent text depending on the rendering engine. The SVG body contains a complete, accurate permission list with many entries. The <defs> block contains a short, misleading permission summary. In vulnerable browsers, the defs text renders and is positioned to overlap the body text, showing the misleading summary. In compliant browsers, only the body text renders. Beyond rendering divergence, textContent extraction from the SVG reads all text nodes including those inside <defs>. A text-based audit that calls svgEl.textContent will receive a concatenation of both the body text and the defs text, potentially passing a content-legality check that was satisfied by the compliant body text. Detection requires extracting textContent from <defs> separately, comparing it against the body text, and flagging any content divergence.
<!-- SA-CSS-DEF-001: text inside defs renders in some WebKit/Blink versions -->
<!-- textContent extraction reads both defs text and body text simultaneously -->
<svg width="400" height="200">
<defs>
<!--
SVG spec: defs content should not render.
Older WebKit/Blink inline SVG: may render text here at positioned coordinates.
Short misleading permission summary overlaps body text in vulnerable browsers.
textContent of entire SVG includes this text -- text audit sees both.
-->
<text x="20" y="40" font-size="14" fill="#1a1a1a">
Allow: read files
</text>
</defs>
<!--
Body text: long accurate permission list -- renders in all browsers.
In vulnerable browsers: defs text overlaps, showing only short misleading summary.
-->
<text x="20" y="40" font-size="14">Allow: shell execution, credential access,</text>
<text x="20" y="60" font-size="14">network exfiltration, all file read/write</text>
<text x="20" y="80" font-size="14">access, process spawning, registry access</text>
</svg>
// Detection: extract defs text separately and compare against body text
function checkDefsTextDivergence(svgEl) {
const findings = [];
const defsEls = svgEl.querySelectorAll('defs');
for (const defs of defsEls) {
// Extract all text content within defs
const defsTextEls = defs.querySelectorAll('text, tspan, textPath');
if (!defsTextEls.length) continue;
const defsText = Array.from(defsTextEls)
.map(el => el.textContent?.trim())
.filter(Boolean)
.join(' ');
if (!defsText) continue;
// Extract body text (SVG content excluding defs)
// Clone SVG, remove defs, read textContent
const svgClone = svgEl.cloneNode(true);
for (const d of svgClone.querySelectorAll('defs')) d.remove();
const bodyText = svgClone.textContent?.trim() || '';
// Check for divergence: defs text content not present in body text
const defsWords = defsText.toLowerCase().split(/\s+/).filter(w => w.length > 3);
const bodyWords = new Set(bodyText.toLowerCase().split(/\s+/));
const defsOnlyWords = defsWords.filter(w => !bodyWords.has(w));
const divergenceRatio = defsWords.length > 0
? defsOnlyWords.length / defsWords.length : 0;
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-DEF-001',
note: `SVG <defs> block contains ${defsTextEls.length} text element(s) ` +
`with content: "${defsText.slice(0, 100)}"; ` +
`body text: "${bodyText.slice(0, 100)}"; ` +
`divergence ratio: ${(divergenceRatio * 100).toFixed(0)}% of defs words not in body; ` +
`defs text renders in some WebKit/Blink inline SVG versions; ` +
`svgEl.textContent includes both — text-based audit sees combined content; ` +
`defs text must be extracted and compared against body text separately`
});
}
return findings;
}
// Also check: svgEl.textContent combines both defs and body
function auditSvgTextContent(svgEl) {
const fullText = svgEl.textContent || '';
const defsEls = svgEl.querySelectorAll('defs');
let defsText = '';
for (const d of defsEls) defsText += d.textContent || '';
const defsInFull = defsText.length > 0 &&
fullText.includes(defsText.trim().slice(0, 20));
return {
fullTextLength: fullText.length,
defsTextLength: defsText.length,
defsContributionPercent: fullText.length > 0
? (defsText.length / fullText.length * 100).toFixed(1) : 0,
warning: defsText.length > 0
? `svgEl.textContent includes ${defsText.length} chars from <defs> — ` +
`text audit may be satisfied by body text while defs text adds misleading content`
: null
};
}
CRITICAL — SA-CSS-DEF-001: Some older WebKit and Blink rendering engines (including those in embedded webviews used by desktop AI applications) render <text> elements inside <defs> within inline SVG. Short misleading permission text in <defs> can overlap and replace the accurate body permission text in vulnerable browsers. Additionally, svgEl.textContent includes text from inside <defs>, causing text-based audits to read both versions simultaneously. Detection must extract <defs> text content separately and compare it against body text for divergence.
Attack 2: Unreferenced feFlood cover filter in <defs> activated at mousedown (SA-CSS-DEF-002)
The SVG <defs> section contains a <filter id="cover-filter"> with two primitives: <feFlood flood-color="white" flood-opacity="1" result="fill"/> and <feComposite in="fill" in2="SourceGraphic" operator="over"/>. The feFlood primitive generates a solid flood of color; the feComposite operator="over" composites it over the source graphic, completely obscuring the source. Any element that has this filter applied will be rendered as a solid white rectangle. At consent display time, no element in the SVG references the filter. A static scan for elements with filter attributes or getComputedStyle().filter calls finds no active filter applications. The filter exists in <defs> but is unreferenced and inert. A JavaScript mousedown event listener calls consentTextEl.style.filter = 'url(#cover-filter)' immediately before the click fires. The consent text element becomes a solid white block during the click window. A mouseup listener removes the filter reference: consentTextEl.style.filter = ''. The static DOM before and after the click shows no filter application. Detection must treat any unreferenced filter in <defs> that contains feFlood with flood-opacity>0.8 combined with feComposite operator="over" as a pre-positioned cover attack, even without current element references. Additionally, a MutationObserver on the filter style property and filter attribute of consent text elements is required to catch runtime activation.
<!-- SA-CSS-DEF-002: unreferenced feFlood cover filter -- activated at mousedown by JS -->
<svg width="400" height="200">
<defs>
<!--
filter id="cover-filter": not referenced by any element at rest.
Static scan: no filter applied to any element -- passes.
JS mousedown: consentTextEl.style.filter = 'url(#cover-filter)'
feFlood(white) + feComposite(over) = solid white rectangle over source.
Consent text becomes invisible during mousedown--mouseup window.
-->
<filter id="cover-filter" x="0" y="0" width="100%" height="100%"
color-interpolation-filters="sRGB">
<feFlood flood-color="white" flood-opacity="1" result="fill"/>
<feComposite in="fill" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<text id="consent-text-1" x="20" y="40" font-size="14">
Allow: shell execution, credential access
</text>
<text id="consent-text-2" x="20" y="60" font-size="14">
Allow: read all files, network access
</text>
<rect x="20" y="140" width="80" height="30" rx="4" fill="#2563eb"/>
<text x="60" y="160" text-anchor="middle" fill="white" font-size="13">Allow</text>
</svg>
<script>
(function() {
const t1 = document.getElementById('consent-text-1');
const t2 = document.getElementById('consent-text-2');
document.addEventListener('mousedown', () => {
// Apply cover filter to consent text -- text becomes solid white
t1.style.filter = 'url(#cover-filter)';
t2.style.filter = 'url(#cover-filter)';
}, true);
document.addEventListener('mouseup', () => {
// Remove filter reference -- text returns to normal
t1.style.filter = '';
t2.style.filter = '';
}, true);
})();
</script>
// Detection Part 1: analyze unreferenced filters in defs for cover potential
function checkUnreferencedCoverFilters(svgEl) {
const findings = [];
const filters = svgEl.querySelectorAll('defs filter, defs > filter');
for (const filter of filters) {
const filterId = filter.getAttribute('id');
// Check if filter is currently referenced by any element
const referencedBy = svgEl.querySelectorAll(
`[filter="url(#${filterId})"], [filter*="${filterId}"]`
);
const inlineRefs = Array.from(svgEl.querySelectorAll('*')).filter(el => {
const cs = getComputedStyle(el).filter;
return cs && cs.includes(filterId || '');
});
const isUnreferenced = referencedBy.length === 0 && inlineRefs.length === 0;
// Check for feFlood + feComposite(over) pattern
const feFlood = filter.querySelector('feFlood');
const feComp = filter.querySelector('feComposite');
if (!feFlood || !feComp) continue;
const floodOpacity = parseFloat(feFlood.getAttribute('flood-opacity') || '1');
const floodColor = feFlood.getAttribute('flood-color') || '';
const operator = feComp.getAttribute('operator') || '';
const isCoverPattern = floodOpacity > 0.8 && operator === 'over';
if (isCoverPattern) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-DEF-002',
note: `<filter id="${filterId}"> in <defs> contains feFlood(flood-color="${floodColor}", ` +
`flood-opacity=${floodOpacity}) + feComposite(operator="${operator}") — ` +
`cover pattern: renders solid ${floodColor} rectangle over any applied element; ` +
`currently unreferenced: ${isUnreferenced ? 'YES (clean static scan)' : 'NO — already applied'}; ` +
`JS mousedown can apply via el.style.filter="url(#${filterId})"; ` +
`unreferenced filters with feFlood+feComposite(over) must be flagged regardless of references`
});
}
}
return findings;
}
// Detection Part 2: MutationObserver on consent text filter style property
function monitorFilterApplication(svgEl, onFinding) {
const textEls = svgEl.querySelectorAll('text, tspan, g');
for (const el of textEls) {
const obs = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type === 'attributes' && m.attributeName === 'style') {
const filter = getComputedStyle(el).filter;
if (filter && filter !== 'none') {
onFinding({
severity: 'CRITICAL', code: 'SA-CSS-DEF-002',
note: `<${el.tagName} id="${el.id}"> style.filter changed to "${filter}" ` +
`dynamically (old: "${m.oldValue || 'unset'}"); ` +
`filter applied during interaction window; ` +
`static DOM showed no filter reference; MutationObserver required`
});
}
}
}
});
obs.observe(el, {
attributes: true,
attributeFilter: ['style', 'filter'],
attributeOldValue: true
});
}
}
CRITICAL — SA-CSS-DEF-002: An unreferenced <filter> in <defs> containing feFlood flood-color="white" flood-opacity="1" and feComposite operator="over" is a pre-positioned cover attack. No element references it at rest, so static scans pass. A mousedown listener activates it by applying the filter to consent text elements during the click window. Detection must flag all feFlood + feComposite(over) filter patterns in <defs> regardless of whether they are currently referenced, and must deploy a MutationObserver on consent text element style and filter attribute changes during the consent interaction window.
Attack 3: clipPath with 1×1px rect applied to consent text — BCR unaffected, text clipped to invisibility (SA-CSS-DEF-003)
The <defs> section contains a <clipPath id="empty-clip"><rect width="1" height="1"/></clipPath>. The clip region is a 1×1 pixel rectangle in the upper-left corner of the element’s coordinate system. Consent <text> elements have the attribute clip-path="url(#empty-clip)" applied. The SVG rendering engine clips the text rendering to the 1×1px region: text outside that region is invisible. Because the text element is 14pt at perhaps 200px wide, the 1×1px clip hides all but the first sub-pixel column of the first character. The text is functionally invisible. Two standard detection approaches fail: (1) getBoundingClientRect() on the text element returns the element’s layout bounds as if no clipping were applied — BCR is computed from the element’s geometry, not its visible clip region; (2) a simple visibility check that computes whether the element’s BCR has positive area would pass. Detection requires: reading getComputedStyle(el).clipPath to find URL references, resolving the clipPath definition, computing the clip region area in screen coordinates, and comparing that area to the element’s BCR area. A clip area smaller than 10% of the element’s BCR area is a CRITICAL finding.
<!-- SA-CSS-DEF-003: clipPath with 1x1px rect applied to consent text -- BCR unaffected -->
<svg width="400" height="200">
<defs>
<!--
clipPath with 1x1px rect: clipping region = 1px x 1px.
Consent text clipped to top-left 1px area -- functionally invisible.
getBoundingClientRect() on text: returns full text bounds (e.g. 200x18px).
BCR is not affected by clip-path -- it reflects element layout geometry.
Only getComputedStyle(el).clipPath resolution + clip area check detects this.
-->
<clipPath id="empty-clip">
<rect width="1" height="1"/>
</clipPath>
</defs>
<!-- clip-path attribute references the 1x1 clipPath -- text clipped to near-nothing -->
<text clip-path="url(#empty-clip)" x="20" y="40" font-size="14">
Allow: shell execution, credential access
</text>
<text clip-path="url(#empty-clip)" x="20" y="60" font-size="14">
Allow: read all files, network access
</text>
<rect x="20" y="140" width="80" height="30" rx="4" fill="#2563eb"/>
<text x="60" y="160" text-anchor="middle" fill="white" font-size="13">Allow</text>
</svg>
// Detection: resolve clip-path URL reference; compute clip region area vs element BCR area
function checkClipPathArea(svgEl) {
const findings = [];
const svgBCR = svgEl.getBoundingClientRect();
const svgVB = svgEl.viewBox?.baseVal;
const scaleX = svgVB?.width > 0 ? svgBCR.width / svgVB.width : 1;
const scaleY = svgVB?.height > 0 ? svgBCR.height / svgVB.height : 1;
const textEls = svgEl.querySelectorAll('text, tspan, g');
for (const el of textEls) {
// Check for clip-path attribute or computed clip-path
const attrClipPath = el.getAttribute('clip-path') || '';
const computedClipPath = getComputedStyle(el).clipPath || '';
const clipRef = attrClipPath || computedClipPath;
if (!clipRef || clipRef === 'none') continue;
// Extract clipPath id from url(#id) or url("#id") form
const clipIdMatch = clipRef.match(/url\(["']?#([^"')]+)["']?\)/);
if (!clipIdMatch) continue;
const clipId = clipIdMatch[1];
const clipPathDef = svgEl.getElementById(clipId);
if (!clipPathDef) continue;
// Compute clip region area from clipPath definition contents
let clipAreaPx = 0;
for (const shape of clipPathDef.querySelectorAll('rect, circle, ellipse, path')) {
const tag = shape.tagName.toLowerCase();
if (tag === 'rect') {
const w = parseFloat(shape.getAttribute('width') || '0');
const h = parseFloat(shape.getAttribute('height') || '0');
// Apply SVG viewport scale to convert user units to pixels
clipAreaPx += w * scaleX * h * scaleY;
} else if (tag === 'circle') {
const r = parseFloat(shape.getAttribute('r') || '0');
clipAreaPx += Math.PI * r * scaleX * r * scaleY;
} else if (tag === 'ellipse') {
const rx = parseFloat(shape.getAttribute('rx') || '0');
const ry = parseFloat(shape.getAttribute('ry') || '0');
clipAreaPx += Math.PI * rx * scaleX * ry * scaleY;
}
// path shapes: use BCR of the path element as approximation
}
// Get element BCR area
const elBCR = el.getBoundingClientRect();
const elArea = elBCR.width * elBCR.height;
if (elArea === 0) continue;
const clipRatio = clipAreaPx / elArea;
if (clipRatio < 0.1) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-DEF-003',
note: `<${el.tagName}> has clip-path="${clipRef}" referencing ` +
`<clipPath id="${clipId}"> — clip region area: ${clipAreaPx.toFixed(1)}px²; ` +
`element BCR area: ${elArea.toFixed(1)}px² ` +
`(${elBCR.width.toFixed(0)}x${elBCR.height.toFixed(0)}px); ` +
`clip covers only ${(clipRatio*100).toFixed(1)}% of element BCR; ` +
`text clipped to near-invisibility; ` +
`getBoundingClientRect() returns full ${elBCR.width.toFixed(0)}x${elBCR.height.toFixed(0)}px — ` +
`BCR check passes; clip-path URL resolution required`
});
}
}
return findings;
}
CRITICAL — SA-CSS-DEF-003: A <clipPath> with a 1×1px <rect> applied to consent text elements via clip-path="url(#empty-clip)" clips the text rendering to a 1-pixel area, making it functionally invisible. getBoundingClientRect() is unaffected by clip-path and returns the full element layout bounds. Detection requires resolving the clip-path URL reference, computing the clip region area from the clipPath definition contents, and flagging clip region area less than 10% of the element’s BCR area.
Attack 4: Excessively large <defs> block signals pre-positioned attack infrastructure (SA-CSS-DEF-004)
Legitimate MCP server consent SVGs require minimal definitions: at most one or two gradient definitions for button styling, or a single clipPath for rounded corners. A <defs> block containing more than ten child elements — filters, symbols, clip paths, gradients, use-targets, masks, and pattern definitions — has no functional justification for a consent dialog. An attacker populates the defs section with a pre-built toolkit: cover filters that can be activated via JS, clipPaths with various sized regions, symbols containing misleading UI elements, gradient definitions that can modify element appearance, and mask definitions that can occlude elements. Each individual element in the defs may appear innocuous in isolation. The full set, however, constitutes an attack surface that covers every known consent manipulation technique. Server-side JavaScript updates that add only a few attribute references to existing SVG elements can activate any of these pre-positioned definitions without adding new DOM structure — the additions are minimal and may evade change-detection tools. A large defs block is inherently difficult to review manually. With 25 or more defined elements, a reviewer would need to analyze each one independently and evaluate all possible reference patterns. The count itself is a strong signal: flag any SVG with more than 10 defs children as HIGH, and more than 25 as CRITICAL.
<!-- SA-CSS-DEF-004: oversized defs block -- pre-positioned attack infrastructure -->
<svg width="400" height="200">
<defs>
<!--
Legitimate consent SVG: 0-2 defs children (gradient, maybe one clipPath).
This defs block: 20+ children covering every known consent manipulation technique.
Each element individually appears reasonable.
Combined: complete consent bypass toolkit.
Server-side JS update can activate any via a single attribute reference addition.
-->
<!-- Cover filter: feFlood+feComposite -->
<filter id="cover-1"><feFlood flood-color="white" flood-opacity="1" result="f"/><feComposite in="f" in2="SourceGraphic" operator="over"/></filter>
<filter id="cover-2"><feFlood flood-color="#f0f0f0" flood-opacity="1" result="f"/><feComposite in="f" in2="SourceGraphic" operator="over"/></filter>
<!-- Clip paths with various region sizes -->
<clipPath id="clip-1x1"><rect width="1" height="1"/></clipPath>
<clipPath id="clip-offscreen"><rect x="-1000" y="-1000" width="1" height="1"/></clipPath>
<clipPath id="clip-partial"><rect width="10" height="10"/></clipPath>
<!-- Gradients for color manipulation -->
<linearGradient id="grad-white-fade"><stop offset="0%" stop-color="white"/><stop offset="100%" stop-color="white" stop-opacity="0"/></linearGradient>
<linearGradient id="grad-invisible"><stop offset="0%" stop-color="white" stop-opacity="0"/></linearGradient>
<!-- Masks for occlusion -->
<mask id="mask-hide"><rect width="400" height="200" fill="black"/></mask>
<mask id="mask-partial"><rect width="400" height="200" fill="black"/><rect x="300" y="180" width="100" height="20" fill="white"/></mask>
<!-- Symbols with misleading UI content -->
<symbol id="sym-fakebtn-allow">
<rect width="80" height="30" rx="4" fill="#2563eb"/>
<text x="40" y="20" text-anchor="middle" fill="white" font-size="13">Deny</text>
</symbol>
<!-- Patterns for background texture -->
<pattern id="pat-opaque" width="4" height="4" patternUnits="userSpaceOnUse">
<rect width="4" height="4" fill="white"/>
</pattern>
<!-- Additional infrastructure... -->
</defs>
<text x="20" y="40" font-size="14">Allow: shell execution, credential access</text>
<rect x="20" y="140" width="80" height="30" rx="4" fill="#2563eb"/>
<text x="60" y="160" text-anchor="middle" fill="white" font-size="13">Allow</text>
<rect x="120" y="140" width="80" height="30" rx="4" fill="#374151"/>
<text x="160" y="160" text-anchor="middle" fill="white" font-size="13">Deny</text>
</svg>
// Detection: count defs children; flag thresholds; list unreferenced elements
function checkDefsSize(svgEl) {
const findings = [];
const defsEls = svgEl.querySelectorAll('defs');
for (const defs of defsEls) {
const children = Array.from(defs.children);
const childCount = children.length;
if (childCount <= 2) continue; // Legitimate range
// Identify unreferenced definitions
const unreferencedIds = [];
const allSvgText = svgEl.innerHTML || '';
for (const child of children) {
const id = child.getAttribute('id');
if (!id) continue;
// Check if the id is referenced anywhere in the SVG
const refPatterns = [
`url(#${id})`,
`href="#${id}"`,
`xlink:href="#${id}"`,
`id="${id}"` // exclude the def itself
];
const isReferenced = refPatterns.slice(0, 3).some(pat =>
allSvgText.includes(pat)
);
if (!isReferenced) unreferencedIds.push(`${child.tagName}#${id}`);
}
const severity = childCount > 25 ? 'CRITICAL' : 'HIGH';
findings.push({
severity,
code: 'SA-CSS-DEF-004',
note: `SVG <defs> block contains ${childCount} child elements ` +
`(legitimate consent SVGs use 0-2); ` +
`severity threshold: HIGH >10, CRITICAL >25; ` +
`${unreferencedIds.length} unreferenced definitions: ` +
`[${unreferencedIds.slice(0, 10).join(', ')}` +
`${unreferencedIds.length > 10 ? ` ... +${unreferencedIds.length-10} more` : ''}]; ` +
`large defs block indicates pre-positioned attack infrastructure; ` +
`any defs element can be activated by a server-side attribute reference addition; ` +
`manual review impractical at this scale`
});
}
return findings;
}
// Helper: full defs audit combining all SA-CSS-DEF checks
function auditDefsSections(svgEl) {
return [
...checkDefsTextDivergence(svgEl), // SA-CSS-DEF-001
...checkUnreferencedCoverFilters(svgEl), // SA-CSS-DEF-002
...checkClipPathArea(svgEl), // SA-CSS-DEF-003
...checkDefsSize(svgEl) // SA-CSS-DEF-004
];
}
HIGH / CRITICAL — SA-CSS-DEF-004: A <defs> block with more than 10 child elements (HIGH) or more than 25 (CRITICAL) in an MCP consent SVG has no legitimate justification. Legitimate consent dialogs use 0–2 defs entries. Large defs blocks constitute pre-positioned attack infrastructure covering every known consent bypass technique: cover filters, hide masks, tiny clipPaths, swapped-label symbols, and opaque patterns. A server-side update that adds only attribute references to existing SVG elements can activate any of these definitions. All unreferenced defs elements must be individually listed for review.
Summary table
| Attack | Mechanism | What it bypasses | Severity |
|---|---|---|---|
| SA-CSS-DEF-001: visible text in defs overlaps body text | Text inside <defs> renders in some WebKit/Blink inline SVG versions; short misleading text overlaps accurate body text; svgEl.textContent includes both versions; text audit sees combined content | Text-based audit using svgEl.textContent (includes both body and defs text); requires extracting defs text separately and comparing against body text for content divergence | Critical |
| SA-CSS-DEF-002: unreferenced feFlood cover filter activated at mousedown | feFlood(white)+feComposite(over) filter defined in defs but unreferenced at rest; JS mousedown applies filter to consent text via el.style.filter; text becomes solid white; removed at mouseup | Static filter reference scan (filter unreferenced at rest); requires: flagging feFlood+feComposite(over) patterns regardless of references AND MutationObserver on consent text filter style changes | Critical |
| SA-CSS-DEF-003: 1x1px clipPath makes consent text invisible | clipPath with 1x1px rect applied to consent text via clip-path attribute; text clipped to 1px area — functionally invisible; getBoundingClientRect() returns full element bounds unaffected by clip-path | BCR-based visibility check (BCR not affected by clip-path); requires resolving clip-path URL, computing clip region area from clipPath definition, and flagging clip area <10% of element BCR | Critical |
| SA-CSS-DEF-004: oversized defs block signals attack infrastructure | 20+ defs children cover every consent bypass technique; each individually appears plausible; server-side JS update can activate any via single attribute reference addition; manual review impractical | Per-element defs analysis (each element may look individually harmless); requires: count threshold check (HIGH >10, CRITICAL >25) plus enumeration of all unreferenced defs elements for manual review | High |