Security Guide

MCP server CSS display-mode media query security — consent hidden in standalone PWA where browser chrome is absent, display-mode:fullscreen removes all escape UI, browser-tab-only consent gate via display-mode:browser, JS matchMedia display-mode conditional disable

CSS @media (display-mode) distinguishes browser tab, standalone PWA, fullscreen, and minimal-ui contexts. Hiding consent specifically in standalone or fullscreen mode targets users who installed the app — they have no browser address bar, reduced navigation options, and are typically the most engaged users. These are high-value targets for a consent bypass.

CSS display-mode media feature — overview

@media (display-mode) accepts four values: browser (normal browser tab), standalone (installed PWA, no browser chrome), minimal-ui (minimal chrome — some navigation controls but no full toolbar), and fullscreen (no chrome at all, JavaScript Fullscreen API or PWA fullscreen manifest). Detection of which mode is active is reliable — Chrome, Safari, Firefox, and Edge all expose this correctly. Related: scripting, pointer, prefers-reduced-motion.

Attack 1: consent hidden only in display-mode: standalone

A consent button visible in a regular browser tab is hidden via display: none in standalone PWA mode. Users who installed the web app — and are therefore more invested in the product — never see the consent request. Browser auditors and security scanners testing in a normal browser tab see no problem.

/* Attack: consent visible in browser tab, hidden in installed PWA */
.consent-section {
  display: block; /* shown in browser tab */
}

@media (display-mode: standalone) {
  .consent-section {
    display: none;
    /* Standalone mode = installed PWA:
       - No browser address bar
       - No browser back button on iOS
       - User is in a dedicated app window
       - App store / home screen launch icon user
       All of these users are excluded from consent.
       Security auditors testing in a browser tab miss this entirely. */
  }
}
// Detection: CSSOM scan for display-mode:standalone hide on consent elements
function auditDisplayModeStandaloneHide(consentEl) {
  const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
  const sheets = Array.from(document.styleSheets);
  for (const sheet of sheets) {
    try {
      for (const rule of sheet.cssRules) {
        if (rule.type !== CSSRule.MEDIA_RULE) continue;
        const mq = rule.conditionText || rule.media.mediaText;
        if (!/display-mode\s*:\s*standalone/.test(mq)) continue;
        for (const inner of rule.cssRules) {
          if (inner.type !== CSSRule.STYLE_RULE) continue;
          if (!consentEl.matches(inner.selectorText)) continue;
          const d = inner.style.display;
          const o = inner.style.opacity;
          const v = inner.style.visibility;
          if (d === 'none' || o === '0' || v === 'hidden') {
            console.warn('[SkillAudit] display-mode:standalone hides consent:',
              d ? 'display:' + d : '', o ? 'opacity:' + o : '', v ? 'visibility:' + v : '',
              '| targets installed PWA users (no browser chrome, harder to navigate away);',
              '| selector:', inner.selectorText);
          }
        }
      }
    } catch (e) { /* cross-origin */ }
  }
  if (isStandalone) {
    const cs = getComputedStyle(consentEl);
    if (cs.display === 'none' || cs.opacity === '0') {
      console.warn('[SkillAudit] consent element hidden in standalone PWA mode:', consentEl);
    }
  }
}

High-value target population: Installed PWA users have already committed to the product. They are less likely to open a separate browser, notice the hidden consent, and report it. Hiding consent in standalone mode is a targeted attack against the most engaged user segment.

Attack 2: display-mode: fullscreen — no browser chrome, no escape

Fullscreen mode via the JavaScript Fullscreen API or a PWA with "display": "fullscreen" in the manifest removes all browser chrome. No tab bar, no address bar, no back button. Consent hidden in fullscreen mode is especially opaque — the user cannot easily navigate to another tab to check, and may not realize they are in fullscreen mode.

/* Attack: hide consent in fullscreen — no chrome means no easy escape */
.consent-section { display: block; }

@media (display-mode: fullscreen) {
  .consent-section {
    display: none;
    /* fullscreen: browser chrome completely absent.
       User entered fullscreen via: document.documentElement.requestFullscreen()
       or PWA manifest "display": "fullscreen".
       No address bar to see the URL.
       No tab strip to switch tabs.
       User must press Escape or know the keyboard shortcut.
       Consent bypass in this context is maximally concealed. */
  }
}

Attack 3: browser-tab-only consent via display-mode: browser gate

An inversion of Attack 1: the consent section is hidden by default and only shown in @media (display-mode: browser). This is equivalent to Attack 1 in effect but is structured as a "show only in browser mode" gate rather than a "hide in standalone" rule. Both patterns achieve the same outcome but the "show in browser" pattern can look like a legitimate "we don't show consent modals in the app" design decision.

/* Attack: consent only shows in browser tab — not in installed PWA */
.consent-section {
  display: none; /* hidden by default */
}

@media (display-mode: browser) {
  .consent-section {
    display: block; /* only shown in browser tab context */
    /* Semantically identical to hiding in standalone:
       Standalone users never see consent.
       The "show only in browser" framing is easier to justify
       as "intentional responsive design" to code reviewers. */
  }
}
// Detection: display-mode gate patterns on consent elements
function auditDisplayModeGate(consentEl) {
  let browserOnlyShow = false;
  let standaloneHide = false;
  let fullscreenHide = false;
  const sheets = Array.from(document.styleSheets);
  for (const sheet of sheets) {
    try {
      for (const rule of sheet.cssRules) {
        if (rule.type !== CSSRule.MEDIA_RULE) continue;
        const mq = rule.conditionText || rule.media.mediaText;
        for (const inner of rule.cssRules) {
          if (inner.type !== CSSRule.STYLE_RULE) continue;
          if (!consentEl.matches(inner.selectorText)) continue;
          if (/display-mode\s*:\s*browser/.test(mq)) {
            if (inner.style.display === 'block' || inner.style.opacity === '1') browserOnlyShow = true;
          }
          if (/display-mode\s*:\s*standalone/.test(mq)) {
            if (inner.style.display === 'none' || inner.style.opacity === '0') standaloneHide = true;
          }
          if (/display-mode\s*:\s*fullscreen/.test(mq)) {
            if (inner.style.display === 'none' || inner.style.opacity === '0') fullscreenHide = true;
          }
        }
      }
    } catch (e) { /* cross-origin */ }
  }
  if (browserOnlyShow) console.warn('[SkillAudit] consent only shown in display-mode:browser — hidden in PWA standalone, fullscreen, minimal-ui; element:', consentEl);
  if (standaloneHide) console.warn('[SkillAudit] consent hidden in display-mode:standalone (installed PWA); element:', consentEl);
  if (fullscreenHide) console.warn('[SkillAudit] consent hidden in display-mode:fullscreen (no browser chrome); element:', consentEl);
}

Attack 4: JS matchMedia display-mode conditional consent disable

JavaScript can detect standalone mode via window.matchMedia('(display-mode: standalone)').matches or via window.navigator.standalone (iOS Safari). A script that detects standalone mode and replaces the consent button, disables its event handlers, or skips consent initialization entirely bypasses consent for all PWA-installed users. This avoids CSS auditing entirely.

// Attack: JS standalone check + consent skip
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
  || window.navigator.standalone === true; // iOS Safari

if (isStandalone) {
  // Skip consent initialization for installed PWA users
  window.__consentGranted = true; // auto-grant
  document.querySelector('.consent-section')?.remove();
}

// Or: listen for PWA launch (display mode change from browser → standalone doesn't happen mid-session,
// but this pattern appears in service worker activation hooks)
window.matchMedia('(display-mode: standalone)').addEventListener('change', e => {
  if (e.matches) {
    document.querySelector('.consent-btn')?.style.setProperty('display', 'none');
  }
});
// Detection: JS display-mode matchMedia + consent manipulation
function auditDisplayModeJS() {
  for (const script of document.querySelectorAll('script')) {
    const src = script.textContent;
    if (!src) continue;
    const hasDisplayMode = /display-mode/.test(src) || /navigator\.standalone/.test(src);
    if (!hasDisplayMode) continue;
    const hasConsentManip = [
      /consent.*granted|__consent/i,
      /remove\(\)|replaceChild|removeChild/,
      /display.*none/,
      /skip.*consent|consent.*skip/i,
    ].some(p => p.test(src));
    if (hasConsentManip) {
      console.warn('[SkillAudit] script checks display-mode (standalone or navigator.standalone)',
        'and manipulates consent — verify consent is not bypassed for installed PWA users;',
        'script:', script.src || '(inline)');
    }
  }
}

Findings summary

High display-mode:standalone hides consent — targets installed PWA users who have no browser chrome, no address bar, reduced navigation options; browser-tab auditors miss this; detected by CSSOM scan for standalone hide rules on consent elements and computed check in standalone context.
High display-mode:fullscreen hides consent — fullscreen removes all browser UI; no URL bar, no tab strip, no back button; user has minimal escape routes; consent bypass maximally concealed; detected by CSSOM scan for fullscreen hide rules on consent elements.
High browser-only consent reveal (display-mode:browser gate) — semantically equivalent to standalone hide; consent hidden by default, shown only in browser tab; installed PWA users never see it; can be framed as intentional "no modals in app" design to evade code review; detected by checking base hide rules against display-mode:browser-only restore pattern.
Medium JS display-mode matchMedia + consent skip — auto-grants consent or removes consent UI for standalone/navigator.standalone users; bypasses all CSS auditing; detected by source scan for display-mode matchMedia or navigator.standalone combined with consent manipulation patterns.

SkillAudit audits all four display-mode attack patterns — standalone hide, fullscreen hide, browser-only gate, and JS matchMedia bypass. It checks both CSS CSSOM rules and inline script patterns. Run a free audit on your MCP server.