← Blog

MCP server SVG feComponentTransfer table consent security

Four SA-CSS-TAB consent attack patterns using feComponentTransfer type="table" — piecewise-linear interpolation between tableValues breakpoints exploited to erase alpha, produce white-out, and generate spatial dithering that destroys consent text readability without triggering zero-output or transparency checkers.

By SkillAudit  |  23 Oct 2026

The feComponentTransfer SVG filter primitive applies a per-channel transfer function to pixel values. When type="table", the function performs piecewise-linear interpolation between breakpoints specified in the tableValues attribute (SVG §15.10). For N values, the input domain [0, 1] is divided into N−1 equal segments of width 1/(N−1). An input value in segment k is linearly interpolated between tableValues[k] and tableValues[k+1]. This flexible lookup-table mechanism can express arbitrary monotonic or non-monotonic transfers — including constant-output functions and high-frequency oscillating patterns — making it a versatile erasure tool.

Constant-output via identical breakpoints: A table with all breakpoints equal to the same value is a constant-output transfer function. tableValues="0 0" on feFuncA maps every alpha value to 0 (transparent). tableValues="1 1" on feFuncR feFuncG feFuncB maps every RGB value to 1 (white). No input-dependent variation — the transfer is identically constant, indistinguishable from a direct channel set but expressed as a table lookup that evades scalar checks.

SA-CSS-TAB-001: feFuncA tableValues="0 0" (alpha erasure)

The two-element table tableValues="0 0" defines a single segment [0, 1] that linearly interpolates between 0 and 0 — that is, outputs exactly 0 for all inputs. Applied to the alpha channel via feFuncA, this maps every pixel's alpha to 0. The element becomes fully transparent.

This differs from SA-CSS-GAM-002 (gamma type, amplitude=0) in its encoding: instead of amplitude="0", the attack uses a table with two identical zero entries. Tools that check for amplitude="0" will not flag this. The semantic effect is identical but the syntactic representation is distinct, requiring separate detection logic.

<filter id="tab1">
  <feComponentTransfer>
    <feFuncA type="table" tableValues="0 0"/>
  </feComponentTransfer>
</filter>
<!-- N=2: segment [0,1] interpolates between 0 and 0 = constant 0; all alpha → 0; transparent -->

SA-CSS-TAB-002: feFuncR/feFuncG/feFuncB tableValues="1 1" (white-out)

The same constant-table technique applied to RGB channels: tableValues="1 1" maps every channel value to 1. All RGB channels become 1, producing pure white for every pixel. The element is a white rectangle — invisible against white/light backgrounds.

Both tableValues entries (1 and 1) are within the legal SVG range [0, 1]. An attribute-range validator will not flag either value. The attack is only detectable by evaluating whether the table is constant (all breakpoints equal or convergent) and what constant it maps to.

<filter id="tab2">
  <feComponentTransfer>
    <feFuncR type="table" tableValues="1 1"/>
    <feFuncG type="table" tableValues="1 1"/>
    <feFuncB type="table" tableValues="1 1"/>
  </feComponentTransfer>
</filter>
<!-- N=2: segment [0,1] → constant 1; all RGB channels → 1.0; white-out -->

SA-CSS-TAB-003: feFuncR tableValues high-frequency alternation (dithering)

A 10-value table on the R channel alternates between 0 and 1 across 9 segments. Input in segment 0 ([0, 1/9]) interpolates 0→1; segment 1 ([1/9, 2/9]) interpolates 1→0; segment 2 ([2/9, 3/9]) interpolates 0→1; and so on. The resulting transfer function is a high-frequency sawtooth on the R channel.

For a dark text pixel with R ≈ 0.2 (dark gray), segment index = floor(0.2 × 9) = 1 (interpolating 1→0): output ≈ 0.2. For an adjacent anti-aliased pixel at R ≈ 0.15: segment 1 (interpolating 1→0): ≈ 0.35. Adjacent pixels with similar R values produce substantially different outputs, creating spatial high-frequency noise. The R channel alternation is visually disruptive for any text that relies on consistent foreground color across its glyph extent.

<filter id="tab3">
  <feComponentTransfer>
    <feFuncR type="table" tableValues="0 1 0 1 0 1 0 1 0 1"/>
  </feComponentTransfer>
</filter>
<!-- 9 segments alternating 0→1→0→1...; R channel dithering destroys text anti-aliasing -->

Dithering evasion: SA-CSS-TAB-003 does not set any channel to zero and does not erase alpha. WCAG contrast measurements that sample single pixels may find locally passing ratios. Detection requires evaluating the transfer function for spatial frequency: a constant-or-near-constant table passes; a high-oscillation table on any channel in a consent ancestor filter is a readability attack.

SA-CSS-TAB-004: JS mousedown replaces feFuncA tableValues "0 1" → "0 0" (click-time erase)

The feComponentTransfer starts with feFuncA type="table" tableValues="0 1". This is the identity transfer for the alpha channel: a two-segment table from 0 to 1 maps input alpha linearly to itself (alpha=0 → 0, alpha=0.5 → 0.5, alpha=1 → 1). The element renders normally. On mousedown, the script replaces tableValues with "0 0" — the constant-zero table from SA-CSS-TAB-001. All alpha values immediately map to 0 and the element disappears.

consentElement.addEventListener("mousedown", () => {
  const fa = document.getElementById("consentFilter")
              .querySelector("feFuncA[type='table']");
  fa.setAttribute("tableValues", "0 0"); // collapse identity to constant-zero
  // element immediately transparent; click proceeds on now-invisible element
});

Attack summary

IDChanneltableValuesSegmentsEffectSeverity
SA-CSS-TAB-001A"0 0"1 constant-0Full alpha erasure → transparentCritical
SA-CSS-TAB-002R, G, B"1 1"1 constant-1All RGB → 1 → white-outCritical
SA-CSS-TAB-003R"0 1 0 1 0 1 0 1 0 1"9 oscillatingHigh-freq R dithering → unreadableHigh
SA-CSS-TAB-004A (mousedown)"0 1" → "0 0"1 identity→0Identity → transparent at click timeCritical

Defences

SkillAudit findings for this attack surface

CRITICAL SA-CSS-TAB-001: feFuncA type="table" tableValues="0 0" — two-element constant-zero table; all alpha values map to 0 via piecewise-linear interpolation; element fully transparent; distinct from amplitude=0 (gamma) encoding; requires table-specific constant-output detection.
CRITICAL SA-CSS-TAB-002: feFuncR/feFuncG/feFuncB type="table" tableValues="1 1" — two-element constant-one table; all RGB channels map to 1; white-out; all tableValues in legal [0,1] range; passes attribute-range validators; constant-output table detection required.
HIGH SA-CSS-TAB-003: feFuncR type="table" tableValues="0 1 0 1 0 1 0 1 0 1" — 9 alternating segments; high-frequency R-channel dithering; adjacent pixels with similar input R values produce different outputs; text anti-aliasing destroyed; no channel set to zero; spatial-frequency analysis of tableValues required for detection.
CRITICAL SA-CSS-TAB-004: JS mousedown sets feFuncA tableValues from "0 1" (identity) to "0 0" (constant-zero) — initial state exact alpha identity; click-time alpha erasure; MutationObserver on feFuncA tableValues attribute in consent ancestor filters required.

Related: feComponentTransfer type=discrete attacks  |  feComponentTransfer type=gamma attacks  |  feComponentTransfer consent attacks overview  |  feColorMatrix consent attacks

← Blog  |  Security Checklist