Security reference · CSS injection · @media print · Consent record manipulation
MCP server CSS @media print consent security — consent absent from printed records
CSS @media print rules apply only during printing — they have no effect on screen rendering and are never executed by automated scanners that check the rendered page. Malicious use: a @media print block that sets consent text to display: none, color: white, or uses page-break tricks to collapse the consent container to zero height — removing consent from any printed record the user makes. Users who print a confirmation of what they agreed to receive a document with no consent text. The screen render is unmodified and passes all screen-based audits.
@media print attack surface overview
| Attack ID | @media print technique | Effect on consent | Audit blind spot |
|---|---|---|---|
| SA-CSS-PM-001 | @media print { .consent-text { display: none } } |
Consent text completely absent from print output — the printed page contains no consent language, only the install confirmation | Screen audits never execute @media print rules; print preview is not checked by automated consent scanners |
| SA-CSS-PM-002 | @media print { .consent-text { color: white } } |
Consent text prints as white on white paper — invisible in printed output even though screen render is correct | Same as SA-CSS-PM-001; print-mode color values are not audited |
| SA-CSS-PM-003 | @media print { .consent-container { page-break-inside: avoid; height: 0 } } |
Consent container forced to zero height in print layout with page-break-inside:avoid — container collapses, content overflows and is clipped, consent absent from printed page | Page-break and height interactions in print layout are not evaluated by screen auditors |
| SA-CSS-PM-004 | Install button styled as bold heading in print | Install button text appears as a prominent "INSTALL" heading in the printed document, implying the printed page is a completed-agreement summary rather than an install confirmation | Print-specific heading promotion is a content misrepresentation attack; not caught by consent visibility scanners |
Record falsification: print media attacks do not prevent initial consent — they remove the consent from the record the user retains. In legal and compliance contexts, consent must be documented. An MCP server that hides consent from printed records creates a situation where: the user was shown consent, agreed, but has no printed evidence of what they agreed to. The server operator has a log of the agreement; the user has no printable record of its content.
Attack 1: display: none in print (SA-CSS-PM-001)
The simplest print-mode consent attack: a @media print rule that sets the consent element to display: none. The screen render is unaffected. Print preview and PDF export both execute print rules, so any print-to-PDF workflow also produces a document without consent text.
/* SA-CSS-PM-001: consent text hidden in print output */
/* Screen: consent is visible */
.consent-text {
color: #1a1a1a;
font-size: 14px;
}
/* Print: consent is removed */
@media print {
.consent-text {
display: none;
/* The printed page contains:
- Page header
- Install confirmation: "You have installed [Server Name]"
- List of granted permissions (if present)
- Footer with date
- No consent language
Users printing this page as a record of their agreement
receive a document containing no consent terms. */
}
}
/* Detection: parse @media print blocks, check selectors against consent elements */
SA-CSS-PM-001 (High). SkillAudit detects this by parsing loaded stylesheets for @media print rule blocks, extracting all selectors, and checking whether any selector matches a consent-text element. Rules applying display: none, visibility: hidden, opacity: 0, or height: 0 to consent-matching selectors are flagged as high severity.
Attack 2: white text in print output (SA-CSS-PM-002)
Instead of removing the consent element, this variant makes it visually invisible in the printed output by setting the text color to white or a color very close to white paper. The element occupies print layout space (so pagination is unchanged) but prints as invisible ink on white paper.
/* SA-CSS-PM-002: consent text color:white in print output */
@media print {
.consent-text {
color: white; /* white ink on white paper */
background: transparent; /* no background to contrast against */
/* Alternative: color matching common paper stocks */
/* color: #fffef0; */ /* cream paper background */
/* color: #f5f5f0; */ /* off-white paper */
/* The element occupies its normal layout area.
Page layout, pagination, and header/footer are undisturbed.
The consent section appears as blank white space in the printed output.
A user who expects "the printed page has the consent terms I agreed to"
sees a blank gap where the consent should be. */
}
}
/* This can also be used to selectively hide only the most sensitive permission lines:
@media print {
.permission-item.filesystem-access { color: white; }
.permission-item.network-access { color: white; }
.permission-item.shell-execution { color: white; }
/* Only the high-risk permissions are invisible in print */
}
Attack 3: page-break + height: 0 collapse (SA-CSS-PM-003)
Page-break properties control how the print layout algorithm allocates page boundaries. page-break-inside: avoid instructs the browser not to split the element across page boundaries. When combined with height: 0 set in the print media query, the result is a container that the print engine tries to keep intact (avoid-break) but that has zero height — the content overflows and is clipped. The element is in the print layout tree but contributes zero visible content.
/* SA-CSS-PM-003: page-break + height:0 consent container collapse */
@media print {
.consent-container {
page-break-inside: avoid; /* do not split this element across pages */
height: 0; /* zero height — content overflows and clips */
overflow: hidden; /* clip the overflow */
/* Print engine behavior:
- page-break-inside:avoid → element must stay on one page
- height:0 → element contributes zero vertical space
- overflow:hidden → content that would overflow the 0px height is clipped
Result: the consent container is in the layout tree but renders as nothing.
Print page contains no consent content.
This is harder to detect than display:none because:
- The element is still display:block (not display:none)
- The element is not visibility:hidden
- Height:0 in print context does not trigger "hidden element" detectors
that rely on offsetHeight (offsetHeight is a screen metric) */
}
}
/* CSS page-break properties also include break-inside (the modern equivalent):
@media print {
.consent-container {
break-inside: avoid;
max-height: 0;
overflow: hidden;
}
} */
Attack 4: install button as agreement heading in print (SA-CSS-PM-004)
This is a content misrepresentation attack rather than a content hiding attack. The install button is styled in the print output to appear as a bold document heading — "INSTALL" displayed as an h1-equivalent heading. Combined with consent text being hidden or moved, the printed document reads as a completed-agreement summary: the prominent "INSTALL" heading implies the document records a finalized agreement, rather than showing the install confirmation dialog where consent was given.
/* SA-CSS-PM-004: install button as agreement heading in print output */
/* Screen: install button appears as a button */
.install-button {
padding: 12px 24px;
background: var(--accent);
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
}
/* Print: install button text appears as bold heading */
@media print {
.install-button {
display: block;
font-size: 24px;
font-weight: 900;
color: black;
background: none;
border: none;
padding: 0;
margin: 32px 0 8px;
text-transform: uppercase;
/* Appears as: INSTALL [server name]
In a print document, this reads like a section heading
"INSTALL [server name] — Agreement confirmed" */
}
/* Add print-only content to reinforce the misrepresentation */
.install-button::after {
content: " — Agreement confirmed";
font-size: 14px;
font-weight: normal;
color: #333;
}
/* Combined with consent text hidden: the printed document looks like:
[Page header]
INSTALL [Server Name] — Agreement confirmed
Date: [timestamp]
[Footer]
No consent language anywhere. */
}
Detection algorithm
/* Detection for SA-CSS-PM patterns — @media print rule analysis */
function detectPrintMediaConsentAttacks() {
const CONSENT_KEYWORDS = ['authorize', 'agree', 'terms', 'consent', 'permission', 'grant'];
const findings = [];
Array.from(document.styleSheets).forEach(sheet => {
try {
Array.from(sheet.cssRules || []).forEach(rule => {
if (rule.type !== CSSRule.MEDIA_RULE) return;
const media = rule.conditionText || rule.media.mediaText;
if (!media.includes('print')) return;
// Found a @media print block
Array.from(rule.cssRules).forEach(innerRule => {
if (innerRule.type !== CSSRule.STYLE_RULE) return;
const style = innerRule.style;
const selector = innerRule.selectorText;
let matchesConsent = false;
try {
document.querySelectorAll(selector).forEach(el => {
const text = (el.textContent || '').toLowerCase();
if (CONSENT_KEYWORDS.some(k => text.includes(k))) matchesConsent = true;
});
} catch {}
if (!matchesConsent) return;
// Check for hiding techniques
if (style.display === 'none') {
findings.push({ id: 'SA-CSS-PM-001', severity: 'HIGH', selector,
desc: 'display:none on consent element in @media print — consent hidden from printed records' });
}
if (style.color) {
const c = style.color.toLowerCase();
if (c.includes('white') || c === '#fff' || c === '#ffffff'
|| c === 'rgb(255, 255, 255)') {
findings.push({ id: 'SA-CSS-PM-002', severity: 'HIGH', selector,
desc: 'color:white on consent element in @media print — invisible on white paper' });
}
}
if (style.height === '0' || style.maxHeight === '0'
|| (style.overflow === 'hidden' && style.height === '0px')) {
findings.push({ id: 'SA-CSS-PM-003', severity: 'HIGH', selector,
desc: 'height:0 + overflow:hidden on consent container in @media print — consent collapsed in print layout' });
}
if (style.visibility === 'hidden' || style.opacity === '0') {
findings.push({ id: 'SA-CSS-PM-001', severity: 'HIGH', selector,
desc: 'visibility:hidden or opacity:0 on consent element in @media print' });
}
});
});
} catch {}
});
return findings;
}
SkillAudit findings for SA-CSS-PM
display: none or visibility: hidden on a consent-matching selector inside @media print — consent text completely absent from printed output.
color: white or other near-white color value on consent element inside @media print — consent text invisible on white paper.
height: 0 + overflow: hidden on consent container in @media print — page-break interaction collapses consent content in print layout.
@media print combined with consent text hiding — document misrepresentation as completed-agreement summary.
For related media-query attacks, see SA-CSS-FC (@media forced-colors) for high-contrast mode consent bypass. To audit your MCP server for all SA-CSS-PM print-media patterns, run a free SkillAudit scan.