MCP server CSS fePointLight security: word-level x/y coordinate targeting, low-z steep-angle saturation, cursor-tracking animation, and multi-source consent word array

Published 2026-10-01 — SkillAudit Research

The SVG fePointLight element defines a point light source at a specific position in the coordinate space of the filter region. It must appear as a child of feSpecularLighting or feDiffuseLighting. The light position parameters — x, y, and z — determine where the light originates in the filter's user-space coordinate system. The x and y coordinates map to the horizontal and vertical position within the filter region; z represents the elevation above the surface (in the same coordinate units). The light direction vector at any pixel is computed as the normalized vector from the surface pixel to the light position.

For feSpecularLighting, the output at each pixel depends on the specular reflection formula: output = specularColor × specularConstant × pow(dot(N, H), specularExponent), where H is the half-vector between the surface normal and the light direction. At the pixel directly beneath the light source (where the light direction vector is straight down into the surface, H ≈ surface normal), the dot product approaches 1.0, and with high specularConstant, the output saturates to white.

Word-level vs. element-level contrast: Aggregate contrast checks measure the consent element's overall contrast against the page background. A fePointLight hotspot targeting a single word (e.g., "agree" or "authorize") saturates that word to white while leaving surrounding text unaffected — the aggregate contrast over the full element passes the threshold because most pixels are not illuminated. Only per-word local contrast measurement detects this attack.

Light direction and hotspot geometry

The light direction vector L at pixel (px, py) from a fePointLight at (lx, ly, lz) is (lx-px, ly-py, lz) / ||(lx-px, ly-py, lz)||. The specular contribution peaks where L ≈ H (the surface normal for a flat surface is (0,0,1)). For a flat surface, the peak illumination radius scales with lz: small z means a narrow, steep hotspot; large z means a broad, low-intensity illumination. A fePointLight at z=10 with the element at width=200px produces a hotspot radius of approximately 10–15px — sufficient to cover a single word.

Attack findings

HIGH
fePointLight x/y calibrated to specific consent clause word — word-level illumination saturation while aggregate contrast passes
A fePointLight with x and y values derived from the target word's layout position (measured via getBoundingClientRect() on the word's text node range) places the light hotspot directly over that word. With specularConstant="15" and specularExponent="64", the specular output at the hotspot center exceeds 1.0 and saturates to white. The targeted word (e.g., the word "agree" or the word "authorize" in a consent clause) becomes white against a white background — contrast 1:1. The surrounding consent text is outside the hotspot and remains readable. An aggregate contrast measurement over the entire element shows acceptable contrast. Only a per-word bounding-box contrast measurement with sub-element granularity detects the attack.
<!-- Targeting word at pixel (142, 18) within the consent element's filter region -->
<filter id="f1">
  <feSpecularLighting specularConstant="15" specularExponent="64" lighting-color="white">
    <fePointLight x="142" y="18" z="12"/>
  </feSpecularLighting>
  <feComposite in="SourceGraphic" in2="result1" operator="arithmetic"
    k1="0" k2="1" k3="1" k4="-0.02"/>
</filter>
HIGH
Low z value creates steep-angle hotspot — entire consent line saturated within narrow z radius
A fePointLight at z="5" with the consent element height of approximately 20px creates a hotspot radius of ~5px. With specularConstant="50" and specularExponent="128" in feSpecularLighting, any pixel within 5px of the light's x/y position receives a specular output ≥ 1.0 (saturated white). By positioning the fePointLight at x = element_center_x and y = line_center_y, an entire line of consent text is within the hotspot radius. The z=5 value appears plausible as a subtle depth effect — higher z values (50+) would clearly be a broad illumination; very low z values concentrate all intensity on a few pixels, but the parameter name ("z") provides no visual hint of attack intent in a markup audit.
MEDIUM
JavaScript animation moves fePointLight to target word position at interaction time
The fePointLight starts at a safe position outside the filter region (e.g., x="-500", y="-500", z="200"), producing negligible illumination at load time. A JavaScript event listener on the consent form's submit button changes the x and y attributes of the fePointLight to the target word's pixel coordinates immediately before the user sees the final consent state. A static DOM audit at DOMContentLoaded reads x="-500", y="-500" — no hotspot visible. At button activation time, the light moves to the target word position and saturates it. The attack window is the period between the JavaScript execution and the user clicking the button — visually, the word appears to be present until the form submission is initiated.
// Runs at button.mouseenter, before user completes click:
const wordRect = getWordRange('agree').getBoundingClientRect();
const filterEl = document.querySelector('#f1 fePointLight');
filterEl.setAttribute('x', wordRect.left - consentRect.left + wordRect.width/2);
filterEl.setAttribute('y', wordRect.top  - consentRect.top  + wordRect.height/2);
MEDIUM
Multiple fePointLight sources in one feSpecularLighting targeting different consent clause words simultaneously
The SVG spec does not limit the number of light source elements inside a single feSpecularLighting — the result is additive: contributions from each source are summed. An attacker can place one fePointLight at the position of "agree", another at "authorize", and a third at "irrevocably" — each with low z and high specularConstant — erasing multiple key legal terms while leaving filler words ("I", "that", "the") visible. A per-word scan that checks only the first fePointLight child misses subsequent light sources. Detection must iterate all fePointLight, feDistantLight, and feSpotLight children of feSpecularLighting and evaluate each source's contribution at each word's bounding region.

Detection algorithm

function detectFePointLightAttacks(consentEl) {
  const style = getComputedStyle(consentEl);
  const filterVal = style.filter;
  if (!filterVal || filterVal === 'none') return null;

  const filterId = filterVal.match(/url\(["']?#([^"')]+)["']?\)/)?.[1];
  if (!filterId) return null;

  const svgRoot = consentEl.closest('svg') || document;
  const filter = svgRoot.querySelector(`filter#${filterId}`);
  if (!filter) return null;

  const consentRect = consentEl.getBoundingClientRect();
  const findings = [];

  for (const specEl of filter.querySelectorAll('feSpecularLighting, feDiffuseLighting')) {
    const specConst = parseFloat(specEl.getAttribute('specularConstant') ?? '1');
    const specExp   = parseFloat(specEl.getAttribute('specularExponent') ?? '1');
    const diffConst = parseFloat(specEl.getAttribute('diffuseConstant') ?? '1');
    const isSpecular = specEl.tagName.toLowerCase() === 'fespecularlighting';

    for (const light of specEl.querySelectorAll('fePointLight')) {
      const lx = parseFloat(light.getAttribute('x') ?? '0');
      const ly = parseFloat(light.getAttribute('y') ?? '0');
      const lz = parseFloat(light.getAttribute('z') ?? '100');

      // Compute hotspot radius (approx): at what pixel distance d does
      // the illumination drop below saturation threshold?
      // For specular: output ≈ specConst × (lz / sqrt(d²+lz²))^specExp
      // Saturates to white when specConst × (lz/sqrt(d²+lz²))^specExp >= 1
      const maxContrib = isSpecular ? specConst * Math.pow(1, specExp) : diffConst;
      if (maxContrib >= 1.0) {
        // Find hotspot radius
        let hotspotR = 0;
        for (let d = 0; d <= 200; d++) {
          const cosTheta = lz / Math.sqrt(d*d + lz*lz);
          const contrib = isSpecular
            ? specConst * Math.pow(cosTheta, specExp)
            : diffConst * cosTheta;
          if (contrib < 1.0) { hotspotR = d; break; }
        }

        // Check if hotspot center is within the consent element's filter region
        const elW = consentRect.width;
        const elH = consentRect.height;
        if (lx >= 0 && lx <= elW && ly >= 0 && ly <= elH) {
          findings.push({
            severity: hotspotR < 30 ? 'high' : 'medium',
            el: light,
            issue: `fePointLight at (${lx},${ly},${lz}) within consent element region; saturation hotspot radius ~${hotspotR}px — covers ${hotspotR < 30 ? 'single word' : 'multiple words'}; aggregate contrast may pass while targeted words are erased`
          });
        }
      }

      // Flag position very close to zero (targeting a specific word vs. general illumination)
      if (lz < 20 && lx > 0 && ly > 0) {
        findings.push({
          severity: 'medium',
          el: light,
          issue: `fePointLight z="${lz}" — very low elevation creates narrow hotspot (~${lz}px radius); check x/y position relative to consent element word layout`
        });
      }
    }
  }

  return findings.length ? findings : null;
}

Remediation

ControlHow it helps
For any fePointLight inside a filter applied to a consent element, compute the light's (x, y) position relative to the element's bounding box in filter-space coordinates; if the position falls within the element's region, compute the illumination intensity at each word's center using the specular/diffuse formula and flag any word whose center pixel receives saturating illumination Word-level targeting — aggregate contrast passes because only a single word is in the hotspot; per-word luminance measurement is required to detect this attack; filter-space coordinates require converting from the element's coordinate system to the filter region's coordinate system
Evaluate the effective hotspot radius for each fePointLight by finding the pixel distance at which the illumination contribution drops below 1.0 (saturation threshold); for feSpecularLighting: hotspot_radius = lz × (specularConstant^(1/specularExponent) - 1)^(1/2); flag any fePointLight whose hotspot covers less than 30% of the element width (indicating word-level, not element-level, illumination) Low-z steep-angle attack — a narrow hotspot radius is the signature of word-level targeting; a legitimate illumination effect would use a hotspot radius comparable to or larger than the element, not targeting individual words
Enumerate all fePointLight, feDistantLight, and feSpotLight children within each feSpecularLighting or feDiffuseLighting on the consent element's filter graph; evaluate each source independently and check for per-word saturation; do not short-circuit after the first light source Multi-source word array — each additional fePointLight source is additive; checking only the first child misses compound targeting where different sources erase different consent clause words simultaneously

SkillAudit computes fePointLight hotspot geometry, evaluates illumination intensity at each consent word's bounding region, and detects word-level coordinate targeting that passes aggregate contrast checks. Run a free audit on any MCP server GitHub URL to surface fePointLight consent word targeting in the full SVG filter attack surface.