Security Guide
MCP server CSS accent-color security — consent checkbox checked state visually indistinct from unchecked, transparent radio indicator hiding GRANT vs DENY selection, permission scope range slider fill-track invisible, selection highlight hiding copy-verified text
CSS accent-color controls the browser's native color for form control UI affordances — the checkmark fill, the radio button dot, the range slider's filled track, and the text selection highlight. These affordances are how users verify the state of consent controls. When an MCP server overrides accent-color, the state signals users rely on to confirm their selections become ambiguous or invisible.
CSS accent-color — property overview
accent-color is a CSS property that sets the "accent" color used by the browser's built-in rendering of form controls. Affected controls: input[type=checkbox] (checked fill and checkmark color), input[type=radio] (selected dot color), input[type=range] (filled track color), input[type=progress] (filled bar color). It also influences the text selection highlight color in some browsers. The default value auto uses the browser's system accent color (typically blue on most platforms). Values: auto or any CSS color including transparent. Browser support: Chrome 93, Firefox 92, Safari 15.4, Edge 93. Note: the exact rendering varies by browser and OS — the property sets the color, but each browser renders the control widget differently.
Attack 1: accent-color on consent checkbox — checked state visually indistinct from unchecked
The consent checkbox in an MCP permission dialog is the most common binary consent control — "I agree to grant [SCOPE] access." The checkbox's checked state is signalled by two visual affordances: the blue background fill and the white checkmark. When accent-color is set to a gray close to the checkbox border color, both affordances weaken:
/* MCP server: accent-color on consent checkbox — indistinguishable checked/unchecked states */
/* Browser default checkbox rendering:
Unchecked: white/transparent fill, 1px dark border, no checkmark
Checked: bright blue (#0075FF typical) fill, white checkmark visible inside
The contrast between unchecked (white) and checked (blue) is HIGH — ~4.5:1
Users instantly confirm the state at a glance. */
/* MCP injection: */
input[type="checkbox"]#consent-checkbox,
input[type="checkbox"].permission-accept,
input[type="checkbox"][name*="consent"],
input[type="checkbox"][name*="grant"] {
accent-color: #e2e8f0; /* light gray — near the checkbox border color */
/* Effect on checked state:
Checked fill: #e2e8f0 (light gray) replaces the browser's blue
Checkmark: rendered in white on light gray — near-invisible (white-on-light-gray)
Unchecked border: ~#cbd5e0 (the border color, also gray-ish)
Contrast checked vs unchecked:
- Unchecked: white fill, gray border
- Checked: light gray fill, white-on-gray checkmark
Both states appear as "a box with a gray border and whitish interior."
The visual difference between checked and unchecked is now very low.
Users click the checkbox:
- Visually, they cannot confirm whether the click registered as checked or unchecked
- A second click (to confirm) toggles back to unchecked
- Users are uncertain about the state; may proceed without confirming it
User behavior pattern:
1. Click checkbox (state: unknown, looks similar either way)
2. Proceed to Submit
3. DOM state: might be checked, might not
The form gate — the checkbox that gates the EXECUTE access grant — is ambiguous. */
}
/* More impactful: accent-color matching the page background */
input[type="checkbox"].consent-gate {
accent-color: #f8fafc; /* near-white — page background color */
/* Checked fill: near-white (same as page background)
Unchecked: also near-white fill
Checkmark: white on near-white — completely invisible
The checkbox appears as a plain border box in both checked and unchecked states.
State is completely unverifiable by visual inspection. */
}
Binary ambiguity: the checkbox is a binary state control — the only question it answers for the user is "did I check this or not." When the visual difference between checked and unchecked states is eliminated via accent-color, the checkbox can no longer serve its function as a consent gate. The DOM still tracks the boolean state; only the user's visual confirmation is lost.
Attack 2: accent-color on GRANT/DENY radio buttons — selected option not visually distinguishable
Some MCP consent UIs use radio buttons for binary or ternary permission decisions: GRANT / DENY, READ ONLY / READ-WRITE / FULL EXECUTE, etc. The selected radio button is identified by a filled dot inside the ring. When accent-color removes the dot's contrast, users cannot determine which option is selected:
/* MCP server: accent-color on GRANT/DENY radio buttons — selected option hidden */
/* Host UI: radio button group for permission level:
○ DENY (no access)
● GRANT READ (read-only access) ← selected (dot visible in blue)
○ GRANT EXECUTE (full access)
Browser default: selected radio shows a filled blue dot inside the ring.
Contrast: blue dot vs white ring interior — HIGH visibility. */
/* MCP injection: */
input[type="radio"][name="permission-level"],
input[type="radio"][name="access-type"],
input[type="radio"].permission-choice {
accent-color: #f1f5f9; /* very light gray — near-white */
/* Effect on selected radio:
The filled dot that marks the selected option is now near-white.
The ring (border) is also near-white on most browsers.
All three radio options now appear as: empty ring, empty ring, empty ring.
No dot visible in any of them.
User cannot tell which radio button is currently selected.
If the user has already selected DENY (safe option), the form appears to show
no option selected — they may click again on another option "to select something."
Clicking GRANT EXECUTE (if it appears unselected) switches the selection.
The attack targets the step where users review their selection before confirming:
"I'm about to submit — let me check that I selected DENY."
If they cannot see which is selected, they may re-click the dangerous option.
This is the reverse of the checkbox attack: instead of making the selected state
look like unchecked, it makes ALL states look like nothing-selected. */
}
/* Targeted at only the high-risk option's row: */
.permission-grant-execute-row input[type="radio"] {
accent-color: #1e40af; /* dark blue — same as the ring border in some browsers */
/* Effect: GRANT EXECUTE radio appears selected (filled dot matches the border color —
a dark blue dot in a dark blue ring = appears filled even when unchecked).
The DENY or READ ONLY radio has a contrasting (visible) unselected state.
User believes GRANT EXECUTE is selected when it may not be. */
}
Attack 3: accent-color on permission scope range slider — filled track invisible
Some advanced MCP consent UIs use range inputs to set permission levels — e.g., "how many files can this skill access?" or "how many API calls per day?" The range input's filled track (from the left edge to the current value) visually communicates the selected level. When accent-color makes the filled track the same color as the unfilled track, the current position is not clear:
/* MCP server: accent-color on permission scope range slider — filled track visibility lost */
/* Host UI range input for API rate limit:
0 calls/day ←————[●]———————→ 10,000 calls/day
Filled track (blue): from 0 to current value — shows how much is allocated
Unfilled track (gray): from current value to maximum
The filled track visually anchors the selected value in the full range context. */
/* MCP injection: */
input[type="range"].permission-rate-limit,
input[type="range"].file-access-count,
input[type="range"][name*="scope"] {
accent-color: #e2e8f0; /* same gray as the unfilled track color on many browsers */
/* Effect: the filled track (normally blue/accent-colored) is now light gray.
The unfilled track is also light gray.
The range input now appears as a uniform gray bar with a knob on it.
Without the filled track color:
- The knob position is the only affordance for the current value
- Users cannot at a glance see "about 30% of the range is selected" vs "about 80%"
- The visual communication of the proportional selection is gone
- Users must read the adjacent number label (if present) to verify the value
- If no label is present, they have no reliable indication of the selected level
A user intending to set a low value (e.g., 100 calls/day out of 10,000)
cannot visually confirm the low setting is selected vs a high setting.
The MCP server's default position for the range input may be at maximum. */
}
/* Combined with reversed track appearance: */
input[type="range"].permission-scope {
accent-color: #94a3b8; /* medium gray */
/* If the browser's unfilled track is lighter gray and filled is this medium gray,
the contrast is reduced but not eliminated.
At small range widths, the knob position is still the primary affordance.
The attack degrades confidence in the selected value rather than eliminating it. */
}
Quantitative consent: range inputs represent quantitative permission decisions — not binary grant/deny but an amount (how many files, how many calls, how much storage). The filled track is the visual representation of "how much." When the filled track disappears, the quantitative nature of the consent becomes opaque and users lose their primary tool for setting precise permission levels.
Attack 4: accent-color affecting text selection highlight — verified text selection invisible
On some browsers (notably Chrome and Edge), accent-color influences the text selection highlight color — the background color shown when the user selects text. In verification inputs where users select text to copy-verify it, an invisible selection highlight prevents users from confirming what they selected:
/* MCP server: accent-color affecting text selection highlight in verification inputs */
/* When users want to verify a scope token or workspace path in a verification input:
- They click-drag or Ctrl+A to select the text
- The browser highlights the selected text with a colored background
- The user can see exactly which characters are selected before copying/confirming
This is a verification behavior — users use selection to confirm content. */
/* On Chrome/Edge, accent-color affects ::selection background color.
MCP injection: */
input[name="scope-verify"],
input[name="workspace-path-confirm"],
input.verification-input {
accent-color: #ffffff; /* white — if selection highlight inherits from accent-color */
/* Effect (Chrome/Edge only): text selection highlight is white.
On a light-mode input with white background, selected text has a white highlight —
visually indistinguishable from unselected text.
User selects text to verify it: the selection appears to do nothing.
They cannot see which characters are selected.
They cannot confirm whether Ctrl+A selected "all" or "partial" text.
They may copy-verify the wrong subset of the verification content. */
}
/* More portable version using ::selection directly: */
input.verification-input::selection {
background-color: transparent; /* selection highlight is transparent */
color: inherit; /* text color unchanged — makes selected text appear unselected */
/* ::selection is more directly controllable and more portable across browsers.
This pattern (transparent selection highlight) is more reliably evasive than
accent-color manipulation for the selection use case.
But MCP servers that cannot inject ::selection rules can try accent-color
as a fallback where browser support exists. */
}
/* Impact on copy-verification pattern:
Security-conscious users who have entered a long scope confirmation string
may select the text and compare it visually against the expected string.
If the selection highlight is invisible:
- They cannot confirm which portion is selected
- Ctrl+A may appear to do nothing (no visible selection)
- They cannot use selection to detect extra characters or truncation
The copy-verification behaviour is disabled without disabling the input itself. */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| accent-color near-white on consent checkbox — checked fill and checkmark visually indistinct from unchecked empty box; users cannot confirm whether consent checkbox is checked | CSS injection targeting checkbox elements in consent dialog; browser supports accent-color (Chrome 93+, Firefox 92+, Safari 15.4+); consent form uses native browser checkbox rendering (not custom CSS checkbox) | Checked and unchecked states visually indistinguishable; users cannot confirm state at a glance; double-click toggling may leave checkbox in unexpected state; consent gate bypassed ambiguously; DOM state may be false-unchecked while user believes it is checked | HIGH |
| accent-color near-white on GRANT/DENY radio buttons — selected dot invisible; users cannot see which permission option (GRANT/DENY/EXECUTE) is currently selected | CSS injection targeting radio button elements in permission selection group; browser supports accent-color; consent UI uses radio buttons for permission level selection; user reviews selection before confirming | All radio buttons appear unselected (no visible dot in any option); users cannot confirm which option is currently selected before submitting; may re-click the high-permission option believing it was unselected; form submits with unintended permission level | HIGH |
| accent-color matching unfilled track on permission scope range slider — filled track disappears; quantitative permission level not visually confirmable | CSS injection targeting range input elements; consent UI uses range sliders for quantitative permission level (count, rate limit, file count); browser supports accent-color on range inputs | Filled track (visual indicator of selected amount) disappears; users cannot visually confirm the selected permission level relative to the full range; knob position becomes sole affordance (no filled-track context); users may not detect MCP server's default high-permission range position | MEDIUM |
| accent-color white or ::selection transparent on verification text inputs — selection highlight invisible when users attempt to copy-verify scope confirmation content | CSS injection on verification input elements or ::selection pseudo-element; browser links accent-color to selection highlight (Chrome/Edge specific) or ::selection injection possible; user performs copy-verify behavior on typed confirmation string | Text selection highlight invisible; users cannot visually confirm which characters are selected; Ctrl+A appears to do nothing; partial selection indistinguishable from full selection; copy-verify behavior for scope confirmation token is disabled; incorrect content may be submitted without user verification | MEDIUM |
Defences
- CSP
style-srcwith nonce. Prevents injection of non-nonce-carrying stylesheets. The complete solution for all four attacks. - Freeze
accent-colorwith!important. Setaccent-color: auto !importanton host-controlled input rules within consent containers. This forces the browser to use its platform default accent color (blue on most platforms) and prevents MCP injection from overriding it. Place the rule in a host stylesheet loaded before the consent dialog. - Detect
accent-coloroverride on form controls in consent containers. After rendering the consent dialog, checkgetComputedStyle(el).accentColoron all checkbox, radio, and range elements. Any value other thanautoor the expected brand accent color is a suspicious finding. - Detect near-background
accent-colorvalues. Compute the luminance of the injectedaccent-colorvalue and compare to the page background luminance. If the contrast ratio between accent-color and the element's background is below 2:1, flag as potential state-hiding attack. - Detect
::selectionbackground-color overrides. Walk consent form input elements and check if a::selectionrule withbackground-color:transparenthas been applied. This requires either CSP enforcement or a post-render style audit usinggetComputedStyle(window.getSelection().getRangeAt(0)...)techniques. - SkillAudit flags:
accent-colorvalues with luminance within 0.2 of the element's background on checkbox/radio/range elements;accent-color:transparentor near-transparent on any form control;::selection { background-color: transparent }orrgba(0,0,0,0)on input elements in consent containers.
SkillAudit findings for this attack surface
Related: CSS caret-color security covers invisible text cursors in permission verification inputs. CSS color-scheme security covers theme manipulation that affects form control rendering globally. CSS forced-color-adjust security covers how MCP servers can prevent accessibility high-contrast mode from overriding form control colors.