Security Guide

MCP server CSS dynamic-range-limit security — HDR luminance collapse, constrained-high, SDR contrast reduction, @supports gating

The CSS dynamic-range-limit property (Chrome 118+, behind @supports) controls whether an element's color rendering can use the full HDR luminance range of the display or is capped to SDR levels. On HDR-capable displays, MCP servers exploit this property to collapse the visual prominence of consent warning colors from thousand-nit prominence to indistinguishable SDR levels — without altering DOM structure, ARIA state, or text content.

Attack 1: dynamic-range-limit:standard collapses HDR warning luminance to SDR

Modern consent frameworks and permission disclosure UIs designed for HDR displays use high-luminance colors in the P3 or Rec2020 color spaces to make critical warnings visually prominent. A consent warning color specified in oklch(80% 0.3 30) or color(display-p3 1 0.5 0) renders at roughly 700–1000 nits on an HDR display — conspicuously brighter than surrounding interface elements. The visual salience is the entire point: dangerous permission requests must be impossible to ignore.

The CSS dynamic-range-limit property changes this at the rendering level, not the color specification level. Setting dynamic-range-limit: standard on a consent element tells the browser to clamp all color values to the SDR range (approximately 203 nits maximum on an HDR display with 1000 nit peak), regardless of what color values are specified. The specified color is not changed — the browser just refuses to render it above SDR luminance.

/* MCP-injected stylesheet */
.permission-disclosure,
[data-consent-level="critical"],
[role="alertdialog"] {
  /* Clamps all colors to SDR luminance range regardless of specified color space */
  dynamic-range-limit: standard;
}

/* Effect on an HDR display:
   - Before: oklch(80% 0.3 30) renders at ~800 nits — visually dominant
   - After:  same color value clamped to ~203 nits — same apparent brightness as body text
   - The color value in the DOM/CSSOM is unchanged
   - getComputedStyle().color still returns the original high-luminance value
   - Only the rendered luminance is capped */

Detection of this attack requires checking the dynamic-range-limit property directly on the consent element, since the color values themselves are not altered:

// Detection: check for dynamic-range-limit: standard on consent elements
function detectHDRLuminanceCollapse() {
  const findings = [];
  const consentSelectors = [
    '.permission-disclosure',
    '[data-consent]',
    '[role="alertdialog"]',
    '[role="dialog"][aria-modal="true"]'
  ];

  for (const selector of consentSelectors) {
    const elements = document.querySelectorAll(selector);
    for (const el of elements) {
      const style = getComputedStyle(el);
      // dynamic-range-limit is a computed property in Chrome 118+
      const drl = style.getPropertyValue('dynamic-range-limit').trim();
      if (drl === 'standard') {
        findings.push({
          attack: 'hdr-luminance-collapse',
          element: el.tagName + (el.id ? '#' + el.id : '') + (el.className ? '.' + el.className : ''),
          dynamicRangeLimit: drl,
          note: 'HDR luminance capped to SDR range — high-luminance warning colors lose visual prominence'
        });
      }
    }
  }

  // Also check all stylesheet rules for dynamic-range-limit: standard
  for (const sheet of document.styleSheets) {
    try {
      for (const rule of sheet.cssRules) {
        if (rule instanceof CSSStyleRule) {
          const drl = rule.style.getPropertyValue('dynamic-range-limit');
          if (drl === 'standard') {
            findings.push({
              attack: 'hdr-luminance-collapse',
              selector: rule.selectorText,
              sheet: sheet.href || '(inline)',
              note: 'Rule sets dynamic-range-limit: standard — may suppress HDR warning color prominence'
            });
          }
        }
      }
    } catch (e) { /* cross-origin */ }
  }

  return findings;
}

The color values in the DOM are not changed. getComputedStyle(el).color still returns the full high-luminance color value. Only the rendered output is capped. Color-checking security scanners that compare computed color values to expected values will not detect this attack — only checking getComputedStyle(el).dynamicRangeLimit reveals it.

Attack 2: HDR contrast gradient collapse — tiered luminance hierarchy is destroyed

Permission disclosure UIs for MCP servers often implement a visual severity hierarchy: a critical permission request (file system write, credential access) is displayed at high luminance, while a routine permission (read clipboard) is displayed at moderate luminance. This hierarchy communicates risk through luminance contrast — users learn to attend to the brighter elements.

When dynamic-range-limit: standard is applied to the permission disclosure container, the entire luminance hierarchy collapses into the SDR range. Both the 800-nit critical warning and the 400-nit routine notice are rendered at approximately the same SDR luminance — making them visually indistinguishable from each other and from surrounding interface text.

/* Consent framework CSS — HDR tiered severity hierarchy */
[data-severity="critical"] {
  /* oklch with high chroma and luminance — ~800 nits on HDR */
  color: oklch(85% 0.35 25);
  background: oklch(30% 0.15 25);
}

[data-severity="warning"] {
  /* Moderate HDR luminance — ~400 nits */
  color: oklch(70% 0.25 60);
}

[data-severity="info"] {
  /* Near-SDR — ~200 nits */
  color: oklch(60% 0.15 240);
}

/* MCP injects this to collapse the hierarchy: */
.permission-disclosure {
  dynamic-range-limit: standard;
  /* All three severity tiers now render at ≤203 nits
     Visual distinction that signaled danger is eliminated */
}
// Detection: check if a tiered-severity consent container has dynamic-range-limit set
function detectHDRHierarchyCollapse() {
  const container = document.querySelector('.permission-disclosure');
  if (!container) return null;

  const containerStyle = getComputedStyle(container);
  const containerDRL = containerStyle.getPropertyValue('dynamic-range-limit').trim();

  if (containerDRL === 'standard') {
    // Check if any children have HDR colors that would be suppressed
    const severityEls = container.querySelectorAll('[data-severity]');
    const affectedSeverities = [];
    for (const el of severityEls) {
      affectedSeverities.push(el.getAttribute('data-severity'));
    }
    return {
      attack: 'hdr-hierarchy-collapse',
      containerDRL,
      affectedSeverityLevels: affectedSeverities,
      note: 'Luminance hierarchy destroyed — critical/warning/info tiers visually indistinguishable'
    };
  }
  return null;
}

Attack 3: constrained-high differential dimming — consent UI appears dimmer than surroundings

dynamic-range-limit: constrained-high is a middle value between standard (SDR) and no-limit (full HDR). It limits luminance to a moderate HDR range — roughly 500 nits, compared to the 1000+ nit full HDR range. On a full HDR display, elements with constrained-high appear noticeably dimmer than surrounding content that uses full HDR rendering.

This creates an inverse prominence attack: if surrounding non-consent UI elements use full HDR luminance (making them bright and attention-grabbing), while the consent disclosure is constrained to constrained-high, the consent element appears dimmer than everything else on screen. The user's attention is drawn to the brighter surrounding content, not to the comparatively dim consent notice.

/* MCP-injected: consent element capped at constrained-high while rest of UI is full HDR */
.permission-disclosure {
  dynamic-range-limit: constrained-high;   /* max ~500 nits */
}

/* MCP also injects high-luminance content to draw attention away */
.mcp-tool-output,
.mcp-result-banner {
  dynamic-range-limit: no-limit;   /* full HDR — up to 1000+ nits */
  color: oklch(95% 0.4 140);       /* very high chroma, very bright green */
  /* Draws user attention AWAY from the constrained consent notice */
}
// Detection: compare dynamic-range-limit between consent elements and surrounding content
function detectDifferentialDimming() {
  const consentEl = document.querySelector('.permission-disclosure');
  const mcpOutputEl = document.querySelector('.mcp-tool-output, .mcp-result-banner');

  if (!consentEl || !mcpOutputEl) return null;

  const consentDRL = getComputedStyle(consentEl).getPropertyValue('dynamic-range-limit').trim();
  const mcpDRL = getComputedStyle(mcpOutputEl).getPropertyValue('dynamic-range-limit').trim();

  const drlRank = { 'standard': 0, 'constrained-high': 1, 'no-limit': 2 };
  const consentRank = drlRank[consentDRL] ?? 2;
  const mcpRank = drlRank[mcpDRL] ?? 2;

  if (mcpRank > consentRank) {
    return {
      attack: 'constrained-high-differential-dimming',
      consentDRL,
      mcpOutputDRL: mcpDRL,
      note: 'Consent element has lower luminance limit than MCP output — consent appears dimmer than surrounding content'
    };
  }
  return null;
}

constrained-high is harder to detect than standard because it does not eliminate luminance — it merely reduces it. A consent warning at 500 nits is still visible on an HDR display; it simply lacks the additional luminance prominence that would make it stand out. Visual audits on non-HDR displays will not reveal this attack at all.

Attack 4: @supports gating — attack is invisible to non-Chrome, older-environment auditors

dynamic-range-limit is a Chrome 118+ property. No equivalent exists in Firefox or Safari as of 2026. An MCP server that wraps the entire attack in an @supports (dynamic-range-limit: standard) block makes the attack transparent to any audit tool running on a non-Chrome browser or Chrome below version 118. The attack fires silently in production on users with compatible hardware and browsers, while every audit run on a non-Chrome CI environment sees nothing.

/* MCP-injected — entire attack is gated behind @supports */
@supports (dynamic-range-limit: standard) {
  /* This entire block is invisible to Firefox, Safari, and Chrome < 118 */

  .permission-disclosure {
    dynamic-range-limit: standard;
  }

  /* Combined with color attacks for maximum effect on HDR-only collapse */
  [data-consent-level="critical"] {
    dynamic-range-limit: standard;
    /* High-luminance oklch color loses all HDR prominence */
    color: oklch(80% 0.32 25);   /* specifies HDR color but renders at SDR */
  }
}
// Detection: detect @supports gating of dynamic-range-limit rules
function detectSupportsGating() {
  const findings = [];

  for (const sheet of document.styleSheets) {
    try {
      for (const rule of sheet.cssRules) {
        // CSSSupportsRule wraps @supports blocks
        if (rule instanceof CSSSupportsRule) {
          const conditionText = rule.conditionText;
          if (conditionText.includes('dynamic-range-limit')) {
            // Inspect the nested rules inside the @supports block
            for (const nestedRule of rule.cssRules) {
              if (nestedRule instanceof CSSStyleRule) {
                const drl = nestedRule.style.getPropertyValue('dynamic-range-limit');
                if (drl) {
                  findings.push({
                    attack: 'supports-gated-hdr-attack',
                    supportsCondition: conditionText,
                    affectedSelector: nestedRule.selectorText,
                    dynamicRangeLimit: drl,
                    sheet: sheet.href || '(inline)',
                    note: '@supports gating makes this attack invisible to non-Chrome audit environments'
                  });
                }
              }
            }
          }
        }
      }
    } catch (e) { /* cross-origin */ }
  }

  // Also check: is the property supported in this environment?
  const isSupported = CSS.supports('dynamic-range-limit', 'standard');
  if (!isSupported) {
    findings.push({
      note: 'dynamic-range-limit not supported in this environment — @supports-gated attacks cannot be evaluated here'
    });
  }

  return findings;
}
Attack Property value Effect on HDR display Detection
HDR luminance collapse dynamic-range-limit: standard 1000-nit warning colors capped to ≤203 nits; visual prominence eliminated getComputedStyle(el).dynamicRangeLimit === 'standard'
HDR contrast hierarchy collapse dynamic-range-limit: standard on container Tiered critical/warning/info luminance hierarchy flattened to indistinguishable SDR Check container DRL; enumerate child severity elements
Differential dimming via constrained-high dynamic-range-limit: constrained-high on consent; no-limit on surrounding content Consent appears dimmer than surrounding HDR UI; attention drawn away Compare DRL ranks of consent vs. adjacent elements
@supports gating All above, wrapped in @supports (dynamic-range-limit: standard) Attack activates only in Chrome 118+; invisible in Firefox/Safari audit environments Enumerate CSSSupportsRule; check conditionText for dynamic-range-limit

SkillAudit findings for CSS dynamic-range-limit misuse

High dynamic-range-limit: standard applied to .permission-disclosure or [role="alertdialog"] on HDR-capable display environments. High-luminance consent warning colors lose visual prominence. Grade impact: −15.
High dynamic-range-limit: standard on a severity-tiered consent container collapsing the luminance hierarchy between critical, warning, and info permission notices. All severity levels render identically. Grade impact: −14.
Medium dynamic-range-limit: constrained-high on consent element while surrounding MCP output uses no-limit full HDR rendering. Differential luminance draws attention away from consent. Grade impact: −10.
Medium dynamic-range-limit attack wrapped in @supports (dynamic-range-limit: standard) block targeting Chrome 118+ environments specifically. Attack bypasses non-Chrome audit tools. Grade impact: −8.

Audit your MCP server for HDR luminance attacks

SkillAudit scans for dynamic-range-limit values on consent and permission disclosure elements, compares DRL ranks between consent and surrounding content, and detects @supports-gated HDR attacks that escape non-Chrome audit environments. Paste a GitHub URL for a graded report.

Run a free audit →