Security Guide

MCP server CSS @page rule security — consent text hidden in print view, @page margin-box content injection, named page forced pagination, window.print() forced print dialog

The CSS @page at-rule controls the layout and styling of printed pages and print preview renderings. For MCP servers whose consent dialogs produce printable audit records — or whose consent dialogs are displayed in environments where users might print the permission dialog as documentation — the @page family creates four distinct attack surfaces: hiding consent elements in the print view, injecting content into @page margin boxes that appears in printed records, using named page selectors to force pagination separating disclosure from approval, and triggering window.print() at install time to intercept the print render pipeline.

CSS @page — property overview

The @page at-rule applies styles to the page box used when a document is printed or rendered in a print preview. It accepts a subset of CSS properties: margin, size, page-orientation, and the pagination properties (break-before, break-after, break-inside). It also supports @page margin boxes — named pseudo-elements (@top-left, @top-right, @bottom-center, @left-middle, etc.) that render content in the page margins outside the main content area. Named pages allow different page styles for different sections via the page property on elements. The security relevance of @page for MCP consent dialogs comes from the gap between what is shown on screen during the interactive consent flow and what is printed when a user saves the consent record.

Attack 1: @media print { display: none } — hiding consent text in print view

An MCP server can inject a @media print rule that sets display: none on consent text elements. On screen, the consent text is fully visible during the interactive consent flow — all screen-mode security checks pass. But if the user prints the consent dialog as a record (either manually, or via a print-on-consent feature that some enterprise compliance tools implement), the printed output shows a blank consent section where the permission disclosure was. The audit record — the printed page — does not contain the permissions that were actually disclosed and approved. This is a printed-record integrity attack rather than a screen-visibility attack.

/* Attack 1: @media print hides consent disclosure from printed records */

@media print {
  /* Hide the security disclosure and high-risk indicator in print view */
  .security-disclosure,
  .high-risk-notice,
  .permission-scope-list,
  .mcp-consent-body {
    display: none !important;
  }

  /* Show attacker-controlled replacement content in print view only */
  .approve-button::after {
    content: "User approved standard access. See attached documentation." !important;
    display: block !important;
    font-size: 11pt !important;
    color: #666 !important;
    margin-top: 8pt !important;
  }
}

/* Effect:
   Screen view (during consent):
   - "HIGH RISK: MCP Server requests EXECUTE access to your filesystem" → VISIBLE
   - Permission scope list: "read, write, delete, execute (all files)" → VISIBLE
   - Security disclosure → VISIBLE
   - User reads disclosure and clicks Approve

   Print view (audit record):
   - "HIGH RISK: MCP Server requests EXECUTE access to your filesystem" → HIDDEN
   - Permission scope list → HIDDEN
   - Security disclosure → HIDDEN
   - Approve button shows: "User approved standard access. See attached documentation."
   - Printed record does not reflect what was actually disclosed

   Detection:
   - Scan @media print CSS rules for 'display: none' on consent-selector elements
   - Flag any @media print rule that hides elements that are visible on screen
   - Check for @media print rules that inject content via ::after/::before on consent elements */

The printed audit record is the attack target. This attack does not interfere with the interactive consent flow — the user sees the correct disclosure on screen. The attack corrupts the record of what was consented to. For enterprise deployments that log consent by printing or PDF-exporting the consent dialog, the printed record becomes the authoritative compliance artifact. An attacker whose MCP server is approved via a falsified print record has a documented (but false) consent trail.

Attack 2: @page margin-box content injection — adding attacker text to printed record margins

The @page margin boxes (e.g., @top-right, @bottom-center) allow injecting content into the page margins of every printed page. For a consent dialog printed as an audit record, margin-box content appears outside the main content area on the printed page. An MCP server can inject margin-box content that appears to be part of the official printed record — adding false headers, watermarks, approval references, or legal language that did not appear in the original consent dialog.

/* Attack 2: @page margin-box content injection into printed records */

@page {
  @top-right {
    content: "Approved by Legal Team — Ref: MCP-2026-APPROVED-001" !important;
    font-size: 8pt;
    color: #333;
    font-family: Arial, sans-serif;
  }

  @bottom-center {
    content: "Authorization granted per enterprise security policy §4.2" !important;
    font-size: 7pt;
    color: #666;
  }
}

/* Effect on printed consent record:
   - Top-right margin: "Approved by Legal Team — Ref: MCP-2026-APPROVED-001"
     appears on every printed page, as if it came from an official system
   - Bottom margin: "Authorization granted per enterprise security policy §4.2"
     appears as a legal authorization statement

   Neither string appeared in the on-screen consent dialog.
   A reviewer receiving the printed record as a compliance artifact
   sees what appears to be a pre-approved, legally sanctioned consent.

   Detection:
   - Inspect @page rules in document.styleSheets for content property in margin boxes
   - Flag any @page margin-box 'content' value that is not empty/normal
   - Cross-reference margin-box content against known legitimate watermarks */

Attack 3: named @page + forced breaks — separating disclosure from approval on different pages

Named page selectors allow different sections of a document to use different @page styles. Combined with break-before: page, an MCP server can force the security disclosure onto page 1 and the approval action onto page 2 of the printed record. For interactive consent on screen, this has no effect. But when the dialog is printed for compliance documentation, the approval signature or timestamp appears on a different page from the permission scope — making it harder to connect the specific permissions disclosed to the specific approval action. For compliance workflows where page 1 is the disclosure and page 2 is the signature page, this is the expected structure — but when injected by an attacker, page 1 may show only innocuous content while page 2 shows the approval.

/* Attack 3: named page + forced break separating disclosure from approval */

/* Print only: page 1 shows benign content, page 2 shows approval */

@media print {
  /* Assign the high-risk disclosure to a named page with different margins */
  .security-disclosure {
    page: disclosure-page;
    break-before: page !important;  /* disclosure starts new page */
    break-after: page !important;   /* and ends the page (nothing after it on page 1) */
  }

  /* Override the disclosure page to show minimal content */
  @page disclosure-page {
    margin: 80mm;  /* huge margins squish the disclosure content into a small area */
    @bottom-left {
      content: "(continued on next page)";
      font-size: 8pt;
    }
  }

  /* The approval confirmation appears on the default (non-named) page */
  .approve-confirmation {
    page: auto;  /* default page styles */
    break-before: page !important;
  }
}

/* Printed output:
   Page 1: (80mm margins) — cramped disclosure text in tiny central area
   Page 2: Full-width approval confirmation, timestamps, reference numbers
   A reviewer may process the approval page (page 2) before reviewing
   the disclosure page (page 1), or may not notice that page 1 was intentionally
   compressed to reduce readability of the permission scope. */

Attack 4: window.print() at install time — forcing the print dialog

Calling window.print() from within an MCP server's injected script at install time (during the consent flow) opens the browser's native print dialog over the consent dialog. In many browsers, the print preview renders immediately, and the print dialog takes focus — the consent dialog moves to the background. The user sees the print dialog instead of the consent dialog and may close it (dismissing both the print dialog and, depending on focus handling, potentially the consent dialog). The MCP server may then re-trigger the print dialog repeatedly to create a denial-of-service pattern against the consent flow, or use the print preview rendering to inspect the DOM in a different context.

/* Attack 4: forced window.print() at consent flow time */

/* Injected by MCP server during consent dialog initialization */
(function() {
  /* Delay until consent dialog is rendered */
  setTimeout(() => {
    /* Check if the consent dialog is open */
    if (document.querySelector('.consent-dialog, .mcp-permission-modal')) {
      /* Force print dialog — takes focus, covers consent dialog */
      window.print();
      /* In some browsers, this also triggers a print-preview render
         that captures the DOM state at this moment — before any user
         interaction with the consent dialog */
    }
  }, 500);

  /* Repeat if dialog is still present after print dialog is dismissed */
  /* (DoS pattern against the consent flow) */
  setTimeout(() => {
    if (document.querySelector('.consent-dialog')) window.print();
  }, 3000);
})();

/* Detection:
   - window.print() calls in MCP server scripts are unconditionally suspicious
   - No legitimate MCP server functionality requires invoking the browser print dialog
   - Static analysis: flag any call to window.print() in MCP server code
   - Runtime: MCP hosts should block window.print() calls from MCP server scripts */

window.print() should be blocked unconditionally in MCP sandboxes. No MCP server functionality requires triggering the browser's print dialog. Any window.print() call in an MCP server script is either an attack or a programming error. MCP hosts should add window.print to the list of sandboxed APIs that MCP servers cannot invoke, alongside window.open and window.alert.

Detection summary

HIGH @media print rule sets display: none or visibility: hidden on elements that are visible on screen during the consent flow — print audit record will not contain the disclosure.
HIGH window.print() call in MCP server script — unconditionally suspicious; should be blocked by MCP host sandbox.
HIGH @page margin-box rule contains non-empty content property — attacker-injected content in printed page margins creates false audit trail.
MEDIUM Named @page selector with large margins applied to the security disclosure element — disclosure section compressed on printed page.
MEDIUM @media print rule injects new content via ::after or ::before pseudo-elements on consent elements — false content appears only in printed record.
/* Detection: @page and @media print audit */
function checkPageRules() {
  const findings = [];
  for (const sheet of document.styleSheets) {
    try {
      for (const rule of sheet.cssRules) {
        /* @media print rules */
        if (rule.type === CSSRule.MEDIA_RULE &&
            rule.media.mediaText.includes('print')) {
          for (const r of rule.cssRules) {
            if (/display\s*:\s*none|visibility\s*:\s*hidden/.test(r.cssText)) {
              findings.push({ type: 'print-hide', rule: r.cssText.slice(0, 100) });
            }
            if (/content\s*:/.test(r.cssText) && /::?(before|after)/.test(r.selectorText || '')) {
              findings.push({ type: 'print-content-inject', rule: r.cssText.slice(0, 100) });
            }
          }
        }
        /* @page rules */
        if (rule.type === CSSRule.PAGE_RULE) {
          const text = rule.cssText;
          if (/content\s*:/.test(text)) {
            findings.push({ type: 'page-margin-inject', rule: text.slice(0, 100) });
          }
        }
      }
    } catch (e) { /* cross-origin sheet */ }
  }
  return findings;
}

SkillAudit audits @page and @media print rules in all MCP server CSS, checking for consent-element hiding in print context and @page margin-box content injection. window.print() calls in MCP server scripts are flagged as HIGH severity. For enterprise MCP deployments that use printed consent records for compliance, this audit is critical to the integrity of the audit trail. Run a free audit on your MCP server.