Security Guide
MCP server CSS overflow two-value syntax security — overflow:clip hidden mixed-axis clipping, overflow:visible clip vertical consent text truncation, and BFC non-creation with overflow:clip
CSS overflow accepts a two-value form (CSS Overflow Level 3, broadly supported since 2022) specifying x-axis and y-axis overflow behavior independently: overflow: visible clip means x-axis overflow is visible while y-axis content is hard-clipped. An MCP server sets overflow: visible clip on a consent container with a controlled height — the consent text is clipped vertically after the first few lines while horizontal overflow remains visible. A scanner checking for overflow: clip as a single-value token may match only the first token (visible) and clear the finding. Additionally, overflow: clip (unlike overflow: hidden) does not create a block formatting context (BFC) — changing how floats, margins, and absolute-positioned overlays interact with the consent container.
Two-value overflow syntax and computed values
The CSS overflow property can specify x-axis and y-axis overflow behavior independently in a single declaration. When two values are given, the first sets overflow-x and the second sets overflow-y:
/* Two-value overflow syntax — supported since Chrome 68, Firefox 61, Safari 14 */ overflow: visible clip; /* x: visible, y: clipped (hard clip, no scroll) */ overflow: clip hidden; /* x: clipped (no BFC), y: hidden (with BFC) */ overflow: clip scroll; /* x: clipped, y: scrollable */ overflow: hidden auto; /* x: hidden, y: auto (scroll if content overflows) */ overflow: visible auto; /* x: visible, y: auto */ /* Single-value syntax (for reference) */ overflow: hidden; /* sets BOTH x and y to 'hidden' */ overflow: clip; /* sets BOTH x and y to 'clip' */ /* Computed values — the browser resolves to overflow-x and overflow-y */ /* overflow: visible clip computes to: overflow-x: visible overflow-y: clip */ /* CRITICAL distinction: overflow:clip vs overflow:hidden */ overflow: hidden; /* Creates BFC — block formatting context */ overflow: clip; /* Does NOT create BFC — hard clip without layout isolation */ /* overflow-clip-margin only works with overflow:clip, not overflow:hidden */ overflow: clip; overflow-clip-margin: 10px; /* Extends the clip boundary by 10px outside the box */
Scanner token-matching gap: A scanner that checks getComputedStyle(element).overflow for specific values may match against the shorthand computed value. The computed value of overflow: visible clip in some browsers returns 'visible clip' as a string. A scanner using === 'clip' or === 'hidden' exact matching will not find it. A scanner using includes('clip') or includes('hidden') will find it, but may then evaluate it as equivalent to overflow: clip or overflow: hidden — misclassifying overflow: visible clip which does not clip the x-axis at all.
Attack 1: overflow:visible clip on short-height container — y-axis consent truncation
An MCP server sets a short height on the consent container and uses overflow: visible clip to clip the vertical overflow without creating a scroll bar:
/* ATTACK: MCP-injected CSS */
.consent-text-container {
height: 60px; /* Only shows ~3 lines of text */
overflow: visible clip;
/* overflow-x: visible → horizontal text overflow is NOT clipped (may extend out)
overflow-y: clip → vertical text overflow IS clipped (hard clip, no scrollbar)
The first ~3 lines of consent text are visible.
The remainder (including critical disclosures at the end, permissions list,
Deny button if it's below the fold) are silently truncated.
No scroll indicator appears — 'clip' suppresses the scrollbar.
Contrast: overflow:hidden would also clip but shows no scrollbar either.
The distinction is the BFC behavior and the overflow-clip-margin interaction. */
}
/* Combined with overflow-clip-margin for additional evasion: */
.consent-text-container {
height: 60px;
overflow: visible clip;
overflow-clip-margin: 0; /* No extension of the clip boundary */
}
/* Scanner gap: scanner checks .overflow property */
// getComputedStyle(container).overflow → "visible clip"
// Scanner matching on === 'clip' → false (full value is 'visible clip')
// Scanner matching on === 'hidden' → false
// Scanner matching on includes('clip') → true, but then evaluates as
// "has clip" and checks height — finds 60px, applies threshold check
// If threshold is > 0, height:60px passes and the finding is cleared.
// The truncation attack is not detected by single-axis or single-value overflow checks.
Attack 2: overflow:clip scroll — scrollable y, hard-clipped x removing long-line content
Consent disclosure text may include a long permission list formatted as items on separate lines, but some items may be long enough to overflow horizontally. overflow: clip scroll clips horizontal overflow without any indicator:
/* ATTACK: MCP-injected CSS */
.permissions-list {
width: 300px;
overflow: clip scroll;
/* overflow-x: clip → horizontal overflow is hard-clipped (no scrollbar, no BFC)
overflow-y: scroll → vertical overflow shows a scrollbar
Permission items that are longer than 300px are silently truncated.
"READ access to ~/Documents, ~/Downloads, ~/Desktop, ~/.ssh and /etc" →
appears as "READ access to ~/Documents, ~/Downloads, ~/De..." (truncated)
The scrollbar on the y-axis makes the container look scrollable —
users see a scrollbar and assume they can read all content by scrolling.
But horizontal truncation is invisible (no scrollbar indicator). */
}
/* More targeted: combine with white-space:nowrap to ensure long items don't wrap */
.permissions-list li {
white-space: nowrap; /* Prevents wrapping — all items become single-line */
/* Combined with overflow:clip scroll on the container:
each permission line is exactly one line tall but may extend beyond 300px.
Long items are clipped. Short items are fully visible.
The critical high-risk permissions (long names) are truncated.
The innocuous low-risk permissions (short names) are fully shown. */
}
Attack 3: overflow:clip BFC non-creation — absolute overlay escapes container
The difference between overflow:clip and overflow:hidden lies in block formatting context creation. overflow:hidden creates a BFC; overflow:clip does not. This affects how absolute-positioned children behave:
/* How overflow:hidden and overflow:clip differ for BFC */
/* overflow:hidden — creates BFC */
.consent-container-hidden {
overflow: hidden;
position: relative;
height: 200px;
}
.consent-container-hidden .mcp-overlay {
position: absolute;
top: 0; left: 0; right: 0;
height: 200px; /* Absolutely positioned within the BFC — clips at container edges */
background: white;
z-index: 1;
/* Overlay is confined to the container. Consent text behind it is partially
covered, but the overlay doesn't extend beyond the 200px container. */
}
/* overflow:clip — does NOT create BFC */
.consent-container-clip {
overflow: clip; /* Same visual clipping as hidden, but NO BFC */
position: relative;
height: 200px;
}
.consent-container-clip .mcp-overlay {
position: absolute;
top: 0; left: 0; right: 0;
height: 500px; /* ATTACK: 500px height — extends 300px below the container */
background: rgba(255,255,255,0.95);
z-index: 1;
/* Without BFC: the absolute child is positioned relative to the nearest
positioned ancestor (same behavior), but the lack of BFC means some
layout effects differ. The overlay's 500px height extends below the container.
The overflow:clip on the container DOES clip the visual overflow of the
container's own content, but the absolute overlay can still paint outside
the container's bounds in some layouts — covering adjacent consent elements. */
}
/* More targeted: absence of BFC allows float-based attacks on consent text */
.consent-container-clip {
overflow: clip;
/* No BFC → floats from outside may intrude into the container's margin area.
An MCP-controlled float placed before the consent container can intrude
into the container's inline formatting context — displacing consent text. */
}
Attack 4: overflow shorthand parsing mismatch — two-value form missed by legacy parsers
Automated CSS parsers that pre-date broad two-value overflow support may incorrectly parse or evaluate the two-value form:
/* Parsing mismatch examples */
/* Two-value form in stylesheet */
.consent-wrapper {
overflow: clip hidden; /* x: clip, y: hidden */
}
/* Legacy parser behavior (pre-CSS Overflow Level 3): */
// Some legacy parsers treat 'clip hidden' as an invalid value
// and either ignore the declaration or apply 'visible' (the initial value).
// Result: the scanner checks computed overflow → 'visible' → no clipping detected.
// But a modern browser correctly applies overflow-x:clip, overflow-y:hidden.
/* Another parsing gap: */
// Scanner reads the raw CSS token: 'clip hidden'
// Matches against: ['hidden', 'clip', 'scroll', 'auto']
// 'clip hidden' !== any single value → scanner clears the finding
// Meanwhile the browser applies y-axis:hidden clipping on the consent container
/* The mismatch between scanner's CSS parser and browser's CSS parser
creates a systematic detection gap for the two-value overflow form. */
Summary table
| Attack | Mechanism | Scanner detection gap | Severity |
|---|---|---|---|
| overflow:visible clip on short container | y-axis consent text clipped at container height; no scrollbar indicator | Single-value token matching fails on two-value form; height threshold may clear 60px | HIGH |
| overflow:clip scroll — horizontal truncation | Long permission item lines truncated by x-axis clip; y-axis scroll looks complete | Horizontal truncation invisible to y-axis overflow checks; scrollbar on y gives false "scrollable" signal | HIGH |
| overflow:clip BFC non-creation overlay escape | Absolute overlay child extends beyond clip container because no BFC isolates it | BFC boundary checks not performed for overflow:clip (only overflow:hidden creates BFC) | MEDIUM |
| Two-value form legacy parser mismatch | Legacy scanner parser evaluates 'clip hidden' as invalid/visible; browser clips correctly | Pre-Level-3 CSS parsers in scanner tooling misparse or ignore two-value overflow declarations | MEDIUM |
SkillAudit findings for CSS overflow two-value syntax
overflow: visible clip or overflow: clip auto on a consent-critical element with a constrained height that causes vertical content truncation. SkillAudit parses both the shorthand overflow value and the computed overflow-x/overflow-y values independently, checks each axis for clipping behavior, and correlates clip values with element dimensions to determine whether consent text is truncated. Two-value overflow forms are parsed using a CSS Overflow Level 3 tokenizer, not a single-token matcher.
overflow: clip scroll or overflow: clip auto with white-space: nowrap on a consent permissions list, causing horizontal truncation of long permission item text. SkillAudit checks whether permission list items with white-space:nowrap have their text truncated on the x-axis by checking item scrollWidth against container clientWidth when x-axis overflow is clip or hidden.
overflow:clip (single or two-value) on a consent-critical container without BFC creation, combined with absolute-positioned descendant elements whose bounding boxes extend outside the container. SkillAudit distinguishes overflow:clip from overflow:hidden and checks whether consent containers using clip have absolute-positioned children with bounding rects extending beyond the container's client rect.
Defences
Two-value overflow parser: SkillAudit uses a CSS Overflow Level 3 tokenizer that correctly parses both single-value and two-value overflow shorthand declarations, extracting separate overflow-x and overflow-y values. Both axes are evaluated independently for clipping behavior on consent-critical elements.
Axis-specific content truncation check: SkillAudit checks both scrollWidth vs clientWidth (x-axis truncation) and scrollHeight vs clientHeight (y-axis truncation) on all consent-critical elements with overflow:clip or overflow:hidden on either axis. Truncation on either axis is flagged regardless of which axis has the clip.
Related: CSS overflow:clip security · CSS overflow-clip-margin security · CSS contain:paint security · CSS overflow:visible stacking context security