Security Guide

MCP server SVG text-transform consent security — uppercase visual category confusion, lowercase salience reduction, CSS cascade asymmetry, and mousedown dynamic injection

The CSS text-transform property controls the capitalization of rendered text without altering the underlying textContent. When applied to SVG <text> elements containing consent language, it creates a disconnect between the DOM-accessible text (used by scanners) and the visually rendered text (seen by users). text-transform: uppercase converts lowercase consent text like “allow: filesystem access, credential read” into all-caps “ALLOW: FILESYSTEM ACCESS, CREDENTIAL READ” — which users interpret as a UI heading or system status message, not a consent prompt. text-transform: lowercase reduces the visual salience of alarming uppercase wording. CSS cascade asymmetry between parent SVG groups and individual consent text elements creates visual confusion about what is consent versus decorative UI chrome. A static scanner reading textContent finds the consent keyword intact; getComputedStyle(el).textTransform is required to detect the presentation mismatch. BCR dimensions are unaffected by text-transform. Four attack patterns: SA-CSS-TTR-001 through SA-CSS-TTR-004.

Attack 1: text-transform: uppercase on lowercase consent text — rendered as heading-like all-caps (SA-CSS-TTR-001)

The CSS text-transform: uppercase property converts the rendered output of a text element to uppercase characters at paint time. The underlying textContent is unmodified — it retains the original lowercase string. When applied to an SVG <text> element containing consent language such as “allow: filesystem access, credential read”, the user sees “ALLOW: FILESYSTEM ACCESS, CREDENTIAL READ”. All-caps presentation is a strong visual cue for UI headings, section labels, and status messages — not consent prompts, which are typically rendered in sentence case. The user’s cognitive processing routes the all-caps text into the “UI label” category rather than the “consent prompt requiring deliberate consideration” category. The Allow/Deny buttons remain correctly cased, creating a mismatch that a user may not consciously register.

A scanner reading textContent finds “allow: filesystem access, credential read” (pre-transform lowercase), identifies the “allow” keyword, and produces no finding. The scanner’s keyword match succeeds precisely because the lowercase source form contains the recognizable consent word. The attack relies on the scanner not checking whether the text renders differently from how it reads in the DOM. BCR.height is not affected by text-transform (unlike opacity or CSS scale attacks), so a BCR check does not detect this pattern. Detection requires calling getComputedStyle(el).textTransform on consent-keyword elements and flagging values other than 'none'.

<style>
  #consent-text-ttr-001 { text-transform: uppercase; }
</style>
<svg width="440" height="120" xmlns="http://www.w3.org/2000/svg">
  <rect width="440" height="120" fill="white"/>
  <!-- textContent: "allow: filesystem access, credential read" (lowercase) -->
  <!-- rendered:   "ALLOW: FILESYSTEM ACCESS, CREDENTIAL READ" (uppercase) -->
  <!-- user sees: heading-like all-caps text; does not read as consent prompt -->
  <!-- scanner: textContent.toLowerCase().includes('allow') → true → no finding -->
  <!--   BUT: does not check text-transform presentation mismatch -->
  <text id="consent-text-ttr-001" x="20" y="50" font-size="13" fill="#111">
    allow: filesystem access, credential read
  </text>
  <rect x="20" y="70" width="80" height="30" fill="#22c55e" rx="4"/>
  <text x="60" y="90" text-anchor="middle" font-size="13" fill="white">Allow</text>
  <rect x="120" y="70" width="80" height="30" fill="#ef4444" rx="4"/>
  <text x="160" y="90" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>

// Detection: getComputedStyle.textTransform check on consent-keyword SVG text elements
function checkTextTransform(svgEl) {
  const findings = [];
  const CONSENT_KEYWORDS = ['allow', 'permission', 'grant', 'accept', 'credential'];

  for (const el of svgEl.querySelectorAll('text, tspan')) {
    const text = el.textContent?.trim() ?? '';
    if (!CONSENT_KEYWORDS.some(k => text.toLowerCase().includes(k))) continue;

    const cs = window.getComputedStyle(el);
    const tt = cs.textTransform;

    if (tt && tt !== 'none') {
      const bcr = el.getBoundingClientRect();
      findings.push({
        severity: 'CRITICAL', code: 'SA-CSS-TTR-001',
        note: `text-transform="${tt}" on consent element; ` +
              `textContent (pre-transform): "${text.slice(0,60)}"; ` +
              `rendered presentation: text-transform=${tt}; ` +
              `textContent retains pre-transform case — scanner keyword match passes; ` +
              `user sees ${tt === 'uppercase' ? 'all-caps heading-like text' : tt + ' transformed text'}; ` +
              `BCR: w=${bcr.width.toFixed(0)}px h=${bcr.height.toFixed(0)}px (unaffected by text-transform); ` +
              `detection: getComputedStyle(el).textTransform !== 'none' on consent-keyword elements; ` +
              `flag: text-transform present; note visual category confusion`
      });
    }
  }
  return findings;
}

CRITICAL — SA-CSS-TTR-001: text-transform: uppercase applied via <style> to consent text “allow: filesystem access, credential read” causes the user to see “ALLOW: FILESYSTEM ACCESS, CREDENTIAL READ” — visually indistinguishable from a UI section heading or system status line. The textContent retains the original lowercase string; a scanner performing keyword matching finds “allow” and produces no finding. BCR is unaffected by text-transform. Detection requires getComputedStyle(el).textTransform !== 'none' on consent-keyword elements, with a finding note explaining the visual category confusion between heading-like all-caps and a consent prompt.

Attack 2: text-transform: lowercase on alarming uppercase DOM content — reduced visual salience (SA-CSS-TTR-002)

The inverse of SA-CSS-TTR-001. The DOM contains uppercase alarming text: “GRANT ACCESS TO ALL YOUR SYSTEM FILES AND CREDENTIALS”. text-transform: lowercase renders it as “grant access to all your system files and credentials” — which reads as a casual sentence rather than an alarming system warning. Users presented with uppercase text in security contexts have heightened attention; lowercase presentation of the same words reduces visual urgency. The consent action being requested is identical, but the user’s perception of its significance is diminished.

The attack surface is the opposite of SA-CSS-TTR-001 in terms of direction, but the scanner bypass is similar: a scanner reading textContent sees the uppercase form, which may trigger an alarming-scope flag (“ALL YOUR SYSTEM FILES” is a high-privilege grant). The user, however, sees only the lowercased rendering and does not experience the same urgency. A scanner might even rate this as more suspicious in the DOM than it appears to the user. Both directions of mismatch — raising visual weight above the DOM text and reducing it below — are attack patterns. Detection: same getComputedStyle.textTransform check; flag lowercase on consent elements as HIGH severity (mismatch between DOM salience and rendered salience).

<style>
  #consent-text-ttr-002 { text-transform: lowercase; }
</style>
<svg width="440" height="120" xmlns="http://www.w3.org/2000/svg">
  <rect width="440" height="120" fill="white"/>
  <!-- textContent: "GRANT ACCESS TO ALL YOUR SYSTEM FILES AND CREDENTIALS" (uppercase) -->
  <!-- rendered:   "grant access to all your system files and credentials" (lowercase) -->
  <!-- user sees: casual-looking sentence; reduced visual salience vs. all-caps -->
  <!-- scanner: textContent → uppercase; may flag alarming scope; user sees lowercased -->
  <text id="consent-text-ttr-002" x="20" y="50" font-size="13" fill="#111">
    GRANT ACCESS TO ALL YOUR SYSTEM FILES AND CREDENTIALS
  </text>
  <rect x="20" y="70" width="80" height="30" fill="#22c55e" rx="4"/>
  <text x="60" y="90" text-anchor="middle" font-size="13" fill="white">Allow</text>
  <rect x="120" y="70" width="80" height="30" fill="#ef4444" rx="4"/>
  <text x="160" y="90" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>

// Detection: flag lowercase text-transform on consent elements (salience reduction)
function checkTextTransformLowercase(svgEl) {
  const findings = [];
  const CONSENT_KEYWORDS = ['allow', 'permission', 'grant', 'accept', 'credential', 'access'];

  for (const el of svgEl.querySelectorAll('text, tspan')) {
    const text = el.textContent?.trim() ?? '';
    if (!CONSENT_KEYWORDS.some(k => text.toLowerCase().includes(k))) continue;

    const cs = window.getComputedStyle(el);
    const tt = cs.textTransform;

    if (tt === 'lowercase') {
      findings.push({
        severity: 'HIGH', code: 'SA-CSS-TTR-002',
        note: `text-transform="lowercase" on consent element; ` +
              `textContent (pre-transform): "${text.slice(0,60)}"; ` +
              `DOM text may carry alarming salience (uppercase); ` +
              `rendered text is lowercased — visual urgency reduced; ` +
              `scanner sees DOM text (alarming); user sees rendered text (casual lowercase); ` +
              `mismatch between DOM salience and rendered salience; ` +
              `detection: getComputedStyle(el).textTransform === 'lowercase' on consent-keyword elements`
      });
    }
  }
  return findings;
}

HIGH — SA-CSS-TTR-002: text-transform: lowercase on DOM content “GRANT ACCESS TO ALL YOUR SYSTEM FILES AND CREDENTIALS” renders as “grant access to all your system files and credentials”. The uppercase DOM text carries visual urgency (all-caps in security contexts signals importance); the lowercase rendering reduces this salience for the user. A scanner reading textContent observes the alarming uppercase form and may flag it, while the user sees only a casual sentence. Detection requires getComputedStyle(el).textTransform check; flag lowercase on consent elements as a DOM-to-render salience reduction attack.

Attack 3: CSS cascade asymmetry — parent text-transform: uppercase on all children, consent text overrides with none — visual context confusion (SA-CSS-TTR-003)

In this pattern the attack is not about hiding the consent text but about its visual context. A parent SVG <g> element has text-transform: uppercase applied. All header and label text elements within the group inherit this rule and render as all-caps. The consent text element specifically overrides this with text-transform: none (or text-transform: initial), rendering in the original lowercase sentence case.

The visual result: the surrounding UI chrome (headers, labels, section titles) all render in all-caps. The consent text alone renders in lowercase sentence case. This creates an asymmetry where the consent prompt — typically the most important text in the frame — appears stylistically inconsistent with the surrounding all-caps headings. Users accustomed to scanning for all-caps labels may not register the lowercase consent sentence as a distinct interactive element requiring deliberate decision-making. The consent text blends into the visual noise as a body-text paragraph rather than standing out as a decision point.

A scanner checking only the computed style on the consent text element finds text-transform: none and concludes the text renders normally, producing no finding. Detection requires also examining sibling and parent elements for text-transform asymmetry: if surrounding elements have a non-none transform while the consent element overrides to none, the visual context is disrupted in a way that may cause cognitive misclassification.

<style>
  /* All children of this group render as uppercase (headings, labels) */
  #consent-group-ttr-003 { text-transform: uppercase; }
  /* Consent text specifically overrides: renders as normal sentence case */
  /* Scanner sees text-transform:none on consent element → no finding */
  /* But visual context: all-caps surroundings make sentence-case consent text */
  /* look like body paragraph, not consent prompt */
  #consent-text-ttr-003 { text-transform: none; }
</style>
<svg width="440" height="160" xmlns="http://www.w3.org/2000/svg">
  <rect width="440" height="160" fill="white"/>
  <g id="consent-group-ttr-003">
    <!-- header: inherits uppercase from group → renders "TOOL PERMISSION REQUEST" -->
    <text x="20" y="30" font-size="12" fill="#888" font-weight="600">
      tool permission request
    </text>
    <!-- label: inherits uppercase → renders "SCOPE" -->
    <text x="20" y="55" font-size="11" fill="#aaa">scope</text>
    <!-- consent text: overrides to none → renders as-is (sentence case) -->
    <!-- scanner: getComputedStyle.textTransform = 'none' → no finding -->
    <!-- visual: surrounded by all-caps labels; sentence-case consent blends in -->
    <text id="consent-text-ttr-003" x="20" y="80" font-size="13" fill="#111">
      allow: filesystem access, credential read
    </text>
    <rect x="20" y="100" width="80" height="30" fill="#22c55e" rx="4"/>
    <text x="60" y="120" text-anchor="middle" font-size="13" fill="white">Allow</text>
    <rect x="120" y="100" width="80" height="30" fill="#ef4444" rx="4"/>
    <text x="160" y="120" text-anchor="middle" font-size="13" fill="white">Deny</text>
  </g>
</svg>

// Detection: check parent/sibling text-transform asymmetry around consent element
function checkTextTransformCascadeAsymmetry(svgEl) {
  const findings = [];
  const CONSENT_KEYWORDS = ['allow', 'permission', 'grant', 'accept', 'credential'];

  for (const el of svgEl.querySelectorAll('text, tspan')) {
    const text = el.textContent?.trim() ?? '';
    if (!CONSENT_KEYWORDS.some(k => text.toLowerCase().includes(k))) continue;

    const cs = window.getComputedStyle(el);
    const tt = cs.textTransform;
    if (tt !== 'none') continue; // handled by TTR-001/TTR-002

    // Check parent and sibling elements for non-none text-transform
    const parent = el.parentElement;
    if (!parent) continue;

    let parentTT = 'none';
    try { parentTT = window.getComputedStyle(parent).textTransform ?? 'none'; } catch(_) {}

    const siblings = Array.from(parent.querySelectorAll('text, tspan'))
      .filter(s => s !== el);
    const siblingTransforms = siblings.map(s => {
      try { return window.getComputedStyle(s).textTransform ?? 'none'; } catch(_) { return 'none'; }
    });
    const nonNoneSiblings = siblingTransforms.filter(t => t !== 'none');

    const asymmetric = parentTT !== 'none' || nonNoneSiblings.length > 0;

    if (asymmetric) {
      findings.push({
        severity: 'HIGH', code: 'SA-CSS-TTR-003',
        note: `CSS cascade asymmetry: consent element has text-transform="none"; ` +
              `parent element text-transform="${parentTT}"; ` +
              `${nonNoneSiblings.length} sibling(s) with non-none text-transform: [${nonNoneSiblings.join(', ')}]; ` +
              `visual context: surrounding elements render as ${parentTT || nonNoneSiblings[0]} (likely all-caps headings); ` +
              `consent text renders in sentence case — appears as body text, not a decision prompt; ` +
              `scanner checking only consent element computed style finds text-transform=none → no finding; ` +
              `detection: also check parent/sibling text-transform for contextual asymmetry`
      });
    }
  }
  return findings;
}

HIGH — SA-CSS-TTR-003: A parent <g> element has text-transform: uppercase, causing all inherited child text (headers, labels) to render as all-caps. The consent text element overrides with text-transform: none, rendering in sentence case. The result is that the consent prompt appears visually inconsistent with the surrounding all-caps UI chrome, causing it to read as body copy rather than a decision point. A scanner checking only the computed style on the consent element finds text-transform: none and produces no finding. Detection requires examining parent and sibling elements for text-transform asymmetry and flagging cases where the consent text is the only element not subject to a non-none transform in an otherwise transformed context.

Attack 4: mousedown JS inject text-transform: uppercase, revert at mouseup — static scan misses dynamic transform (SA-CSS-TTR-004)

At rest, the consent text element has text-transform: none (or no text-transform property at all). At mousedown, JavaScript injects an inline style="text-transform: uppercase" attribute on the consent text element. During the click window — while the user’s mouse button is depressed but before mouseup — the consent text renders as all-caps (heading-like, non-consent-looking). At mouseup, the style is restored to none (or the inline style attribute is removed). Static inspection always observes text-transform: none.

Unlike opacity or scale attacks where the injection hides the text completely, this attack operates by changing the visual category of the consent text at the moment of click. The user sees the heading-like all-caps presentation at the exact instant they are committing to a decision. The pre-click and post-click states appear normal; only the click-window state is manipulated. A MutationObserver with attributeFilter: ['style'] on the consent text element, combined with a synthetic mousedown dispatch and a post-mutation getComputedStyle.textTransform check, detects this pattern.

<!-- SA-CSS-TTR-004: at rest text-transform:none; mousedown injects uppercase; mouseup restores -->
<svg id="consent-svg-ttr" width="440" height="120" xmlns="http://www.w3.org/2000/svg">
  <rect width="440" height="120" fill="white"/>
  <!-- at rest: no text-transform → normal sentence case -->
  <!-- mousedown: style="text-transform:uppercase" injected → all-caps heading-like -->
  <!-- mouseup: style removed → restored to none -->
  <!-- static scan: text-transform=none → no finding -->
  <text id="consent-text-ttr-004" x="20" y="50" font-size="13" fill="#111">
    allow: filesystem access, credential read
  </text>
  <rect x="20" y="70" width="80" height="30" fill="#22c55e" rx="4"/>
  <text x="60" y="90" text-anchor="middle" font-size="13" fill="white">Allow</text>
  <rect x="120" y="70" width="80" height="30" fill="#ef4444" rx="4"/>
  <text x="160" y="90" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>

<script>
(function() {
  const svg  = document.getElementById('consent-svg-ttr');
  const txt  = document.getElementById('consent-text-ttr-004');
  svg.addEventListener('mousedown', () => {
    // Inject uppercase during click window — text looks like a heading, not a consent prompt
    txt.setAttribute('style', 'text-transform: uppercase');
  }, true);
  svg.addEventListener('mouseup', () => {
    // Restore to normal before mouseup handlers fire
    txt.removeAttribute('style');
  }, true);
})();
</script>

// Detection: MutationObserver on style attribute + synthetic mousedown
function watchTextTransformInject(svgEl) {
  const findings = [];
  const CONSENT_KEYWORDS = ['allow', 'permission', 'grant', 'accept', 'credential'];

  const observer = new MutationObserver(mutations => {
    for (const mut of mutations) {
      if (mut.attributeName !== 'style') continue;
      const el = mut.target;
      const text = el.textContent?.trim() ?? '';
      if (!CONSENT_KEYWORDS.some(k => text.toLowerCase().includes(k))) continue;

      const cs = window.getComputedStyle(el);
      const tt = cs.textTransform;

      if (tt && tt !== 'none') {
        findings.push({
          severity: 'CRITICAL', code: 'SA-CSS-TTR-004',
          note: `Dynamic text-transform inject: style attribute mutated; ` +
                `post-mutation getComputedStyle.textTransform="${tt}" (was "none"); ` +
                `consent text: "${text.slice(0,60)}"; ` +
                `during click window: text renders as ${tt} (heading-like category confusion); ` +
                `at rest: text-transform=none — static scan PASSES; ` +
                `MutationObserver attributeFilter=['style'] on consent element required; ` +
                `post-mutation getComputedStyle.textTransform check detects inject; ` +
                `note: BCR unaffected; text remains visible but category-confused at click time`
        });
      }
    }
  });

  for (const el of svgEl.querySelectorAll('text, tspan')) {
    const text = el.textContent?.trim() ?? '';
    if (!CONSENT_KEYWORDS.some(k => text.toLowerCase().includes(k))) continue;
    observer.observe(el, { attributes: true, attributeFilter: ['style'] });
  }

  // Synthetic mousedown to trigger dynamic inject
  svgEl.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
  svgEl.dispatchEvent(new MouseEvent('mouseup',   { bubbles: true, cancelable: true }));

  return findings;
}

CRITICAL — SA-CSS-TTR-004: At rest the consent text element has text-transform: none. At mousedown, JavaScript injects style="text-transform: uppercase", causing the consent text to render as all-caps during the click window. At mouseup the style is removed. The user commits to a decision while seeing heading-like all-caps presentation rather than a consent prompt. Static inspection always observes text-transform: none. BCR is unaffected; the text remains visible but is visually misclassified at the moment of click. Detection requires a MutationObserver with attributeFilter: ['style'] on consent text elements, a synthetic mousedown dispatch, and a post-mutation getComputedStyle.textTransform check.

Summary table

AttackMechanismWhat it bypassesSeverity
SA-CSS-TTR-001: text-transform:uppercase on lowercase consent text Lowercase textContent ("allow: filesystem access") renders as all-caps ("ALLOW: FILESYSTEM ACCESS"); user reads it as a UI heading, not a consent prompt; textContent retains lowercase — scanner keyword match passes; BCR unaffected textContent keyword scanner; BCR check; requires: getComputedStyle(el).textTransform !== 'none' on consent-keyword elements Critical
SA-CSS-TTR-002: text-transform:lowercase on alarming uppercase DOM text Uppercase DOM text ("GRANT ACCESS TO ALL YOUR SYSTEM FILES") renders as lowercase sentence; visual salience reduced; scanner sees alarming uppercase in textContent; user sees casual lowercase presentation Visual salience scanner (sees DOM); requires: getComputedStyle(el).textTransform === 'lowercase' on consent elements — flag as salience reduction attack High
SA-CSS-TTR-003: parent uppercase cascade, consent text overrides to none All sibling/parent text renders as uppercase (all-caps heading context); consent element overrides to text-transform:none; consent text in sentence case visually blends with body copy; scanner checking consent element finds none → no finding Per-element text-transform check; requires: also check parent/sibling text-transform for contextual asymmetry around consent element High
SA-CSS-TTR-004: mousedown injects text-transform:uppercase; mouseup reverts At rest: text-transform:none; mousedown: style attribute injects uppercase → all-caps during click window; mouseup: style removed; text visible but visually misclassified at decision moment; static scan passes Static DOM; requires: MutationObserver attributeFilter=['style'] on consent text + synthetic mousedown + post-mutation getComputedStyle.textTransform check Critical

SkillAudit findings for this attack surface

CRITICAL SA-CSS-TTR-001: CSS text-transform:uppercase on SVG consent text element — the CSS text-transform property transforms the rendered presentation of text without modifying textContent; text-transform:uppercase converts every character to uppercase at paint time; consent text "allow: filesystem access, credential read" (lowercase in the DOM) renders as "ALLOW: FILESYSTEM ACCESS, CREDENTIAL READ"; all-caps presentation is the dominant visual convention for UI headings, section labels, and status indicators — not consent prompts; a user scanning the UI routes all-caps text into the "label/heading" cognitive category and does not treat it as a decision point requiring deliberate consent; the SVG Allow/Deny buttons remain correctly cased, creating a disconnect; scanner reads textContent = "allow: filesystem access, credential read" → finds "allow" keyword → no finding; detection requires getComputedStyle(el).textTransform !== 'none' on elements whose textContent matches consent keywords; flag text-transform present; note: BCR unaffected by text-transform (unlike opacity/scale attacks).
HIGH SA-CSS-TTR-002: CSS text-transform:lowercase on alarming uppercase consent DOM text — text-transform:lowercase converts rendered output to lowercase regardless of the source case in textContent; when applied to consent text with alarming uppercase wording ("GRANT ACCESS TO ALL YOUR SYSTEM FILES AND CREDENTIALS"), the user sees the visually calm lowercase form ("grant access to all your system files and credentials") while the DOM retains the alarming uppercase; the uppercase DOM text carries higher visual salience — all-caps in security contexts is associated with warnings and critical actions; lowercase rendering reduces this urgency; a scanner reading textContent observes the uppercase alarming text and may flag excessive scope, but the user experiences only the low-salience lowercase version; detection: getComputedStyle(el).textTransform === 'lowercase' on consent-keyword elements — flag as DOM-to-render salience reduction.
HIGH SA-CSS-TTR-003: CSS cascade asymmetry — parent text-transform:uppercase; consent element overrides to none — a parent SVG group element has text-transform:uppercase applied, causing all inheriting text (headers, labels, nav items) to render as all-caps; the consent text element specifically overrides with text-transform:none, rendering in the author-specified sentence case; the visual context created is: all-caps UI chrome surrounding a sentence-case consent prompt; users accustomed to scanning all-caps labels as navigation/UI structure may not register the sentence-case consent text as a distinct interactive decision point; it visually resembles body copy in a labeled form rather than a grant prompt; a scanner checking only the computed style on the consent element finds text-transform:none → no finding; detection requires examining parent and sibling computed text-transform values for asymmetry and flagging when the consent element is uniquely exempted from a non-none transform applied to its context.
CRITICAL SA-CSS-TTR-004: dynamic text-transform:uppercase injection at mousedown, reverted at mouseup — at rest the consent text element carries text-transform:none (or no text-transform property); at mousedown (capture phase, fires before click) JavaScript sets the element's inline style attribute to "text-transform: uppercase"; for the duration of the click window (mousedown through mouseup) the consent text renders as all-caps heading-like text; the user commits to the Allow/Deny decision while the text appears as a system heading rather than a consent prompt; at mouseup (capture phase) the inline style attribute is removed and the element reverts to normal rendering; static inspection always observes text-transform:none; BCR is unaffected; the text is not hidden — it is visually misclassified at the exact moment of decision; detection requires a MutationObserver with attributeFilter:['style'] on consent text elements, a synthetic mousedown dispatch to trigger the injection, and a post-mutation getComputedStyle.textTransform check flagging non-none values.

Related: SVG CSS transform consent attacks  |  SVG font-size consent attacks  |  SVG text-rendering consent attacks  |  SVG writing-mode consent attacks

← Blog  |  Security Checklist