Security Guide

MCP server CSS border-block-width security — em-relative font-size coupling, calc(100% - 1px) height collapse, transparent invisible border consuming layout height, JS mousedown injection

The CSS border-block-width property sets the width of both block-axis borders (border-block-start-width and border-block-end-width) without affecting their color or style. Because border width consumes layout height in the box model, an extreme border-block-width can reduce a consent element's content area to zero, clipping all rendered text — while getBoundingClientRect() remains unchanged, textContent remains non-empty, and visibility remains visible. Several width-specification techniques make this attack resistant to numeric threshold checks.

CSS border-block-width — property overview

The border-block-width shorthand accepts the same values as any border-width property: keyword values (thin, medium, thick), length values with any unit (px, em, rem, vw, vh, % — though percentage is not valid for border widths in standard CSS), and calc() expressions. The em unit makes border-block-width proportional to the element's computed font-size, creating a coupling between injected font-size changes and effective border width. The property defaults to medium (typically 3px) when border-block-style is set without an explicit width. Related properties: border-block shorthand, border-block-color.

Attack 1: em-relative border-block-width — coupling to injected font-size

When border-block-width is set in em units, its resolved pixel value depends on the element's computed font-size. An attacker can split the attack across two properties: first inject font-size: 28px (appears to be a text-size styling change), then set border-block-width: 2.5em (appears to be a moderate em-relative border). The combination produces a 70px block border on both sides, consuming 140px of a 160px consent container — leaving 20px for content. Neither injection in isolation hits a simple numeric threshold.

/* Two-phase attack: font-size amplifies em-relative border-block-width */

/* Phase 1: inject font-size (may appear as a branding/style change) */
.consent-text {
  font-size: 28px !important;  /* "larger text for accessibility" */
}

/* Phase 2: set em-relative border-block-width */
.consent-text {
  border-block-width: 2.5em !important;  /* = 2.5 × 28px = 70px per side */
  border-block-style: solid !important;
  border-block-color: transparent !important; /* invisible border */
}

/* Result:
   font-size: 28px → 1em = 28px
   border-block-width: 2.5em = 70px per side
   Total border height: 140px
   If container height = 160px: content area = 160 - 140 = 20px ← clipped

   A scanner checking border-block-width > 50px (in px) misses this attack
   because the specified value is '2.5em' (not a resolved number).
   Correct detection: check RESOLVED pixel value via getComputedStyle:
   parseFloat(getComputedStyle(el).getPropertyValue('border-block-start-width'))
   → 70px ← this triggers the threshold check */

Always read resolved pixel values, not specified values. getPropertyValue('border-block-start-width') on the computed style returns the resolved pixel value regardless of the original unit. A scanner that reads the specified value from the stylesheet source (e.g., parsing CSS text) and sees 2.5em cannot determine if this is dangerous without also resolving the current font-size. Always use getComputedStyle() and parse the resulting pixel value.

Attack 2: calc(100% - 1px) — collapsing content to exactly one pixel

The calc() function in border-block-width accepts length expressions but not bare percentages. However, when the element has a fixed pixel height and the attacker knows that height, a calc() expression can be constructed to leave exactly 1px of content area. This is more precise than a large fixed pixel value and more resistant to threshold detection: a check for "border-block-width > 50px" might not flag a 79px border on an 80px element because the attacker knows the exact height and calculates the exact value needed.

/* Precision height collapse: leave exactly 1px of content area */

/* Assume attacker has measured clientHeight = 120px via JS */
const containerHeight = document.querySelector('.consent-text').clientHeight; // 120

/* Set border-block-width to consume (height - 1px), leaving 1px for content */
document.querySelector('.consent-text').style.cssText = `
  border-block-start-width: ${Math.floor((containerHeight - 1) / 2)}px !important;
  border-block-end-width:   ${Math.ceil((containerHeight - 1) / 2)}px !important;
  border-block-style: solid !important;
  border-block-color: transparent !important;
  overflow: hidden !important;
`;
/* At height=120px:
   border-block-start-width: 59px
   border-block-end-width: 60px
   content area: 120 - 59 - 60 = 1px ← text visible in 1px strip only
   BCR.height: 120px ← unchanged (border-box)
   textContent: non-empty ← unchanged

   Detection:
   contentArea = clientHeight - parseFloat(borderBlockStartWidth)
                              - parseFloat(borderBlockEndWidth);
   if (contentArea < 40) flag('near-zero content area'); */

Attack 3: transparent border-block-width — consuming layout height without visual signal

Setting border-block-style: solid and border-block-color: transparent combined with a large border-block-width collapses the element's content area while leaving no visible mark. The element appears to have no border — the transparent border is invisible to the eye. Only a DOM inspection that reads the computed border-block-start-width value and checks it against a threshold will detect the height consumption. This technique is particularly stealthy because transparent borders are a common layout pattern (used for hover effects, pseudo-element spacing, etc.) and may not trigger automated alerts.

/* Transparent block border: no visual signal, full height consumption */
.consent-container {
  border-block-start-width: 90px !important;
  border-block-end-width:   30px !important;
  border-block-style: solid !important;
  border-block-color: transparent !important; /* invisible */
}

/* What the user sees: a normal-looking dialog with no visible border
   What the box model shows:
   - clientHeight: 160px (assumed fixed height)
   - border-block-start: 90px transparent
   - border-block-end: 30px transparent
   - content area: 160 - 90 - 30 = 40px ← text wrapped into 40px strip
   - overflow: hidden clips remaining text beyond 40px

   At 16px font-size with line-height 1.5 = 24px per line:
   Only 1 line (24px) fits in 40px content area.
   A three-line consent text is reduced to one visible line.
   scrollHeight > clientHeight is detectable.

   Key: transparent border is the norm on some decorative elements.
   Flag only when border-block-width > 20px AND border-block-color is transparent
   AND scrollHeight > clientHeight (content is being clipped). */

Transparent borders are used legitimately. Some dialog designs use transparent block borders for spacing instead of padding (to preserve box-sizing behavior during hover transitions). A scanner must check for both a suspicious width threshold and an overflow condition (scrollHeight > clientHeight) to avoid false-positive alerts on legitimate transparent border spacing patterns.

Attack 4: JS mousedown injection of extreme border-block-width — at click time

At page load, the consent container has no block border — the element renders its full content height. A mousedown listener on the approve button immediately sets border-block-start-width to a value equal to the container's measured height, collapsing content to zero for the duration of the button press. At mouseup, the width is removed and the content reappears. The net effect: the consent text is visible before the user initiates the click, hidden during the press, and visible again after. Static analysis of stylesheets finds no border-block-width values at all.

/* Mousedown: inject extreme border-block-width at click moment */
(function () {
  const CONSENT = '.consent-text, [data-consent-body]';
  const APPROVE = '.approve-btn, [data-action="allow"]';

  function collapseContent() {
    document.querySelectorAll(CONSENT).forEach(el => {
      const h = el.clientHeight;
      el.style.setProperty('border-block-start-width', h + 'px', 'important');
      el.style.setProperty('border-block-start-style', 'solid',   'important');
      el.style.setProperty('border-block-start-color', 'transparent', 'important');
      el.style.setProperty('overflow', 'hidden', 'important');
    });
  }

  function restoreContent() {
    document.querySelectorAll(CONSENT).forEach(el => {
      ['borderBlockStartWidth','borderBlockStartStyle',
       'borderBlockStartColor','overflow'].forEach(p => el.style[p] = '');
    });
  }

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

Detection summary

HIGH Resolved border-block-start-width + border-block-end-width > 60% of element's clientHeight — content area critically reduced; verify scrollHeight > clientHeight.
HIGH Transparent border-block-color combined with border-block-width > 20px and scrollHeight > clientHeight — invisible border actively clipping content.
MEDIUM border-block-width in em units on an element with injected font-size — evaluate resolved pixel value, not the specified em value.
MEDIUM Available content area (clientHeight minus both block border widths) < 40px on a consent text element — one visible line or less of text.
MEDIUM Mousedown listener on approve button sets border-block-start-width or border-block-end-width style property of consent container at click time.
/* Detection: border-block-width content area collapse check */
function checkBorderBlockWidth(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 clientH  = consentEl.clientHeight;
  const scrollH  = consentEl.scrollHeight;
  const contentH = clientH - startW - endW;

  const startColor = cs.getPropertyValue('border-block-start-color');
  const isTransparent = /transparent|rgba\(0,\s*0,\s*0,\s*0\)/.test(startColor);

  return {
    contentAreaCritical:    contentH < 40 && clientH > 0,
    contentAreaPercentage:  clientH > 0 ? (contentH / clientH) : 1,
    transparentWithWidth:   isTransparent && startW > 20,
    contentClipped:         scrollH > clientH,
    totalBlockBorderWidth:  startW + endW,
  };
}

SkillAudit resolves all CSS length units to pixels when checking border-block-width — including em, rem, vh, and calc() expressions — via getComputedStyle() rather than parsing the specified stylesheet value. This catches font-size-coupled em attacks that fixed-pixel threshold checks miss. Run a free audit on your MCP server to check for block border width evasion.