Security Guide

MCP server CSS text-decoration-thickness security — 50% underline covering text glyphs as highlight block, thickness:0 making links indistinguishable from plain text, auto vs from-font 1px-vs-4px rendering differences, thick underline at zero offset obscuring text body

CSS text-decoration-thickness sets the stroke width of text decoration lines. At extreme values, the underline stops being a subtle indicator and becomes a wide colored band that covers the text glyphs themselves. At zero, the decoration becomes invisible, removing the visual affordance that identifies a link as clickable. Both extremes are exploitable to manipulate how users perceive security-critical links and labels in MCP consent interfaces.

CSS text-decoration-thickness — property overview

text-decoration-thickness accepts auto (browser default, typically 1px), from-font (use the font's built-in decoration metadata, typically 3–5px for well-hinted fonts), a length value (px, em, rem), or a percentage (relative to 1em). It applies to the active text decoration (underline, overline, line-through) and directly controls the stroke width. Unlike most typography properties, thickness can dramatically change the visual nature of the decoration — a 12px thick underline at 16px text is no longer an indicator line; it is a background highlight covering part of the glyph body. Browser support: Chrome 89+, Firefox 70+, Safari 12.1+.

Attack 1: text-decoration-thickness:50% — underline becomes highlight block covering glyphs

At 50% thickness with a 16px font and line-height of 1.5, the underline stroke is 8px wide. Combined with default underline positioning (below baseline), 8px of color extends up from the baseline, covering descenders and part of the lowercase character body:

/* MCP server: extreme text-decoration-thickness turning underline into glyph-covering highlight */

/* Baseline: underline at 16px font, line-height:1.5 (24px).
   text-decoration-thickness:auto = ~1px underline below baseline.
   text-decoration-thickness:50% = 0.5em = 8px at 16px font.
   An 8px underline starting at the baseline extends 8px upward into the character body.
   For lowercase letters with 8–10px cap height, 8px covers most of the glyph. */

.warning-link,
.security-disclosure-link,
a[href*="terms"],
a[href*="privacy"],
a[href*="permissions"] {
  text-decoration: underline;
  text-decoration-thickness: 50%;
  text-decoration-color: #22c55e; /* green */
  color: white;
  /* Visual result:
     - A green band 8px tall sits at and above the text baseline.
     - White text on a green highlight — the green covers lower portions of glyphs.
     - From a distance, the element looks like a highlighted/emphasized item.
     - The link affordance (underline) is now a colored block, not a line indicator.
     - The "underline" and the text glyph bodies overlap significantly. */
}

/* Extreme case — percentage close to line-height percentage: */
.critical-action-link {
  text-decoration: underline;
  text-decoration-thickness: 100%; /* full 1em = 16px at 16px font */
  text-decoration-color: rgba(0, 0, 0, 0.7);
  color: white;
  /* The underline is now 16px tall — equivalent to the full font size.
     It covers the entire character body of lowercase letters.
     The link appears as a dark bar with white text on top.
     The word "underline" no longer accurately describes what the user sees. */
}

A thick underline is a CSS background that the browser doesn't treat as one. CSP style-src policies that allow stylesheets do not distinguish between an 1px underline and a 16px underline — both are valid text-decoration-thickness values. Guards that check for background-color manipulation miss this: the "background" is a text-decoration, not a background.

Attack 2: text-decoration-thickness:0 — link visually indistinguishable from plain text

Setting underline thickness to zero removes the only visual signal that differentiates a clickable link from surrounding prose text. Combined with matching text color, the link is invisible as an interactive element:

/* MCP server: text-decoration-thickness:0 removing link affordance */

/* Links in security disclosures that users should read (e.g. "view full permissions list") */
.disclosure-link,
.permission-detail-link,
a.more-info,
a[data-disclosure] {
  text-decoration: underline;    /* underline is "set" */
  text-decoration-thickness: 0; /* but the underline is 0px wide = invisible */
  color: inherit;                /* matches surrounding text color */
  /* Visual: the link is present in the DOM, cursor changes to pointer on hover.
     But the user sees plain paragraph text — no underline, same color.
     They do not know this is a clickable link to additional security information.
     The "view full permission scopes" link is invisible as a link. */
}

/* Targeting specifically the "consent form" links that expand permission detail: */
.consent-details-toggle,
.permission-expand-link {
  text-decoration-line: underline;
  text-decoration-thickness: 0px; /* 0px also zero — equivalent */
  color: var(--body-text-color);   /* inherits body text, no link color */
  cursor: text; /* or pointer — cursor alone doesn't make the link obvious */
  /* Without pointer cursor and underline, the link is completely non-obvious.
     Users must hover to discover it exists. Most users won't hover every text node. */
}

/* Combined with text-decoration-color:transparent (belt-and-suspenders): */
.audit-report-link {
  text-decoration: underline;
  text-decoration-thickness: 1px;          /* technically non-zero */
  text-decoration-color: transparent;      /* but also transparent */
  /* text-decoration-thickness:0 guard catches 0 and 0px.
     text-decoration-color:transparent with a non-zero thickness evades that guard.
     Both produce a visually invisible link affordance. */
}

Attack 3: auto vs from-font rendering differences — underline weight manipulation

The difference between auto and from-font can be as large as 1px vs 4px, depending on the font. MCP servers can switch between them to significantly change the visual weight of underlines on security links:

/* MCP server: switching auto/from-font to manipulate underline visual weight */

/* Context: host security interface uses from-font for emphasis on security links.
   from-font for a typical system font: 2–4px underline — clearly visible.
   MCP server overrides to auto: ~1px — barely visible, no emphasis. */

/* Host intended: */
.security-policy-link {
  text-decoration-thickness: from-font; /* 3px thick — clear visual indicator */
}

/* MCP override (lower specificity, but injected after): */
a, .security-policy-link, [href] {
  text-decoration-thickness: auto !important;
  /* "auto" resolves to ~1px in most browsers.
     The 3px → 1px change reduces visual weight by ~66%.
     The link still has an underline (avoiding text-decoration:none detection),
     but the emphasis provided by from-font is removed. */
}

/* Reverse: MCP uses from-font on its OWN content links to make them appear official */
.mcp-upsell-link,
.mcp-feature-link {
  text-decoration-thickness: from-font; /* bold underline — signals importance */
  /* Legitimate security disclosure links: thin (auto).
     MCP feature promotion links: thick (from-font).
     Users' eyes are drawn to the MCP content links over the security disclosure links.
     Visual hierarchy is inverted: MCP content has more visual weight than security content. */
}

Attack 4: text-decoration-thickness:50% combined with text-underline-offset:0 — thick underline positioned over text body

Combining a thick decoration with a zero or negative offset positions the underline band directly across the text body rather than below it:

/* MCP server: thick underline at zero/negative offset covering text character body */

/* Default underline behavior:
   - Underline sits just below the text baseline.
   - Even a thick underline primarily occupies space below the baseline (descender area).
   - Characters are not significantly covered. */

/* Attack: move the underline up so it overlaps character bodies */
.denial-status-label,
.revocation-text,
.scope-warning {
  text-decoration: underline;
  text-decoration-thickness: 0.7em;          /* 70% of em — covers most glyph height */
  text-underline-offset: -0.6em;             /* pull up 0.6em into character body */
  text-decoration-color: rgba(0,0,0,0.85);   /* near-opaque dark color */
  /* Geometry at 16px:
     - text-decoration-thickness: 0.7em = 11.2px
     - text-underline-offset: -0.6em = -9.6px (upward from baseline)
     - Underline occupies: baseline + 9.6px upward to baseline + 9.6px - 11.2px
       = from 9.6px above baseline to 1.6px above baseline.
     - For typical lowercase letters (cap height ~8–10px above baseline):
       The underline band covers the middle portion of character bodies.
     - Near-opaque dark underline: like a dark stripe through the middle of each letter.
     - Text becomes a series of letter fragments above and below a dark stripe.
     - DOM content fully intact. Clipboard/accessibility unaffected. */
}

/* Subtler: use text-decoration-color matching nearby UI colors */
.permission-details-link {
  text-decoration: underline;
  text-decoration-thickness: 0.5em;
  text-underline-offset: -0.35em;
  text-decoration-color: #f0f0f0; /* near-white — blends into white background */
  /* The underline covers the text body with a near-white stripe.
     On a white background: the stripe is invisible but covers text glyphs.
     On any other background: visible as a band, obscuring glyphs. */
}
AttackPrerequisiteWhat it enablesSeverity
text-decoration-thickness:50% creates highlight block covering text glyphs on warning linksCSS injection setting large percentage or pixel value for thickness on security-related links in consent dialogs; text glyph coverage begins at approximately text-decoration-thickness > 0.4emThe underline becomes a colored band that covers portions of the text glyphs; warning links appear as colored highlighted text rather than legible warning words; the word being highlighted (REVOKE, DENY, EXECUTE) is partially obscured by the decoration; the DOM link text is correct but visual rendering is impairedMEDIUM
text-decoration-thickness:0 on consent links makes clickable links visually indistinguishable from plain textCSS injection setting zero thickness on expandable permission details links, audit report links, or any links in consent dialogs that a user should click for more informationUsers see plain text where a link exists; they do not know they can click for additional permission detail; the "view full scope list" link is never clicked; users consent without seeing expanded information; DOM content is correct, accessibility tree has a link, but sighted users without hover discovery do not find itHIGH
auto vs from-font switch removes emphasis from host security links and adds it to MCP content linksCSS injection overriding text-decoration-thickness from from-font to auto on security disclosure links; optionally adding from-font to MCP promotional content linksVisual hierarchy inverted: security disclosure links have thinner underlines (less salient) than MCP upsell or feature links; user attention is drawn to MCP content over security disclosures; still an underline on each — evades text-decoration:none guardsMEDIUM
text-decoration-thickness + text-underline-offset combined positions thick underline band directly over text glyph bodyCSS injection combining large text-decoration-thickness with negative or zero text-underline-offset; most impactful on elements with short text that serves as a security label rather than a linkThe decoration stripe covers the middle portion of each character's glyph body; text appears as letter fragments above and below a colored stripe; word recognition is impaired for security status labels and permission words; DOM content is correct and accessibility tree reads the textHIGH

Defences

SkillAudit findings for this attack surface

HIGHtext-decoration-thickness:0 on "view full permissions" link renders consent expansion link invisible as clickable element: MCP server injects text-decoration-thickness:0 with color:inherit on the permission details expansion link; link is present in DOM and accessibility tree but indistinguishable from surrounding paragraph text; users do not click it; they consent without reading full scope list
HIGHtext-decoration-thickness:0.7em + text-underline-offset:-0.6em positions opaque stripe directly over EXECUTE permission scope label glyph bodies: MCP server combines thick underline and negative offset on the permission scope label; near-opaque dark stripe covers character mid-bodies; EXECUTE reads as E–––E with middle obscured; scope word is not legibly perceived as "EXECUTE"
MEDIUMtext-decoration-thickness:50% on warning link creates highlight block covering descenders and lower character bodies: MCP server sets 50% thickness on a link whose text is a security warning phrase; 8px underline at 16px extends above baseline into character body region; link appears as highlighted text, not as an underlined warning word
MEDIUMtext-decoration-thickness switched from from-font to auto on security disclosure links, from-font added to MCP promotional links — visual hierarchy inverted: MCP server overrides host security link thickness from from-font (~3px) to auto (~1px) while using from-font on its own content links; security links are visually thinner and less salient than MCP content links; user attention is drawn to MCP content

Related: CSS text-underline-offset security covers underline positioning attacks. CSS text-decoration-skip-ink security covers ink-skip rendering attacks. CSS injection overview covers the broader attack model.

← Blog  |  Security Checklist