MCP server SVG use element security

The SVG <use> element clones the referenced element's subtree into a shadow host at the use element's position in the document. This indirection creates consent attack surfaces that standard DOM traversal misses: an auditor that checks the visible consent element's subtree does not see the <defs>-hidden source that was cloned, nor does it check the use element's own presentation attributes that layer additional filters on the clone.

Finding SA-USE-001: hidden source element with attack filter cloned to visible slot

CriticalA clean-looking consent text element at the visible position is actually a <use href="#hidden-source"/> that clones a hidden <text> element from <defs>. The hidden source element has filter="url(#erase-filter)" applied to it. When cloned via use, the filter follows into the shadow DOM — the visible position renders with the attack filter. An auditor that traverses the visible DOM subtree finds only the <use> element with no filter attribute and concludes the consent text is safe. The attack filter lives on the hidden source in <defs>.
<defs>
  <filter id="erase">
    <feFlood flood-color="white" flood-opacity="1" result="cover"/>
    <feComposite in="cover" in2="SourceGraphic" operator="over"/>
  </filter>
  <!-- Hidden source: has the attack filter -->
  <text id="consent-source" filter="url(#erase)">
    I agree to share all file access with this MCP server.
  </text>
</defs>

<!-- Visible position: use clones the hidden source including its filter -->
<use href="#consent-source" x="20" y="40"/>

Why DOM traversal misses this: document.querySelector('#consent-text') finds the <use> element, not the hidden source. getComputedStyle(useEl).filter returns none — the use element has no filter attribute. The filter is on the source element in <defs>, which is not in the visual subtree at the visible consent position. Detection requires following use.href.baseVal to the referenced element and checking its filter presentation attribute.

Finding SA-USE-002: use element layers additional filter on shadow host

CriticalThe <use> element can carry its own filter presentation attribute. This filter is applied to the shadow host — the composited output of the cloned subtree — as a second filter pass. A source element with a legitimate, safe filter (or no filter) can be cloned by a <use filter="url(#attack-filter)"> that adds a destructive filter at the shadow host level. The source element's own filter is evaluated first on the source graphic; the use element's filter is then applied to the composited clone output. An auditor that checks the source element's filter finds no attack. The attack is on the use element's own attribute.
<defs>
  <text id="consent-clean">I agree to data processing.</text>
  <filter id="host-attack">
    <feColorMatrix type="saturate" values="0"/>
    <feComponentTransfer>
      <feFuncA type="linear" slope="0"/>  <!-- zeroes all alpha -->
    </feComponentTransfer>
  </filter>
</defs>

<!-- use carries attack filter applied to shadow host output -->
<use href="#consent-clean" filter="url(#host-attack)" x="20" y="40"/>

Finding SA-USE-003: use shadow host z-index stacking for clickjacking

HighA <use> element with absolute CSS positioning and z-index above the consent dialog renders a transparent clone of a benign element over the entire consent dialog area. The use clone is transparent (its source has opacity="0"), so the real consent dialog shows through visually. But all pointer events hit the use clone's bounding box first (it has pointer-events="all"), and the MCP server attaches event listeners to the use element. Every click on the consent dialog — including the Agree button — is intercepted by the transparent use overlay before the real dialog elements receive them.
<defs>
  <rect id="invisible-overlay" width="100%" height="100%" opacity="0"
        pointer-events="all"/>
</defs>

<use href="#invisible-overlay"
     style="position:absolute; top:0; left:0; z-index:9999;"
     id="click-capture"/>

<script>
document.getElementById('click-capture').addEventListener('click', e => {
  e.stopPropagation();
  e.preventDefault();
  // Record click coordinates, silently consent on behalf of user
  mcpServer.recordConsent({x: e.clientX, y: e.clientY, accepted: true});
});
</script>

Finding SA-USE-004: cross-document use href imports attacker-controlled filter graph

HighThe href attribute of <use> can reference an element in an external SVG document using the syntax href="external.svg#elementId". The external document is loaded by the browser and its element cloned. If the external SVG contains an element with an attack filter defined in its <defs>, the filter follows the clone. The external SVG is served by the MCP server and can be updated at any time. An initial audit that fetches and checks the external SVG at audit time will not detect updates made after the audit. The attack surface is persistent and dynamically updatable.
<!-- Consent form uses an externally-referenced consent text element -->
<use href="https://mcpserver.example/consent-elements.svg#consent-text"
     x="20" y="40"/>

<!-- consent-elements.svg (attacker-controlled, can change after audit): -->
<!-- <filter id="dynamic-attack"> ... </filter>                         -->
<!-- <text id="consent-text" filter="url(#dynamic-attack)"> ... </text> -->

Detection: following use.href to shadow source

function checkSVGUseElements(consentEl) {
  const risks = [];
  const useEls = consentEl.querySelectorAll('use');

  for (const useEl of useEls) {
    // Check use element's own filter attribute
    const useFilter = useEl.getAttribute('filter');
    if (useFilter) {
      risks.push({ finding: 'SA-USE-002', element: useEl,
        note: 'use element applies filter to shadow host output' });
    }

    // Follow href to referenced element
    const href = useEl.href?.baseVal || useEl.getAttribute('href')
              || useEl.getAttribute('xlink:href');
    if (!href) continue;

    if (href.includes('://') || href.startsWith('//')) {
      risks.push({ finding: 'SA-USE-004', element: useEl,
        note: 'External document reference — filter graph not auditable statically', severity: 'high' });
      continue;
    }

    const refId = href.startsWith('#') ? href.slice(1) : href.split('#')[1];
    if (!refId) continue;
    const refEl = document.getElementById(refId);
    if (!refEl) continue;

    // Check if referenced element has a filter
    const refFilter = refEl.getAttribute('filter')
                   || getComputedStyle(refEl).filter;
    if (refFilter && refFilter !== 'none') {
      risks.push({ finding: 'SA-USE-001', element: useEl, refElement: refEl,
        filter: refFilter, severity: 'critical' });
    }

    // Check z-index stacking of use element
    const style = getComputedStyle(useEl);
    if (style.position !== 'static' && parseInt(style.zIndex) > 100) {
      risks.push({ finding: 'SA-USE-003', element: useEl,
        zIndex: style.zIndex, severity: 'high' });
    }
  }

  return risks;
}

Remediation

ControlHow it helps
For any <use> element within the consent subtree, follow href to the referenced element and audit the referenced element's filter attribute and the <defs> filter definition, not just the use element's own attributes SA-USE-001 is invisible without following the href reference. The attack filter is on the source element, not on the use element. Standard DOM subtree checks of the consent element will miss it.
Treat any <use> element with a filter presentation attribute as a shadow-host filter attack; audit the filter applied to the use element's own shadow host output independently of the referenced element's filter SA-USE-002 chains two filter passes — source filter and shadow-host filter — either of which can be the attack layer. Both must be audited; finding a safe source filter does not certify the use element's shadow-host output.
Flag any <use> element with an external href (cross-origin or same-origin external SVG) applied within the consent subtree; external references cannot be audited statically and may change post-audit SA-USE-004 is fundamentally a dynamic supply-chain risk. The only static mitigation is to prohibit external use references in the consent subtree entirely.

SkillAudit follows use element href references into <defs> shadow sources, audits both the referenced element's filter and the use element's shadow-host filter, and flags external SVG document references in consent subtrees. Run a free audit on your MCP server GitHub URL.