Security Guide
MCP server CSS @media (grid) consent security — character cell display type attacks
The CSS grid media feature distinguishes character cell displays (grid: 1 — terminal emulators, some embedded systems, legacy text-mode environments) from bitmap displays (grid: 0 — all standard browsers and audit tools). Every automated security scanner runs on a grid: 0 (bitmap) display. Consent-hiding CSS inside a @media (grid: 1) block is invisible to all standard audit tools and active only in character-cell environments where rendering constraints make CSS-based attacks more effective.
How @media (grid) works
The grid media feature is a CSS Media Queries Level 2 legacy feature. It takes a boolean value: 0 for bitmap displays (the rendering unit is a pixel; each pixel can display any color) and 1 for character cell displays (the rendering unit is a fixed-width character position; each cell displays one character from a character set). All modern web browsers report grid: 0. Terminal emulators rendering HTML, some embedded kiosk displays, and historical environments like the TTY class of devices report grid: 1. Because all standard audit tooling runs in bitmap-display environments, the grid: 1 block is functionally an audit-bypass conditional: its contents are never evaluated during a standard security scan.
/* Grid media feature — boolean discrete feature */
@media (grid: 0) { /* bitmap display — all standard browsers, all audit tools */ }
@media (grid: 1) { /* character cell display — terminal-like environments */ }
/* Behavior notes:
- All desktop and mobile browsers: grid:0
- matchMedia('(grid: 1)') → false in all modern browsers
- @media (grid) shorthand → same as @media (grid: 1) → false
- Static CSS scanner: parses the block structure, cannot evaluate condition
- The grid:1 block is never activated in standard audit environments
*/
Complete audit bypass: Because no standard browser or headless audit tool activates grid: 1, any CSS placed inside a @media (grid: 1) block is structurally invisible to every automated consent audit. The condition never fires in the tools used to detect CSS-based attacks. While modern browsers don't activate this condition, static CSS parsers still parse the block — meaning the rules exist in the stylesheet and could activate in any future character-cell rendering environment.
Attack 1 (CRITICAL): consent replaced with empty content on character cell display
In character cell environments, the content property can replace the displayed text of an element (for elements with display: block the CSS Generated Content spec allows content to control pseudo-element output; for actual text nodes, the attack uses ::before and ::after with text covering the element). Inside a grid: 1 block, the consent element text can be covered by a pseudo-element overlay that renders blank space in the character cell rendering model, filling the character positions where consent text would appear.
/* Attack 1: content overlay covers consent in character cell context */
@media (grid: 1) {
/* ::before covers the consent text area with blank characters */
.consent-dialog .consent-body::before {
content: "\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0" /* 10 NBSP */
"\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0" /* 10 NBSP */
"\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0\00A0"; /* 10 NBSP */
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: inherit; /* same background as parent */
/* In character cell rendering: covers the consent text with blank chars */
/* The consent element DOM text content is intact */
/* Character cell renderer draws the ::before overlay over it */
}
.consent-dialog { position: relative; overflow: hidden; }
}
/* Bitmap display auditor (grid:0):
@media (grid:1) block never activated
.consent-body::before not rendered
Consent text visible → audit passes
Character cell renderer (grid:1):
::before overlay rendered with blank characters
Consent text obscured by blank character overlay */
Attack 2 (HIGH): consent hidden from bitmap displays — targeting all modern users
The higher-impact inverse: using @media (grid: 0) to hide consent from all bitmap display users — which is effectively every modern browser user. The grid: 0 condition fires universally. Consent hidden inside this block is invisible to all real users. The desktop auditor, also running on a bitmap (grid:0) display, does activate this condition — so this attack is actually detectable. However, it creates a plausible cloaking argument: "we only show the consent in character-cell mode." The audit must verify the semantic intent of display: none on consent in a grid: 0 block.
/* Attack 2: consent hidden on all bitmap (grid:0) displays = all real users */
@media (grid: 0) {
.consent-dialog {
display: none;
visibility: hidden;
}
}
/* Note: this attack IS visible to desktop auditors (grid:0 fires on desktop)
It can be detected by: find @media (grid: 0) blocks with display:none on consent
It targets ALL modern browser users
The "only show in character cell mode" argument is a pretext attack */
Attack 3 (HIGH): extreme tab-size displacement in character cell context
In character cell displays, the tab-size property controls the width of the horizontal tab character (\t, U+0009) in terms of character cells. Setting tab-size: 9999 in a grid: 1 block causes any consent text that contains tab characters to be displaced 9999 character positions horizontally. If consent text is dynamically constructed from a server response that includes tab characters (e.g., formatted legal text), the consent is pushed far off the visible portion of the character display. The DOM textContent is intact; only the visual rendering is affected.
/* Attack 3: extreme tab-size displacement in character cell context */
@media (grid: 1) {
.consent-dialog .consent-body {
/* 9999 character cells of horizontal tab displacement */
tab-size: 9999;
white-space: pre; /* preserves tab characters */
}
}
/* Effect in character cell environment:
If consent text contains tab characters:
"You agree to\tthe following terms..."
→ "You agree to" + 9999 blank chars + "the following terms..."
The text after the tab is 9999 columns off screen
overflow: hidden (or terminal viewport) clips it without indication
Bitmap browser (grid:0): block not activated, tab-size remains default
Pixel rendering: tab-size:9999px would be detectable in layout check */
Attack 4 (MEDIUM): compound grid + color — targeting character cell environments specifically
Combining the grid: 1 gate with color manipulation for character cell environments. Character cell displays typically have a limited set of available foreground and background color pairs; a color set to value outside the available palette may be mapped to the nearest available color, which in some terminals means text and background are mapped to the same color index.
/* Attack 4: color index approximation in character cell rendering */
@media (grid: 1) {
.consent-dialog .consent-body {
/* In 8-color terminal environments, RGB values are quantized to
the 8 ANSI colors. Choosing a color that quantizes to the same
terminal color as the background makes text invisible. */
color: rgb(1, 1, 1); /* quantizes to ANSI black */
background-color: rgb(0, 0, 0); /* also ANSI black */
/* 8-color terminal: both map to color index 0 = black = invisible */
/* Bitmap browser: rgb(1,1,1) on rgb(0,0,0) = near-black on black */
/* WCAG contrast: 1.02:1 → fail — but auditor never activates this block */
}
}
/* Extended variant with 256-color terminal targeting */
@media (grid: 1) {
.consent-dialog {
color: rgb(0, 95, 0); /* closest 256-color: color index 22 (#005f00) */
background: rgb(0, 95, 0); /* same 256-color index */
/* → both map to index 22 in 256-color terminals → invisible */
}
}
Detection
/* Enumerate @media (grid) rules */
function auditGridMediaRules() {
const attacks = [];
for (const sheet of document.styleSheets) {
try {
for (const rule of sheet.cssRules) {
if (rule instanceof CSSMediaRule) {
const cond = rule.conditionText || rule.media.mediaText;
/* Match grid:0, grid:1, and bare (grid) */
if (/\bgrid\s*(?::\s*[01])?\b/.test(cond)) {
const innerRules = Array.from(rule.cssRules).map(r => r.cssText);
attacks.push({
condition: cond,
isCharCell: /grid\s*:\s*1/.test(cond) || /\(grid\)/.test(cond),
isBitmap: /grid\s*:\s*0/.test(cond),
innerText: innerRules.join('\n'),
affectsConsent: /consent|terms|disclosure|privacy|accept|decline/i
.test(innerRules.join(' '))
});
}
}
}
} catch (e) { /* cross-origin */ }
}
return attacks;
}
/* Check current grid media value */
const isGrid1 = window.matchMedia('(grid: 1)').matches; /* always false in browsers */
const isGrid0 = window.matchMedia('(grid: 0)').matches; /* always true in browsers */
/* Note: grid:0 blocks ARE activated in browser scanners
grid:1 blocks are NEVER activated in browser scanners
Both should be flagged when they contain consent-affecting rules */
const gridAttacks = auditGridMediaRules().filter(a => a.affectsConsent);
if (gridAttacks.length > 0) {
console.error('CSS grid media feature used on consent elements:', gridAttacks);
}
| Attack | Severity | Visible to standard scanner? | Detection method |
|---|---|---|---|
| Content overlay covers consent in character cell (grid:1) | CRITICAL | No — grid:1 never activated | Enumerate CSSMediaRule for grid media feature; flag consent selectors in grid:1 blocks |
| Consent hidden from all bitmap users (grid:0) | HIGH | Yes — grid:0 activates on scanner | Flag display:none/visibility:hidden on consent in grid:0 block |
| Extreme tab-size displacement in character cell (grid:1) | HIGH | No — grid:1 not activated | Flag tab-size values > threshold inside grid:1 blocks affecting consent |
| Color index matching in character cell (grid:1) | MEDIUM | No — grid:1 not activated | Static enumeration of color values in grid:1 blocks; compute terminal-quantized contrast |