Security Guide

MCP server CSS text-decoration consent security — thick line-through redaction bar, combined decoration full-height coverage, negative text-underline-offset line overlap, runtime decoration injection

CSS text-decoration can physically cover consent text with opaque decoration lines while the text itself retains a dark color, legible font-size, and normal element dimensions. Standard consent legibility checks — contrast ratio between color and background-color, getBoundingClientRect() dimensions, visibility and opacity — all pass. The consent text exists in the DOM and is technically legible at the property level; but what the user actually sees is a redaction bar.

How CSS text-decoration works

text-decoration is a shorthand for four sub-properties: text-decoration-line (which lines to draw: underline, overline, line-through), text-decoration-color (the line color), text-decoration-thickness (the line thickness, as a length or from-font), and text-decoration-style (solid, dashed, dotted, wavy, double). Crucially, text-decoration-color is independent of color: the text glyph color and the decoration line color can be set separately. This independence is the core of all four consent bypass attacks described below.

Attack 1: Thick line-through in background-matching color creates a redaction bar (SA-CSS-TDC-001)

Setting text-decoration-line: line-through with text-decoration-thickness: 1.2em and text-decoration-color: #ffffff (white, matching a white background) draws a white bar 1.2em tall directly through the consent text. Since 1.2em exceeds the typical cap height (0.7em) and x-height (0.5em) of most fonts, the white bar covers the full vertical span of the glyphs. The text color remains dark — the WCAG contrast ratio between color and background-color passes — but the decoration bar physically paints over the glyphs in the same white as the background. The text looks like a redacted document section.

/* Redaction bar via thick line-through in background color */
.consent-text {
  color: #1a1a1a;                         /* dark text color — contrast audit passes */
  background-color: #ffffff;              /* white background */

  /* The attack: */
  text-decoration-line: line-through;
  text-decoration-color: #ffffff;         /* matches background — audit may check text color not decoration color */
  text-decoration-thickness: 1.2em;       /* 1.2em covers the full glyph height */

  /* Result: white bar 1.2em tall drawn through all text */
  /* getComputedStyle(el).color = "rgb(26, 26, 26)"  → legible */
  /* getComputedStyle(el).backgroundColor = "rgb(255, 255, 255)" */
  /* contrast ratio: 16.1:1 → passes WCAG AAA */
  /* what the user sees: white stripe through the middle of every line */
}

/* Variant: near-background color instead of exact match */
.consent-text {
  color: #1a1a1a;
  background-color: #f8f8f8;             /* light grey background */
  text-decoration-line: line-through;
  text-decoration-color: #f8f8f8;        /* exactly matches background */
  text-decoration-thickness: 1.4em;      /* covers descenders too */
  /* contrast of decoration-color vs background: 1:1 → invisible bar */
}

What audits miss: WCAG contrast ratio checkers compare color vs background-color. text-decoration-color is a separate property not included in standard contrast calculations. SkillAudit explicitly checks the contrast ratio between text-decoration-color and the effective background-color when text-decoration-thickness exceeds 0.5em on consent-containing elements.

Attack 2: Combined overline + underline + line-through at 0.5em covers full text height (SA-CSS-TDC-002)

Each of overline, underline, and line-through positions its decoration line at a different vertical position relative to the text baseline. By applying all three simultaneously with text-decoration-thickness: 0.5em each, three half-em-wide bands are layered at the overline position (above the cap height), the midline (through the middle of caps), and the underline position (below the baseline). Together, these three bands span from above the ascenders to below the descenders — covering the full vertical extent of every character. With a near-background decoration color, the consent text is visually obliterated while the DOM structure remains intact.

/* Three-band full-height coverage */
.consent-text {
  color: #222222;
  background-color: #ffffff;

  /* Three decoration lines, each 0.5em thick, at three vertical positions */
  text-decoration-line: overline underline line-through;
  text-decoration-color: #ffffff;        /* background-matching */
  text-decoration-thickness: 0.5em;      /* each band = 0.5em */

  /* Band positions (approximate, font-dependent):
     overline:    above ascenders (~0.9em above baseline)
     line-through: ~0.35em above baseline (through cap midline)
     underline:   ~0.1em below baseline

     With 0.5em thickness each:
     overline band:    0.9em to 1.4em above baseline  (covers ascenders/caps)
     line-through band: 0.35em to 0.85em above baseline (covers cap body)
     underline band:   -0.1em to 0.4em above baseline (covers lower half + baseline)

     Combined coverage: roughly -0.1em to 1.4em above baseline
     Typical font glyph span: -0.3em to 0.9em above baseline
     → All three bands together cover the full glyph range */
}

/* Audit sees: color #222222, background #ffffff → 16.1:1 contrast ratio ✓
   Reality:    three white bands covering the full text height */

Attack 3: Negative text-underline-offset overlaps adjacent line's text (SA-CSS-TDC-003)

text-underline-offset moves the underline relative to its default position — positive values move it down (away from text), negative values move it up (toward and into the text). For multi-line consent text with a standard line-height of 1.5em, a text-underline-offset of -1.5em repositions the underline one full line-height above its default, placing it directly over the text on the line above. This creates a thick underline bar on line 2 that overlaps and covers the text on line 1. Each line's underline covers the previous line's text, creating a cascade of bars across the multi-line consent block.

/* Negative offset underline covers preceding line's text */
.consent-text {
  line-height: 1.5em;
  color: #1a1a1a;
  background-color: #ffffff;

  text-decoration-line: underline;
  text-decoration-color: #ffffff;        /* background-matching color */
  text-decoration-thickness: 0.6em;      /* thick enough to cover text above */
  text-underline-offset: -1.5em;         /* move underline up by one full line-height */

  /* Effect on a 3-line consent block:
     Line 1: "By installing this plugin you agree to..."
     Line 2: "grant access to your file system and..."  ← underline offset to cover Line 1
     Line 3: "network connections."                     ← underline offset to cover Line 2

     The underline on Line 2 paints 1.5em above its baseline = over Line 1's text
     The underline on Line 3 paints 1.5em above its baseline = over Line 2's text
     Line 1's text has no preceding line to receive a covering underline from
     → Lines 2 and 3 of the consent are covered; only Line 1 is partially visible */
}

/* Variant: use text-underline-offset: calc(-1 * var(--line-height)) to
   programmatically match the actual line-height value and cover all lines */

Partial-coverage attacks: This attack covers all but the first line of multi-line consent. Combined with a very long first line that wraps without containing a complete sentence — e.g., starting mid-sentence from a structure that appears to be a heading — the user sees only a fragment of the consent's opening without the critical permission-granting clauses on subsequent lines.

Attack 4: JS injects decoration styles at mousedown after static audit (SA-CSS-TDC-004)

Consent text is correctly displayed with no obstructing decoration at page load. The static audit captures a clean DOM: dark text, no overriding decoration thickness, legible font-size. At mousedown on the install button — before the click event, before any confirmation — a JavaScript handler applies text-decoration-color, text-decoration-thickness, and text-decoration-line to the consent element, producing a redaction bar within the ~150ms mousedown-to-click window. The decoration reverts after the install completes so that any post-install audit of the consent element finds it in the clean state.

// Runtime text-decoration injection at mousedown
const installBtn = document.querySelector('.install-button');
const consentEl = document.querySelector('.consent-text');

installBtn.addEventListener('mousedown', () => {
  // Apply redaction decoration on mousedown — before click
  consentEl.style.textDecorationLine = 'line-through';
  consentEl.style.textDecorationColor = getComputedStyle(consentEl).backgroundColor;
  consentEl.style.textDecorationThickness = '1.4em';
  // The decoration now matches the background color → invisible bar over text
  // The user's cursor is already positioned on the install button
  // The 150ms between mousedown and click is imperceptible
});

installBtn.addEventListener('click', () => {
  // Revert to clean state after install completes
  setTimeout(() => {
    consentEl.style.textDecorationLine = '';
    consentEl.style.textDecorationColor = '';
    consentEl.style.textDecorationThickness = '';
  }, 300);
});

/* Pre-install audit (static): textDecoration = '' → no decoration → passes
   Install time (runtime): textDecoration = line-through white 1.4em → covered
   Post-install audit: textDecoration = '' → clean → passes */
Attack IDMechanismWhat passes / what failsDetection method
SA-CSS-TDC-001Thick line-through in background-matching text-decoration-colorColor contrast ✓, dimensions ✓, visibility ✓ — decoration-color vs background contrast ✗Check text-decoration-color contrast against background when thickness > 0.5em
SA-CSS-TDC-002All three decoration lines at 0.5em thickness, background-matching colorAll standard checks ✓ — combined vertical coverage uncheckedSum all active decoration thicknesses; flag when combined spans > 0.8× font-size
SA-CSS-TDC-003Negative text-underline-offset by one line-height covers preceding lineSingle-line checks ✓ — multi-line coverage via offset uncheckedCheck text-underline-offset magnitude against line-height; flag when |offset| > 0.5× line-height
SA-CSS-TDC-004JS applies decoration at mousedown, reverts after clickStatic audit ✓ — behavioral test at install interaction requiredMonitor decoration property mutations during simulated install interaction

SkillAudit findings for CSS text-decoration

HIGHSA-CSS-TDC-001: Thick line-through redaction bar: text-decoration-line: line-through with text-decoration-thickness ≥ 0.8em and text-decoration-color matching or near-matching the effective background color creates an opaque bar covering consent text. Text color contrast passes; text-decoration-color vs background contrast fails. SkillAudit evaluates decoration-color contrast independently when decoration thickness is non-trivial on consent-containing elements.
HIGHSA-CSS-TDC-002: Combined decoration lines span full text height: three simultaneous decoration types (overline underline line-through) at equal thickness collectively cover the full vertical glyph span at near-background colors. Individual decoration-line checks find no single line covering the text; only aggregate vertical coverage analysis detects this attack.
MEDIUMSA-CSS-TDC-003: Negative text-underline-offset covers preceding line: setting text-underline-offset to a negative value equal in magnitude to the element's line-height repositions each line's underline to paint over the text on the preceding line, covering all but the first line of multi-line consent in a cascade of background-colored bars.
MEDIUMSA-CSS-TDC-004: Runtime decoration injection at mousedown: JavaScript handler applies redaction-level decoration properties at mousedown on the install button and reverts them after click. The decoration is active during the critical consent-review window at install time but invisible in static analysis and post-install audits.

Defences

Audit text-decoration-color contrast independently: Standard WCAG contrast checks compare color vs background-color and miss decoration-color attacks. When text-decoration-thickness exceeds 0.4em on consent-containing elements, SkillAudit computes the contrast ratio between text-decoration-color and the effective background and flags ratios below 4.5:1.

Flag aggregate decoration thickness: Compute the sum of all active decoration line thicknesses on consent text. If the combined thickness exceeds 80% of the element's font-size, flag as potentially covering — even if individual lines appear non-suspicious.

Check text-underline-offset magnitude relative to line-height: A negative text-underline-offset whose absolute value approaches the element's line-height indicates an offset-into-previous-line attack. Flag when |text-underline-offset| > 0.4 × line-height on multi-line consent elements.

Behavioral testing at install interaction: Monitor decoration property mutations during a simulated mousedown-click sequence on the install button. Any decoration property change on consent elements during this window should be flagged as a runtime consent interference finding.

Related: CSS color consent security · CSS custom property inheritance consent security · CSS font-size clamp() consent security