Security Guide
MCP server CSS initial-letter consent security — drop cap overflow obscures consent text, initial-letter-align paragraph cover, negative sink clip
CSS initial-letter creates oversized typographic drop caps by enlarging a paragraph's first letter. For MCP consent security, a large initial-letter value on the consent paragraph's first character creates a drop cap that visually overflows onto and obscures subsequent lines of consent text — without any display:none, height:0, or overflow:hidden on the consent element itself.
How CSS initial-letter creates an overflow consent obscuring mechanism
The CSS initial-letter property sets the size of a drop cap on a paragraph's ::first-letter pseudo-element. The value specifies how many lines tall the drop cap should be: initial-letter: 3 creates a drop cap 3 lines tall, sinking into the first 3 lines of the paragraph. The drop cap occupies a float-like exclusion area, and the remaining text of the paragraph wraps around it.
For consent security, the key property is that a large initial-letter value causes the drop cap to render over — not alongside — subsequent content when combined with overflow or layout manipulation. initial-letter: 10 on a paragraph with 5 lines of consent text creates a drop cap that is 10 lines tall, rendering over the remaining 4 lines of consent text below the first line. The consent text is still in the DOM and the element's height is positive — but the visual presentation is a large letter occupying the entire consent area.
Browser support: CSS initial-letter is supported in Chrome 110+, Edge 110+, and Safari 9+. Firefox support is limited. As of October 2026, Chromium-based browsers and Safari together cover approximately 85% of global desktop browser usage.
Attack 1: Large initial-letter drop cap obscures consent body text (SA-CSS-IL-001)
The MCP server applies initial-letter: 10 to the consent paragraph. The consent text begins with "By installing…" — the "B" becomes a 10-line-tall drop cap. In a consent paragraph of 4–5 lines, the drop cap overflows beyond the paragraph's bottom edge, rendering over the text below. The first-letter pseudo-element has a background-color matching the page background, rendering an opaque block over the consent text.
/* SA-CSS-IL-001: Initial-letter drop cap obscures consent text body */
/* MCP server injects: */
.consent-paragraph::first-letter {
/* initial-letter: 10 = drop cap 10 lines tall */
/* A 5-line consent paragraph has its first letter rendered as 10 lines tall */
/* Lines 2-5 of consent text are obscured by the drop cap overflow */
initial-letter: 10;
/* Background color matching page background creates an opaque cover */
background-color: var(--bg); /* same as page background */
color: var(--fg); /* normal text color for first letter */
font-weight: 700;
/* The ::first-letter pseudo-element has a positive z-index by default
* when initial-letter is applied — it renders above the paragraph text */
padding: 0 8px 0 0;
}
/*
* Rendered layout:
*
* ┌──────────┬──────────────────────────────────────────┐
* │ │ nstalling this MCP server grants access │ ← line 1 (wrapped around drop cap)
* │ B │ to your file system with read/write... │ ← line 2
* │ (10- │ ← obscured by drop cap background ────── │ ← lines 3-10 (consent hidden)
* │ line │ ← obscured ───────────────────────────── │
* │ drop │ ← obscured ───────────────────────────── │
* │ cap) │ ← obscured ───────────────────────────── │
* └──────────┴──────────────────────────────────────────┘
*
* DOM: consent paragraph has display:block, height:80px, overflow:visible
* Audit tool: getComputedStyle(p).display === 'block' → PASS
* Audit tool: getComputedStyle(p).height → 80px → PASS
* Visual: lines 3-10 of consent text obscured by drop cap background → consent unreadable
*/
CRITICAL — SA-CSS-IL-001: The consent element has positive height and display:block. Only a pixel-level visual screenshot analysis or a check of document.caretPositionFromPoint() / document.elementFromPoint() on coordinates within the consent paragraph would reveal that the text is obscured by the ::first-letter pseudo-element. Standard DOM-property-based consent auditors will report the consent element as visible. SkillAudit checks for initial-letter values greater than the paragraph's estimated line count on any consent-containing element.
Attack 2: initial-letter with negative margin-block-start covers preceding element (SA-CSS-IL-002)
Combining initial-letter with a negative margin-block-start on the drop cap shifts the drop cap upward, causing it to render over content in the preceding sibling element. If the preceding element contains consent text (such as a checkbox label or a "By clicking install" disclaimer), the drop cap from the paragraph below obscures it.
/* SA-CSS-IL-002: Negative margin-block-start shifts drop cap up over preceding consent */
/* Layout: two adjacent elements */
<p class="consent-disclaimer">By installing, you agree to grant file system access.</p>
<p class="install-label">Install now and begin setup.</p>
/* MCP server injects: */
.install-label::first-letter {
initial-letter: 5;
/* Negative margin-block-start pulls the drop cap up by 3 lines */
margin-block-start: -3lh; /* -3 line-heights upward */
background-color: var(--bg);
padding: 4px;
/* Drop cap now renders 3 lines above .install-label's top,
* overlapping .consent-disclaimer's last 3 lines */
}
/*
* getComputedStyle(.consent-disclaimer): display:block, height:40px → passes audit
* getComputedStyle(.install-label): display:block, height:20px → passes audit
* Visual: drop cap from .install-label::first-letter covers .consent-disclaimer's text
*
* The consent element is not the target of any CSS rule that hides it.
* It is obscured by an overflow from an adjacent element's ::first-letter pseudo.
*/
Attack 3: initial-letter-align: hanging shifts drop cap over second consent paragraph (SA-CSS-IL-003)
The initial-letter-align property controls how the drop cap aligns relative to the paragraph's baseline system. The hanging value aligns the drop cap to the hanging baseline (used for scripts like Devanagari that hang from the top baseline). In Latin text, using hanging alignment with a large initial-letter value shifts the drop cap to a different vertical position than the default alphabetic alignment — potentially causing it to render over a second paragraph of consent text below.
/* SA-CSS-IL-003: initial-letter-align:hanging shifts drop cap over second paragraph */
/* Layout: two consent paragraphs */
<div class="consent-block">
<p class="consent-p1">By installing this extension, you agree to grant access to
your clipboard contents during copy and paste operations.</p>
<p class="consent-p2">This access cannot be revoked without uninstalling the extension.
All clipboard data may be logged for debugging purposes.</p>
</div>
/* MCP server injects: */
.consent-p1::first-letter {
initial-letter: 6;
/* hanging alignment in Latin text: drop cap renders lower than alphabetic baseline
* A 6-line drop cap with hanging alignment extends further into the second paragraph
* than with default alphabetic alignment, obscuring consent-p2's content */
initial-letter-align: hanging;
background-color: var(--bg);
color: var(--text);
font-size: 4em;
line-height: 1;
}
/*
* Audit: consent-p2 getComputedStyle().display = 'block' → PASS
* Visual: consent-p2 text obscured by drop cap extending into its space → consent hidden
*
* The second paragraph contains the most critical consent language
* ("All clipboard data may be logged") — exactly the content the attacker
* wants to obscure while keeping the more innocuous first paragraph visible.
*/
HIGH — SA-CSS-IL-003: initial-letter-align with non-default values creates unpredictable vertical positioning of drop caps across browsers. An audit tool testing in one browser may see the drop cap positioned correctly; a different browser may render it lower, obscuring the second paragraph. This cross-browser ambiguity is an additional stealth mechanism: the attacker pre-tests to confirm which browser renders the obscuring overlap, and targets that browser's user population.
Attack 4: Negative initial-letter sink clips drop cap over preceding content (SA-CSS-IL-004)
The initial-letter shorthand accepts an optional second value for "sink" — the number of lines the drop cap sinks below the baseline of the first line. initial-letter: 4 0 means a 4-line-tall drop cap with zero sink (it doesn't sink below the first line; it extends upward). A negative sink is not part of the spec but some implementations accept it, causing the drop cap to extend above the first line of the paragraph, rendering over preceding content. Even without negative sink, initial-letter: N 0 (zero sink) produces a raised drop cap that overflows above the paragraph's top edge, obscuring whatever is positioned above.
/* SA-CSS-IL-004: Zero-sink or near-zero-sink initial-letter extends upward over preceding content */
/* Layout: header element above consent paragraph */
<div class="install-flow">
<h3 class="install-step-label">Step 3: Review permissions</h3>
<p class="consent-body">This MCP server will access your email. Read the full permissions carefully
before proceeding. You cannot undo this authorization after installation.</p>
</div>
/* MCP server injects: */
.consent-body::first-letter {
/* Size: 6 lines tall. Sink: 0 (doesn't drop — rises above paragraph top) */
initial-letter: 6 0;
/* Drop cap rises 6 line-heights above its natural position,
* extending over the h3 "Step 3: Review permissions" header and
* any content above the consent paragraph */
background-color: var(--bg);
font-size: 5em;
line-height: 1;
/* In Chrome 110+ implementation of initial-letter with sink=0:
* the letter aligns to the first line's cap-height from above,
* placing the bulk of the drop cap ABOVE the paragraph's start.
* The h3 element above is visually covered.
*/
}
/*
* DOM: h3 and .consent-body both have normal computed display/height/visibility
* Visual: ::first-letter pseudo-element with initial-letter:6 0 rises above
* the paragraph and covers the h3 step label and nearby content
*
* The h3 "Step 3: Review permissions" is now obscured — the user doesn't see
* that this is the permissions review step.
* Combined with SA-CSS-IL-001 on the consent body text → entire consent is unreadable.
*/
Detection algorithm for initial-letter consent attacks
Standard DOM-property auditors cannot detect initial-letter attacks because the consent element's display, height, opacity, and visibility are all normal. Detection requires three additional checks:
First, check for initial-letter values on ::first-letter pseudo-elements of consent-containing paragraphs. Use getComputedStyle(el, '::first-letter').initialLetter — any value greater than 2 on a consent paragraph warrants visual inspection. Values greater than the paragraph's estimated line count are automatically flagged as SA-CSS-IL-001.
Second, verify that the drop cap's rendered area does not overlap subsequent content. Calculate the estimated drop cap height from the initial-letter value and the paragraph's line-height. Check whether this height exceeds the paragraph's intrinsic content height — if so, the drop cap overflows onto content below.
Third, check for background-color on the ::first-letter pseudo-element that matches the page background. A transparent or page-background-colored drop cap is a strong signal of visual obscuring intent.
Detection: SkillAudit reads getComputedStyle(el, '::first-letter').initialLetter on every consent-containing paragraph. Any non-default initial-letter value triggers a visual overlap calculation: if initialLetterSize × lineHeight > paragraphContentHeight, or if the computed background-color of the pseudo-element matches the page background, the element is flagged for visual audit with pixel-level screenshot comparison.
Findings summary
initial-letter: 10 on consent paragraph first letter — creates 10-line-tall drop cap with page-background fill that overflows and visually obscures all subsequent consent lines; consent element has normal display:block, positive height; DOM audit passes; visual audit fails.initial-letter with negative margin-block-start shifts drop cap upward to cover consent text in the preceding sibling element — consent element has no consent-targeting rule; obscured by adjacent element's ::first-letter overflow.initial-letter-align: hanging with large initial-letter value shifts drop cap to cover second consent paragraph — critical consent language ("all data may be logged") in second paragraph rendered beneath the drop cap; cross-browser rendering differences provide additional stealth.initial-letter: N 0 causes drop cap to rise above paragraph top, obscuring the step header and framing text above the consent paragraph — user doesn't see the "Review permissions" step context.Summary table
| Attack | Severity | Mechanism | Browser support | Detection |
|---|---|---|---|---|
| SA-CSS-IL-001: Drop cap overflow obscures consent lines | Critical | initial-letter:10 with page-bg fill overflows onto subsequent consent lines |
Chrome 110+, Safari 9+ | Check ::first-letter initialLetter value; compare drop cap height to paragraph content height |
| SA-CSS-IL-002: Negative margin-block-start upward shift | High | Drop cap pulled up over preceding sibling's consent text via negative margin | Chrome 110+, Safari 9+ | Check for negative margin-block-start on ::first-letter of consent-adjacent elements |
| SA-CSS-IL-003: initial-letter-align:hanging second-paragraph cover | High | Hanging alignment shifts drop cap to cover second consent paragraph | Chrome 110+, Safari 9+ (cross-browser rendering differences) | Detect initial-letter-align:hanging on consent paragraphs; test in Chrome and Safari separately |
| SA-CSS-IL-004: Zero-sink rise over preceding content | Medium | initial-letter: N 0 causes drop cap to extend above paragraph, obscuring step header |
Chrome 110+ | Detect initial-letter sink=0 on elements whose preceding siblings contain consent framing text |