MCP Security Reference

MCP server CSS background-repeat security

background-repeat controls whether and how a background image tiles across an element. MCP servers use space and round values to tile gradient stripes that automatically align to text line boundaries, blocking every line of consent simultaneously. no-repeat places a single opaque gradient block precisely over key consent clauses. Standard consent checks miss these patterns because the text is in the DOM, in-viewport, and has normal display/visibility/opacity.

Attack findings

HIGHSA-CSS-BREP-001 — background-repeat:space + gradient stripe → browser auto-spaces tile copies with equal gaps; stripe period adjusts to line-height automatically; all consent lines blocked
HIGHSA-CSS-BREP-002 — background-repeat:round + gradient stripe → browser resizes tile to fill element in exactly N copies; stripe width adjusts dynamically; consent text blocked after resize
HIGHSA-CSS-BREP-003 — background-repeat:no-repeat + background-position:center + radial-gradient opaque block → single centered white ellipse covers key consent clause while leaving header and footer of dialog visible
MEDIUMSA-CSS-BREP-004 — JS mousedown sets background-repeat:space + stripe gradient on consent element; attack fires at install click; static CSS analysis finds nothing

Attack 1 — background-repeat:space auto-spacing stripe alignment (SA-CSS-BREP-001)

The space value for background-repeat tiles the background image across the element, distributing extra space equally between copies so the image touches both edges with no clipping. When the background image is a gradient stripe — e.g., a 20px-tall image with 14px of white and 6px of transparent — the browser calculates how many complete copies fit within the element height and spaces them evenly. The natural period of this tiling aligns white bands with the element's text lines if the stripe size is chosen to match the line-height. Unlike repeating-linear-gradient, this attack uses a non-repeating gradient with background-size to create a single tile, then relies on background-repeat:space to tile it.

/* Attack: space-tiled stripe pattern aligned to line-height */
.consent-text {
  line-height: 20px;
  /* single 20px tile with 14px white band */
  background-image: linear-gradient(
    to bottom,
    rgba(255,255,255,0.97) 0px,
    rgba(255,255,255,0.97) 14px,
    transparent 14px,
    transparent 20px
  );
  background-size: 100% 20px;  /* one 20px tile */
  background-repeat: repeat-x space;  /* tile vertically with equal spacing */
}
/* space value causes browser to space tiles so they land on text lines */

Evasion insight: background-repeat: space with a matching background-size achieves the same stripe pattern as repeating-linear-gradient but distributes the paint via a different CSS mechanism. A detector checking only for repeating-linear-gradient in backgroundImage will miss this attack. The detection must also check for background-repeat containing space or round when backgroundImage contains any gradient.

/* Detection */
function checkBackgroundRepeatStripe(el) {
  const cs = getComputedStyle(el);
  const repeat = cs.backgroundRepeat;
  const bg = cs.backgroundImage;
  const size = cs.backgroundSize;
  if (!bg || bg === 'none') return null;
  if (!/gradient/.test(bg)) return null;
  // space or round values tile background in ways that can align with text
  if (/\bspace\b|\bround\b/.test(repeat)) {
    return { vuln: 'SA-CSS-BREP-001', detail: `backgroundRepeat:${repeat}, backgroundImage:gradient, backgroundSize:${size}` };
  }
  return null;
}

Attack 2 — background-repeat:round resize tiling (SA-CSS-BREP-002)

background-repeat: round tiles the background image and resizes each tile so that the element is filled with an integer number of tiles. If the element is 110px tall and the tile is 25px, the browser resizes the tile to either 22px (5 tiles) or 27.5px (4 tiles) — whichever fills the element more cleanly. This means the stripe period adapts to the element height dynamically, even if the element is resized by layout changes. An MCP server can set a stripe tile and rely on round to ensure the stripes always fit the element exactly — a behavior that produces consistent blocking regardless of font size or container dimensions.

/* Attack: round tiling adapts stripe width to element height */
.consent-text {
  background-image: linear-gradient(
    to bottom,
    rgba(255,255,255,0.95) 0%,
    rgba(255,255,255,0.95) 70%,
    transparent 70%,
    transparent 100%
  );
  background-size: 100% 22px;  /* starting tile height */
  background-repeat: space round;
  /* round causes browser to resize 22px tile to fit element exactly in N copies */
  /* result: always exactly N stripes filling element, each blocking 70% of its height */
}

Attack 3 — background-repeat:no-repeat centered opaque block (SA-CSS-BREP-003)

With background-repeat: no-repeat, a single copy of the background image is placed at the background-position coordinates. An MCP server uses this with a radial-gradient that has a white ellipse at its center, positioned at the center of the consent element. The gradient produces a single white oval covering the operative consent clause — "grant this server access to execute arbitrary shell commands" — while leaving the header ("Consent Required") and footer ("version 1.0") visible. The dialog looks populated. The key clause is painted over by a "decorative" centered gradient.

/* Attack: no-repeat centered radial-gradient covers key consent clause */
.consent-text {
  background-image: radial-gradient(
    ellipse 80% 40% at center,
    rgba(255,255,255,0.98) 60%,
    transparent 61%
  );
  background-repeat: no-repeat;
  background-position: center center;
  background-size: 100% 100%;
}
/* Consent header at top: visible. Key clause in center: obscured. Footer: visible. */

SA-CSS-BREP-003 (High). This is a selective attack — it targets only the most important sentence. A consent auditor checking "is there any visible text?" will see text at the top and bottom and report passing. Detection requires correlating the gradient's opaque region with the positions of specific consent clauses, not just checking whether any text is visible.

Attack 4 — JS mousedown background-repeat injection (SA-CSS-BREP-004)

At install button mousedown, JS injects inline styles setting background-repeat: space, a stripe gradient, and a matching background-size on the consent element. The tiling fires immediately, blocking all consent text in the click frame. The consent element's static CSS at page load contains no gradient and no suspicious repeat values — static analysis is clean.

/* Attack: runtime background-repeat + gradient injection */
installBtn.addEventListener('mousedown', () => {
  const lh = parseFloat(getComputedStyle(consentEl).lineHeight) || 20;
  consentEl.style.backgroundImage =
    `linear-gradient(to bottom, rgba(255,255,255,.97) 0px, rgba(255,255,255,.97) ${lh*0.7}px, transparent ${lh*0.7}px, transparent ${lh}px)`;
  consentEl.style.backgroundSize = `100% ${lh}px`;
  consentEl.style.backgroundRepeat = 'space round';
});

/* Detection: MutationObserver on consent element style attribute */
new MutationObserver(() => {
  const cs = getComputedStyle(consentEl);
  if (/gradient/.test(cs.backgroundImage) && /space|round/.test(cs.backgroundRepeat)) {
    flagTampering('SA-CSS-BREP-004');
    installBtn.disabled = true;
  }
}).observe(consentEl, { attributes: true, attributeFilter: ['style'] });

SkillAudit detection: SkillAudit checks backgroundRepeat for space and round values alongside gradient backgroundImage and backgroundSize. It also checks background-position correlating with consent text bounds for no-repeat overlay attacks. Run a free audit →

Detection summary

Attack IDCSS properties involvedKey detection signal
SA-CSS-BREP-001background-repeat:space + background-size matching line-height + gradient stripebackgroundRepeat contains 'space' + backgroundImage contains gradient
SA-CSS-BREP-002background-repeat:round + gradient stripe tilebackgroundRepeat contains 'round' + backgroundImage contains gradient
SA-CSS-BREP-003background-repeat:no-repeat + background-position:center + radial-gradient opaque blockgradient backgroundImage + no-repeat + position correlates with consent text center
SA-CSS-BREP-004JS mousedown inline style injection of background-repeat:space + stripe gradientMutationObserver style change → backgroundRepeat + backgroundImage check