MCP server SVG switch element security

The SVG <switch> element implements conditional rendering: it evaluates each direct child element's test attributes (systemLanguage, requiredExtensions, requiredFeatures) in document order and renders only the first child whose conditions all pass. All subsequent children are not rendered, even if they pass their own conditions. This first-match, exclusive-render semantic creates a consent attack surface: an attacker can place an attack consent variant first with a condition targeting the user's environment, ensuring only the attack text is displayed while the full disclosure text sits in later DOM children that never render.

Finding SA-SW-001: systemLanguage attribute shows attack consent variant to English speakers

CriticalA <switch> element contains two child text elements. The first has systemLanguage="en" and displays a truncated consent sentence: "I grant access." The second has systemLanguage="xx" (an invalid language code that never matches) and displays the full disclosure: "I authorize SkillAudit MCP server to read and write all files in your home directory and make outbound network requests." For an English-language browser, systemLanguage="en" matches, the truncated consent renders, and the switch stops. The full disclosure never renders. textContent on the switch element returns both text nodes concatenated. An auditor reading textContent finds the full disclosure and concludes the consent is legitimate.
<switch>
  <!-- First child: systemLanguage=en matches all English browsers -->
  <!-- Renders: "I grant access." -- truncated, vague -->
  <text x="10" y="30" font-size="14" fill="#1a1a1a"
        systemLanguage="en">
    I grant access.
  </text>

  <!-- Second child: systemLanguage=xx never matches -->
  <!-- Never renders -- but textContent includes this text -->
  <text x="10" y="30" font-size="14" fill="#1a1a1a"
        systemLanguage="xx">
    I authorize SkillAudit MCP server to read and write all files in your
    home directory and make outbound network requests to external services.
  </text>
</switch>
<!-- Rendered text: "I grant access." -->
<!-- textContent: "I grant access. I authorize SkillAudit MCP server ..." -->

Any consent audit that uses textContent or innerText as the consent verification string is bypassed by this attack. The full disclosure is in the DOM but is unreachable by any user — only the attack text ever renders. Detection requires enumerating each <switch> child's text content independently and checking which child's condition would evaluate to true for the target browser environment.

Finding SA-SW-002: requiredExtensions removes the consent text on browsers with SVG rendering extensions

HighA consent text element inside a <switch> has requiredExtensions="http://www.w3.org/1999/xhtml". The SVG specification defines that requiredExtensions evaluates to true only if the browser supports the listed extension URI. The XHTML extension URI historically evaluated to true in some SVG renderers (SVG was embedded in XHTML documents). Modern browsers' behavior on this URI varies. An attacker uses this to target specific browser environments: the consent text renders in environments where the extension check fails (treating false as "no match, try next switch child"), while in targeted environments the presence of the extension removes the consent element in favor of a no-consent fallback.
<switch>
  <!-- requiredExtensions check -- may evaluate to false on modern browsers -->
  <!-- If false: consent text is not rendered, switch moves to next child -->
  <text x="10" y="30" font-size="14" fill="#1a1a1a"
        requiredExtensions="http://www.w3.org/1999/xhtml">
    I authorize all file system and network access.
  </text>

  <!-- Fallback: no consent text -- empty element -->
  <text x="10" y="30" font-size="14" fill="#1a1a1a">
    <!-- intentionally empty -->
  </text>
</switch>

Finding SA-SW-003: switch first-match attack places misleading scope text before full disclosure

CriticalA <switch> element has no conditional test attributes on its children — both children render without conditions by SVG spec (a child with no test attributes always passes). However, the switch element itself has a CSS display: none override on all children except the first via a CSS rule, effectively simulating the switch first-match behavior with CSS. The first child contains a narrow-scope consent ("I allow basic skill operation.") and the second child contains the full disclosure. Only the first child renders. The DOM contains both. This uses CSS to implement a switch-like exclusive visibility pattern outside the SVG conditional rendering model.
<style>
  /* Custom CSS first-child exclusive rule -- only first consent text renders */
  .consent-switch text:not(:first-child) { display: none; }
</style>

<g class="consent-switch">
  <!-- First child: narrow scope -- the only text the user sees -->
  <text x="10" y="30" font-size="14" fill="#1a1a1a">
    I allow basic skill operation.
  </text>
  <!-- Second child: full disclosure -- hidden by CSS, present in DOM -->
  <text x="10" y="50" font-size="14" fill="#1a1a1a">
    I authorize SkillAudit to read all files and make network requests.
  </text>
</g>

Finding SA-SW-004: switch systemLanguage targets the auditor's locale, not the user's

HighThe attack consent variant has systemLanguage="de" (German) and the full disclosure has systemLanguage="en". The MCP server operates in Germany and its users have German-language browsers. German users see the attack variant; English-language auditors see the full disclosure. The attack is locale-targeted: the audience most likely to audit the consent (English-speaking security reviewers) sees the legitimate text, while the actual user population (German speakers) sees the attack variant. Static analysis that reads only textContent misses this entirely; locale-conditioned analysis requires evaluating systemLanguage against target-user locale, not auditor locale.
<switch>
  <!-- Shown to German users (the actual user population) -->
  <text systemLanguage="de" x="10" y="30" font-size="14" fill="#1a1a1a">
    Ich erteile Zugriff.<!-- "I grant access" -- vague, no scope -->
  </text>

  <!-- Shown to English auditors -- full disclosure -->
  <text systemLanguage="en" x="10" y="30" font-size="14" fill="#1a1a1a">
    I authorize SkillAudit to read all files and make network requests.
  </text>
</switch>

This attack highlights that consent audit localization must match the deployment context. An audit running in English for a German-deployed MCP server must evaluate SVG systemLanguage conditions against 'de', not 'en'. SkillAudit identifies the target locale from deployment metadata and evaluates switch conditions for that locale when auditing consent text variants.

Detection algorithm: SVG switch consent audit

Check Method Catches
Find switch elements in consent subtree Query all <switch> elements in and around the consent element hierarchy. For each switch, enumerate all direct children and their test attributes Baseline: identifies all consent text variants controlled by switch
Evaluate which child renders Simulate the SVG switch evaluation for the target user's environment: check systemLanguage against the target locale, requiredExtensions against browser extension support. Flag if the rendering child has different textContent from the full-disclosure child SA-SW-001: attack consent variant shown to target locale users
Check requiredExtensions conditions For any child with requiredExtensions, flag as High for manual review — the evaluation is browser-specific and may suppress the consent text in targeted environments SA-SW-002: requiredExtensions removing consent text on targeted browsers
Compare all child textContent variants Collect the textContent of all switch children. If any child has significantly shorter or different-scope text than the others, flag the switch as containing consent variants — only one renders SA-SW-001, SA-SW-003: any switch containing consent text variants of different scope
Check CSS-based exclusive visibility Check for CSS rules that implement single-child exclusive visibility on a parent group (:not(:first-child) display:none). These simulate SVG switch behavior outside the native switch element SA-SW-003: CSS-based first-child exclusive consent variant hiding

SkillAudit evaluates SVG switch element children for consent text variants, simulates condition evaluation for the target deployment locale, and compares variant text content to identify narrow-scope or misleading consent text presented to users. Run a free audit on your MCP server GitHub URL.