Security Guide
MCP server CSS object-fit consent security — iframe viewport shift via object-position, 1px scaled render, and consent image cropping
CSS object-fit and object-position control how replaced content — iframes, images, videos — is sized and positioned within their display boxes. In MCP install flows that render consent text inside a sandboxed iframe or as a consent screenshot image, these properties can shift the rendered content away from the consent area entirely. An iframe with object-fit: none; object-position: -500px 0 shows only blank space. A 1px iframe scaled to fill a display box via object-fit: fill renders consent at single-pixel resolution. The iframe element itself is fully present in the DOM.
Context: when object-fit applies to consent in MCP install flows
Standard HTML consent containers (divs, paragraphs) are not replaced elements and are not affected by object-fit. However, some MCP installation interfaces use replaced elements to display consent:
- A sandboxed
<iframe>to render tool-provider HTML (containing consent terms) in isolation - An
<img>element displaying a screenshot or rendering of consent terms (a pattern in some SDK-generated install dialogs) - A
<video>or<canvas>element used to display animated consent terms
For any replaced element used to display consent, object-fit and object-position can manipulate which portion of the replaced content is visible within the element's display box.
Attack 1: object-fit: none + large negative object-position shifts iframe viewport (SA-CSS-OF-001)
When object-fit: none is set on an iframe, the iframe's intrinsic dimensions are used without scaling — the content is rendered at its natural size and the object-position property determines which portion of that content is visible within the display box. A large negative object-position value shifts the visible viewport of the iframe to a region that contains no consent text — typically a blank area to the left of or above the rendered content.
/* Attack: object-fit:none + object-position:-500px shifts iframe away from consent */
iframe.consent-frame {
width: 400px;
height: 200px;
object-fit: none; /* no scaling — intrinsic dimensions used */
object-position: -500px 0; /* shift viewport 500px left — blank space shown */
}
/* The iframe itself renders at 400x200px display size
The consent content in the iframe is rendered at its natural size
object-position:-500px means the display window into the iframe starts
at x=-500 in the iframe's coordinate space — well before the left edge
of any content. A white/background-colored blank area is shown.
Detection:
getComputedStyle(iframe).objectFit → "none"
getComputedStyle(iframe).objectPosition → "-500px 0px"
Flag: any replaced element containing consent with non-default object-position */
Critical attack vector: This attack is particularly difficult to detect without explicit checking of replaced element styles, because the iframe element is fully present in the DOM with its src attribute intact. Standard DOM auditors see a consent iframe; they do not check whether the viewport into it is pointing at blank space.
Attack 2: 1px iframe with object-fit: fill scales consent to unreadable resolution (SA-CSS-OF-002)
An iframe is given both a 1×1px native size via HTML attributes (width="1" height="1") and a large display box via CSS (width: 300px; height: 150px). With object-fit: fill, the replaced content is stretched to fill the display box regardless of aspect ratio — so the 1px×1px rendered surface is scaled up to 300×150px. The consent content is rendered at 1×1 pixel resolution and then pixelated across the display box. The result is a blurry, unreadable mosaic that technically contains all the consent text at the DOM level.
/* Attack: 1px iframe + object-fit:fill stretches to unreadable blur */
/* HTML: <iframe src="/consent" width="1" height="1" class="consent-iframe"> */
iframe.consent-iframe {
width: 300px; /* display box: large and visible */
height: 150px;
object-fit: fill; /* stretch 1x1 content to fill 300x150 — severe pixelation */
}
/* The iframe loads consent content at 1x1px resolution
object-fit:fill stretches this to 300x150px (300x upscale)
Result: consent text is present but rendered as a single-color blur
getBoundingClientRect(iframe) → { width: 300, height: 150 } — looks normal
iframe.width attribute → "1" — the tell
Detection: compare iframe HTML width/height attributes vs CSS display dimensions */
Attack 3: object-position: center 200% on consent screenshot image (SA-CSS-OF-003)
When consent text is displayed as a screenshot image (an <img> element containing a PNG/JPEG of consent terms), object-position controls which portion of the image is visible within the element's display box. Setting object-position: center 200% shifts the visible area to 200% down the image's height — well below the image content. A white or transparent area below the consent text is shown instead of the terms.
/* Attack: object-position:center 200% on consent screenshot shifts display below text */
img.consent-screenshot {
width: 100%;
height: 200px;
object-fit: none; /* don't scale — show crop */
object-position: center 200%; /* vertical position at 200% — below image content */
}
/* The image contains consent text in its upper portion
object-position:center 200% shifts the visible window to 200% of the image height
No image content exists at 200% height — browser shows the element background
(typically transparent or matching page background)
img.src → valid consent screenshot URL — DOM audit passes
img.naturalWidth/Height → full image dimensions — content audit passes
Only the viewport offset reveals the attack */
Image vs iframe: The image variant is harder to detect automatically because there is no accessible DOM tree within an <img> element — the consent text exists only as pixels in the image file. An auditor checking the DOM finds a valid image source; the consent text is only detectable via visual rendering or by reading the image file content.
Attack 4: object-fit: cover with small display box crops consent to center (SA-CSS-OF-004)
With object-fit: cover, the replaced content is scaled to cover the entire display box while maintaining aspect ratio — content outside the display box is clipped. When the display box is made very small (e.g., 80px × 40px) and the consent iframe or image is normal-sized, cover crops the content to show only the central portion of the replaced content. If the consent text is at the top of the iframe and the displayed crop shows only the middle of the install dialog, the consent is clipped from view.
/* Attack: object-fit:cover with small display box crops consent from view */
iframe.consent-frame {
width: 80px;
height: 40px;
object-fit: cover; /* scale to cover 80x40, crop remainder */
/* If the iframe's natural size is 400x600px:
The cover crop shows the center of the 400x600 content at scale
Top of iframe (consent text at y=0–100) is cropped out
Center of iframe (install button at y=250–350) is shown */
}
/* Evasion note: getBoundingClientRect reports 80x40px display
To a user, the element looks like a small widget showing the install button
The consent text at the top of the iframe content is entirely outside the crop
Detection: flag object-fit:cover on any replaced element containing consent */
Findings summary
Defences
Audit object-fit and object-position on replaced elements: Enumerate all <iframe>, <img>, and <video> elements in the install flow. For any that contain or reference consent content, check computed object-fit and object-position values. Flag any non-default values (object-fit other than fill, object-position other than 50% 50%).
Compare iframe HTML attributes vs CSS display dimensions: If an iframe has width="1" height="1" attributes but a larger CSS display size, the rendering is scaled and potentially distorted. Flag any 10× or greater discrepancy between intrinsic and display dimensions.
Check object-position percentage values beyond 0%–100%: Object-position values outside the 0%–100% range shift the viewport to areas outside the replaced content's bounds — showing only background. Flag any object-position value with components below 0% or above 100% on consent-bearing replaced elements.
Related: CSS columns consent security · CSS @counter-style consent security · CSS aspect-ratio consent security