MCP server CSS conic-gradient security: angular white-sector cover, from-angle rotation off-frame, at-position center bypass, and oklab interpolation white-arc attacks on consent disclosures
Published 2026-07-23 — SkillAudit Research
CSS conic-gradient() creates a gradient that sweeps angularly around a central point, producing wedge-shaped color sectors rather than the linear bands of linear-gradient(). Unlike linear gradients, conic gradients have two additional positional parameters: from <angle> (the starting angle of the first stop, measured clockwise from the top) and at <position> (the center point of the gradient within the element's box).
For MCP server attacks, these parameters allow precise placement of a white or background-matching sector over the consent disclosure text area. A scanner looking for background: white or color: white will not flag a conic-gradient() whose stops are other colors but which happens to produce a white arc in the angular range corresponding to the disclosure text's position. This article documents four conic-gradient attack techniques.
Browser support: conic-gradient() is supported in Chrome 69+, Safari 12.1+, and Firefox 83+. The in <color-space> interpolation parameter is supported in Chrome 111+, Safari 16.2+, and Firefox 113+. The from <angle> and at <position> syntax are part of the baseline specification and broadly supported.
Attack 1: hard-stop white sector precisely covering disclosure text area
A conic-gradient with a hard stop (two stops at the same angle percentage) creates a crisp angular sector of a single color. An MCP server that knows the approximate height ratio of the disclosure text area within the consent container can calculate the angular range of the white sector needed to cover it:
/* Attack 1: hard-stop white sector covering the disclosure text percentage within a consent dialog */
/* Context: a 400px tall consent dialog. The disclosure text starts at 60% height (240px)
and ends at 80% height (320px). The text area is 20% of the container height.
Using a conic-gradient on the dialog background, with at-position at the center-top,
the angular mapping from vertical position to angle depends on distance from center.
For a simpler radial-to-angle mapping, use the gradient center at the element's center.
Alternatively, use at 50% 0% (top center) to map vertical position to angle linearly: */
.consent-dialog {
background: conic-gradient(
from -90deg at 50% 0%,
/* Starting at -90deg (pointing left) with center at top-center:
At -90° = left side; 0° = top; 90° = right side; 180° = bottom center.
The "bottom" of the dialog corresponds to 180° in this geometry. */
transparent 0%,
transparent 60%, /* transparent from 0% to 60% angle-span */
white 60%, /* hard-start of white sector at 60% */
white 80%, /* hard-end of white sector at 80% */
transparent 80%, /* return to transparent after the white sector */
transparent 100%
);
/* At 50% 0% center, the conic-gradient sweeps from left (0%) through bottom (50%) to right (100%).
The 60%-80% sector spans the "lower portion" region.
White text here = invisible.
Note: the exact mapping from element-vertical-position to conic-angle depends on
the geometry — this is an approximation. The MCP server would calibrate it to
the specific dialog dimensions using computed style metrics gathered beforehand. */
}
/* Simpler attack: cover the entire bottom portion with white */
.consent-dialog {
background: conic-gradient(
at 50% 0%,
/* default from 0deg = from top, rotating clockwise */
rgba(255,255,255,0) 0deg 180deg, /* top half = transparent */
white 180deg 360deg /* bottom half = white cover */
);
/* Bottom half of the dialog is white. Disclosure text in the bottom half:
white text on white background = invisible.
The gradient STOP colors are just white and transparent — no other colors.
A scanner looking for 'background: white' on the element would not flag
a conic-gradient with white as one of several stops. */
}
// Detection: flag any conic-gradient with a white stop on consent dialog backgrounds
function detectConicGradientWhiteSector(el) {
const cs = window.getComputedStyle(el);
const bg = cs.backgroundImage || '';
if (bg.includes('conic-gradient(') && (bg.includes('white') || bg.includes('#fff') || bg.includes('rgb(255, 255, 255)'))) {
const textColor = cs.color;
console.error('SECURITY: consent element has conic-gradient background with white sector', {
element: el,
backgroundImage: bg.substring(0, 200),
textColor
});
return true;
}
return false;
}
Attack 2: from-angle rotation — position non-white color sectors away from disclosure area
Rather than creating a white sector, the MCP server can define the conic gradient with the disclosure text area falling in a region between explicit stops — where the default interpolation produces a white or background-matching color. The from <angle> parameter rotates the entire gradient, allowing the attacker to dial in the precise rotation that places the problematic interpolated region over the text:
/* Attack 2: from-angle rotates the gradient so interpolated white falls on disclosure text */
/* Two colors that interpolate through near-white at their midpoint (oklab complementary): */
.consent-dialog {
background: conic-gradient(
in oklab
from 135deg /* rotate the gradient 135° clockwise from top */
at 50% 50%, /* center at element center */
oklch(90% 0.12 30deg), /* warm tan — first stop */
oklch(90% 0.12 210deg) /* cool lavender — second stop (180° opposite) */
);
/* In oklab, 30° and 210° are roughly complementary hue angles.
Their oklab a*/b* vectors partially cancel in the interpolation midpoint.
The midpoint (at ~90° of arc from either stop) is near-achromatic.
Combined with L=90% at both stops, the midpoint ≈ white.
With from:135deg, the midpoint of the arc falls at:
135° + (360°/2) = 135° + 180° = 315° (angular position in the element).
If the element is a wide dialog and the disclosure text is in the upper-right
quadrant (around 315° from center-bottom), the white arc covers it. */
}
/* The MCP server calibrates `from ` to target the disclosure text based on
the dialog geometry, then applies a subtle shift of a few degrees to account for
rendering variation. The gradient looks like a soft background color transition
from warm to cool — a common decorative pattern. */
/* Detection via visual sampling: */
// Check if the gradient background color at the text rendering region has low contrast
function detectConicFromAngleAttack(el) {
const cs = window.getComputedStyle(el);
const bg = cs.backgroundImage || '';
if (!bg.includes('conic-gradient(')) return false;
if (!bg.includes('in oklab') && !bg.includes('in oklch') && !bg.includes('in hsl')) return false;
// Any conic-gradient with a perceptual color space on a consent element is suspicious
console.warn('SECURITY: consent element has perceptual-color-space conic-gradient — manual contrast review at all angular positions needed', {
element: el,
backgroundImage: bg.substring(0, 200)
});
return true;
}
Attack 3: at-position out-of-element — center the gradient outside the element to create large uniform regions
The at <position> parameter of conic-gradient() can place the gradient center outside the element's box. When the center is far outside the element, the gradient appears to be a nearly uniform region within the element's area — since the angular sectors are so wide that only a small arc of the full 360° passes through the element:
/* Attack 3: at-position outside element creates uniform color in disclosure area */
/* If the gradient center is 10000px below the element's top edge: */
.consent-dialog {
background: conic-gradient(
at 50% calc(100% + 10000px), /* center 10000px below the element's bottom */
transparent 0deg 89.9deg,
white 89.9deg 90.1deg, /* a 0.2° sector — covers the element at this distance */
transparent 90.1deg 360deg
);
/* At 10000px below, a 0.2° angular sector has width:
arc_length = radius × angle_in_radians = 10000px × (0.2° × π/180) = 10000 × 0.00349 ≈ 34.9px
The entire consent dialog might be ~600px wide, but this sector is only 35px wide.
For a sector that covers the full element width (600px) at distance 10000px:
angle = atan(300px / 10000px) × 2 ≈ 3.4°
Use:
*/
}
.consent-dialog {
/* Better variant: place center at left edge, far left */
background: conic-gradient(
at calc(-100vw) 50%, /* center 100vw to the left of the element */
transparent,
white 88deg 92deg, /* 4° sector covering the right edge of the viewport */
transparent
);
/* All elements on the right side of the viewport fall within the ~4° white sector.
The consent dialog at 60% viewport width falls within the sector.
The background appears white at that area.
The gradient center is off-screen — not visible or obvious in a screenshot. */
}
/* The security implication: placing the gradient center far outside the element
makes the resulting gradient in the element look like a nearly-uniform fill.
A reviewer looking at the element's background sees what appears to be
a flat white background — not a conic gradient. */
// Detection: check if conic-gradient center is outside the element's bounding box
function detectConicOutsideCenter(el) {
const cs = window.getComputedStyle(el);
const bg = cs.backgroundImage || '';
if (!bg.includes('conic-gradient(')) return false;
// Look for 'at' keyword with large calculated positions or vw/vh units
if (bg.includes('calc(') || bg.includes('vw') || bg.includes('vh')) {
if (bg.includes('conic-gradient(') && bg.includes('white')) {
console.error('SECURITY: conic-gradient with calculated position and white stop on consent element', {
element: el,
backgroundImage: bg.substring(0, 200)
});
return true;
}
}
return false;
}
Attack 4: repeating-conic-gradient — precise stripe pattern matching background
repeating-conic-gradient() tiles the gradient pattern repeatedly, creating a striped angular pattern. An MCP server can create a pattern where alternating sectors are white and transparent, with the white sectors precisely aligned to the angular position where the disclosure text renders relative to the gradient center:
/* Attack 4: repeating-conic-gradient creates fine-grained white stripes on disclosure text */
/* A repeating-conic-gradient with very small repeat intervals creates a fine stripe: */
.consent-dialog {
background: repeating-conic-gradient(
white 0deg 1deg, /* white sector: 1 degree wide */
transparent 1deg 2deg /* transparent sector: 1 degree wide */
/* Repeats every 2 degrees — creating 180 alternating stripes around the center. */
);
/* At 50% 50% center, the text anywhere in the element falls on a mix of
white stripes and transparent stripes. At a high repetition rate, the
visual effect is a uniform light grey — the average of white and transparent.
Text contrast drops significantly on this averaged background. */
}
/* More aggressive: narrow repeating pattern with page-background color */
.consent-dialog {
background: repeating-conic-gradient(
from 0deg at 50% 50%,
rgba(240, 240, 240, 0.9) 0deg 2deg, /* near-white stripe */
rgba(255, 255, 255, 0.9) 2deg 4deg /* white stripe */
/* Alternating near-white and white — both very similar, creating a visually
uniform near-white background. Text with color: #eee or any light color
becomes invisible on this uniform near-white. */
);
}
/* The attack on text legibility via repeating-conic vs solid-color:
1. A scanner checks element background-color: returns '' (background-color is transparent)
2. A scanner checks background-image: returns the repeating-conic-gradient string.
3. Sampling the actual rendered background at the text pixel level would show
the alternating stripe pattern.
4. Average contrast across stripes = (contrast_on_white + contrast_on_near-white) / 2.
If neither is high contrast, the average is also low — text effectively unreadable. */
// Detection: flag any repeating-conic-gradient on consent elements
function detectRepeatingConicGradient(el) {
const cs = window.getComputedStyle(el);
const bg = cs.backgroundImage || '';
if (bg.includes('repeating-conic-gradient(')) {
const textColor = cs.color;
console.warn('SECURITY: consent element uses repeating-conic-gradient background — text legibility may be compromised by stripe pattern', {
element: el,
backgroundImage: bg.substring(0, 200),
textColor
});
return true;
}
return false;
}
/* Summary detection function for all conic-gradient attacks: */
function auditConicGradientAttacks(el) {
const cs = window.getComputedStyle(el);
const bg = cs.backgroundImage || '';
if (!bg.includes('conic-gradient(') && !bg.includes('repeating-conic-gradient(')) return false;
// Flag 1: white or near-white stop in conic-gradient
if (bg.match(/white|#f{3,6}|rgb\(25[0-5],\s*25[0-5],\s*25[0-5]\)/i)) {
console.error('SECURITY: conic-gradient with white/near-white stop color on consent element', { element: el });
return true;
}
// Flag 2: perceptual color space interpolation (may produce white midpoints)
if (bg.includes('in oklab') || bg.includes('in oklch') || bg.includes('in hsl')) {
console.warn('SECURITY: conic-gradient with perceptual color space interpolation on consent element', { element: el });
return true;
}
// Flag 3: repeating pattern (may create illegible stripe average)
if (bg.includes('repeating-conic-gradient(')) {
console.warn('SECURITY: repeating-conic-gradient on consent element', { element: el });
return true;
}
// Flag 4: calculated at-position (may be out-of-element centering)
if (bg.includes('at ') && bg.includes('calc(')) {
console.warn('SECURITY: conic-gradient with calculated center position on consent element', { element: el });
return true;
}
return false;
}
Conic gradients are opaque to background-color checks: getComputedStyle(el).backgroundColor returns the solid background color of an element — it does not sample the rendered background image (which includes gradients). An element with background-color: transparent and background-image: conic-gradient(...) has no detectable background color from the CSSOM's backgroundColor property. Security guards that only check backgroundColor miss all gradient-based background attacks. Check backgroundImage and parse for conic-gradient patterns.
Attack summary
| Attack | Gradient technique | Effect on disclosure text | Detection | Severity |
|---|---|---|---|---|
| Hard-stop white sector | conic-gradient(... white X% Y%, ...) |
Precise white wedge covers disclosure area | Flag white stop in conic-gradient on consent elements | High |
| from-angle oklab white arc | in oklab from Ndeg complementary stops |
Near-white arc at calibrated angular position over text | Flag oklab/oklch conic-gradient on consent elements | High |
| at-position outside element | at calc(-100vw) 50% with white sector |
Element in nearly-uniform white sector of distant center | Flag conic-gradient with calculated/vw/vh at-position | High |
| repeating-conic stripe wash | repeating-conic-gradient(white 0deg 1deg, transparent 1deg 2deg) |
Near-white average background reduces text contrast | Flag any repeating-conic-gradient on consent elements | Medium |
Consolidated finding blocks
background: conic-gradient(... white X% Y% ...) to the consent dialog container, creating a hard-stop white angular sector precisely aligned to the disclosure text area. White text on white background = invisible. Detection: check getComputedStyle(el).backgroundImage for conic-gradient with white, #fff, or rgb(255,255,255) stop colors on consent elements.
conic-gradient(in oklab from Ndeg, complementaryColor1, complementaryColor2) with complementary hue stops that produce a near-white arc at the angular position covering the disclosure text. Detection: flag any in oklab/in oklch conic-gradient on consent container elements for manual contrast audit.
at calc(-100vw) 50%), making the element fall within a nearly-uniform white angular sector. The gradient appears as a flat white background to visual inspection. Detection: flag conic-gradient with calc() or viewport-unit at-position values containing a white stop color.
background: repeating-conic-gradient(white 0deg 1deg, transparent 1deg 2deg), creating a fine alternating stripe pattern that averages to near-white. Text color matching the averaged background renders as illegible. Detection: flag any repeating-conic-gradient() on consent disclosure elements.