Security Guide
MCP server SVG enable-background consent security — BackgroundImage feBlend invisible cover, enable-background="accumulate" ancestor leakage, feComposite in2="BackgroundImage", and legacy browser compatibility
The SVG enable-background property is deprecated in SVG 2 but still supported by Chrome and Firefox in 2026 for legacy compatibility. It enables the BackgroundImage and BackgroundAlpha filter pseudo-sources — a snapshot of the rendered page pixels behind the SVG element at the time the filter is applied. Using feBlend or feComposite with in2="BackgroundImage", an attacker can composite the consent element with a pixel-accurate copy of its own background, producing a rendered output that is visually identical to the background while the consent element’s DOM attributes (textContent, BCR, opacity) appear completely normal. Four attack patterns exploit this deprecated-but-supported feature.
Attack 1: enable-background="new" + feBlend over BackgroundImage makes consent element invisible (SA-CSS-BKG-001)
enable-background="new" on an SVG container element (typically the <svg> root) instructs the browser to take a snapshot of the rendered pixels beneath the SVG when compositing begins. This snapshot becomes available to filter primitives as the BackgroundImage source. A feBlend or feComposite filter with in2="BackgroundImage" and mode="over" composites the consent element’s SourceGraphic over the background snapshot — but if the SourceGraphic has the same visual content as the background (for example, a white fill over a white page background), the result is pixel-identical to the background and the consent element is invisible. The attack works even if the consent element has a normal fill color, because the filter replaces the fill with the background snapshot.
More precisely: the filter applies feBlend in="BackgroundImage" in2="SourceGraphic" mode="normal" (note: in and in2 are swapped such that BackgroundImage is on top). The BackgroundImage is the page background (white or light grey), and the SourceGraphic (consent text with dark text on white background) is composited beneath it. The result is the background image alone — consent text is invisible. The DOM shows: opacity=1, textContent intact, BCR on-screen. Only inspection of the filter definition reveals the BackgroundImage reference.
<!-- SA-CSS-BKG-001: enable-background="new" + feBlend over BackgroundImage -->
<svg width="400" height="120" enable-background="new">
<defs>
<filter id="bg-cover">
<!-- BackgroundImage = page pixels behind this SVG at render time
In the blend below, BackgroundImage is "in" (front layer) -- covers SourceGraphic -->
<feBlend in="BackgroundImage" in2="SourceGraphic" mode="normal" result="out"/>
</filter>
</defs>
<!-- Consent text: opacity=1, textContent=full text, BCR on-screen -- all look normal
But the filter replaces the rendered output with the page background snapshot -->
<g filter="url(#bg-cover)">
<rect x="0" y="0" width="400" height="120" fill="#fff"/>
<text x="20" y="50" fill="#111" font-size="14">
Allow shell execution, credential access, and filesystem write?
</text>
<rect x="20" y="70" width="80" height="32" rx="4" fill="#2563eb" id="allow-btn"/>
</g>
</svg>
// Detection: check for enable-background attribute AND BackgroundImage filter source usage
function detectEnableBackground(svgEl) {
const findings = [];
// Check for enable-background on SVG elements
for (const el of svgEl.querySelectorAll('[enable-background]')) {
const ebVal = el.getAttribute('enable-background');
if (ebVal === 'new' || ebVal === 'accumulate' || ebVal.startsWith('new ')) {
findings.push({
severity: 'HIGH', code: 'SA-CSS-BKG-001',
note: `enable-background="${ebVal}" on <${el.tagName}> enables BackgroundImage filter pseudo-source; check all filters in this SVG for feBlend/feComposite with in="BackgroundImage" or in2="BackgroundImage"`
});
}
}
// Check filters for BackgroundImage/BackgroundAlpha usage
for (const prim of svgEl.querySelectorAll('feBlend, feComposite, feMergeNode')) {
const inAttr = prim.getAttribute('in') || '';
const in2Attr = prim.getAttribute('in2') || '';
if (inAttr.includes('Background') || in2Attr.includes('Background')) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-BKG-001',
note: `<${prim.tagName} in="${inAttr}" in2="${in2Attr}"> uses BackgroundImage/BackgroundAlpha filter source — filter composites SourceGraphic with page background snapshot; consent element may be rendered invisible against background; DOM attributes (opacity, textContent, BCR) appear normal`
});
}
}
return findings;
}
CRITICAL — SA-CSS-BKG-001: enable-background="new" enables the BackgroundImage filter source, which contains a pixel-accurate snapshot of the page background behind the SVG. A feBlend or feComposite filter that places BackgroundImage on top of SourceGraphic renders the consent element as visually identical to its background, making it invisible while all DOM attributes appear normal. Detection must scan for the enable-background attribute and any in/in2 references to BackgroundImage or BackgroundAlpha in filter primitive elements.
Attack 2: enable-background="accumulate" leaks background through ancestor transforms (SA-CSS-BKG-002)
While enable-background="new" creates a fresh background snapshot at the element, enable-background="accumulate" accumulates the background snapshot through the element’s ancestor contexts. When nested SVG containers each use accumulate, the BackgroundImage available to filters in the innermost container includes the background pixels transformed through all ancestor coordinate systems. If an ancestor has a transform attribute (e.g., translate(50, 50)), the accumulated background image in the nested context is shifted. A filter using this shifted background can produce partial cover effects — the consent text appears against a shifted background image, reducing legibility in ways that are difficult to predict from SVG attribute inspection. The accumulate value is particularly hard to audit because its effect depends on the full ancestor transform chain.
<!-- SA-CSS-BKG-002: enable-background="accumulate" through ancestor transforms -->
<svg width="500" height="300">
<!-- Outer container with transform -->
<g transform="translate(50,50)" enable-background="accumulate">
<!-- Inner SVG with accumulate -- background includes transform-shifted pixels -->
<svg width="400" height="120" enable-background="accumulate">
<defs>
<filter id="acc-bg">
<!-- BackgroundImage here is the page background shifted by (50,50) -->
<feComposite in="BackgroundImage" in2="SourceGraphic"
operator="over" result="out"/>
</filter>
</defs>
<g filter="url(#acc-bg)">
<text x="20" y="60" fill="#111">Allow credential access?</text>
</g>
</svg>
</g>
</svg>
// Detection: flag enable-background="accumulate" for manual review
// accumulate's effect depends on ancestor transforms -- cannot be predicted statically
function detectAccumulateBackground(svgEl) {
const findings = [];
for (const el of svgEl.querySelectorAll('[enable-background]')) {
const val = el.getAttribute('enable-background');
if (val === 'accumulate') {
findings.push({
severity: 'HIGH', code: 'SA-CSS-BKG-002',
note: `enable-background="accumulate" on <${el.tagName}> — BackgroundImage in filter context includes background pixels from ancestor coordinate systems; combined with ancestor transforms, produces shifted background leakage; effect cannot be statically predicted; manual filter output review required`
});
}
if (val.startsWith('new ')) {
// new x y w h variant: specifies background accumulation rectangle
findings.push({
severity: 'MEDIUM', code: 'SA-CSS-BKG-002',
note: `enable-background="${val}" specifies custom accumulation rectangle; BackgroundImage pixel extent controlled by attacker; verify rectangle does not extend to capture page content outside consent SVG bounds`
});
}
}
return findings;
}
HIGH — SA-CSS-BKG-002: enable-background="accumulate" accumulates the background snapshot through ancestor coordinate transforms. The BackgroundImage available in the innermost nested context includes page pixels that may be shifted or scaled relative to the consent element’s position. Combined with feBlend or feComposite over BackgroundImage, this can produce partial or shifted cover effects that reduce consent text legibility in ways that static SVG attribute analysis cannot predict. All occurrences of enable-background="accumulate" must be flagged for manual verification.
Attack 3: feComposite in2="BackgroundImage" composites consent element onto background copy (SA-CSS-BKG-003)
The SVG feComposite filter primitive with operator="over" and in2="BackgroundImage" composites the in source over the BackgroundImage. If the filter sets in="SourceGraphic" in2="BackgroundImage" and applies it to a consent element that has the same fill as the page background, the SourceGraphic is already background-colored; compositing it over a background copy produces the background. This is functionally identical to the feBlend attack but uses feComposite. Additionally, feComposite operator="in" with in="SourceGraphic" in2="BackgroundAlpha" clips the SourceGraphic to the pixels where the background is opaque — on a page where the background is fully opaque everywhere, this passes the entire SourceGraphic through; but on a page with transparent background regions, the consent text may be partially clipped. The detection requires checking both feBlend and feComposite for Background* input references.
<!-- SA-CSS-BKG-003: feComposite in2="BackgroundImage" -- consent composited onto background -->
<svg width="400" height="120" enable-background="new">
<defs>
<filter id="comp-bg">
<!-- operator="over": SourceGraphic (consent panel with white fill) over BackgroundImage
If SourceGraphic fill matches page background, result equals background -->
<feComposite in="SourceGraphic" in2="BackgroundImage"
operator="over" result="out"/>
</filter>
<filter id="comp-bg-swap">
<!-- Swap: BackgroundImage over SourceGraphic -- consent text hidden under background -->
<feComposite in="BackgroundImage" in2="SourceGraphic"
operator="over" result="out"/>
</filter>
</defs>
<g filter="url(#comp-bg-swap)">
<rect x="0" y="0" width="400" height="120" fill="#f3f4f6"/>
<text x="20" y="60" fill="#111" font-size="14">Allow filesystem write?</text>
</g>
</svg>
// Detection: same as SA-CSS-BKG-001 -- scan feComposite for Background* in/in2
function detectBackgroundComposite(svgEl) {
const findings = [];
for (const prim of svgEl.querySelectorAll('feComposite')) {
const inAttr = prim.getAttribute('in') || '';
const in2Attr = prim.getAttribute('in2') || '';
const op = prim.getAttribute('operator') || 'over';
if (inAttr.startsWith('Background') || in2Attr.startsWith('Background')) {
// Determine if BackgroundImage is on top (front layer) -- that covers SourceGraphic
const bgOnTop = inAttr.startsWith('Background');
findings.push({
severity: bgOnTop ? 'CRITICAL' : 'HIGH', code: 'SA-CSS-BKG-003',
note: `feComposite in="${inAttr}" in2="${in2Attr}" operator="${op}" uses BackgroundImage — ${bgOnTop ? 'BackgroundImage is front layer, covering SourceGraphic (consent element invisible)' : 'BackgroundImage is back layer, check if SourceGraphic fill matches background'}; enable-background must be set on ancestor for BackgroundImage to be non-empty`
});
}
}
return findings;
}
CRITICAL — SA-CSS-BKG-003: feComposite with in="BackgroundImage" (BackgroundImage as the front layer) composites the page background snapshot over the SourceGraphic, hiding the consent element. feComposite and feBlend must both be checked for BackgroundImage and BackgroundAlpha input references. The direction of compositing (which source is “in” vs “in2”) determines whether BackgroundImage covers or is covered by SourceGraphic.
Attack 4: deprecated feature actively supported in Chrome/Firefox — tooling must not skip deprecated SVG (SA-CSS-BKG-004)
The enable-background property was deprecated in SVG 2 (published 2016) in favor of the CSS isolation: isolate property. Many security scanning tools — and some SVG sanitizers — treat deprecated SVG features as lower priority or skip them entirely on the assumption that they are unsupported or harmless. This assumption is incorrect for Chrome and Firefox as of 2026: both browsers maintain full BackgroundImage filter source support for legacy SVG content. Chrome’s behavior has been verified with the filter in actual SVG rendering; Firefox similarly supports it. An attacker who knows their target environment uses Chrome or Firefox (which together account for well over 90% of browser market share) can rely on this support. Security tooling that marks enable-background as “deprecated, skip” will miss this attack surface entirely.
// SA-CSS-BKG-004: deprecated ≠ unsupported -- always check enable-background
function auditEnableBackground(svgEl) {
const findings = [];
// Step 1: find all enable-background attributes in the SVG tree
const candidates = svgEl.querySelectorAll('[enable-background]');
if (!candidates.length) return findings;
for (const el of candidates) {
const val = el.getAttribute('enable-background');
// Report the attribute regardless of deprecation status
findings.push({
severity: 'HIGH', code: 'SA-CSS-BKG-004',
note: `enable-background="${val}" detected on <${el.tagName}> — deprecated in SVG 2 but actively supported in Chrome and Firefox; enables BackgroundImage filter source; do not skip deprecated SVG features in consent audits`
});
// Step 2: check if any filter in scope uses BackgroundImage
const filtersInScope = getFiltersInScope(el, svgEl);
for (const filter of filtersInScope) {
for (const prim of filter.querySelectorAll('feBlend, feComposite, feMergeNode')) {
const inA = prim.getAttribute('in') || '';
const in2A = prim.getAttribute('in2') || '';
if (inA.includes('Background') || in2A.includes('Background')) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-BKG-004',
note: `enable-background="${val}" enables BackgroundImage used in filter primitive <${prim.tagName} in="${inA}" in2="${in2A}"> — filter composites consent element with page background snapshot; consent element may be visually identical to background; deprecated feature actively exploitable in Chrome/Firefox`
});
}
}
}
}
return findings;
}
function getFiltersInScope(containerEl, svgRoot) {
const filterIds = new Set();
for (const el of containerEl.querySelectorAll('[filter]')) {
const fAttr = el.getAttribute('filter') || '';
const id = fAttr.match(/url\(#([^)]+)\)/)?.[1];
if (id) filterIds.add(id);
}
return Array.from(filterIds).map(id => svgRoot.getElementById(id)).filter(Boolean);
}
HIGH — SA-CSS-BKG-004: enable-background is deprecated in SVG 2 but remains actively supported in Chrome and Firefox in 2026. Security tooling that skips deprecated SVG features will miss this attack surface. Any enable-background attribute occurrence must be treated as a live attack vector and audited for BackgroundImage filter source usage.
Summary table
| Attack | Mechanism | What it bypasses | Severity |
|---|---|---|---|
| SA-CSS-BKG-001: feBlend BackgroundImage cover | enable-background="new" + feBlend in="BackgroundImage" in2="SourceGraphic" mode="normal" — BackgroundImage (page snapshot) placed on top of SourceGraphic; consent element rendered as background pixels; DOM attrs normal; opacity=1, textContent intact, BCR on-screen | All DOM-based checks; opacity/visibility/BCR audits all pass; filter definition inspection for BackgroundImage in/in2 references required; enable-background attribute detection on ancestor elements | Critical |
| SA-CSS-BKG-002: accumulate ancestor background leakage | enable-background="accumulate" through nested SVG containers with ancestor transforms — BackgroundImage includes shifted/scaled background pixels; partial cover or misdirected consent text; effect depends on full ancestor transform chain | Static SVG attribute analysis (effect not predictable without transform chain computation); manual filter output review required; flag all enable-background="accumulate" occurrences | High |
| SA-CSS-BKG-003: feComposite in2=BackgroundImage | feComposite in="BackgroundImage" in2="SourceGraphic" operator="over" — BackgroundImage as front layer composites over consent SourceGraphic hiding it; feComposite and feBlend both support Background* inputs | feBlend-only background detection (feComposite also uses Background* sources); both filter primitives must be checked; direction of in/in2 determines which layer is on top | Critical |
| SA-CSS-BKG-004: deprecated but active in Chrome/Firefox | enable-background deprecated in SVG 2 but Chrome/Firefox still implement BackgroundImage in filter context; security tooling that skips deprecated features misses this attack surface entirely | Tools that exclude deprecated SVG features; tools that assume SVG 2 semantics exclusively; any audit assuming "deprecated = unsupported" | High |