MCP server CSS overflow-clip-margin-inline security: inline axis clip without scrollbar, RTL consent clipping, start-edge-only silencing, and inline paint outside container

Published 2026-09-25 — SkillAudit Research

The CSS property overflow-clip-margin-inline is the inline-axis logical variant of overflow-clip-margin. It controls how far content is allowed to paint outside an element's border box along the inline axis when overflow: clip is active. In standard horizontal writing modes (writing-mode: horizontal-tb), the inline axis is horizontal — it is the direction text flows within a line, left-to-right for direction: ltr and right-to-left for direction: rtl. In vertical writing modes (writing-mode: vertical-rl), the inline axis becomes vertical.

Unlike overflow: hidden, which creates a scroll container that users can interact with, overflow: clip performs hard clipping with no scroll container formation. There is no scrollbar, no programmatic scrollLeft access, and no user-interaction path to reveal the clipped content. When overflow-clip-margin-inline: 0px is set, the clip boundary sits exactly at the border box edge on the inline axis. Content that extends past that boundary is painted over and cannot be accessed by the user.

Why inline axis matters for consent: Most consent text flows horizontally in LTR languages. Setting overflow-clip-margin-inline: 0px on a container with a fixed width shorter than the text line length silently clips the end of each line — including the acceptance clause that often appears in the final paragraph. Unlike overflow: hidden (which a user could scroll), overflow: clip leaves no affordance. Audit tools checking textContent see the full text; only the visual is suppressed.

Attack 1: overflow:clip + overflow-clip-margin-inline:0px — horizontal consent truncation

The most direct attack sets overflow: clip with overflow-clip-margin-inline: 0px on a container whose inline size (width in horizontal-tb) is smaller than the consent text line length. The end of each line is clipped at the border edge without a horizontal scrollbar. This differs from white-space: nowrap + overflow: hidden (which would at least create a scroll container) — here the clipped content is completely inaccessible.

/* Narrow container clips end of each consent line */
.consent-block {
  width: 240px;           /* Inline size: 240px */
  overflow: clip;
  overflow-clip-margin-inline: 0px;  /* Hard clip at inline edges */
  white-space: nowrap;               /* Forces single-line; end is clipped */
}
/* Example consent line:
   "By proceeding you agree to our Terms of Service and Privacy Policy."
   Visible at 240px: "By proceeding you agree to our Terms of Se..."
   Clipped (invisible): "rvice and Privacy Policy."
   DOM textContent: "By proceeding you agree to our Terms of Service and Privacy Policy."
   getBoundingClientRect: width=240 — no indication of overflow.
   No horizontal scrollbar. No scroll affordance. Clipped text unreachable. */

/* Detection */
function detectInlineClip(root) {
  const findings = [];
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
  let el;
  while (el = walker.nextNode()) {
    const cs = window.getComputedStyle(el);
    if (cs.overflow !== 'clip' && cs.overflowX !== 'clip') continue;
    const clipMarginInline = cs.getPropertyValue('overflow-clip-margin-inline');
    const parsed = parseFloat(clipMarginInline);
    if (el.scrollWidth > el.clientWidth + 4) {
      findings.push({
        element: el,
        scrollWidth: el.scrollWidth,
        clientWidth: el.clientWidth,
        overflowClipMarginInline: clipMarginInline,
        note: 'overflow:clip + inline axis overflow — consent text may be horizontally clipped without scrollbar',
      });
    }
  }
  return findings;
}

Attack 2: overflow-clip-margin-inline-start:0px — RTL start-edge silencing

In a right-to-left (direction: rtl) document or element, the inline-start edge is on the right side of the box and the inline-end edge is on the left. Setting overflow-clip-margin-inline-start: 0px clips the inline-start edge — the right side in RTL — which is where text begins and where the most important words of a sentence typically appear. An MCP server rendering consent for RTL users can use this to clip the beginning of the consent sentence: the acceptance phrase "I agree to" and the service name may be entirely on the clipped right side.

/* RTL mode: inline-start = right side. Clip the right side. */
.rtl-consent-dialog {
  direction: rtl;
  overflow: clip;
  overflow-clip-margin-inline-start: 0px;   /* Clips right side (rtl start) */
  overflow-clip-margin-inline-end: 80px;    /* Allows 80px left-side overflow */
}
/* In RTL: text reads right-to-left.
   Inline-start = right.
   The sentence beginning (right side) is clipped.
   The sentence ending (left side) has 80px allowance.
   For Arabic/Hebrew consent text, the critical framing words are on the right.
   The user sees only the tail of the consent clause. */

/* Subtler variant: large inline-end allowance makes the container appear wider */
.rtl-subtle {
  direction: rtl;
  width: 200px;
  overflow: clip;
  overflow-clip-margin-inline-start: 0px;
  overflow-clip-margin-inline-end: 150px;
  /* Visually: the container clips the right (start) but allows content
     to paint 150px past the left (end) border. The painted area looks
     like it spans 350px total, masking the fact that the start is clipped.
     A developer checking the rendered width sees 350px of painted content
     and may not notice the start-edge clip. */
}

Attack 3: large overflow-clip-margin-inline painting consent outside scroll container

The inverse attack exploits a large overflow-clip-margin-inline value on an ancestor to allow consent-masking content to paint far outside the scroll container boundary. The consent element itself is positioned inline-end of the viewport using negative margins or transforms. The ancestor's large inline clip margin allows this out-of-viewport content to paint without triggering scroll, keeping the DOM appearance valid while placing the consent text in an area the user never sees.

/* Ancestor allows large inline overflow */
.dialog-wrapper {
  width: 400px;
  overflow: clip;
  overflow-clip-margin-inline: 200vw; /* Allow content to paint 200vw past edges */
}

/* Consent positioned far to the inline-end (right in LTR) */
.hidden-consent {
  position: relative;
  left: 500px;   /* 500px past the wrapper's right edge */
  width: 300px;
  /* With overflow-clip-margin-inline:200vw on the ancestor, this element's
     paint is permitted (200vw >> 500px on typical screens).
     The consent text renders in the DOM, has valid computed styles,
     and getBoundingClientRect returns rect.left ≈ 900px (off-screen right).
     The user never sees it without horizontally scrolling the page itself. */
}

/* Detection: check elements whose bounding rect is outside viewport inline axis */
function detectOffscreenInlineContent(root) {
  const findings = [];
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
  const vw = window.innerWidth;
  let el;
  while (el = walker.nextNode()) {
    const rect = el.getBoundingClientRect();
    if (rect.width === 0 || rect.height === 0) continue;
    if (rect.left > vw + 50 || rect.right < -50) {
      const cs = window.getComputedStyle(el);
      const text = el.textContent.trim();
      if (text.length > 20) {
        findings.push({
          element: el,
          rect: { left: rect.left, right: rect.right },
          viewportWidth: vw,
          note: 'Element with text content positioned fully outside inline viewport — consent may be hidden off-screen',
        });
      }
    }
  }
  return findings;
}

Attack 4: overflow-clip-margin-inline in vertical writing mode — blocking vertical scroll consent

In writing-mode: vertical-rl, the inline axis is vertical — text flows top to bottom within a line, and lines advance from right to left. The inline axis is now the vertical dimension. Setting overflow-clip-margin-inline: 0px with overflow: clip in a vertical writing mode container clips the top and bottom of the element along the inline (vertical) axis. Consent text that overflows vertically in a vertical writing mode container is clipped without a scrollbar.

/* Vertical writing mode: inline = vertical, block = horizontal */
.vertical-consent {
  writing-mode: vertical-rl;
  height: 120px;             /* Inline size in vertical-rl = height dimension */
  overflow: clip;
  overflow-clip-margin-inline: 0px;
  /* Consent text at 480px inline extent (vertical).
     Only first 120px visible (top portion of vertical text).
     The remaining 360px is clipped inline-end (bottom).
     No vertical scrollbar — overflow:clip has no scroll container.
     The acceptance clause at the bottom of the vertical text column is never seen.

     Compounded with overflow-clip-margin-block: 100vw on the same element:
     block overflow (horizontal, right-to-left in vertical-rl) is allowed to spill.
     The element appears to have large horizontal content, distracting from
     the fact that vertical (inline) content is clipped. */
}

/* Audit note: when checking overflow:clip elements, always check both axes.
   An auditor checking overflow-clip-margin-block may miss -inline.
   Check all four longhands individually:
     overflow-clip-margin-block-start
     overflow-clip-margin-block-end
     overflow-clip-margin-inline-start
     overflow-clip-margin-inline-end */
function detectFullClipMarginAudit(el) {
  const cs = window.getComputedStyle(el);
  if (cs.overflow !== 'clip' && cs.overflowX !== 'clip' && cs.overflowY !== 'clip') return null;
  return {
    blockStart: cs.getPropertyValue('overflow-clip-margin-block-start'),
    blockEnd: cs.getPropertyValue('overflow-clip-margin-block-end'),
    inlineStart: cs.getPropertyValue('overflow-clip-margin-inline-start'),
    inlineEnd: cs.getPropertyValue('overflow-clip-margin-inline-end'),
    shorthand: cs.getPropertyValue('overflow-clip-margin-inline'),
    writingMode: cs.writingMode,
    direction: cs.direction,
    scrollWidth: el.scrollWidth, clientWidth: el.clientWidth,
    scrollHeight: el.scrollHeight, clientHeight: el.clientHeight,
  };
}

Summary

AttackMechanismSeverityDetection method
HIGHHorizontal inline clip truncation
overflow:clip + overflow-clip-margin-inline:0px on narrow container with nowrap text End of each consent line clipped; acceptance clause on last line invisible; no scrollbar Check overflow:clip elements where scrollWidth > clientWidth
HIGHRTL start-edge silencing
direction:rtl + overflow-clip-margin-inline-start:0 clips right side (sentence beginning) Critical consent framing words at start of RTL sentence clipped; tail of clause visible but context missing Flag overflow-clip-margin-inline-start on direction:rtl elements; check right-side paint boundary
MEDIUMLarge inline clip margin outside scroll container
overflow-clip-margin-inline:200vw allows consent to paint far off-screen inline Consent has valid DOM position and styles; rect.left > viewport width; user never sees it Check elements with text content where rect.left > window.innerWidth + 50
MEDIUMVertical writing mode inline-axis clip
writing-mode:vertical-rl + overflow:clip + overflow-clip-margin-inline:0 Consent text clipped vertically (inline axis in vertical mode); bottom acceptance clause invisible Audit all four clip-margin longhands; check writing-mode on overflow:clip elements

See also: CSS overflow-clip-margin-block security for the block-axis variant and CSS overflow-clip-margin security for the full shorthand attack surface. Related: CSS overflow:clip for the base clipping mechanism.

SkillAudit audits all four overflow-clip-margin longhands in its consent CSS analysis, checking both LTR and RTL writing modes. Start a free scan.