MCP server CSS all property security: all:initial consent framework reset, all:revert UA stylesheet override, all:unset positioning collapse, and @layer revert-layer cascade wipe attacks

Published 2026-07-25 — SkillAudit Research

The CSS all shorthand property resets every CSS property except direction and unicode-bidi to a specified keyword: initial (specification initial values), inherit (inherited parent values), unset (initial for non-inherited, inherit for inherited properties), revert (browser user-agent stylesheet values), or revert-layer (values from the next lower cascade layer). In a single CSS declaration, all: initial resets display, position, width, height, color, background, z-index, visibility, opacity, overflow, and every other CSS property to their specification-defined initial values.

MCP servers exploit all to strip consent framework styling wholesale. A consent modal relying on position: fixed; z-index: 9999; background: rgba(0,0,0,0.7); width: 100vw; height: 100vh loses all of these properties when a higher-specificity all: initial rule fires — the modal collapses to an inline element with no positioning, no background, no dimensions, and no z-index. The consent disclosure disappears from the visual rendering while remaining fully present in the DOM.

all:initial vs all:revert vs all:unset: all: initial resets to specification initial values regardless of the browser's UA stylesheet. For <div>, display: initial = inline (not block). For <button>, display: initial = inline, removing the button's block-level appearance. all: revert resets to the user-agent stylesheet values — for <div>, UA default is display: block, so all: revert on a <div> gives a block-level element but strips all framework-applied positioning, colors, and dimensions. all: revert-layer only affects values from the current cascade layer, reverting to the next lower layer's values — useful for targeted attacks within @layer architectures.

Attack 1: all:initial on consent modal strips position:fixed, z-index, and dimensions — modal collapses

A consent framework styles its modal backdrop with position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 9999; background: rgba(0,0,0,0.7). An MCP server applies a higher-specificity all: initial rule targeting the modal. After reset, every property reverts to its initial value: position: static, width: auto, height: auto, z-index: auto, background-color: transparent, display: inline. The modal collapses from a viewport-filling overlay to an inline element with no background, no positioning, no dimensions — invisible in the page flow. The DOM node remains; its text content is accessible to screen readers; but the visual consent overlay has disappeared.

/* Consent framework modal: */
#consent-modal-backdrop {
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  z-index: 9999;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
}
#consent-modal-dialog {
  background: white;
  border-radius: 8px;
  padding: 32px;
  max-width: 560px;
  width: 90%;
}

/* MCP attack — all:initial wipes entire modal styling: */
/* Using high-specificity selector or !important: */
body #consent-modal-backdrop,
html body [id="consent-modal-backdrop"],
#consent-modal-backdrop:not(dialog) {
  all: initial;
  /* After reset:
     position: static (from position: initial = static)
     width: auto; height: auto
     background: transparent; background-color: transparent
     z-index: auto (not above any other element)
     display: inline (initial value for display is 'inline' per spec)
     The modal backdrop: collapses to inline, no background, in-flow.
     Rendered as: nothing visible — a zero-size inline element in the page flow.
     The full-screen blocking consent overlay disappears from visual rendering.
     User interacts with the page beneath as if no consent was required. */
}

body #consent-modal-dialog {
  all: initial;
  /* Same result: dialog collapses to inline zero-size element.
     Dialog text content is in the DOM but invisible. */
}

// Detection: check for all:initial on consent modal elements
function detectAllInitialOnModal() {
  const modalSelectors = ['[id*="modal"]', '[id*="consent"]', '[class*="modal"]',
                          '[class*="consent"]', '[role="dialog"]', '[aria-modal="true"]'];

  modalSelectors.forEach(sel => {
    document.querySelectorAll(sel).forEach(el => {
      const cs = window.getComputedStyle(el);
      // all:initial resets display to 'inline' — check if a modal-type element is inline
      if ((el.id.includes('modal') || el.getAttribute('role') === 'dialog') &&
          cs.display === 'inline' && el.textContent.trim().length > 0) {
        console.error('SECURITY: consent modal/dialog element has display:inline — may be reset by all:initial:', {
          el, display: cs.display, position: cs.position
        });
      }
      // Check position: if a consent overlay has position:static, it's been reset
      if (cs.position === 'static' && cs.zIndex === 'auto' && cs.backgroundColor === 'rgba(0, 0, 0, 0)') {
        const hasConsentText = /consent|agree|gdpr|privacy|accept/i.test(el.textContent);
        if (hasConsentText && el.textContent.trim().length > 30) {
          console.warn('SECURITY: consent element with no positioning, no background, no z-index — may be collapsed by all:initial:', {
            el, position: cs.position, zIndex: cs.zIndex, background: cs.backgroundColor
          });
        }
      }
    });
  });
}

Attack 2: all:revert strips consent framework overrides while preserving UA stylesheet — framework appears but is non-functional

all: revert reverts each property to the value it would have had from the user-agent stylesheet, as if no author stylesheets had been applied. For a consent framework that relies on its stylesheet to set pointer-events: all, user-select: text, overflow-y: auto, and max-height: 70vh on the consent scrollable container, all: revert removes all of these — the UA stylesheet does not specify them, so they revert to their initial values: pointer-events: auto (normal), user-select: auto, overflow: visible, max-height: none. The visual appearance may look roughly normal (the UA stylesheet gives divs their block display), but the scrollable consent container with max-height: 70vh + overflow-y: auto reverts to a non-scrollable full-height element, potentially pushing the "Accept" button far below the viewport (below a large amount of unconstrained consent text) so the user cannot reach it — or more importantly, the consent text is now unconstrained and grows to full height, potentially pushing content off-page.

/* MCP attack — all:revert breaks consent scroll container functionality: */
.consent-scrollable-body,
[class*="consent-scroll"],
[data-scroll-consent] {
  all: revert;
  /* Reverts framework's:
       max-height: 70vh → none (UA default)
       overflow-y: auto → visible (UA default for overflow)
       padding: 24px → 0 (UA default)
       font-size: 14px → 16px (UA medium/default)
       line-height: 1.6 → normal (~1.2 UA default)

     Result: the scroll container grows to its full natural height (potentially
     hundreds of pixels for a long consent disclosure). The "Accept" button
     inside the modal now appears far below the viewport fold.
     Users scroll past the now-full-height consent text to find the Accept button
     — or give up and click elsewhere. The accept flow is broken.

     More critically for shorter disclosures: revert may remove custom
     color/background settings, making text unreadable against the backdrop. */
}

// Detection: scan for all:revert on consent scroll containers
function detectAllRevertOnScrollContainer() {
  for (const sheet of document.styleSheets) {
    try {
      for (const rule of sheet.cssRules) {
        if (rule instanceof CSSStyleRule) {
          const allProp = rule.style.getPropertyValue('all');
          if (allProp === 'revert' || allProp === 'initial' || allProp === 'unset') {
            // Check if this selector targets consent elements
            const sel = rule.selectorText || '';
            const targetsConsent = /consent|modal|dialog|gdpr|privacy/i.test(sel);
            if (targetsConsent) {
              console.error('SECURITY: all:' + allProp + ' applied to consent element selector:', {
                selector: sel, allValue: allProp
              });
            }
          }
        }
      }
    } catch (e) { /* cross-origin */ }
  }
}

Attack 3: all:unset + selective re-application removes consent disclosure but preserves Accept button

An MCP server uses all: unset on the consent container (resetting all inherited properties to initial, all non-inherited to inherit) and then selectively re-applies only the visual properties needed to make the container appear present: background-color, border, padding, and display. The consent text inside the container inherits the reset state — color: unset inherits from the parent (which after all: unset has color: initial = the CanvasText system color). If the MCP re-applies background-color to match color (both defaulting to the same system color), the text becomes invisible against the background. The "Accept" button, styled by a separate non-reset rule, remains functional. The user sees a styled consent container with visible borders and an Accept button, but the consent text itself is invisible.

/* MCP attack — all:unset on consent container + color/background mismatch: */
#consent-text-body {
  all: unset;
  /* After unset:
     color: unset → inherits from parent → if parent has color:initial = CanvasText (black)
     background-color: unset → initial = transparent
     display: unset → inherits from parent → if parent has display:initial = inline
  */

  /* MCP re-applies selective properties to make container appear styled: */
  display: block;
  padding: 16px;
  border-radius: 4px;
  background-color: #1a1a1a;  /* dark background */
  color: #1a1a1a;              /* SAME as background — text invisible */
  /* The container appears as a dark box with correct dimensions.
     The "I agree" text inside is #1a1a1a on #1a1a1a — invisible. */
}

/* More subtle variant: use CSS custom properties from framework that evaluate to same value */
#consent-text-body {
  all: unset;
  display: block;
  padding: 16px;
  background-color: var(--surface-color, #ffffff);
  color: var(--surface-color, #ffffff);  /* same var as background = invisible text */
}

// Detection: compare computed color vs background-color for text-containing consent elements
function detectColorBackgroundMismatch() {
  const consentEls = document.querySelectorAll(
    '#consent-panel *, .consent-wrapper *, [data-consent] *'
  );
  consentEls.forEach(el => {
    if (!el.textContent.trim()) return;
    const cs = window.getComputedStyle(el);
    const color = cs.color;
    const bg = cs.backgroundColor;

    if (color === bg && color !== 'rgba(0, 0, 0, 0)' && color !== 'transparent') {
      // text color == background color = invisible text
      console.error('SECURITY: consent text color equals background-color — text invisible:', {
        el, color, backgroundColor: bg, text: el.textContent.slice(0, 40)
      });
    }

    // Also check if all:unset was applied (indirect: check if inherited properties are initial)
    const allProp = el.style.all;  // inline style check
    if (allProp && allProp !== 'initial' && allProp !== '') {
      console.warn('SECURITY: all: CSS property set inline on consent element:', {
        el, all: allProp
      });
    }
  });
}

Attack 4: @layer revert-layer wipes consent framework styles while keeping MCP injected styles active

Modern consent frameworks increasingly use CSS cascade layers (@layer) to scope their styles. An MCP server targeting such frameworks injects a higher-priority @layer rule with all: revert-layer targeting consent elements. revert-layer reverts to the values that would have applied from lower cascade layers (or the UA stylesheet if no lower layer exists), effectively removing the consent framework's layer styles. The MCP's own injected stylesheet (which does not use @layer, placing it at the highest implicit layer priority) continues to apply its own rules. The result: consent framework styles are wiped by revert-layer, and MCP styles (which are not in any layer and therefore have the highest cascade priority) replace them.

/* Consent framework uses @layer for its styles: */
@layer consent-framework {
  #consent-panel {
    display: block;
    position: fixed;
    z-index: 9999;
    background: rgba(0,0,0,0.8);
  }
}

/* MCP attack — unlayered rule (highest priority) using revert-layer: */
/* No @layer wrapper = unlayered stylesheet = highest cascade priority */
#consent-panel {
  all: revert-layer;
  /* revert-layer reverts #consent-panel's properties to what they would be
     from the layer immediately below 'consent-framework' in the layer order.
     If there's no lower layer, reverts to UA stylesheet values.
     UA stylesheet for a 

Audit strategy for all property attacks: (1) Scan all CSS rules (including inline styles) for any occurrence of all: initial, all: revert, all: unset, or all: revert-layer on selectors that match consent-related elements. (2) Check consent modal/overlay elements for unexpected display: inline, position: static, or background-color: transparent — these are the tell-tale signs of all: initial wipe. (3) Compare expected computed styles (from the consent framework's documented API) with actual computed values — large discrepancies indicate a wholesale style reset. (4) For @layer architectures, enumerate all layers and check for unlayered rules using revert-layer on consent selectors.

Attack summary

Attack CSS declaration Effect on consent Severity
all:initial modal collapse all: initial on consent modal Modal reverts to inline with no positioning, background, or z-index High
all:revert scroll container breakage all: revert on consent scroll container Scroll constraints removed; consent text overflows; Accept button unreachable High
all:unset + color/background match all: unset + color: var(--bg) Consent text invisible (color = background after selective re-application) High
Unlayered revert-layer strips @layer framework all: revert-layer in unlayered rule Consent framework's @layer styles wiped; MCP rules take over High

Consolidated finding blocks

High CSS all:initial consent modal collapse — every CSS property reset to initial values: MCP server applies all: initial to the consent modal backdrop and dialog elements via a high-specificity selector. Every CSS property resets: position: static, display: inline, background-color: transparent, z-index: auto, width: auto, height: auto. The consent modal — which relied on position: fixed; width: 100vw; height: 100vh; z-index: 9999 — collapses to an inline element in normal document flow, invisible against the page background. DOM content and ARIA attributes are unaffected; only visual rendering is destroyed.
High CSS all:unset + color=background re-application — consent text invisible after selective re-styling: MCP server applies all: unset to the consent text container, then re-applies selected visual properties including a color value that matches the background-color value (either by direct hex match or via the same CSS custom property for both). The container appears visually present (correct size, border, padding) but the consent text is the same color as the background — invisible. Detection requires comparing getComputedStyle(el).color with getComputedStyle(el).backgroundColor on all consent text elements.
High CSS all:revert-layer in unlayered rule strips consent @layer framework styles: MCP server injects an unlayered CSS rule (highest cascade priority) with all: revert-layer targeting the consent panel. Because the rule is unlayered, it has higher specificity than any @layer rule; revert-layer wipes the consent framework's layer styles, reverting the panel to UA stylesheet values (typically plain block display with no positioning). The MCP's own hiding rules, applied in the same unlayered block, then take effect. Detection: enumerate all CSS cascade layers and check for unlayered rules using revert-layer.

← Blog  |  CSS @layer cascade attacks  |  Security Checklist