Security Guide
MCP server CSS aspect-ratio security — container dimension oracle, cqw/cqh ratio fingerprint, aspect-ratio DoS on dynamic content, ratio constraint layout injection
CSS aspect-ratio (Chrome 88+, Firefox 89+, Safari 15+) constrains an element to a fixed width-to-height ratio. When combined with container queries and percentage sizing, it becomes a dimension oracle: a probe element in a container-type:size host container has its cqw and cqh values resolve from the container's layout dimensions, which the probe's getBoundingClientRect then reports. Beyond measurement, an injected aspect-ratio on content-sized host elements degrades them to fixed-proportion boxes that break dynamic content rendering — a DoS attack that requires only a CSS injection, no DOM manipulation.
How CSS aspect-ratio works
The aspect-ratio property sets a preferred width-to-height ratio for an element. If only one dimension is explicitly set, the browser computes the other from the ratio. If the element is content-sized (no explicit width or height), aspect-ratio sets a preferred ratio that the browser uses during intrinsic size calculation. This means aspect-ratio: 1/1 on a width: 100% element gives it a height equal to its container's width.
Attack 1: container dimension oracle via aspect-ratio + percentage sizing
A probe element with width: 100%; aspect-ratio: 1/1 inside a block container has its height set to equal the container's width. Reading the probe's getBoundingClientRect().height returns the container's width. This is a height-from-width axis-swap (similar to the writing-mode axis-swap) that can be combined with a direct width read to extract both container dimensions:
// Read a host container's width AND height from a probe child
const probe = document.createElement('div');
probe.style.cssText = `
width: 100%;
aspect-ratio: 1/1;
position: absolute;
top: -9999px;
left: -9999px;
visibility: hidden;
`;
hostContainer.appendChild(probe);
const rect = probe.getBoundingClientRect();
const containerWidth = rect.width; // width:100% gives container width directly
const containerHeight = rect.height; // aspect-ratio:1/1 makes height = container width
// → Wait: this gives width twice. Need a different approach for height:
// To get container HEIGHT: use height:100% + aspect-ratio
probe.style.height = '100%';
probe.style.width = 'auto';
// probe.getBoundingClientRect().width now = container height × (aspect W / aspect H)
// With aspect-ratio:1/1: probe width = container height
const containerH = probe.getBoundingClientRect().width;
probe.remove();
This two-probe technique extracts both width and height of any block container that the MCP server can inject a child into, without calling getBoundingClientRect on the container itself — useful when the container's element reference is not directly accessible to the MCP server's script context.
Attack 2: cqw/cqh container query unit fingerprint
Inside a container-type: size element, the container query units cqw (1% of container width) and cqh (1% of container height) resolve to pixel values from the container's layout dimensions. An MCP server can probe these units by injecting an element with width: 100cqw; height: 100cqh — the element's pixel dimensions then directly reveal the container's dimensions:
/* MCP server injects inside a container-type:size host component */
.mcp-cq-probe {
position: absolute;
width: 100cqw;
height: 100cqh;
visibility: hidden;
pointer-events: none;
}
/* probe.getBoundingClientRect(): width = container width, height = container height */
This attack requires the host to have already declared container-type: size on the target element — which many modern component libraries do for responsive component styling. The probe reads both dimensions in one CSS rule with no arithmetic needed. Unlike the aspect-ratio probe which requires two measurements, the cqw/cqh probe is a direct read of both dimensions simultaneously.
Framework exposure: React, Vue, and Angular component libraries that implement container-query-based responsive components commonly declare container-type: size on card, panel, and layout components. The cqw/cqh fingerprint works against any of these. The CSS container queries inline security reference documents the cqi/cqb variant for inline-size containers.
Attack 3: aspect-ratio DoS on content-sized host elements
CSS aspect-ratio can be applied to any element. When injected on a host element that is content-sized — a chat bubble, a product description card, an accordion panel — it overrides the element's natural height with a fixed proportion of its width. If the host element's content is tall relative to its width, the ratio constraint collapses the visible area, hiding content:
/* MCP server injects — destroys content-height layout of host chat bubbles */
.message-bubble, .chat-message, [data-message], .msg {
aspect-ratio: 3/1 !important;
overflow: hidden;
}
/* Result: each chat message is capped at 1/3 of its width in height
regardless of content length. Long messages are truncated.
The !important overrides host height rules. */
This is a user-visible degradation attack: the chat interface looks broken, messages are cut off, and users may believe the application is malfunctioning. The attacker can time this degradation to coincide with delivery of a malicious message that the truncation hides from view while the visible portion appears legitimate.
Attack 4: ratio constraint layout displacement for click hijacking
In a flex or grid layout, injecting an aspect-ratio constraint on one child forces its siblings to reflow to fill the remaining space. If the MCP server controls one flex child (its own injected element) and sets a very large aspect-ratio on it, it can predictably push host UI elements (buttons, navigation items, form controls) to specific screen positions, enabling click hijacking against those moved elements without knowing their original position.
/* MCP server injects a flex child with oversize aspect-ratio
into a host flex nav row — displaces host nav items */
.mcp-flex-injected {
aspect-ratio: 20/1; /* very wide ratio */
flex-grow: 1; /* takes all available width */
}
/* Remaining flex siblings (host nav items) are pushed off-screen or
compressed to zero width, then host overflow:visible shows them at
predictable coordinates the MCP server can target */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| aspect-ratio dimension oracle | Ability to inject child into host container | Container width + height without direct reference | MEDIUM |
| cqw/cqh container fingerprint | Host uses container-type:size | Both container dimensions in one CSS probe | MEDIUM |
| aspect-ratio DoS on content elements | CSS injection on host element selectors | Content truncation, UI degradation | HIGH |
| Ratio constraint layout displacement | MCP element in shared flex/grid with host elements | Predictable repositioning of host UI for click hijacking | HIGH |
Defences
- CSP
style-srcblocks injection of all aspect-ratio-based attack rules. - Use
contain: layout sizeon security-critical containers to prevent injected children from affecting the container's layout.contain:sizeprevents a container from being sized by its children — and prevents child probes from extracting the container's dimensions via percentage-based sizing. - Avoid
container-type: sizeon elements that untrusted content can inject children into. The cqw/cqh fingerprint requires container query context; if the MCP server's injection target does not havecontainer-type: size, the cqw/cqh attack fails. - Set explicit
min-heightormin-contentheights on chat/card components with!importantdeclarations on the host's own stylesheet that the MCP server's specificity cannot overcome. This limits the DoS damage from an injectedaspect-ratio. - Audit MCP server CSS for flex/grid layout injection. SkillAudit flags MCP elements sharing a flex or grid context with host UI elements and applying
flex-grow,grid-column, oraspect-ratioin combinations that could displace sibling elements.
SkillAudit findings for this attack surface
aspect-ratio with !important on broad selectors matching host content elements (chat messages, cards, descriptions)aspect-ratio + flex-grow:1 displacing host sibling elements from their expected positionswidth:100cqw; height:100cqh inside a container-type:size host element, reading dimensions via getBoundingClientRectwidth:100%; aspect-ratio:1/1 or height:100%; aspect-ratio:1/1 injected into host container, reading height as container width proxyRelated: CSS container queries inline security covers the cqi/cqb unit variant. CSS contain security documents contain:layout/size as both an attack surface and a defence. CSS Grid layout security covers the grid-track-displacement attack in shared grid contexts.