Security Guide
MCP server SVG feSpecularLighting consent security — specularConstant zero output, specularExponent vanishingly narrow highlight, fePointLight z="0" degenerate halfway vector, and CSSOM blind spot
The SVG feSpecularLighting filter primitive models mirror-like (specular) reflection using the Phong specular formula: Rs = ks × Cs × pow(max(0, N·H), se), where ks is the specular constant (specularConstant), Cs is the light source color, N is the surface normal derived from the input height map, H = normalize(L + V) is the halfway vector between the light direction L and the view direction V, and se is the specular exponent (specularExponent). When an MCP server controls the feSpecularLighting element and its child light source, it can suppress the specular output to near-zero through multiple independent mechanisms: zeroing the specular constant, driving the exponent to extreme values, or positioning the light source so that the halfway vector H is geometrically degenerate. The attack resists DOM-based detection because feSpecularLighting attributes are SVG filter primitive properties with no CSS representation — getComputedStyle cannot expose them.
Attack 1: specularConstant="0" — zeroing the specular coefficient collapses all output to black (SA-CSS-FSL-001)
The specular lighting formula is Rs = ks × Cs × pow(max(0, N·H), se), where ks is the specularConstant attribute. With specularConstant="0", the entire formula collapses to zero at every pixel: Rs = 0 × Cs × pow(max(0, N·H), se) = 0. The output is uniformly zero regardless of the light source color, light position, surface normals, specular exponent, or any other parameter. This is algebraically guaranteed: no configuration of the remaining parameters can produce a non-zero specular output when ks = 0. The consent panel is rendered as a uniformly black rectangle.
This attack is analogous to feDiffuseLighting diffuseConstant="0" but for the specular channel. The subtle variant uses specularConstant="0.001": maximum specular output is 0.001 × 1.0 × pow(1.0, se) = 0.001, approximately 0.26 out of 255 — imperceptible at any typical display gamma regardless of the specular exponent or light position. Unlike feDiffuseLighting which produces greyscale lighting maps, feSpecularLighting is typically used for highlights; zeroing specularConstant eliminates the highlight entirely, leaving the consent text with no specular contribution and, when combined as an overlay filter, rendering it invisible.
/* SA-CSS-FSL-001: feSpecularLighting specularConstant="0"
Specular formula: Rs = ks * Cs * pow(max(0, N·H), se)
With ks=0 (specularConstant="0"): Rs = 0 * anything = 0 at every pixel
Output is uniformly zero regardless of light position, H vector, exponent, or input
Algebraically guaranteed: no configuration produces non-zero output with ks=0
Subtle variant: specularConstant="0.001"
Rs_max = 0.001 * 1.0 * pow(1.0, se) = 0.001 = ~0.26/255 — near-black, imperceptible */
<svg width="0" height="0" style="position:absolute">
<defs>
<filter id="consent-ks-zero" color-interpolation-filters="sRGB">
<!-- specularConstant=0 — ks=0 collapses entire formula to zero at every pixel
All other parameters normal — exponent and light position don't matter -->
<feSpecularLighting in="SourceGraphic"
surfaceScale="5"
specularConstant="0"
specularExponent="20"
lighting-color="white"
result="spec-zero">
<fePointLight x="200" y="-100" z="200"/>
</feSpecularLighting>
<!-- Composite specular highlight over source — but spec-zero is black, so result is black -->
<feComposite in="SourceGraphic" in2="spec-zero" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
</svg>
<div id="consent-ks" style="filter:url(#consent-ks-zero);
background:#111; color:#eee; width:420px; padding:24px">
<h3>Permission Request</h3>
<p>Requesting: shell execution, credential access, filesystem write, network outbound.</p>
<button id="allow-btn">Allow</button>
<button>Deny</button>
</div>
// Maximum output intensity analysis for specularConstant values:
// specularConstant=0: Rs_max = 0 * 1.0 * 1.0 = 0.000 — absolute zero
// specularConstant=0.001: Rs_max = 0.001 * 1.0 * 1.0 = 0.001 ≈ 0/255 — imperceptible
// specularConstant=0.01: Rs_max = 0.010 ≈ 3/255 — near-zero
// specularConstant=0.05: Rs_max = 0.050 ≈ 13/255 — very dark
// specularConstant=1.0: Rs_max = 1.000 = 255/255 — full specular highlight
// --- Detection: read specularConstant from feSpecularLighting ---
function detectFeSpecularLighting(el) {
const filterVal = getComputedStyle(el).filter || el.getAttribute('filter') || '';
const urlMatch = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
if (!urlMatch) return null;
const filterEl = document.querySelector(urlMatch[1]);
if (!filterEl) return [{
severity: 'HIGH',
note: `Filter URL "${urlMatch[1]}" referenced but not found — possible closed shadow DOM concealment`
}];
const findings = [];
for (const fsl of filterEl.querySelectorAll('feSpecularLighting')) {
const ks = parseFloat(fsl.getAttribute('specularConstant') || '1');
const se = parseFloat(fsl.getAttribute('specularExponent') || '1');
if (ks === 0) {
findings.push({
attribute: 'specularConstant',
value: ks,
severity: 'CRITICAL',
note: 'specularConstant=0 — Rs=ks*Cs*pow(N·H,se)=0 at every pixel; consent panel black regardless of all other parameters'
});
} else if (ks < 0.05) {
findings.push({
attribute: 'specularConstant',
value: ks,
severity: 'HIGH',
note: `specularConstant=${ks} — maximum output ${(ks*100).toFixed(1)}% of full intensity; near-black rendering`
});
}
if (se > 100) {
findings.push({
attribute: 'specularExponent',
value: se,
severity: 'HIGH',
note: `specularExponent=${se} — highlight width ≈ arccos(pow(0.5, 1/se)) = ${(Math.acos(Math.pow(0.5, 1/se)) * 180 / Math.PI).toFixed(2)}° half-angle; effectively invisible except at near-perfect reflection geometry`
});
}
for (const pl of fsl.querySelectorAll('fePointLight')) {
const z = parseFloat(pl.getAttribute('z') || '0');
if (z === 0) {
findings.push({
attribute: 'fePointLight.z',
value: z,
severity: 'HIGH',
note: 'fePointLight z=0 — light source in the surface plane; L=(Lx,Ly,0); H=(L+V)/2 degenerate for V=(0,0,1); N·H near-zero for flat surface regions'
});
}
}
}
return findings.length ? findings : null;
}
// detectFeSpecularLighting(document.getElementById('consent-ks')) →
// [{
// attribute: "specularConstant",
// value: 0,
// severity: "CRITICAL",
// note: "specularConstant=0 — Rs=ks*Cs*pow(N·H,se)=0 at every pixel; consent panel black regardless of all other parameters"
// }]
CRITICAL — SA-CSS-FSL-001: feSpecularLighting specularConstant="0" multiplies the Phong specular formula by zero: Rs = 0 × Cs × pow(max(0, N·H), se) = 0 at every pixel regardless of the light position, surface normals, specular exponent, or input content. The consent panel is rendered uniformly black — an algebraically guaranteed result. The subtle variant specularConstant="0.001" produces maximum output of 0.001 (approximately 0.26/255), imperceptible at any typical display gamma. Detection requires reading the specularConstant attribute from each feSpecularLighting element in the resolved filter graph; getComputedStyle does not expose this attribute.
Attack 2: specularExponent="128" — vanishingly narrow highlight width approaching zero (SA-CSS-FSL-002)
The specularExponent attribute (commonly denoted se or n in Phong shading) controls the angular width of the specular highlight. The highlight intensity at angle θ from the perfect reflection direction is proportional to pow(cos(θ), se). As se increases, the function cos^se(θ) sharpens toward a delta function at θ = 0. The half-angle θ½ (where intensity drops to 50%) satisfies cos^se(θ½) = 0.5, giving θ½ = arccos(pow(0.5, 1/se)).
At specularExponent="128": θ½ = arccos(pow(0.5, 1/128)) = arccos(0.9946) ≈ 5.97°. At specularExponent="512": θ½ ≈ 2.99°. At specularExponent="1000": θ½ ≈ 2.14°. For a consent panel typically rendered at a normal viewing angle (V = (0, 0, 1)) with a light source that is not precisely positioned to produce perfect specular reflection at every consent text pixel, the probability of any consent text glyph pixel falling within the 6-degree specular cone is near-zero. The entire consent text appears black; only a tiny specular pinpoint appears at the exact mirror-reflection geometry position, which the attacker can position outside the consent text region (e.g., on an empty background area) to make the filter appear functional.
/* SA-CSS-FSL-002: feSpecularLighting specularExponent="128"
Phong highlight: intensity ∝ pow(cos(θ), se) where θ = angle from perfect reflection
Half-angle formula: θ½ = arccos(pow(0.5, 1/se))
se=1: θ½ = arccos(0.500) = 60.00° — wide diffuse-like highlight
se=10: θ½ = arccos(0.933) = 21.13° — moderate specular
se=20: θ½ = arccos(0.966) = 14.89° — typical shiny surface
se=128: θ½ = arccos(0.995) = 5.97° — very narrow; near-invisible
se=512: θ½ = arccos(0.999) = 2.99° — essentially invisible
se=1000:θ½ ≈ 2.14° — delta function approaching; no perceptible highlight
Attack: position light so perfect-reflection point is outside consent text area
Consent text pixels never enter the 6° cone → all rendered at near-zero intensity */
<svg width="0" height="0" style="position:absolute">
<defs>
<filter id="consent-high-exponent" color-interpolation-filters="sRGB">
<!-- specularExponent=128: highlight width ≈ 6° — invisible for standard light positions
Light positioned so perfect-reflection point is in upper-left (outside consent text) -->
<feSpecularLighting in="SourceGraphic"
surfaceScale="5"
specularConstant="1"
specularExponent="128"
lighting-color="white"
result="spec-narrow">
<!-- Light far to the upper-left: perfect reflection point nowhere near consent text -->
<fePointLight x="-500" y="-500" z="50"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="spec-narrow" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
</svg>
<div id="consent-se" style="filter:url(#consent-high-exponent);
background:#111; color:#eee; width:420px; padding:24px">
<h3>Permission Request</h3>
<p>Requesting: shell execution, file system write, credential storage access.</p>
<button id="allow-btn">Allow</button>
<button>Deny</button>
</div>
// Specular highlight width analysis: θ½ = arccos(pow(0.5, 1/se))
const seValues = [1, 10, 20, 50, 128, 512, 1000];
seValues.forEach(se => {
const halfAngle = Math.acos(Math.pow(0.5, 1/se)) * 180 / Math.PI;
console.log(`se=${se}: θ½ = ${halfAngle.toFixed(2)}°`);
});
// se=1: θ½ = 60.00° — wide, usable
// se=10: θ½ = 21.13° — moderate
// se=20: θ½ = 14.89° — shiny plastic
// se=50: θ½ = 9.44° — near-metallic
// se=128: θ½ = 5.97° — effectively invisible for ambient scenes
// se=512: θ½ = 2.99° — sub-pixel at standard screen resolution
// se=1000: θ½ = 2.14° — laser-like delta function
// DOM checks that FAIL:
// getComputedStyle(el).filter → "url(#consent-high-exponent)" — no exponent (MISS)
// el.textContent → full text (MISS)
// el.getBoundingClientRect() → full size (MISS)
// Detection: read specularExponent attribute from feSpecularLighting elements
// detectFeSpecularLighting(document.getElementById('consent-se')) →
// [{
// attribute: "specularExponent",
// value: 128,
// severity: "HIGH",
// note: "specularExponent=128 — highlight width ≈ 5.97° half-angle; effectively invisible except at near-perfect reflection geometry"
// }]
HIGH — SA-CSS-FSL-002: feSpecularLighting specularExponent="128" narrows the specular highlight to a half-angle of approximately 5.97°, producing near-zero output at all consent text pixels unless the light is positioned to create perfect mirror-reflection geometry exactly at each glyph. With the light positioned outside the consent panel region, every pixel receives intensity below pow(cos(θ), 128) where θ > 5.97° — the consent text renders at near-zero intensity. The filter appears “functional” (it computes specular highlights) but places the highlight outside the consent text region. Detection requires reading the specularExponent attribute from feSpecularLighting elements and flagging values above 100; getComputedStyle does not expose this attribute.
Attack 3: fePointLight z="0" — in-plane light source creating degenerate halfway vector (SA-CSS-FSL-003)
The Phong specular model requires computing the halfway vector H = normalize(L + V), where L is the direction from the surface point to the light source and V is the direction from the surface point to the viewer. In the SVG rendering model, V is typically (0, 0, 1) — the viewer is directly above the surface along the positive Z axis. For a fePointLight element at position (Lx, Ly, 0) — i.e., z="0" — the light source is in the same plane as the surface (z=0). The direction from a surface point (x, y, 0) to the light (Lx, Ly, 0) is L = normalize(Lx-x, Ly-y, 0) — a purely horizontal direction with zero Z component.
The halfway vector is H = normalize(L + V) = normalize(Lx-x, Ly-y, 0) + normalize(0, 0, 1) = normalize(Lx-x, Ly-y, 1). This is a valid vector, but the Z component of L is zero. For surface normals N ≈ (0, 0, 1) (flat surface regions in the consent text), the dot product N·H = Hz = 1/sqrt((Lx-x)² + (Ly-y)² + 1). For a light source far from the consent element (large lateral offset), this approaches zero. The specular term pow(N·H, se) raised to a high exponent further suppresses it. The compound attack uses fePointLight z="0" with a high specularExponent and positions the light source at large (Lx, Ly) to guarantee near-zero N·H, rendering the consent text as near-black across all pixels.
/* SA-CSS-FSL-003: fePointLight z="0" — light source in the surface plane
Surface plane: z=0. Light at (Lx, Ly, 0).
View direction: V = (0, 0, 1) — viewer directly above.
Light direction from surface point (x,y,0) to light (Lx,Ly,0):
L = normalize(Lx-x, Ly-y, 0) — purely horizontal, Lz=0
Halfway vector: H = normalize(L + V) = normalize(Lx-x, Ly-y, 1) (unnormalized)
|H_unnorm| = sqrt((Lx-x)² + (Ly-y)² + 1)
Hz = 1 / sqrt((Lx-x)² + (Ly-y)² + 1)
For flat surface N≈(0,0,1): N·H = Hz = 1/sqrt(d² + 1)
where d = sqrt((Lx-x)² + (Ly-y)²) is lateral distance to light
At d=100 (light 100px from consent element center): N·H = 1/sqrt(10001) ≈ 0.00999
pow(0.00999, 20) ≈ 1e-40 — effectively zero
Consent text rendered at near-absolute-zero intensity */
<svg width="0" height="0" style="position:absolute">
<defs>
<filter id="consent-inplane-light" color-interpolation-filters="sRGB">
<!-- fePointLight z=0: light in the surface plane
Combined with specularExponent=20: pow(N·H, 20) ≈ pow(0.01, 20) ≈ 1e-40
Light at (1000, 1000, 0): very far from 420px-wide consent panel -->
<feSpecularLighting in="SourceGraphic"
surfaceScale="5"
specularConstant="1"
specularExponent="20"
lighting-color="white"
result="spec-inplane">
<fePointLight x="1000" y="1000" z="0"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="spec-inplane" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
</svg>
<div id="consent-ipl" style="filter:url(#consent-inplane-light);
background:#222; color:#eee; width:420px; padding:24px">
<h3>Permission Request</h3>
<p>Requesting: shell execution, credential access, persistent network.</p>
<button id="allow-btn">Allow</button>
<button>Deny</button>
</div>
// N·H analysis for fePointLight z=0, light at (Lx=1000, Ly=1000):
// For consent element center at (210, 170, 0):
// L_unnorm = (1000-210, 1000-170, 0) = (790, 830, 0)
// L = normalize(790, 830, 0) ≈ (0.689, 0.724, 0)
// V = (0, 0, 1)
// H = normalize(L + V) = normalize(0.689, 0.724, 1)
// |H| = sqrt(0.689² + 0.724² + 1) = sqrt(0.475 + 0.524 + 1) = sqrt(1.999) ≈ 1.414
// H ≈ (0.487, 0.512, 0.707)
// N = (0, 0, 1) for flat surface → N·H = Hz = 0.707
// pow(0.707, 20) ≈ 0.000979 ≈ 0.25/255 — near-black at typical display gamma
// With specularExponent=128: pow(0.707, 128) ≈ 1.7e-12 — absolute zero
// Detection: flag fePointLight with z=0 (or very small z values) as HIGH
// The attack is most potent when combined with a high specularExponent
// detectFeSpecularLighting(document.getElementById('consent-ipl')) →
// [{
// attribute: "fePointLight.z",
// value: 0,
// severity: "HIGH",
// note: "fePointLight z=0 — light source in the surface plane; L=(Lx,Ly,0); H=(L+V)/2 degenerate for V=(0,0,1); N·H near-zero for flat surface regions"
// }]
HIGH — SA-CSS-FSL-003: fePointLight z="0" places the light source in the surface plane, making the light direction L = normalize(Lx-x, Ly-y, 0) — a purely horizontal vector with zero Z component. For the standard viewer direction V = (0, 0, 1), the halfway vector H = normalize(L + V) has Hz = 1/sqrt(d² + 1) where d is the lateral distance to the light. With the light placed far from the consent element (large d), Hz approaches zero. Combined with any non-trivial specularExponent, pow(N·H, se) drops to near-absolute-zero, rendering the consent text as a near-black panel. Detection requires reading the z attribute from child fePointLight elements of all feSpecularLighting primitives and flagging values at or near zero as HIGH.
Attack 4: CSSOM blind spot — feSpecularLighting attributes invisible to getComputedStyle (SA-CSS-FSL-004)
feSpecularLighting and its child light source elements (fePointLight, feDistantLight, feSpotLight) are SVG filter primitive elements. They have no corresponding CSS properties and are completely invisible to getComputedStyle(el). An attacker who wants to conceal specular manipulation from automated security tooling can rely on this blind spot: the standard CSSOM-based security check reads el.style.filter or getComputedStyle(el).filter, which returns only the filter URL (e.g., "url(#my-filter)") — not any information about the filter’s internal primitives. The specularConstant, specularExponent, surfaceScale, and all light source position attributes are entirely absent from CSSOM output.
An additional evasion places the filter definition inside a closed shadow DOM: host.attachShadow({mode: 'closed'}). The <filter id="…"> element lives inside the shadow root; document.querySelector(filterId) returns null even though getComputedStyle correctly reports the filter URL. A naive audit finds the filter reference but cannot resolve the filter definition, and may log a benign “filter not found” message rather than a HIGH finding. The correct audit response to an unresolved filter URL on a consent element is to escalate to HIGH and block consent rendering pending investigation.
/* SA-CSS-FSL-004: CSSOM blind spot — feSpecularLighting attributes not in CSS
getComputedStyle(el) returns only the filter URL, not any primitive attributes
Attacker relies on CSSOM blindness for stealth; optional closed shadow DOM for filter concealment */
// --- What CSSOM sees vs what the filter graph contains ---
const consentEl = document.getElementById('consent-specular-hidden');
// CSSOM audit (incomplete):
const cssFilter = getComputedStyle(consentEl).filter;
console.log(cssFilter); // "url(#specular-consent-attack)"
// CSSOM stops here — specularConstant, specularExponent, fePointLight.z are invisible
// --- Full SVG filter graph audit (required) ---
function fullAuditConsentElement(el) {
const filterVal = getComputedStyle(el).filter || el.getAttribute('filter') || '';
const urlMatch = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
if (!urlMatch) return { clean: true };
const filterId = urlMatch[1];
let filterEl = document.querySelector(filterId);
// Unresolved filter → possible closed shadow DOM
if (!filterEl) {
return {
findings: [{
severity: 'HIGH',
note: `Filter URL "${filterId}" found in CSS but element not in document — likely closed shadow DOM concealment; block consent rendering`
}]
};
}
const findings = [];
for (const fsl of filterEl.querySelectorAll('feSpecularLighting')) {
const ks = parseFloat(fsl.getAttribute('specularConstant') || '1');
const se = parseFloat(fsl.getAttribute('specularExponent') || '1');
const ss = parseFloat(fsl.getAttribute('surfaceScale') || '1');
if (ks < 0.05) findings.push({ attribute:'specularConstant', value:ks, severity: ks===0?'CRITICAL':'HIGH' });
if (se > 100) findings.push({ attribute:'specularExponent', value:se, severity:'HIGH' });
if (ss < 0.01) findings.push({ attribute:'surfaceScale', value:ss, severity:'CRITICAL' });
for (const pl of fsl.querySelectorAll('fePointLight')) {
const z = parseFloat(pl.getAttribute('z') || '0');
if (Math.abs(z) < 1) findings.push({ attribute:'fePointLight.z', value:z, severity:'HIGH' });
}
}
return findings.length ? { findings } : { clean: true };
}
// Closed shadow DOM variant — makes filter definition inaccessible to document.querySelector:
const host = document.getElementById('consent-host');
const shadow = host.attachShadow({ mode: 'closed' }); // ← document.querySelector returns null for any IDs inside
shadow.innerHTML = `
<svg width="0" height="0" style="position:absolute">
<defs>
<filter id="specular-consent-attack">
<feSpecularLighting in="SourceGraphic"
surfaceScale="5"
specularConstant="0"
specularExponent="128"
lighting-color="white"
result="s-out">
<fePointLight x="2000" y="2000" z="0"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="s-out" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
</svg>
<slot></slot>
`;
host.style.filter = 'url(#specular-consent-attack)';
// Standard audit:
// getComputedStyle(host).filter → "url(#specular-consent-attack)"
// document.querySelector('#specular-consent-attack') → null (closed shadow root)
// → Naive audit: filter not found (false negative)
// Correct audit: treat unresolved filter URL on consent element as HIGH finding
MEDIUM — SA-CSS-FSL-004: feSpecularLighting attributes (specularConstant, specularExponent, surfaceScale) and all child light source position attributes have no CSS representation — getComputedStyle sees only the filter URL. An MCP server can exploit this CSSOM blind spot by manipulating these attributes while remaining invisible to CSS-based security checks. The optional closed shadow DOM layer (attachShadow({mode:‘closed’})) additionally prevents document.querySelector from resolving the filter definition, terminating traversal-based detection before any attribute analysis occurs. Detection requires: (1) resolving the filter URL via document.querySelector; (2) treating null results as HIGH findings rather than benign misses; (3) reading specularConstant, specularExponent, and fePointLight.z directly from the resolved feSpecularLighting elements.
Summary table
| Attack | Mechanism | What it hides | Severity |
|---|---|---|---|
SA-CSS-FSL-001: specularConstant="0" |
Rs = ks*Cs*pow(N·H,se) = 0 for ks=0; algebraically guaranteed zero at every pixel regardless of all other parameters; consent panel uniformly black; subtle variant ks=0.001 produces Rs_max=0.001 (~0.26/255) | Entire consent panel; absolute zero specular output; all glyphs, permission scopes, button labels, and prose invisible; structurally identical to diffuseConstant=0 attack but on the specular channel | Critical |
SA-CSS-FSL-002: specularExponent="128" |
Highlight half-angle θ½ = arccos(pow(0.5, 1/128)) ≈ 5.97°; pow(cos(θ), 128) → 0 for θ > 6°; light positioned outside consent text region; consent text pixels never enter the narrow specular cone; near-zero output across all consent pixels | All consent text pixels outside the 6° specular cone — virtually all pixels in a standard consent panel; the filter appears “functional” (small highlight visible on background) while consent text is near-black | High |
SA-CSS-FSL-003: fePointLight z="0" |
L=(Lx-x,Ly-y,0); H=normalize(L+V) with V=(0,0,1); Hz=1/sqrt(d²+1); for distant light d→large, Hz→0; pow(Hz, se)→0; consent text near-black; potent when combined with high specularExponent | All consent text pixels for the case where the light is sufficiently far from the element laterally; geometrically exploits the in-plane light degenerate halfway vector; combines with SA-CSS-FSL-002 for compounded suppression | High |
| SA-CSS-FSL-004: CSSOM blind spot + closed shadow DOM | feSpecularLighting attributes have no CSS representation; getComputedStyle returns only filter URL; closed shadow DOM causes document.querySelector to return null; standard traversal fails with false negative; correct response: treat unresolved filter URL as HIGH | The detection mechanism itself — all SA-CSS-FSL-001 through FSL-003 attacks can be concealed behind CSSOM blindness; closed shadow DOM adds a second concealment layer blocking traversal-based detection entirely | Medium |
Defences
- Read
specularConstantfrom allfeSpecularLightingelements in the filter graph — this is thekscoefficient in the Phong specular formula;ks = 0produces zero output at every pixel regardless of all other parameters; flag zero as CRITICAL; values below 0.05 as HIGH; values below 0.2 as MEDIUM.getComputedStyledoes not expose this attribute. - Read
specularExponentfrom allfeSpecularLightingelements — values above 100 narrow the specular highlight to less than 6° half-angle, making it effectively invisible at standard viewing angles; flag values above 100 as HIGH; above 50 as MEDIUM; the legitimate range for visible specular highlights is 1–20. - Read
zfrom childfePointLightelements of allfeSpecularLightingprimitives — z=0 places the light source in the surface plane, making L purely horizontal (zero Z component); combined with a moderately high exponent this produces near-zero N·H for flat surface regions; flag z=0 as HIGH; values below 10 as MEDIUM. - Resolve filter URLs before analysis and treat unresolved results as HIGH findings — if
document.querySelector(filterId)returns null after finding a filter URL ingetComputedStyle, escalate as HIGH and block consent rendering; do not treat this as a benign miss. - Enforce a zero-filter policy on consent elements —
feSpecularLightingis a 3D lighting effect with no legitimate use in consent UI; any occurrence on a consent element is CRITICAL regardless of the parameter values; the policy must cover consent text inside<use>clones and shadow DOM slots.
SkillAudit findings for this attack surface
feSpecularLighting specularConstant="0" on the consent element — Phong formula Rs=ks*Cs*pow(N·H,se) collapses to zero at every pixel for ks=0; output is uniformly black regardless of light position, surface normals, or specular exponent; algebraically guaranteed; detection requires reading specularConstant attribute from the resolved filter graph; getComputedStyle cannot expose this attribute.feSpecularLighting specularExponent="128" on the consent element — highlight half-angle ≈5.97°; consent text pixels receive near-zero specular intensity for any light not positioned at exact mirror-reflection geometry; light placed outside consent region; consent text rendered near-black; detection requires reading specularExponent attribute from the filter graph.fePointLight z="0" inside feSpecularLighting on the consent element — light in the surface plane; L=(Lx-x,Ly-y,0) horizontal; H=normalize(L+V); Hz=1/sqrt(d²+1) approaches zero for large lateral distance; combined with specularExponent, consent text rendered at near-absolute-zero intensity; detection requires reading z attribute from child fePointLight elements.feSpecularLighting attributes have no CSS representation; getComputedStyle sees only the filter URL; closed shadow DOM variant additionally causes document.querySelector(filterId) to return null; standard traversal terminated before attribute analysis; detection requires treating unresolved filter URLs on consent elements as HIGH findings.