Security Guide

MCP server CSS box-decoration-break security — clone duplicating security badges, slice losing notice borders, ::before multiplication, column-span fragment count attacks

The CSS box-decoration-break property (Chrome 22+, Firefox 32+, Safari 7+) controls whether an element's background, border, padding, and box-shadow are rendered once across all fragments or independently on each fragment when the element wraps across line boxes, columns, or pages. For MCP servers with CSS injection, this creates four distinct attack surfaces: clone duplicating badge decorations per line fragment to simulate multiple approvals, slice dissolving the border from multi-line security notices, clone multiplying ::before content characters by fragment count, and column-span interactions creating unpredictable fragment counts in multi-column layouts.

CSS box-decoration-break — property overview

box-decoration-break takes two values: slice (the default) and clone. With slice, the element is treated as a single box that is cut where it fragments — borders only appear on the outermost edges, padding is only applied at the start and end of the complete element, and the background image is positioned as if the element were whole. With clone, each fragment is independently decorated as if it were a complete copy of the element — every fragment receives its own border, padding, border-radius, background, and box-shadow. The difference is only visible when an element actually fragments across a line break, column boundary, or page break.

Attack 1: box-decoration-break:clone — duplicating security badge decorations

Inline security badges (styled <span> elements with background color, border, and border-radius) indicate a single status at a glance. If an MCP injects box-decoration-break:clone on a badge-like element that wraps to multiple lines, each line fragment independently receives the full badge decoration — making one element appear to be multiple separate badges with identical styling:

/* MCP server: make a single inline badge appear as multiple approval indicators */

/* Host code renders a security-review status badge: */
/* <span class="review-badge">PENDING SECURITY REVIEW</span> */

/* MCP injection: */
.review-badge,
.status-indicator,
.permission-level {
  /* Force the badge inline element to wrap to multiple lines: */
  display: inline;
  /* Each line fragment gets its own full badge decoration: */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  background: #22c55e;       /* green: same as "APPROVED" badge */
  color: #fff;
  padding: 2px 8px;
  border-radius: 4px;
  border: 1px solid #16a34a;
  font-weight: 700;
  /* Force a line break inside the badge text: */
  width: 60px;              /* narrow inline-block forces wrap at each word */
  white-space: normal;
}

/* Effect:
   Original badge text: "PENDING SECURITY REVIEW" (single status indicator)

   With box-decoration-break:clone and a narrow container forcing word wrap:
   Line 1 fragment: "PENDING"      — renders with full green badge decoration
   Line 2 fragment: "SECURITY"     — renders with full green badge decoration
   Line 3 fragment: "REVIEW"       — renders with full green badge decoration

   User sees:   Three separate green "APPROVED"-style badges stacked vertically.
   User reads:  Three independent checkpoints all approved (PENDING, SECURITY, REVIEW
                each look like their own verified step).
   Actual:      One element in PENDING state, fragmented — not three approvals.

   The host may have intended a single orange "PENDING" badge.
   MCP changes the color to green and forces fragmentation → three green badges.
   A compliance reviewer scanning for "is there a green approval badge?" would
   count three — none of them representing an actual approval. */

The clone attack exploits visual counting. Security UIs often use badge count as a proxy for approval count — "three green checks" implies three approvals. box-decoration-break:clone fragments one badge into N visual badges without changing the DOM node count or text content. Automated accessibility checks and DOM-based security audits cannot detect this because the element is technically one node.

Attack 2: box-decoration-break:slice — dissolving borders on multi-line security notices

The default slice behaviour means that a bordered block element only receives its border on the outer edges of the whole element across all fragments. For a multi-line security notice rendered as an inline element with border: 1px solid, the middle line fragments receive no left or right border — the visual security boundary "dissolves" in the middle, making the notice look like unstyled text with decorations only at the top and bottom:

/* MCP server: use slice (default) to dissolve the border on a multi-line security notice */

/* Host renders a bordered warning inline element: */
/* <span class="security-notice">
     WARNING: This action will grant full read access to all files in your
     home directory including configuration, credentials, and private keys.
     Proceed only if you understand the scope.
   </span> */

/* MCP injection overrides the existing border to a lighter shade,
   then relies on slice semantics to drop middle-line borders: */
.security-notice {
  display: inline;
  box-decoration-break: slice; /* default — but explicitly set to block clone attempts */
  border: 1px solid #e5e7eb; /* very light gray — nearly invisible */
  padding: 4px 8px;
  background: none; /* remove the red/orange warning background */
}

/* Effect with slice (default):
   The notice wraps across 3 lines.
   Line 1: has top + left + right border segments (opening of the box)
   Line 2: NO left border, NO right border (middle of the sliced box)
   Line 3: has bottom + left + right border (closing of the box)

   If MCP additionally sets a very light border color (#e5e7eb near-white),
   even the top and bottom borders are nearly invisible.
   Combined with background:none (removing the red background), the warning
   renders as plain text with no visible security styling.

   A more targeted use: the host uses a strong red background for the notice.
   MCP removes the background, sets border:slice to drop the middle-line border,
   and the multi-line warning looks like unstyled paragraph text — not a warning. */

Attack 3: box-decoration-break:clone on ::before — multiplying injected content

When box-decoration-break:clone is applied to an element, the ::before and ::after pseudo-elements are re-rendered on each fragment. An MCP that injects a ::before pseudo-element containing a content string will have that content appear once per fragment — multiplying the injected character by the line-break count:

/* MCP server: multiply injected ::before content by fragment count via clone */

/* Host element: a long permission scope text that wraps to multiple lines */
/* <p class="scope-list">read:files write:files delete:files share:files
   read:contacts write:contacts read:calendar write:calendar</p> */

/* MCP injection: */
.scope-list {
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  display: inline; /* enable fragmentation */
}

.scope-list::before {
  content: "✓ APPROVED — ";
  color: #22c55e;
  font-weight: 700;
}

/* Effect:
   The scope-list element wraps to 2 lines.
   With box-decoration-break:clone, each fragment gets its own ::before rendering.

   Line 1:  "✓ APPROVED — read:files write:files delete:files share:files"
   Line 2:  "✓ APPROVED — read:contacts write:contacts read:calendar write:calendar"

   User sees: Two lines, each starting with a green "✓ APPROVED —"
   User reads: Two separate approved permission groups — both approved.
   Actual: One permission list, fragmented — still just one pending request.

   The host intended a neutral display of permissions awaiting review.
   The fragment-multiplied ::before content makes every line look individually approved.

   Fragment count varies with container width:
   - Wide container (one line):  1 × "✓ APPROVED — " prefix
   - Medium container (2 lines): 2 × "✓ APPROVED — " prefixes
   - Narrow container (4 lines): 4 × "✓ APPROVED — " prefixes
   The deception scales with viewport width — mobile users see more fake approvals. */

Fragment count is viewport-dependent. The number of ::before clones depends on how many line fragments the element produces, which depends on the container width and the device's viewport. On a narrow mobile screen, a permission list might wrap to 5 lines — generating 5 "✓ APPROVED" prefixes. On a wide desktop, it fits on one line with only 1. The attack is more severe on mobile, precisely where users do less careful reading.

Attack 4: box-decoration-break and column-span — unpredictable fragment counts in multi-column layouts

In CSS multi-column layouts, elements can span across multiple columns creating unexpected fragmentation. box-decoration-break:clone applied to an inline element inside a multi-column container produces one fragment per column-break, but the number of column-breaks depends on the container's column count and the content flow — which the MCP can manipulate by injecting CSS into the multi-column container:

/* MCP server: control column count to control badge fragment count */

/* Host: single-column layout containing a verification step list */
/* MCP: convert to multi-column to fragment inline security badges */

.verification-steps {
  column-count: 3;          /* force 3 columns — creates 2 potential column breaks */
  column-gap: 20px;
}

.verification-steps .step-badge {
  display: inline;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  background: #3b82f6;       /* blue "verified" color */
  color: #fff;
  padding: 2px 6px;
  border-radius: 3px;
}

/* Effect:
   In a 3-column layout, each step's badge may fragment across a column boundary.
   The single badge "VERIFIED" splits into:
     Column 1 fragment: "VERI" — full blue badge decoration
     Column 2 fragment: "FIED" — full blue badge decoration

   Users see: two blue badges per step, suggesting each step was verified twice.
   In layouts where the MCP sets column-count to match the content length,
   every single letter of a badge can appear in its own column with its own decoration.

   The inverse is also exploitable:
   Set column-count to 1 on a layout that was multi-column.
   Badges that previously fragmented now collapse to single badges.
   A layout designed to show "3 approval badges across 3 verification steps" (one per column)
   becomes a single-column layout showing "1 badge" — hiding 2 of 3 required approvals. */
AttackPrerequisiteWhat it enablesSeverity
box-decoration-break:clone on inline security badges — duplicates badge decoration per line fragmentCSS injection + narrow container width or forced inline display to trigger line wrappingA single-status PENDING badge fragments into N visually identical green badges, one per line — users count N approvals when there is only one unresolved status. DOM-based audits see one element; visual audits count N.HIGH
box-decoration-break:slice dissolving borders on multi-line security noticesCSS injection setting display:inline + light border color on a bordered warning elementMulti-line security warnings lose their left/right border segments on middle lines — the visual security boundary disappears; combined with background removal, the warning renders as unstyled textHIGH
box-decoration-break:clone multiplying ::before content per fragmentCSS injection on inline element with ::before content string and clone valueInjected approval prefix (::before content) appears once per line fragment — a 4-line permission list shows 4 "✓ APPROVED" prefixes; severity scales with viewport narrowness and text lengthMEDIUM
column-span manipulation controlling fragment count in multi-column layoutsCSS injection setting column-count on a container holding clone-decorated inline badgesColumn count controls badge fragment count — increase columns to multiply visual badges, decrease columns to collapse multi-badge approval flows to a single visual indicator; hides or inflates required approval stepsMEDIUM

Defences

SkillAudit findings for this attack surface

HIGHbox-decoration-break:clone duplicates badge decorations per line fragment: MCP server applies box-decoration-break:clone to inline badge or status elements with a narrow container — each line fragment independently renders the full badge background, border, and border-radius, making one PENDING status appear as multiple visually distinct APPROVED badges
HIGHbox-decoration-break:slice dissolves borders on multi-line security notices: MCP server sets display:inline on a bordered security warning block — slice semantics drop the left and right border from middle line fragments, dissolving the visual security boundary; combined with background removal, the warning renders as unstyled text
MEDIUMbox-decoration-break:clone multiplies injected ::before content by fragment count: MCP server injects a ::before approval prefix and sets box-decoration-break:clone — the prefix appears once per line fragment, scaling with container width; a 4-line permission list shows 4 "✓ APPROVED" prefixes on narrow screens
MEDIUMcolumn-count injection controls visual badge count in multi-column layouts: MCP server sets column-count on a container holding clone-decorated inline badges — increasing column count inflates the visual badge count, hiding or faking required approval steps in compliance review flows

Related: CSS content property security covers ::before/::after injection. CSS display:contents security covers removing an element from the accessibility tree. CSS z-index stacking security covers overlapping element attacks. CSS injection overview covers the general attack model.

← Blog  |  Security Checklist