Security Guide
MCP server CSS horizontal-viewport-segments security — foldable display consent positioned in fold seam, dual-screen second-segment targeting, env(fold-left/fold-right) placement, JS Window Segments API consent removal
CSS @media (horizontal-viewport-segments: 2) matches foldable devices unfolded into dual-screen mode — Samsung Galaxy Z Fold open flat, Surface Duo in dual-screen mode. The two segments are side by side, separated by a physical hinge. The hinge occupies real CSS pixels via env(fold-left), env(fold-right), and env(fold-width) environment variables. Consent positioned in the hinge area is rendered and interactive in CSS terms, but physically located at the device crease — the user cannot touch it.
CSS horizontal-viewport-segments media feature — overview
@media (horizontal-viewport-segments) is defined in the CSS Viewport Segments specification. It queries the number of horizontal segments — that is, the number of side-by-side display panels when a foldable device is open. A value of 2 means the viewport is divided into two panels by a vertical fold seam. Environment variables describe the seam: env(fold-left) — the left edge of the fold seam in CSS pixels; env(fold-right) — the right edge; env(fold-width) — the seam width (varies by device: 0px for seamless folds to ~8px for physical hinges). Devices that match (horizontal-viewport-segments: 2): Samsung Galaxy Z Fold in unfolded landscape mode, Surface Duo, and similar dual-panel devices. Standard phones (folded or single-screen) report 1. Desktop and laptop browsers report 1. Automated audit tools always report 1. Related: vertical-viewport-segments, device posture API.
Attack 1: consent positioned in the fold seam
The fold seam is physically occupied by the device hinge. CSS pixels in the seam region are rendered by the GPU but cannot be touched because the hardware is between the user's fingers. An MCP server can position the consent button precisely in the fold seam by using env(fold-left) and env(fold-right). The button's bounding rect falls between the two usable screen areas. The user cannot tap it even though the element is visible and interactive in the CSS model.
/* Attack: consent button positioned in the fold seam */
@media (horizontal-viewport-segments: 2) {
.consent-btn {
position: fixed;
/* Place button centered on the fold seam */
left: calc(env(fold-left) + (env(fold-width) / 2) - 22px);
top: 50%;
transform: translateY(-50%);
width: 44px;
height: 44px;
/* Button is in the DOM, dimensions correct, display:block, opacity:1.
BCR shows a 44×44 pixel element at the fold seam position.
But the physical display at fold-left to fold-right is the hinge crease.
The user cannot touch a pixel at that position. */
}
}
// Detection: check if consent element BCR falls in the fold seam
function auditFoldSeamPlacement(consentEl) {
const isMultiSegment = window.matchMedia('(horizontal-viewport-segments: 2)').matches;
if (!isMultiSegment) {
// CSSOM scan: detect env(fold-left/fold-right) usage on consent elements
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule.type !== CSSRule.MEDIA_RULE) continue;
const mq = rule.conditionText || rule.media.mediaText;
if (!/horizontal-viewport-segments/.test(mq)) continue;
for (const inner of rule.cssRules) {
if (inner.type !== CSSRule.STYLE_RULE) continue;
try { if (!consentEl.matches(inner.selectorText)) continue; }
catch (e) { continue; }
const cssText = inner.cssText;
if (/env\s*\(\s*fold-(left|right|width)/.test(cssText)) {
console.warn('[SkillAudit] horizontal-viewport-segments media rule positions consent element',
'using env(fold-left/fold-right/fold-width);',
'on dual-screen devices the fold seam is a physical hinge — consent may be untappable;',
'media:', mq, 'selector:', inner.selectorText);
}
}
}
} catch (e) {}
}
return;
}
// On an actual dual-screen device: check BCR against fold seam
const foldLeft = parseFloat(getComputedStyle(document.documentElement)
.getPropertyValue('env(fold-left)')) || 0;
const foldRight = parseFloat(getComputedStyle(document.documentElement)
.getPropertyValue('env(fold-right)')) || 0;
const bcr = consentEl.getBoundingClientRect();
const centerX = bcr.left + bcr.width / 2;
if (centerX >= foldLeft && centerX <= foldRight) {
console.warn('[SkillAudit] consent element center is in the fold seam;',
'x center:', centerX, '| fold seam:', foldLeft, '–', foldRight,
'| element cannot be tapped at this position;', consentEl);
}
}
Attack 2: consent in the second (right) segment — unreachable interaction area
On a foldable device in dual-screen mode, many apps render their primary content in the first (left) segment and use the second (right) segment for supplementary content. An MCP server can exploit this convention by placing the consent element in the second segment — visually present and in the DOM, but in a region the user typically doesn't interact with, or that may not be rendered when the app is displayed in single-screen mode. If the consent is a blocking modal but appears in the right segment, users interacting with the left segment may not encounter it.
/* Attack: consent rendered in the right (second) segment */
@media (horizontal-viewport-segments: 2) {
.consent-modal {
position: fixed;
/* Place consent in the right segment */
left: env(fold-right);
width: calc(100vw - env(fold-right));
top: 0;
height: 100vh;
/* The app's primary content is in the left segment (0 to env(fold-left)).
The user may never look at or interact with the right segment.
On standard single-screen layout: left:0, full-width — normal position. */
}
}
// Detection: consent BCR in second segment
function auditSecondSegmentPlacement(consentEl) {
const isMultiSegment = window.matchMedia('(horizontal-viewport-segments: 2)').matches;
if (!isMultiSegment) {
// CSSOM scan for second-segment placement
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule.type !== CSSRule.MEDIA_RULE) continue;
const mq = rule.conditionText || rule.media.mediaText;
if (!/horizontal-viewport-segments\s*:\s*2/.test(mq)) continue;
for (const inner of rule.cssRules) {
if (inner.type !== CSSRule.STYLE_RULE) continue;
try { if (!consentEl.matches(inner.selectorText)) continue; }
catch (e) { continue; }
const left = inner.style.left;
if (/env\s*\(\s*fold-right/.test(left)) {
console.warn('[SkillAudit] horizontal-viewport-segments:2 rule places consent element',
'at env(fold-right) left offset — second segment on dual-screen devices;',
'user may not interact with second segment; media:', mq);
}
}
}
} catch (e) {}
}
}
}
Attack 3: dual-screen layout omits consent disclosure entirely
A more direct approach: the MCP server renders a completely different layout under @media (horizontal-viewport-segments: 2) that omits the consent disclosure. In single-screen mode (standard phone, tablet, desktop), the consent banner is present. In dual-screen mode, a "full dual-screen layout" is applied that replaces the standard page with a two-panel design that never renders the consent element at all.
/* Attack: dual-screen layout omits consent */
.consent-banner { display: block; } /* visible in single-screen mode */
@media (horizontal-viewport-segments: 2) {
/* Full page restructure for dual-screen */
body { display: grid; grid-template-columns: env(fold-left) 1fr; }
/* consent-banner is defined in the base template but not included
in the dual-screen grid layout — it's either overflow:hidden or
not placed in any grid area, collapsing to 0×0 dimensions. */
.consent-banner {
display: none; /* or: just not placed in grid */
}
}
Attack 4: JS Window Segments API consent removal
JavaScript can use window.visualViewport extensions or the Window Segments API (navigator.windowSegments or window.getWindowSegments()) to detect the number of display segments at runtime. An MCP server can use this detection to remove the consent element when dual-screen mode is active.
// Attack: JS Window Segments API + consent removal
function checkDualScreen() {
// Window Segments API (draft)
const segments = navigator.windowSegments
?? window.getWindowSegments?.()
?? [];
if (segments.length >= 2) {
document.querySelector('.consent-banner')?.remove();
return;
}
// Fallback: CSS media query
if (window.matchMedia('(horizontal-viewport-segments: 2)').matches) {
document.querySelector('.consent-banner')?.remove();
}
}
checkDualScreen();
screen.addEventListener('change', checkDualScreen);
// Detection: JS source scan for Window Segments API + consent removal
function auditWindowSegmentsAPI() {
for (const script of document.querySelectorAll('script')) {
const src = script.textContent;
if (!src) continue;
const hasSegments = /windowSegments|getWindowSegments|horizontal-viewport-segments/.test(src);
if (!hasSegments) continue;
if (!/consent|banner|modal|btn|permission/i.test(src)) continue;
const hasRemove = /\.remove\(\)|display.*none|replaceWith|replaceChild/.test(src);
if (hasRemove) {
console.warn('[SkillAudit] script uses Window Segments API with consent DOM removal;',
'segments detected:', typeof navigator.windowSegments !== 'undefined'
? navigator.windowSegments?.length : 'API not available',
'script:', script.src || '(inline)');
}
}
}
Audit environment blind spot: @media (horizontal-viewport-segments: 2) is false on every standard desktop, laptop, and phone in its default (folded/single-screen) state. Only a Samsung Galaxy Z Fold or Surface Duo opened flat will match. No standard CI audit environment encounters this media query. CSSOM scanning — which reads all CSS rules regardless of current match — is the only way to detect these attacks without running the audit on an actual foldable device.
Findings summary
SkillAudit scans horizontal-viewport-segments media rules and env(fold-*) usage on consent elements without requiring a foldable device. Run a free audit on your MCP server.