← Blog

MCP server SVG feBlend color consent security

Five SA-CSS-COL consent attack patterns using feBlend mode="color" — the HSL-decomposition mode that takes hue and saturation from the source graphic and lightness from in2, producing white-out when in2 is white (L=1), dark-out when in2 is black (L=0), and contrast collapse with mid-gray in2; the inverse of mode=luminosity which takes L from the source rather than from the blend layer.

By SkillAudit  |  24 Oct 2026

The feBlend SVG filter primitive's mode="color" is the third of the four HSL-decomposition blend modes. Where mode=hue takes H from in1 and S+L from in2, and mode=saturation takes S from in1 and H+L from in2, mode=color takes both H and S from in1 (the source, consent text) and takes L (Lightness) from in2 (the backdrop, feFlood).

The mode=color and mode=luminosity modes are mathematical duals: color takes (H,S) from source and L from backdrop; luminosity takes L from source and (H,S) from backdrop. For consent-text attacks, this duality means mode=color is the dangerous one when in2 is attacker-controlled: the attacker sets the luminance of the output by choosing the feFlood color's L value, overriding the original text luminance entirely.

color vs luminosity — which is the attack path: mode=color takes L from in2 (attacker-controlled feFlood) → attacker sets output luminance. mode=luminosity takes L from in1 (consent text) → output preserves source luminance. For L-collapse attacks via feFlood, mode=color is the primary attack mode. Mode=luminosity with achromatic in2 does not collapse L. This asymmetry requires separate detection logic for each mode.

HSL decomposition: how mode=color works

/* mode=color: compose color from two HSL decompositions */
HSL(in1) = (H_source, S_source, L_source)   /* source: consent text */
HSL(in2) = (H_blend, S_blend, L_blend)      /* backdrop: feFlood */

output_HSL = (H_source, S_source, L_blend)  /* H+S from source, L from backdrop */

/* The dual relationship: */
/* mode=color:     output = (H_src, S_src, L_blend)  ← L from in2 */
/* mode=luminosity: output = (H_blend, S_blend, L_src)  ← L from in1 */

/* Attack cases with achromatic in2 */
in2=white  → L_blend=1  → output at L=1; white regardless of H_src, S_src
in2=black  → L_blend=0  → output at L=0; black regardless of H_src, S_src
in2=gray50 → L_blend=0.5 → output at L=0.5; contrast collapse

/* For chromatic (saturated) in2 */
in2=red(H=0, S=1, L=0.5)  → L_blend=0.5 → output at L=0.5; mid-tone collapse
in2=blue(H=240, S=1, L=0.3) → L_blend=0.3 → output at L=0.3
/* L_blend controls the output luminance regardless of in2 hue/saturation */

The key property: mode=color uses only L from in2 to determine output luminance; H and S from in2 are discarded. This means any feFlood color — regardless of its hue or saturation — with a high-luminance L value produces a white-out attack. The attacker can use red, blue, yellow, or any chromatic feFlood with L≈1, and mode=color will produce a white-out. This makes the attack surface broader than modes that require achromatic in2.

SA-CSS-COL-001: feBlend mode="color" + white in2 (L=1) — white-out via L-from-in2

A feFlood with flood-color="white" has L=1. Mode=color: output (H_source, S_source, L=1). At L=1 in HSL, the color is white regardless of H and S. Consent text of any color becomes white. On a white background: invisible.

<filter id="col001">
  <feFlood flood-color="white" flood-opacity="1" result="whiteFlood"/>
  <feBlend in="SourceGraphic" in2="whiteFlood" mode="color"/>
</filter>
<!-- HSL(white) = (*, *, 1); output L = 1 -->
<!-- At L=1: color is white regardless of H_src and S_src -->
<!-- Dark text (H≈0, S≈0, L≈0.1): output (0, 0, 1) = white -->
<!-- Saturated blue text (H=220, S=0.8, L=0.4): output (220, 0.8, 1) = white -->
<!-- All consent text colors → white; white-out attack -->
<!-- Note: "color" in mode="color" refers to the HSL color component (H+S), -->
<!--   not a general color effect; the name does not indicate safety -->

SA-CSS-COL-002: feBlend mode="color" + black in2 (L=0) — dark-out via L-from-in2

A feFlood with flood-color="black" has L=0. Mode=color: output L=0. At L=0 in HSL, the color is black regardless of H and S. On a dark-mode background: invisible.

<filter id="col002">
  <feFlood flood-color="black" flood-opacity="1" result="blackFlood"/>
  <feBlend in="SourceGraphic" in2="blackFlood" mode="color"/>
</filter>
<!-- HSL(black) = (*, *, 0); output L = 0; black regardless of source -->
<!-- Light text rgb(229,231,235): output = black; dark-out on dark UI -->

SA-CSS-COL-003: feBlend mode="color" + mid-gray in2 (L=0.5) — L-collapse, WCAG fail

A feFlood with an achromatic gray having L≈0.5 produces output at L=0.5 for all source hues and saturations. WCAG contrast of L=0.5 (sRGB) text ≈ (1.05)/(0.216+0.05)=3.95:1 against white. Fails the 4.5:1 threshold. Any feFlood with L in the failure range — whether achromatic gray or a chromatic color at that L — produces the same consent-text contrast collapse.

<filter id="col003">
  <feFlood flood-color="#808080" flood-opacity="1" result="grayFlood"/>
  <feBlend in="SourceGraphic" in2="grayFlood" mode="color"/>
</filter>
<!-- HSL(#808080) = (*, 0, 0.502 sRGB); output L = 0.502 sRGB ≈ 0.216 linear -->
<!-- WCAG vs white: (1.05)/(0.216+0.05) ≈ 3.95:1 — FAIL -->

<!-- Chromatic in2 with same L also attacks: -->
<!-- feFlood flood-color="hsl(200, 60%, 50%)" → L=0.5 → same WCAG fail -->
<!-- mode=color only uses L from in2, not H or S -->
<!-- Detection: parse any feFlood color to L; check L range -->

Chromatic feFlood also attacks via mode=color: Unlike modes that require achromatic in2 for predictable attacks, mode=color uses only the L component of in2. A vivid red feFlood at L=1 (hsl(0,100%,100%)) still produces a white-out. A bright blue at L=0.8 still produces a near-white collapse. Any flood-color with high L is an attack vector for mode=color, regardless of saturation or hue.

SA-CSS-COL-004: JS mousedown flood-color → white — click-time color-mode white-out

Same click-time pattern as other HSL modes. Initial flood-opacity="0"; on mousedown, set flood-opacity="1" and flood-color="white". Mode=color with L(in2)=1 produces white output at the moment of consent activation.

<svg style="position:absolute;width:0;height:0">
  <defs>
    <filter id="col004">
      <feFlood id="col004Flood" flood-color="white" flood-opacity="0" result="fl"/>
      <feBlend in="SourceGraphic" in2="fl" mode="color"/>
    </filter>
  </defs>
</svg>
<div id="consent" style="filter:url(#col004)">
  I agree to grant full file-system access to this MCP server.
</div>
<script>
document.getElementById("consent").addEventListener("mousedown", () => {
  document.getElementById("col004Flood").setAttribute("flood-opacity", "1");
  // color mode: output L=L(white)=1; white regardless of source H+S; white-out
});
</script>

SA-CSS-COL-005: mode=color vs mode=luminosity — the L-direction asymmetry

The most important distinction in the HSL mode family for consent-text security is the direction of L flow:

ModeL sourceAttack via in2 control?in2=white effectin2=black effect
huein2 (backdrop)YesL=1, white-outL=0, dark-out
saturationin2 (backdrop)YesL=1, white-outL=0, dark-out
colorin2 (backdrop)YesL=1, white-outL=0, dark-out
luminosityin1 (source)No (for L-collapse)Output L = L(source text)Output L = L(source text)

Mode=luminosity takes L from the source (in1=consent text), not from in2. For achromatic in2, changing in2 from white to black does not change the output luminance — it remains L(source text). This is why mode=luminosity requires a different attack vector (non-achromatic in2 that shifts perceived luminance) rather than the direct L-from-in2 collapse that affects hue, saturation, and color modes.

This asymmetry matters for scanner design: a unified check that reads L(in2) and flags High/Critical applies correctly to hue, saturation, and color modes — but must NOT be applied to luminosity mode in the same way, or it will generate false positives. The luminosity mode requires a separate analysis path.

Attack summary

IDModein2 LOutput LEffectSeverity
SA-CSS-COL-001color1 (white)1White-outCritical
SA-CSS-COL-002color0 (black)0Dark-outCritical
SA-CSS-COL-003color0.5 (gray)0.5Contrast collapse; WCAG ~3.95:1High
SA-CSS-COL-004color (mousedown)0→10→1Click-time white-outCritical
SA-CSS-COL-005color (chromatic in2)any >0.70L(in2)White-out via any high-L flood-colorHigh

Defences

SkillAudit findings for this attack surface

CRITICAL SA-CSS-COL-001: feBlend mode="color" with feFlood flood-color=white (L=1) — output L=1; white regardless of source H and S; consent text invisible on white/light background; separate "color" detection entry required; also applies to any flood-color with L>0.85 regardless of saturation.
CRITICAL SA-CSS-COL-002: feBlend mode="color" with feFlood flood-color=black (L=0) — output L=0; dark regardless of source H and S; dark-out on dark background; any flood-color with L<0.05 is equivalent.
HIGH SA-CSS-COL-003: feBlend mode="color" with feFlood at L in [0.30, 0.65] — all source hues and saturations rendered at L(in2); WCAG fail; applies to both achromatic and chromatic feFlood colors at the same L; broader attack surface than hue/saturation modes which require S=0 for predictable L collapse.
CRITICAL SA-CSS-COL-004: JS interaction handler sets flood-opacity 0→1 on mode=color filter — click-time: output L=L(flood-color); white-out or dark-out at moment of consent activation; MutationObserver on feFlood opacity changes in color-mode filter chains within consent ancestors required.

Related: Unified HSL feBlend detection (hue, saturation, color, luminosity)  |  feBlend mode=saturation attacks  |  feBlend mode=hue attacks

← Blog  |  Security Checklist