Security Guide

MCP server CSS border-block-style security — nonesolid activation of dormant border widths, hidden specificity override, double style thickness amplification, JS mousedown injection

The CSS border-block-style property sets the line style of both block-axis borders at once. A border only renders when its style is not none — making border-block-style an on/off switch for any pre-set border-block-width. An attacker can pre-load a large transparent block border width, then activate it in a separate style injection that sets border-block-style: solid. The activation step alone appears harmless; only the combination produces a layout collapse.

CSS border-block-style — property overview

The border-block-style shorthand sets border-block-start-style and border-block-end-style simultaneously. Valid values are the standard <line-style> set: none, hidden, solid, dashed, dotted, double, groove, ridge, inset, outset. When set to none (the initial value), the border occupies no layout space regardless of the specified border-block-width. The combination of pre-set width and deferred style activation is the primary attack mechanism. Related properties: border-block-width, border-block shorthand.

Attack 1: nonesolid swap — activating a dormant border-block-width

When border-block-style is none, a browser renders no border regardless of the specified width. A pre-set border-block-width waits dormant. An attacker can inject the width in one step — appearing as an innocuous value like border-block-width: 3px (the user agent default for medium) — and then inject a second style rule that only changes border-block-style from none to solid. If the attacker can also inject a larger width in the same second step, the activation goes unnoticed because each injection targets a different property.

/* Two-phase activation: pre-load width, then activate with style change */

/* Phase 1: inject border-block-width (no visual effect — style is none) */
.consent-text {
  border-block-width: 60px !important; /* dormant — style is none */
  border-block-color: transparent !important;
}
/* Audit at this point: no border visible, no layout change. Clean. */

/* Phase 2 (later injection): activate style only */
.consent-text {
  border-block-style: solid !important; /* activates the dormant 60px width */
}
/* After Phase 2:
   border-block-start-width: 60px, transparent, solid → layout height consumed
   border-block-end-width:   60px, transparent, solid → layout height consumed
   content area: containerHeight - 120px → may be negative (clamped to 0)
   Audit checking only the style injection sees border-block-style: solid.
   No numeric threshold to check — the numeric value was set in a prior injection. */

Audits must check computed border width values at every point, not just at style-injection time. The activation step (setting border-block-style: solid) appears entirely harmless unless the scanner simultaneously reads the current computed border-block-start-width and border-block-end-width and checks whether enabling this style would collapse the content area.

Attack 2: hidden style — border-conflict-resolution override

The CSS border-conflict-resolution algorithm (used primarily in table cells but also in some non-table border cascade scenarios) assigns hidden style higher priority than other style values. More importantly for consent bypass: when a consent dialog uses the border-block shorthand to set a protective visible border (as a visual integrity signal), injecting border-block-style: hidden on a more-specific selector silently overrides it without changing the element's dimensions — the border area remains in the layout, but the border is now invisible. An audit expecting a visible protective border finds none.

/* Scenario: consent dialog uses a visible border as a visual integrity indicator */
.consent-dialog {
  border-block: 2px solid #ccc; /* visible protective border */
}

/* Attacker injects hidden on a more-specific selector — overrides the shorthand */
.consent-dialog.active {
  border-block-style: hidden !important; /* border disappears; width: 2px still in layout */
}

/* border-block-width is still 2px (not zero), so layout is unchanged.
   But the visible indicator is gone. The element no longer appears bordered.
   An audit checking visibility of the protective border now finds nothing. */

hidden vs none: Both suppress the border's visual rendering. The difference is in border-conflict resolution: hidden wins over all other styles in conflict resolution, while none loses to any other style. For consent dialog attacks, both values erase the border, but hidden is harder to override with a subsequent style injection attempting to restore the border.

Attack 3: double style — thickness amplification at minimum widths

The CSS double border style renders two lines separated by a gap. At 3px total width, it renders as three equal segments: 1px line, 1px gap, 1px line. At 5px, it renders as two 2px lines with a 1px gap. The effective visual rendering of a double border uses the same box-model space as the specified width — but the two-line rendering distributes that space differently. More critically: the CSS specification states that double borders must be at least 3px wide to render both lines. At widths below 3px, the browser may render only one line — making a double border at 1px or 2px behave as a narrower visible border while claiming full box-model width. An attacker injecting border-block-style: double on an element with a fixed width exploits this: the rendered visual width may differ from the box-model width, confusing audits that check rendered visual output rather than computed properties.

/* double style: two-line rendering distributes box-model space differently */
.consent-text {
  border-block-width: 7px !important;
  border-block-style: double !important;
  border-block-color: transparent !important;
}
/* At 7px: renders as 3px line + 1px gap + 3px line = two visible stripes
   But if color is transparent: no visible stripe, only layout space consumed.
   Box model: 7px consumed per side → 14px total block border height consumed.
   A "visible border" check finds no rendering. A width check finds 7px (below threshold).
   Both checks pass individually; only content-area check catches the collapse. */

/* Non-transparent double border with background matching: */
.consent-text {
  border-block-width: 9px !important;
  border-block-style: double !important;
  border-block-color: #1a1a1a !important; /* matches dark consent dialog background */
}
/* Renders as two 4px stripes with 1px gap, all background-colored.
   Appears as a subtle texture rather than a border.
   Content area reduced by 18px. No color-contrast violation on the border itself. */

Attack 4: JS mousedown injection of border-block-style: solid — at click time

At page load the consent container has border-block-style: none and a large pre-set border-block-width. Both are present in the stylesheet from the start — static analysis finds both. A mousedown listener on the approve button changes border-block-style from none to solid, activating the border for the duration of the button press. At mouseup, the style reverts to none. The consent text is obscured during the press but readable before and after.

/* Mousedown: activate pre-set border by switching style to solid at click time */
(function () {
  const CONSENT = '.consent-text, [data-consent-body]';
  const APPROVE = '.approve-btn, [data-action="allow"]';

  /* Pre-set the dormant width in a stylesheet rule:
     .consent-text {
       border-block-width: 80px;
       border-block-color: transparent;
       border-block-style: none; ← static analysis sees this and rates as safe
     } */

  function activateBorder() {
    document.querySelectorAll(CONSENT).forEach(el =>
      el.style.setProperty('border-block-style', 'solid', 'important')
    );
  }

  function deactivateBorder() {
    document.querySelectorAll(CONSENT).forEach(el =>
      el.style.setProperty('border-block-style', 'none', 'important')
    );
  }

  document.querySelectorAll(APPROVE).forEach(btn => {
    btn.addEventListener('mousedown', activateBorder,   { passive: true });
    btn.addEventListener('mouseup',   deactivateBorder, { passive: true });
    btn.addEventListener('mouseleave',deactivateBorder, { passive: true });
  });
})();

Two-property static analysis is required. The width property is set to an extreme value at page load. The style property is set to none at page load. Neither value alone is suspicious. The attack requires recognizing the combination: a large border-block-width present in the stylesheet alongside a mousedown handler that switches border-block-style to solid. A scanner must correlate these two signals.

Detection summary

HIGH Large computed border-block-start-width or border-block-end-width (>20px) with border-block-style: solid — immediately check content area collapse.
HIGH Large border-block-width set in any stylesheet rule where border-block-style: none in same or other rule — active dormant pattern; flag for mousedown injection correlation.
MEDIUM border-block-style: hidden on consent element — may be suppressing a visible protective border that the audit expected to find.
MEDIUM border-block-style: double on consent element with transparent border color and >5px width — two-line rendering covers same box-model space, color-match evasion.
MEDIUM Mousedown listener on approve button sets border-block-style of consent container — activates dormant width at click time.
/* Detection: check combined border-block-style + width for content collapse */
function checkBorderBlockStyle(consentEl) {
  const cs      = getComputedStyle(consentEl);
  const startW  = parseFloat(cs.getPropertyValue('border-block-start-width')) || 0;
  const endW    = parseFloat(cs.getPropertyValue('border-block-end-width'))   || 0;
  const style   = cs.getPropertyValue('border-block-start-style'); // 'none','solid','hidden',…
  const clientH = consentEl.clientHeight;
  const content = clientH - startW - endW;

  return {
    styleEnabled:     style !== 'none', // border is rendering
    contentCollapsed: content < 40 && clientH > 0,
    dormantWidth:     (style === 'none') && (startW + endW) > 40, // pre-loaded but dormant
    hiddenOverride:   style === 'hidden',
    doubleStyle:      style === 'double',
    totalBorderPx:    startW + endW,
    contentAreaPx:    content,
  };
}

SkillAudit checks border-block-style in combination with border-block-width — detecting dormant pre-loaded widths, hidden overrides on protective borders, and mousedown handler correlations that activate style at click time. Run a free audit →