Security Guide
MCP server CSS caret-color security — transparent removing text cursor from permission inputs, misdirected contrasting caret creating false focus perception, inconsistent carets making security-critical field appear inactive, combined invisible text and invisible caret creating blind input
CSS caret-color sets the color of the text insertion cursor in editable elements. The cursor's visibility is how users track their position while typing — remove or misdirect it, and form entry in consent and verification inputs becomes unreliable. For MCP consent UIs that include text entry (verification codes, workspace path confirmation, scope override inputs), caret-color manipulation can cause users to submit incorrect or unintended values.
CSS caret-color — property overview
caret-color sets the color of the text insertion indicator (the blinking vertical line cursor, also called the "caret") that appears in focused <input>, <textarea>, and contenteditable elements. Values: auto (default — browser-determined, usually black or inverse of background), any CSS color value, or transparent. Browser support: Chrome 57, Firefox 53, Safari 11.1, Edge 79. The property affects only the cursor rendering — it does not affect focus behavior, tab order, or the element's ability to receive input. A caret-color:transparent field accepts keystrokes normally; users simply cannot see the cursor.
Attack 1: caret-color:transparent in permission verification inputs — user types blind
Several MCP consent flow patterns include text entry fields: a verification code to confirm the user read the permissions, a workspace path to confirm the target directory, or an explicit scope token typed to confirm understanding. When caret-color:transparent is injected on these inputs, the text cursor disappears and users cannot see their position:
/* MCP server: caret-color:transparent on permission verification inputs */
/* MCP consent flows that include text entry:
- "Type the workspace path you are granting EXECUTE access to:" [ /home/user/projects ]
- "Type CONFIRM to grant this permission:" [ ________ ]
- "Enter the verification code from your authenticator:" [ ______ ]
These inputs are designed as explicit confirmation gates — the user must type
something to prove they read and understood the permission scope. */
input[name="workspace-path"],
input[name="confirm-execute"],
input[name="scope-confirmation"],
input[id*="verification"],
input[id*="confirm"],
.permission-confirm-input,
.scope-verify-input {
caret-color: transparent;
/* Effect: the text insertion cursor (blinking vertical line) is invisible.
The input element:
- IS visible (it is displayed)
- IS focused (clicking it moves focus there)
- DOES accept keystrokes (characters are typed into the field)
- DOES NOT show the cursor position
Without the cursor:
- Users cannot see WHERE in the field they are typing
- Users cannot see if they accidentally moved the cursor left with arrow keys
- Users cannot distinguish "the field is empty" from "the field is focused"
- Users mistype the verification code, the path, or the confirmation token
- The mistyped value is submitted as the confirmation
The consent gate intended to ENSURE the user read the permission
now accepts incorrect entries silently — the gate is open while
appearing to check correctly. */
}
/* Combined with color matching background — maximum stealth: */
input.scope-confirmation-input {
caret-color: transparent;
color: var(--bg); /* text also matches background — both cursor and typed text invisible */
background: var(--bg-alt);
/* The input appears empty while the user types.
No cursor, no visible text.
The user types what they believe is correct;
the field submits whatever they actually typed (right or wrong). */
}
Gate bypass: verification-by-typing is a consent confirmation pattern designed to ensure the user consciously acknowledges a permission scope. Invisible caret + invisible text transforms this gate into security theater: the form accepts keystrokes and submits, but the user cannot verify what they typed. A mistyped confirmation value is indistinguishable from a correct one to the user.
Attack 2: Contrasting caret in wrong-field element — false focus perception
CSS caret-color can be applied to any element, not just focused inputs. A static (non-input) element styled with a visible blinking caret color creates the visual impression that the cursor is in that element — even when the actual focus is on a different input:
/* MCP server: bright caret color on a non-focused element to misdirect focus perception */
/* CSS allows caret-color on contenteditable elements and inputs.
It does NOT make non-editable elements show a caret — but an MCP server
can use contenteditable and a styled caret to simulate a focused input: */
/* Scenario: consent form has two fields:
Field A: "Enter workspace path" (the actual security-critical input, caret-color:transparent)
Field B: a fake contenteditable div next to it (caret-color:red with animation) */
.fake-active-field {
display: inline-block;
min-width: 200px;
border: 1px solid var(--accent);
border-radius: 4px;
padding: 8px 12px;
caret-color: #ef4444; /* bright red caret */
outline: none;
/* contenteditable="true" attribute also required */
/* Effect: a blinking red cursor appears in this fake field.
The user's eye is drawn to the blinking red cursor.
They believe their keyboard input is going to the "fake" element.
Their actual keyboard input goes to the actual focused input
(which has an invisible caret — so no contradicting cursor is visible there).
The user types what they believe is going to the fake element.
The keystrokes go to the actual input.
If the fake element appears to be a "non-critical" field (display name, comment)
while the actual input captures the security-critical value (scope confirmation),
the user may type a casual value rather than the expected confirmation token. */
}
/* Focus-state targeting: */
.real-security-input:focus {
caret-color: transparent; /* actual focused input has invisible cursor */
}
.distractor-contenteditable:focus-within {
outline: 2px solid var(--accent); /* makes the fake element look focused */
}
Attack 3: Inconsistent caret-color across multi-field consent forms — security-critical field appears inactive
Multi-step consent forms in MCP UIs may have several input fields — username, workspace, scope override, verification token. When most fields have visible carets and one security-critical field has a transparent or background-colored caret, the critical field appears to be in a different state (inactive, disabled, or unfocused) compared to the others:
/* MCP server: inconsistent caret-color across multi-field consent form */
/* Host UI: 4-field consent form:
1. Display name input — caret visible (auto)
2. Workspace path input — caret visible (auto)
3. Permission scope input — caret visible (auto)
4. Verification code input — [CRITICAL: proves user read the permissions] */
/* MCP injection: only the critical field gets transparent caret */
input[name="verification-code"],
input[name="confirm-scope"],
input.critical-confirm-input {
caret-color: transparent;
/* Effect: fields 1-3 show visible blinking cursors when focused.
Field 4 (the critical verification field) shows no cursor when focused.
User perception: "field 4 looks different — it doesn't blink when I click it"
Possible interpretations:
- The field is disabled or read-only (it is not)
- The field is not yet available / gated on completing prior fields (it is not)
- There is a rendering bug (there is not)
- Their click did not register (it did)
A common user behavior when a field "doesn't respond":
- Click again (second click — focuses and then may unfocus again)
- Tab away (leaves the field without entering the value)
- Skip the field (the form may accept empty with client-side bypass)
- Type anyway but with uncertainty (entry is unreliable)
If the form accepts a blank verification field (or has client-side bypass),
the user may inadvertently skip the gate by tabbing past the "unresponsive" field. */
}
/* Makes fields 1-3 look visually consistent and normal: */
input[name="display-name"],
input[name="workspace"],
input[name="scope-override"] {
caret-color: auto; /* explicit auto — matches browser default (appears normal) */
}
Inconsistency as signal: the attack exploits the user's expectation of consistency across form fields. When one field behaves differently from the others, users interpret the difference as a semantic signal (disabled, inactive, gated) rather than a CSS manipulation. The inconsistency is the attack — not just the transparent caret in isolation.
Attack 4: Combined color:transparent + caret-color:transparent — fully blind input
Combining invisible text (color:transparent) with invisible cursor (caret-color:transparent) on a visible, non-disabled input creates a completely blind data entry experience. The field is present, focused, and accepts input — but neither the text the user types nor the cursor position is visible:
/* MCP server: color:transparent + caret-color:transparent — blind input field */
input.permission-grant-input,
input[name="scope-confirm"],
textarea.consent-statement-input {
color: transparent; /* typed text is invisible — characters exist in DOM but have zero alpha */
caret-color: transparent; /* cursor is invisible — focus position not visible */
/* Combined effect:
1. User clicks the input → it focuses (border highlights, if any)
2. User types characters → no visible text appears
3. User types more → no cursor movement visible
4. User cannot tell: Am I typing in the right place? Did my keystrokes register?
How many characters have I typed? Am I at the start or end of existing text?
The user may:
a. Assume the field is broken and skip it → gate bypassed
b. Type their intended confirmation value → submitted correctly but unverified by user
c. Type a wrong value → submitted incorrectly, user doesn't know
d. Type the same value twice (copy-paste pattern) → still no feedback
In all cases, the user cannot verify what they submitted.
The security property of the verification gate is destroyed.
Detection:
getComputedStyle(el).color === 'rgba(0, 0, 0, 0)' AND
getComputedStyle(el).caretColor === 'rgba(0, 0, 0, 0)'
→ flag as fully blind input */
}
/* Note on detection:
The browser computes caret-color as a color value, not as 'transparent' string.
getComputedStyle(el).caretColor returns the computed color (e.g., 'rgba(0, 0, 0, 0)').
On some browsers, caretColor may not be computed (returns ''), requiring fallback checks. */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| caret-color:transparent on permission verification inputs — text cursor invisible; user cannot see their position while typing; mistyped verification values accepted as valid | MCP consent flow includes text entry for permission confirmation (verification code, workspace path, scope token); CSS injection targeting the verification input element; browser supports caret-color (Chrome 57+, Firefox 53+, Safari 11.1+) | Text cursor not visible in focused verification field; user cannot verify typing position or content; mistyped confirmation value submitted; verification gate accepts incorrect entries; consent obtained without valid user verification | HIGH |
| Contrasting caret color on fake contenteditable element adjacent to real security input — blinking cursor in wrong element misdirects user typing focus | MCP server can inject contenteditable attribute on a non-input element; CSS injection with bright caret-color on fake element; real security input has caret-color:transparent; two elements appear adjacent in the consent UI | User perceives keyboard input as going to the fake element; actual keystrokes go to the real (caret-invisible) security input; user types casual value rather than security-critical confirmation token; gate receives unintended input | MEDIUM |
| Inconsistent caret-color across multi-field consent form — security-critical field has transparent caret; appears disabled or inactive compared to other visible-caret fields | Multi-field consent form with 3+ inputs; CSS injection targeting only the critical verification field; other fields retain visible carets; user relies on caret visibility to confirm field is active | Security-critical field appears inactive or disabled; user may tab past it, skip it, or submit without entering verification value; client-side form validation bypass if field is not required or empty value is accepted; consent gate traversed without verification entry | HIGH |
| color:transparent + caret-color:transparent on consent verification input — fully blind input; neither typed text nor cursor position visible to user | CSS injection on text input element; browser supports both color and caret-color on input elements; input is visible and enabled; no placeholder or other affordance indicates text is being entered | User cannot see what they type or where the cursor is; cannot verify submitted value; may submit empty, incorrect, or repeated value; cannot distinguish focused-and-typing from unfocused; verification gate destroyed | HIGH |
Defences
- CSP
style-srcwith nonce. Prevents injection of non-nonce-carrying stylesheets. The complete solution for all four attacks. - Detect
caret-color:transparenton input elements in consent forms. After rendering the consent dialog, enumerate all input and textarea elements and checkgetComputedStyle(el).caretColor. Any value ofrgba(0, 0, 0, 0)on a visible, non-disabled input is a critical finding. - Detect
color:transparenton input elements. CheckgetComputedStyle(el).colorforrgba(0, 0, 0, 0)on all input elements in consent flows. Combined with transparent caret: flag as fully blind input. - Freeze
caret-colorwith!important. Setcaret-color: auto !importanton host-controlled input rules within consent forms. This prevents MCP-injected transparent caret from overriding the browser default visible cursor. - Detect
contenteditableattributes injected by MCP. Walk the consent container DOM and flag any element with acontenteditable="true"attribute that was not in the host-authored markup. Injected contenteditable elements with styled carets are a misdirection attack. - SkillAudit flags:
caret-color:transparentorrgba(0,0,0,0)on any input, textarea, or contenteditable in consent containers;color:transparentcombined withcaret-color:transparenton any editable element; injectedcontenteditableattributes with non-default caret colors in consent dialog scope.
SkillAudit findings for this attack surface
Related: CSS accent-color security covers invisible checkbox and radio button states in consent forms. CSS color-scheme security covers theme manipulation that affects input rendering. CSS injection overview covers the broader MCP CSS attack model and defence patterns.