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

HIGH SA-CSS-PM-001: display: none or visibility: hidden on a consent-matching selector inside @media print — consent text completely absent from printed output.
HIGH SA-CSS-PM-002: color: white or other near-white color value on consent element inside @media print — consent text invisible on white paper.
HIGH SA-CSS-PM-003: height: 0 + overflow: hidden on consent container in @media print — page-break interaction collapses consent content in print layout.
MEDIUM SA-CSS-PM-004: Install button or action element promoted to heading-level prominence in @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.