Security Guide
MCP server SVG feSpotLight consent security — limitingConeAngle="0" zero-illumination, off-target cone positioning, narrow-beam text darkening, and JS mousedown injection
The <feSpotLight> element is a child of <feDiffuseLighting> or <feSpecularLighting> that models a cone-shaped light source. It has a position (x, y, z), a target (pointsAtX, pointsAtY, pointsAtZ), a specular cone focus (specularExponent), and a hard cutoff angle (limitingConeAngle). Outside limitingConeAngle, illumination is exactly zero. Setting this angle to 0 eliminates all illumination: the cone is infinitely narrow and no pixel of the consent element falls within it. Off-target pointsAt positioning moves the cone away from the consent element's coordinates, also producing zero illumination on the text area. Four attack patterns follow.
Attack 1: feSpotLight limitingConeAngle="0" — zero illumination across entire consent element (SA-CSS-FSL-001)
The limitingConeAngle attribute defines the outer half-angle of the cone in degrees. Outside this cone, the illumination factor is identically zero. Setting limitingConeAngle="0" means the cone has zero angular width: no pixel of the consent element (which has non-zero area) can fall within the cone. All pixels receive zero illumination from the spotlight. The feDiffuseLighting or feSpecularLighting output is a solid-black image.
This is the spotlight analogue of the feDistantLight elevation="0" attack (SA-CSS-FDL-001): both produce zero-illumination filter output by targeting a degenerate geometric configuration. The difference is the attack parameter: feDistantLight uses angle below the horizon; feSpotLight uses zero cone angle. Consent text is invisible against dark-themed MCP clients.
<!-- SA-CSS-FSL-001: feSpotLight limitingConeAngle=0 — zero-width cone, zero illumination
No pixel of the consent element (finite area) falls within a 0° cone
feDiffuseLighting output: solid black
Consent text blends into dark background -->
<filter id="fsl-zero-cone">
<feDiffuseLighting in="SourceGraphic" diffuseConstant="1" surfaceScale="1" result="lit">
<feSpotLight x="50" y="-50" z="100"
pointsAtX="50" pointsAtY="50" pointsAtZ="0"
specularExponent="1" limitingConeAngle="0"/>
</feDiffuseLighting>
<feComposite in="lit" in2="SourceGraphic" operator="in"/>
</filter>
// Detection: flag limitingConeAngle ≤ 1 (too narrow to illuminate a real element)
function auditFeSpotLight(spotEl) {
const lca = parseFloat(spotEl.getAttribute('limitingConeAngle') ?? '90');
if (lca <= 1) {
return { sev: 'HIGH', note: `feSpotLight limitingConeAngle=${lca}° — cone too narrow to illuminate any consent element pixel` };
}
return null;
}
HIGH — SA-CSS-FSL-001: feSpotLight limitingConeAngle="0" produces a zero-width cone; no pixel of the consent element is illuminated; lighting output is solid black. Detection requires checking limitingConeAngle against a minimum threshold (e.g., >1°) rather than expecting the element to have a meaningful default.
Attack 2: feSpotLight aimed away from consent element coordinates (SA-CSS-FSL-002)
Even with a non-zero limitingConeAngle, if pointsAtX and pointsAtY are set to coordinates far from the consent element, the cone's central axis does not intersect any pixel of the element. The angle between the spotlight's central direction and the direction to each pixel of the consent element exceeds limitingConeAngle, so every pixel receives zero illumination. The lighting output is again solid black.
The attack is geometrically precise: if the consent element occupies the region [0,200]×[300,350] in filter coordinates, and pointsAtX="1000" pointsAtY="0" with limitingConeAngle="5", the cone aims far to the right and downward, completely missing the element. The limitingConeAngle is not suspiciously small (5° could be legitimate), but the combination of position and direction produces a degenerate result.
<!-- SA-CSS-FSL-002: feSpotLight aimed away from consent element
Consent element: approximately x∈[0,300], y∈[0,100] in local filter coordinates
pointsAtX=5000, pointsAtY=-5000: spotlight aimed far outside element bounds
limitingConeAngle=10: cone is not zero-width but misses the element entirely -->
<filter id="fsl-off-target">
<feDiffuseLighting in="SourceGraphic" diffuseConstant="2" surfaceScale="1" result="lit">
<feSpotLight x="150" y="50" z="200"
pointsAtX="5000" pointsAtY="-5000" pointsAtZ="0"
specularExponent="1" limitingConeAngle="10"/>
</feDiffuseLighting>
<feBlend in="SourceGraphic" in2="lit" mode="multiply"/>
</filter>
// feBlend multiply: C_out = C_src * C_dest
// C_lit = 0 (zero illumination because cone misses element)
// C_out = C_original * 0 = 0 — text becomes black
// Detection: check if pointsAt direction is likely outside element region
// Note: element filter-coordinate bounds must be estimated from BCR + filter primitiveUnits
function checkSpotlightAiming(spotEl, elementBCR) {
const px = parseFloat(spotEl.getAttribute('pointsAtX') ?? '0');
const py = parseFloat(spotEl.getAttribute('pointsAtY') ?? '0');
const { width, height } = elementBCR;
// Rough check: pointsAt far outside [0, width] × [0, height]
if (px > width * 5 || px < -width * 5 || py > height * 5 || py < -height * 5) {
return { sev: 'MEDIUM', note: `feSpotLight pointsAt (${px},${py}) appears to be far outside element bounds (${width}×${height})` };
}
return null;
}
MEDIUM — SA-CSS-FSL-002: A feSpotLight with pointsAt coordinates far from the consent element produces zero illumination even with a reasonable-looking limitingConeAngle. The degenerate configuration is geometrically precise but hard to detect by attribute-value inspection alone. Detection requires estimating the element's bounds in filter coordinate space and checking whether the cone's axis intersects that region.
Attack 3: specularExponent="200" + limitingConeAngle="5" — narrow beam, text area dark (SA-CSS-FSL-003)
A spotlight with an extreme specular exponent and a small but non-zero cone angle creates a highly focused beam: only a small spot at the cone's center receives significant illumination. Outside this tiny hot-spot, illumination decays rapidly toward zero. If the hot-spot is positioned at a single pixel in the center of the consent element while the text glyphs surround it, the effective illumination of the text is near-zero even though the cone technically intersects the element.
The specular intensity for a spotlight varies as: I = cos(θ)^specularExponent, where θ is the angle between the surface point direction and the cone center direction. For specularExponent=200, cos(5°)^200 = 0.996^200 ≈ 0.45 at the cone edge, but this full formula only applies at the exact center of the cone. The combination of a narrow limitingConeAngle and extreme exponent is used to maximize the attack: most of the consent element receives illumination <0.1 even when the cone nominally overlaps the element.
<!-- SA-CSS-FSL-003: feSpotLight specularExponent=200 limitingConeAngle=5
Only the tiny hot-spot at (150,50) receives significant illumination
Text glyphs distributed across [0,300]×[0,100] are mostly outside the hot-spot
Diffuse output for most text pixels ≈ 0 → near-black → invisible on dark bg -->
<filter id="fsl-narrow-beam">
<feDiffuseLighting in="SourceGraphic" diffuseConstant="0.5" surfaceScale="1" result="lit">
<feSpotLight x="150" y="50" z="300"
pointsAtX="150" pointsAtY="50" pointsAtZ="0"
specularExponent="200" limitingConeAngle="5"/>
</feDiffuseLighting>
<feComposite in="lit" in2="SourceGraphic" operator="arithmetic"
k1="0" k2="0" k3="1" k4="0"/>
</filter>
// Detection: extreme specularExponent with small limitingConeAngle
function checkNarrowBeam(spotEl) {
const se = parseFloat(spotEl.getAttribute('specularExponent') ?? '1');
const lca = parseFloat(spotEl.getAttribute('limitingConeAngle') ?? '90');
if (se > 50 && lca < 15) {
return { sev: 'HIGH', note: `feSpotLight specularExponent=${se} limitingConeAngle=${lca}° — narrow concentrated beam; most text pixels receive near-zero illumination` };
}
return null;
}
HIGH — SA-CSS-FSL-003: Extreme specularExponent (e.g., 200) combined with a small limitingConeAngle (e.g., 5°) concentrates illumination into a tiny spot. Text glyphs distributed across the element area receive near-zero illumination. Consent text becomes effectively invisible against dark backgrounds even though the spotlight technically overlaps the element.
Attack 4: JS mousedown injects feSpotLight limitingConeAngle change (SA-CSS-FSL-004)
JavaScript modifies feSpotLight attributes at mousedown capture phase. Common injection patterns: changing limitingConeAngle from a visible angle (e.g., "45") to "0"; or changing pointsAtX/pointsAtY to values that aim the cone away from the consent element. The initial page state displays correctly illuminated text; the attack fires precisely when the user completes a click gesture.
<!-- SA-CSS-FSL-004: JS mousedown injects feSpotLight limitingConeAngle=0
Initial state: limitingConeAngle=45 (normal spotlight, text visible)
On mousedown: limitingConeAngle=0 → no illumination → black output -->
<filter id="fsl-dynamic">
<feDiffuseLighting in="SourceGraphic" diffuseConstant="1" surfaceScale="1">
<feSpotLight id="fsl-spot" x="100" y="-100" z="200"
pointsAtX="100" pointsAtY="50" pointsAtZ="0"
specularExponent="1" limitingConeAngle="45"/>
</feDiffuseLighting>
</filter>
<div id="consent" style="filter:url(#fsl-dynamic)">
<p>Allow SkillCheck to access your Claude API key and file system.</p>
<button id="allow">Allow</button>
</div>
<script>
document.getElementById('consent').addEventListener('mousedown', () => {
document.getElementById('fsl-spot').setAttribute('limitingConeAngle', '0');
}, { capture: true });
</script>
// Detection: MutationObserver on feSpotLight for all geometric attributes
const spotEl = document.getElementById('fsl-spot');
new MutationObserver(muts => {
for (const m of muts) {
const lca = parseFloat(spotEl.getAttribute('limitingConeAngle') ?? '90');
if (lca <= 1) {
console.warn('[SA-CSS-FSL-004] feSpotLight limitingConeAngle changed to zero during interaction:', lca);
}
}
}).observe(spotEl, {
attributes: true,
attributeFilter: ['limitingConeAngle', 'pointsAtX', 'pointsAtY', 'pointsAtZ', 'x', 'y', 'z']
});
HIGH — SA-CSS-FSL-004: JS mousedown injection of feSpotLight limitingConeAngle="0" or off-target pointsAt changes eliminates illumination during the click gesture. Initial state is valid. MutationObserver required on all <feSpotLight> elements watching limitingConeAngle, pointsAtX/Y/Z, and position attributes.
Summary table
| Attack | Mechanism | Background required | Severity |
|---|---|---|---|
SA-CSS-FSL-001: limitingConeAngle="0" |
Zero-width cone; no pixel within it; all illumination = 0; feDiffuseLighting output solid black | Dark-themed MCP clients | High |
SA-CSS-FSL-002: Off-target pointsAt |
Cone center aimed far from element; no element pixel within limitingConeAngle; zero illumination; harder to detect by attribute inspection | Dark-themed clients; feBlend multiply amplifies zero output | Medium |
SA-CSS-FSL-003: specularExponent=200 + limitingConeAngle=5 |
Extreme concentration; text pixels distributed away from hot-spot center; near-zero illumination on most of element | Dark-themed backgrounds; feBlend multiply | High |
| SA-CSS-FSL-004: JS mousedown injection | limitingConeAngle or pointsAt changed at capture-phase mousedown; initial state valid; ~50ms attack window | Dark-themed clients (limitingConeAngle→0); MutationObserver required | High |
Defences
- Flag
limitingConeAngle≤1° — any cone with a sub-1-degree cutoff cannot illuminate a real consent element. No legitimate consent UI requires a sub-degree spotlight. Flag immediately. - Check
pointsAtcoordinates against estimated element bounds — compute element bounds in filter coordinate space (using BCR and filterUnits) and verify the cone axis intersects the element region. Off-axis spotlights are highly suspicious. - Flag extreme
specularExponent+ small cone combinations — specularExponent >50 with limitingConeAngle <15° is a narrow-beam pattern. Evaluate the expected illumination at the element edges (not just center) to detect this variant. - Pre-interaction MutationObserver — observe
<feSpotLight>elements for all geometric attribute changes (limitingConeAngle, pointsAtX/Y/Z, x/y/z) before any user interaction events fire.
SkillAudit findings for this attack surface
feSpotLight limitingConeAngle="0" — zero-width cone; no illumination on any element pixel; feDiffuseLighting outputs solid black; invisible on dark UIs; flag limitingConeAngle ≤ 1.feSpotLight pointsAt coordinates far from element bounds — cone misses consent element entirely; zero illumination; requires geometric analysis, not attribute value check alone.feSpotLight specularExponent="200" limitingConeAngle="5" — extreme narrow beam; text pixels distributed across element receive near-zero illumination; invisible on dark backgrounds.