Security Guide

MCP server CSS outline consent security — thick outline covers adjacent consent text, negative outline-offset covers text content area, outline:none on checkbox, parent outline over child text

CSS outline is painted above page content in the stacking order and does not affect document layout. A thick white outline on the install button extends outside the button's border box and can cover adjacent consent text below it — without any change to the consent element's own styles. Negative outline-offset draws the outline inside the element's border box, creating a white ring that covers the text content area from within. The consent element's color, dimensions, opacity, and display values remain correct throughout.

How CSS outline differs from border

CSS border is part of the box model — it occupies space in the layout and contributes to the element's dimensions. CSS outline is a separate rendering layer: it is always painted after all other box-model elements and does not affect layout flow. No other element is displaced when an outline is added or enlarged. The outline is drawn around the outside of the element's border box (at a distance determined by outline-offset), or inside the element when outline-offset is negative.

The key implication for consent attacks: an outline on element A can visually cover content in element B without element B having any CSS changes. The consent element's computed styles — color, opacity, display, visibility — are all unaffected. Standard consent auditors that inspect only the consent element's own computed styles cannot detect an outline on a sibling element that physically covers the consent text in the composited frame.

Attack 1: Thick white outline on install button covers adjacent consent text (SA-CSS-OL-001)

In a typical MCP install UI, the install button is positioned directly above the consent text in the DOM and visually in the viewport. outline: 20px solid white on the install button draws a 20px white outline band outside the button's border box on all four sides. The bottom edge of this outline extends 20px below the button's lower border. If the consent text begins within 20px below the button, the bottom edge of the outline covers it with a white band — without any change to the consent element.

/* Attack: thick white outline on install button covers consent text below */
.install-button {
  outline: 20px solid white;      /* 20px white band on all sides including bottom */
  outline-offset: 0;              /* outline starts at button's border edge */
  /* no other unusual properties */
}

.consent-text {
  /* all correct — auditor checks these and reports PASS */
  color: #1a1a1a;
  opacity: 1;
  display: block;
  visibility: visible;
  /* but: install button's 20px white outline covers this element from above */
}

Audit gap: Standard consent auditors inspect the consent element's computed styles. This attack has no changes to the consent element — the hiding is entirely via a sibling element's outline. Detection requires checking outlines on siblings and ancestors of the consent element, not just the consent element itself.

Attack 2: Negative outline-offset draws outline inside consent element (SA-CSS-OL-002)

When outline-offset is negative, the outline is drawn inside the element's border box rather than outside it. A large negative value moves the outline inward until it reaches the element's content area. outline: 30px solid white; outline-offset: -30px on the consent container draws a 30px white ring inside the container, starting at the element's border edge and extending 30px inward — covering text at the content area boundary from all four sides.

/* Attack: negative outline-offset draws white ring inside consent container */
.consent-container {
  outline: 40px solid white;     /* 40px wide white ring */
  outline-offset: -40px;         /* ring drawn 40px inside the border edge */
  /* effect: white band 40px thick covers the inner content area perimeter */
  /* text near element edges is covered; deeply centered text may still show */
  background: white;             /* background matches — outline blends in */
}

/* Variant: outline-offset just barely reaching text */
.consent-text {
  outline: 2px solid white;
  outline-offset: -2px;          /* covers border area only — subtle visual effect */
}

The negative-offset form is detectable via getComputedStyle — both outline and outline-offset are readable properties. The challenge is that legitimate UI uses negative outline-offset for focus-ring styling. Auditors must evaluate whether the outline color matches the background and whether the outline width is large enough to reach the text content area.

Attack 3: outline: none on consent checkbox removes focus indicator (SA-CSS-OL-003)

The consent confirmation checkbox is a required interaction in many MCP install flows — users must check the box to confirm they read the consent terms. Browsers render a focus ring (implemented via outline) when the checkbox has keyboard focus, providing a visual indicator that keyboard users can see where focus is. outline: none on the consent checkbox removes this indicator without any ARIA attribute change or DOM modification.

/* Attack: remove focus ring from consent checkbox — keyboard users lose focus indicator */
input[type="checkbox"].consent-checkbox {
  outline: none;
  /* or: */
  outline: 0;
}

/* WCAG 2.1 SC 2.4.7 requires visible keyboard focus for all interactive elements.
   outline:none on the consent checkbox violates SC 2.4.7.
   Users tabbing through the install flow cannot see the checkbox has focus.
   Combined with a small click target, keyboard-only users may inadvertently skip. */

WCAG violation: outline: none on interactive elements violates SC 2.4.7 (Focus Visible). On a consent checkbox specifically, this prevents keyboard-only users from confirming that focus is on the checkbox before activating it. Many screen reader setups depend on the focus ring as a visual confirmation signal.

Attack 4: Parent element outline with negative offset covers child consent text (SA-CSS-OL-004)

The negative-offset outline attack can be applied to a parent or ancestor of the consent element rather than to the consent element itself. A parent with outline: 50px solid white; outline-offset: -50px draws a thick white ring inside the parent's border box. If the consent text sits within 50px of the parent's padding edge, the outline covers it from the outside inward. The consent element's own styles are unchanged; the covering outline belongs to an ancestor.

/* Attack: ancestor outline with large negative offset covers consent text inside */
.install-panel {
  outline: 60px solid white;
  outline-offset: -60px;
  /* white ring 60px from the panel's border edge covers child content near edges */
}

.install-panel .consent-text {
  /* positioned near the panel's edge — covered by parent's inward outline */
  padding: 16px;   /* 16px from edge — within the 60px outline coverage zone */
}

Findings summary

HIGH SA-CSS-OL-001: thick white outline on install button covers adjacent consent text — changes to sibling element, not consent element
HIGH SA-CSS-OL-002: negative outline-offset on consent container draws outline inside content area — all consent element properties correct
MEDIUM SA-CSS-OL-003: outline:none on consent checkbox removes keyboard focus ring — WCAG 2.4.7 violation
MEDIUM SA-CSS-OL-004: ancestor element large negative-offset outline covers consent text within 50px of padding edge

Defences

Check outlines on consent element siblings and ancestors: The consent element's own outline is not the only attack vector. Audit outlines on the install button, adjacent siblings, and all ancestors of the consent element. Any outline with a color matching the background color and sufficient width to reach the consent element's position should be flagged.

Evaluate negative outline-offset on consent containers: An outline with a large negative offset (absolute value greater than half the element's smaller dimension) can cover text content areas. Check getComputedStyle(el).outlineOffset on consent elements and their ancestors — flag large negative values combined with white or background-matching outline colors.

Check consent checkbox focus ring explicitly: For consent checkbox elements, verify that the computed outline is not none or 0. Check both the element's own styles and any CSS rule matching :focus, :focus-visible pseudo-classes that might suppress the outline at interaction time.

Related: CSS clip-path consent security · CSS mask consent security · CSS mask-image as a consent bypass vector