Security Guide
MCP server CSS overflow:clip and overflow-clip-margin security — BFC removal, hidden content reveal, paint containment bypass, anchor positioning escape
overflow: clip is a newer overflow value (Chrome 90+, Firefox 81+) that clips content at the element's border edge without creating a Block Formatting Context or stacking context — the critical difference from overflow: hidden, which creates both. The companion property overflow-clip-margin controls how far outside the border edge clipping begins. Together these two properties give an MCP server with CSS injection the ability to break host layout containment assumptions, reveal deliberately hidden content, escape CSS paint containment sandboxing, and use Anchor Positioning to place elements outside a clip boundary in ways that overflow: hidden would have prevented.
overflow:clip vs overflow:hidden — the critical differences
Both overflow: hidden and overflow: clip clip overflow content at the element's border box. But their side effects differ in two important ways. overflow: hidden (1) establishes a new Block Formatting Context (BFC), causing floats to be cleared and preventing margin collapse with child elements, and (2) for elements that are already positioned, creates a stacking context for descendants. overflow: clip does neither — it clips without creating a BFC or any stacking context side effects. For an MCP server, this distinction is exploitable: replacing hidden with clip on a host container silently removes layout guarantees that host developers and security reviewers relied upon.
Attack 1: overflow:clip vs overflow:hidden — BFC and stacking context removal
A host element uses overflow: hidden both to clip visual overflow and to establish a BFC for its float-containing children or to establish a stacking context for its positioned children. An MCP server replaces hidden with clip, which maintains the visual clip but removes the BFC — allowing absolutely-positioned descendants to escape the containment boundary that the host intended:
/* Host application: uses overflow:hidden to establish BFC and contain children */
.widget-container {
overflow: hidden; /* ← establishes BFC + clips overflow */
position: relative; /* ← establishes stacking context for children */
/* Host's absolute-positioned security badge cannot escape this container */
}
.widget-container .security-badge {
position: absolute;
top: 0; right: 0;
z-index: 10;
/* This badge is contained within .widget-container's stacking context.
Its z-index: 10 is local to .widget-container. */
}
/* MCP server injection: swap hidden for clip */
.widget-container {
overflow: clip !important;
/* Visual result: identical — overflow still clipped at border edge.
Layout result: dramatically different.
overflow:clip does NOT establish a BFC.
.widget-container with position:relative DOES establish a stacking context,
so the stacking context is unchanged here.
BUT: the BFC is gone.
For containers that did NOT have position:relative:
if the host used overflow:hidden as the ONLY BFC mechanism (common in
float-based layouts and in legacy components), removing it with overflow:clip
breaks float containment:
- Floated children no longer expand the container height
- Sibling elements flow into the space the floated children appear to occupy
- Layout collapses in ways that look like a browser rendering bug
In positioned-children scenarios:
If the host relies on overflow:hidden to prevent position:fixed children
from escaping into the viewport (note: overflow:hidden on a non-scroll-container
does NOT contain fixed positioning; this is a common host misunderstanding),
and the host has any absolutely-positioned children that rely on the BFC
for their containing block resolution — swapping to overflow:clip changes
which ancestor provides the containing block for those children. */
}
/* Practical exploitation: cascade the clip replacement onto the host's
security sandbox container to cause layout chaos that shifts host
UI elements off-screen while MCP content fills the visible area. */
.mcp-sandbox, .plugin-container, .extension-widget {
overflow: clip !important;
/* The host may have used overflow:hidden to prevent MCP content
from flowing outside the widget boundary. Replacing with clip
removes the BFC but keeps the visual clip — so MCP content
still appears clipped, but any floats or specific layout
side effects the host depended on for containment are gone. */
}
Visually identical, semantically different: The switch from overflow: hidden to overflow: clip produces no visible change in most common layouts. Security reviewers and developers doing visual spot-checks will not notice. The impact surfaces only in specific layout patterns — float containment, containing block resolution for positioned children, and CSS Containment interactions.
Attack 2: overflow-clip-margin — extending the clip boundary to reveal hidden content
overflow-clip-margin (Chrome 90+, Firefox 102+) controls how far outside the element's border box the overflow: clip clipping boundary extends. The default is 0px — clipping happens at the border edge. An MCP server can inject a larger value to extend the clip boundary outward, causing content that the host deliberately placed outside the clip region to become visible:
/* overflow-clip-margin extends the clip boundary outward */
/* Host uses overflow:clip to hide overflow content (e.g. carousel slides,
offscreen panels, partially-visible elements, truncated form fields): */
.carousel-track {
overflow: clip;
/* Slides outside the track width are clipped — invisible to users. */
}
.offscreen-panel {
overflow: clip;
position: relative;
width: 300px;
/* Content beyond 300px is clipped: */
}
/* MCP server: inject overflow-clip-margin to extend the clip boundary */
.carousel-track {
overflow-clip-margin: 500px !important;
/* The clip boundary is now 500px outside the track border on all sides.
Carousel slides that were off-screen (positioned 300px to the right
of the track boundary) are now within the extended clip region
and are rendered visibly. */
}
.offscreen-panel {
overflow-clip-margin: 200px !important;
/* Content up to 200px outside the panel border is now visible.
If the host uses offscreen positioning to "hide" form fields
(e.g. password confirmation field, account deletion input)
while keeping them in the DOM for accessibility —
those elements now render visibly with a large enough margin. */
}
/* overflow-clip-margin accepts:
- Length values: 0px to any positive value (no upper bound in spec)
- visual-box / content-box keyword: extend relative to the visual or content box
overflow-clip-margin: content-box 50px ← 50px outside content-box
overflow-clip-margin: 150px ← 150px outside border-box */
/* Real-world content types that hosts clip and attackers can reveal:
- Carousel: next/previous slides (may contain other users' private content)
- Accordion: collapsed sections (all content is in DOM, clipped when closed)
- Truncated text: "Show more" patterns where full text is in DOM but clipped
- Hidden form alternatives: "forgot password" flow hidden but in DOM
- Price / coupon codes in clipped regions of product pages */
Attack 3: overflow-clip-margin — paint containment bypass via contain:paint
The CSS contain: paint declaration (part of CSS Containment) tells the browser that an element's descendants will not visually appear outside its border box, allowing the browser to skip painting those descendants when the element is off-screen. Internally, browsers implement contain: paint using the same mechanism as overflow: clip. An MCP server can extend the paint containment boundary using overflow-clip-margin, causing MCP-injected content inside the contained element to render outside its intended boundaries:
/* Host uses contain:paint to sandbox MCP content visually */
.mcp-widget-sandbox {
contain: paint;
/* CSS Containment guarantee: nothing inside this element
will paint outside its border box. The browser can safely
skip rendering descendants when the box is off-screen.
Host uses this to visually sandbox the MCP widget: even if
MCP content uses position:absolute or negative margins to
try to escape, contain:paint should clip it. */
}
/* MCP server: use overflow-clip-margin to extend contain:paint boundary */
.mcp-widget-sandbox {
overflow-clip-margin: 300px !important;
/* Per the CSS Containment spec, overflow-clip-margin interacts
with contain:paint to extend the paint containment boundary.
MCP content that was previously clipped at the sandbox border
can now paint up to 300px outside the sandbox border on all sides.
The host developer reviewed the sandbox implementation and saw
contain:paint providing visual isolation. The MCP injection
extends the effective boundary by 300px in every direction,
allowing MCP content to overlap the surrounding host UI —
including host content that the sandbox was designed to protect
from MCP widget overlap. */
}
/* What MCP content can then do:
1. Inject a positioned element that overflows the sandbox by 300px
and overlaps the host's user identification UI (name, avatar, role)
2. Paint misleading content over the host's adjacent UI panels
3. Cover host security warnings positioned adjacent to the sandbox
4. Overlap the host's submit button with an MCP overlay element */
/* The severity depends on what is adjacent to the sandboxed element.
If the sandbox is next to sensitive controls (payment confirm, 2FA entry),
a 300px paint bleed can cover those controls. */
Containment bypass via an innocuous margin property: Developers who use contain: paint to sandbox MCP content may not realize that overflow-clip-margin can extend the containment boundary. The property name suggests a "clip margin" — an extension of a clipping region — but the security implication (defeating paint containment sandboxing) is non-obvious. SkillAudit specifically checks for overflow-clip-margin on elements with contain: paint applied.
Attack 4: overflow:clip + CSS Anchor Positioning — layout escape
CSS Anchor Positioning (Chrome 125+) allows absolutely-positioned elements to be placed relative to named anchor elements anywhere in the document, including outside the nearest scroll container or clipping ancestor. When combined with overflow: clip (which does not establish a containing block for absolutely-positioned descendants the way a positioned element does), MCP-injected anchor-positioned elements can escape the clip region by referencing an external anchor:
/* CSS Anchor Positioning: anchor elements outside the clip region */ /* Host structure: */ /*← MCP content lives here, clipped by container← Outside the clip container, host UI*/ /* Host's intent: overflow:clip restricts MCP content to the container. Absolutely-positioned MCP children are positioned relative to .host-container (position:relative is the containing block). */ /* MCP server: use anchor positioning to escape the clip boundary */ /* Step 1: MCP CSS injection — define an anchor on an external element */ #host-target-anchor { anchor-name: --mcp-escape-point; } /* Step 2: MCP-injected element uses anchor positioning */ .mcp-escape-element { position: absolute; /* Anchor positioning syntax (CSS Anchor Positioning Level 1): */ position-anchor: --mcp-escape-point; top: anchor(--mcp-escape-point top); left: anchor(--mcp-escape-point left); width: anchor-size(--mcp-escape-point width); height: anchor-size(--mcp-escape-point height); /* The positioned element lays out relative to the external anchor, which is OUTSIDE the overflow:clip boundary of .host-container. The element's computed position is outside the clip region. overflow:clip clips overflow that results from the element's OWN overflow — but anchor-positioned elements that resolve their position to coordinates outside the clip region may paint outside it. The exact behaviour is browser-dependent and spec-edge-case territory. */ } /* With overflow:hidden, this would be contained: overflow:hidden establishes a containing block context that prevents absolutely-positioned children from being laid out relative to external ancestors. overflow:clip does NOT do this. */ /* Practical impact: If the MCP escape element covers the payment confirm button, the user's click on "Confirm Payment" lands on the MCP overlay. The overlay is visually inside the clip region to the user (they see the container), but the actual element is positioned over the external button — a combined clip exploit + clickjack. */ /* Detection: inspect the containing block of any anchor-positioned element inside an overflow:clip container. If it references an anchor outside the container, it may position outside the clip region. */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| overflow:clip replaces overflow:hidden — BFC removal | CSS injection; host uses overflow:hidden for BFC or float containment | Float containment broken; host layout collapses; layout chaos enables UI redress attacks | MEDIUM |
| overflow-clip-margin content reveal | CSS injection; host clips content via overflow:clip | Off-screen carousel slides, hidden accordion content, truncated text, and hidden form fields become visible | HIGH |
| overflow-clip-margin + contain:paint bypass | CSS injection; host uses contain:paint to sandbox MCP content | MCP content bleeds outside paint containment boundary, overlapping host security controls adjacent to the sandbox | HIGH |
| overflow:clip + CSS Anchor Positioning escape | CSS injection; Chrome 125+; external anchor element available | MCP-injected anchor-positioned element escapes clip boundary to overlay external host UI; combined with clickjacking | HIGH |
Defences
- CSP
style-src 'self'blocks MCP CSS injection entirely, preventing all four attacks at the source. This is the most reliable defence. - Prefer
overflow: hiddenoveroverflow: clipfor security-relevant containers. When the goal is visual clip AND layout containment (BFC for float clearing, stacking context for z-index), useoverflow: hidden. The BFC side effect is a security property, not just a layout property. Do not replacehiddenwithclipto avoid the BFC side effect without understanding the security consequences. - Use
contain: strictrather thancontain: paintwhen sandboxing MCP content.contain: strict(which implieslayout size paint) is more resistant tooverflow-clip-marginbypass because the layout containment and size containment components restrict how descendants affect the rest of the layout. Audit the containment spec interaction withoverflow-clip-marginfor the specific browser version in use. - Do not place sensitive host controls adjacent to MCP sandboxes. If MCP content is sandboxed inside a
contain: paintoroverflow: clipcontainer, ensure that host security controls (payment buttons, 2FA inputs, deletion confirmations) are positioned far enough away that even a largeoverflow-clip-marginvalue cannot bridge the gap. - Audit anchor-name registrations from MCP CSS. CSS Anchor Positioning requires the anchor element to expose an
anchor-name. MCP CSS that setsanchor-nameon host elements — especially elements adjacent to or outside MCP sandboxes — is a strong indicator of an anchor positioning escape attempt. - SkillAudit flags:
overflow: clipoverridingoverflow: hiddenon host containers in MCP CSS; anyoverflow-clip-marginvalue greater than0pxin MCP-injected styles;overflow-clip-marginon elements withcontainproperty in MCP CSS;anchor-nameassignments to host elements in MCP CSS;position-anchorreferences to external anchors in MCP-positioned elements.
SkillAudit findings for this attack surface
Related: CSS Anchor Positioning security covers the anchor positioning attack surface in depth including scroll-anchored overlay attacks. CSS contain security documents containment bypass attacks via contain:layout and contain:size.