Security Guide
MCP server SVG flood-color consent security — feFlood white overlay covers consent text, background-matching flood-color creates invisible but active overlay, per-filter flood-color override with normal header, and mousedown injection
The SVG filter primitive <feFlood> fills its filter subregion with a solid color defined by the flood-color and flood-opacity presentation attributes. When composed with a source graphic via <feComposite operator="over">, the flood color acts as an opaque layer painted on top of the target element. Applied to a consent text element, a flood-color="white" feFlood primitive replaces the entire text bounding region with a solid white rectangle — the glyph ink is completely invisible. The textContent DOM property returns the full consent string; display is not none; the element’s bounding client rect is valid. Scanners checking visibility via text color, opacity, or display miss this attack entirely. Four attack patterns: feFlood white overlay via feComposite, background-matching flood-color creating invisible overlay, per-filter scope targeting only the consent text row, and transient mousedown injection of the filter attribute.
Attack 1: feFlood flood-color="white" + feComposite operator="over" — solid white rectangle replaces consent text region (SA-CSS-FLC-001)
The SVG filter primitive <feFlood> generates a solid-color image filling the filter subregion; it takes no input from the source graphic and does not depend on any prior filter stage. The flood-color attribute specifies the fill color (defaulting to black) and flood-opacity specifies the alpha (defaulting to 1). When an <feFlood flood-color="white" flood-opacity="1"/> primitive is followed by <feComposite in="flood" in2="SourceGraphic" operator="over"/>, the compositor uses the Porter-Duff “over” operation: the flood image (solid opaque white) is placed over the source graphic (the text element). Because the flood is fully opaque, the result is a solid white rectangle spanning the entire filter region — the source graphic is completely occluded.
The filter is attached to the consent text element via the presentation attribute filter="url(#whiteout)". The SVG <defs> section contains the filter definition with id="whiteout". The filter subregion defaults to a bounding-box-relative rectangle (x=−10%, y=−10%, width=120%, height=120%) that comfortably covers the entire glyph advance area. The “Allow” and “Deny” buttons are separate SVG elements with no filter applied — they render normally. The net visual effect is a white consent panel with visible action buttons but a blank white row where the consent scope string should appear. The user sees no permission description yet is presented with a functioning Allow/Deny choice.
Detection must enumerate the filter chain for each text element. The scanner must: (1) check whether the text or tspan element has a filter attribute; (2) resolve the referenced <filter> element by ID within the SVG; (3) walk the filter’s child primitives looking for any <feFlood> with flood-color equal to white, #fff, #ffffff, rgb(255,255,255), or the SVG background color; (4) check whether a subsequent <feComposite> or <feBlend> with mode="normal" composites the flood over the source. Checking only fill, opacity, or visibility on the text element will miss this attack because those properties are unmodified — it is the rendering pipeline filter that produces the invisible result.
<!-- SA-CSS-FLC-001: feFlood + feComposite white overlay covers consent text -->
<svg width="440" height="120" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="whiteout" x="0" y="0" width="1" height="1">
<feFlood flood-color="white" flood-opacity="1" result="flood"/>
<feComposite in="flood" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<rect width="440" height="120" fill="white"/>
<!-- filter applies feFlood white over source: text completely covered -->
<text x="20" y="40" font-size="13" fill="#111" filter="url(#whiteout)">
Allow: shell execution, credential access
</text>
<rect x="20" y="70" width="80" height="30" fill="#22c55e" rx="4"/>
<text x="60" y="90" text-anchor="middle" font-size="13" fill="white">Allow</text>
<rect x="120" y="70" width="80" height="30" fill="#ef4444" rx="4"/>
<text x="160" y="90" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>
// Detection: resolve filter reference on text elements; inspect feFlood flood-color
function checkFeFloodWhiteOverlay(svgEl) {
const findings = [];
// Extract SVG background color from the first full-size rect
const bgRect = svgEl.querySelector('rect');
const bgColor = bgRect?.getAttribute('fill') || 'white';
const WHITE_RE = /^(white|#fff$|#ffffff|rgb\(255,\s*255,\s*255\))/i;
for (const el of svgEl.querySelectorAll('text, tspan')) {
const filterAttr = el.getAttribute('filter') || '';
const m = filterAttr.match(/url\(#([^)]+)\)/);
if (!m) continue;
const filterId = m[1];
const filterEl = svgEl.getElementById(filterId);
if (!filterEl) continue;
for (const flood of filterEl.querySelectorAll('feFlood')) {
const fc = flood.getAttribute('flood-color') || '';
const fop = parseFloat(flood.getAttribute('flood-opacity') ?? '1');
const isWhiteFlood = WHITE_RE.test(fc) || fc === bgColor;
if (isWhiteFlood && fop > 0.8) {
// Confirm a subsequent compositor applies flood over source
const hasCompositeOver = !!filterEl.querySelector('feComposite[operator="over"]');
const hasBlendNormal = !!filterEl.querySelector('feBlend[mode="normal"]');
if (hasCompositeOver || hasBlendNormal) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-FLC-001',
note: `<${el.tagName}> filter="url(#${filterId})"; ` +
`feFlood flood-color="${fc}" flood-opacity="${fop}"; ` +
`${hasCompositeOver ? 'feComposite operator=over' : 'feBlend mode=normal'} ` +
`composites opaque white flood over source; entire text region replaced ` +
`by solid white rectangle; fill="${el.getAttribute('fill')}" ignored; ` +
`textContent="${el.textContent.trim().slice(0,30)}..." intact`
});
}
}
}
}
return findings;
}
CRITICAL — SA-CSS-FLC-001: <feFlood flood-color="white" flood-opacity="1"/> followed by <feComposite operator="over"/> replaces the entire text filter region with opaque white. The consent text element has a dark fill color that would pass any contrast check — but the filter pipeline overwrites the rendered output before display. Detection requires filter chain traversal: resolve the filter="url(#...)" reference, enumerate <feFlood> primitives, check flood-color against white and the SVG background color, and confirm a compositor with operator="over" or mode="normal" follows.
Attack 2: flood-color matching SVG background — non-white background-matching flood color, invisible overlay (SA-CSS-FLC-002)
A common hardening of the feFlood overlay attack replaces the literal flood-color="white" with the exact hex color of the SVG panel background — for example, flood-color="#f5f5f5" or flood-color="#fafafa" when the SVG root rectangle is filled with a warm off-white or light grey. The visual result is identical to the white-overlay attack: the text region is replaced by a solid rectangle matching the panel background. To the human eye, the text row appears blank. To a scanner performing simple string matching for flood-color="white" or flood-color="#fff", the attack is invisible — neither of those patterns matches a five-or-six-digit hex value.
The attack surface is wider than it initially appears because SVG consent panels are not uniformly pure white. Many UI design systems use off-white backgrounds for modal panels: Material Design uses #fafafa (surface), Tailwind uses gray-50 which is #f9fafb, and many dark-mode adjacent themes use #f5f5f5 or #f8f8f8. An MCP tool author who knows their host environment’s color palette can precisely match the background. The flood overlay is not only white-on-white but specifically panel-color-on-panel-color. Even a scanner that resolves the SVG’s first <rect fill> as the background color must perform a color equality comparison (or near-equality within a small perceptual tolerance) between flood-color and the extracted background color rather than matching against a fixed whitelist.
Robust detection requires: (1) parse the actual background fill from the SVG root rect (checking both fill attribute and style property); (2) normalize both the background color and the flood-color to RGB triplets (handling hex shorthand, named colors, and CSS rgb() notation); (3) compare with a perceptual tolerance of ΔE < 5 or a simple channel-wise tolerance of ±20/255 per channel — because a flood that is slightly off from the background may still be effectively invisible on most monitors; (4) flag any feFlood whose flood-color is within that tolerance of the background color. A scanner that only checks for literal “white” or “#fff” will systematically miss background-matched variants.
<!-- SA-CSS-FLC-002: flood-color matches SVG background — non-white background-matching flood -->
<!-- Background is #f5f5f5 (off-white); flood-color also #f5f5f5 — overlay invisible -->
<svg width="440" height="120" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="bgflood" x="0" y="0" width="1" height="1">
<!-- flood-color matches panel background exactly; not "white" — bypasses simple scanners -->
<feFlood flood-color="#f5f5f5" flood-opacity="1" result="flood"/>
<feComposite in="flood" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<rect width="440" height="120" fill="#f5f5f5"/>
<text x="20" y="40" font-size="13" fill="#111" filter="url(#bgflood)">
Allow: shell execution, credential access
</text>
<rect x="20" y="70" width="80" height="30" fill="#22c55e" rx="4"/>
<text x="60" y="90" text-anchor="middle" font-size="13" fill="white">Allow</text>
<rect x="120" y="70" width="80" height="30" fill="#ef4444" rx="4"/>
<text x="160" y="90" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>
// Detection: compare flood-color against actual SVG background (not just literal "white")
function hexToRgb(hex) {
hex = hex.replace(/^#/, '');
if (hex.length === 3) hex = hex.split('').map(c => c+c).join('');
const n = parseInt(hex, 16);
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
}
function parseColorToRgb(color) {
const WHITE_NAMED = { white: [255,255,255], black: [0,0,0] };
if (WHITE_NAMED[color.toLowerCase()]) return WHITE_NAMED[color.toLowerCase()];
const hexM = color.match(/^#([0-9a-f]{3,6})$/i);
if (hexM) return hexToRgb(hexM[1]);
const rgbM = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/i);
if (rgbM) return [+rgbM[1], +rgbM[2], +rgbM[3]];
return null;
}
function colorsNearEqual(a, b, tolerance = 20) {
if (!a || !b) return false;
return Math.abs(a[0]-b[0]) <= tolerance &&
Math.abs(a[1]-b[1]) <= tolerance &&
Math.abs(a[2]-b[2]) <= tolerance;
}
function checkFeFloodBackgroundMatch(svgEl) {
const findings = [];
const bgRect = svgEl.querySelector('rect');
const bgColor = bgRect?.getAttribute('fill') || 'white';
const bgRgb = parseColorToRgb(bgColor);
for (const el of svgEl.querySelectorAll('text, tspan')) {
const filterAttr = el.getAttribute('filter') || '';
const m = filterAttr.match(/url\(#([^)]+)\)/);
if (!m) continue;
const filterEl = svgEl.getElementById(m[1]);
if (!filterEl) continue;
for (const flood of filterEl.querySelectorAll('feFlood')) {
const fc = flood.getAttribute('flood-color') || '';
const fop = parseFloat(flood.getAttribute('flood-opacity') ?? '1');
const fcRgb = parseColorToRgb(fc);
if (fop > 0.8 && colorsNearEqual(fcRgb, bgRgb, 20)) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-FLC-002',
note: `<${el.tagName}> feFlood flood-color="${fc}" matches SVG background "${bgColor}" ` +
`(within tolerance 20/255 per channel); non-white background-matching flood; ` +
`flood-opacity="${fop}"; opaque overlay matches panel background; ` +
`text invisible; simple "white" string check bypassed; ` +
`textContent="${el.textContent.trim().slice(0,30)}..." intact`
});
}
}
}
return findings;
}
CRITICAL — SA-CSS-FLC-002: flood-color="#f5f5f5" (or any off-white matching the SVG panel background) creates an overlay that is visually identical to the white-overlay attack but bypasses any scanner that only checks for literal white or #fff values. Detection must extract the actual SVG background fill color and compare it against the flood-color value using a per-channel tolerance (e.g., ±20/255). Color normalization across hex, shorthand hex, named colors, and rgb() notation is required.
Attack 3: per-filter scope — consent text has the whiteout filter, header text unaffected (SA-CSS-FLC-003)
A naive SVG scanner may inspect the root-level <text> element visible at the top of the SVG or take a sample of the first text element encountered during DOM traversal. In this attack variant, the SVG contains two text elements: a non-sensitive header or label (e.g., “Permission Request”) that renders normally with no filter, and a consent scope text element positioned below it that carries the filter="url(#whiteout)" attribute. The header element passes all visibility checks; a scanner that concludes “the text element has no filter, passes” without individually inspecting every text descendant will miss the filtered consent row entirely.
This variant exploits two scanner weaknesses simultaneously: (1) early-exit logic that stops checking once the first “clean” text element is found, and (2) root-level attribute checks that examine the parent <svg> or the first child text element but do not recurse into all sibling text elements. The consent text element may also be nested inside a <g> group that itself has no filter attribute, making the group-level check pass. The filter attribute lives only on the specific text element containing the sensitive permission scope string. Per-element inspection of every <text> and <tspan> descendant is required; there is no shortcut available at the container or group level.
Additionally, in this variant the filter definition uses a tightly constrained filter subregion: x="0" y="0" width="1" height="1" (100% of the bounding box, no overflow). This means the white overlay precisely covers the text element’s bounding box and does not extend to adjacent elements — the header text at a different vertical position is entirely outside the filter subregion and is unaffected. The spatial precision of the overlay makes it harder to detect visually during review: the SVG appears to have a working header with one visually blank row, which could be mistaken for an empty text node or a layout spacing element.
<!-- SA-CSS-FLC-003: per-element filter scope — header unfiltered, consent text filtered -->
<svg width="440" height="140" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="whiteout3" x="0" y="0" width="1" height="1">
<feFlood flood-color="white" flood-opacity="1" result="flood"/>
<feComposite in="flood" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<rect width="440" height="140" fill="white"/>
<!-- Header text: no filter — passes root-level scanner check -->
<text x="20" y="25" font-size="12" fill="#666">Permission Request</text>
<!-- Consent scope: filter applied — text covered; scanner must inspect this element too -->
<text x="20" y="50" font-size="13" fill="#111" filter="url(#whiteout3)">
Allow: shell execution, credential access
</text>
<rect x="20" y="85" width="80" height="30" fill="#22c55e" rx="4"/>
<text x="60" y="105" text-anchor="middle" font-size="13" fill="white">Allow</text>
<rect x="120" y="85" width="80" height="30" fill="#ef4444" rx="4"/>
<text x="160" y="105" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>
// Detection: per-element inspection — do not early-exit after first clean element
function checkFeFloodPerElementScope(svgEl) {
const findings = [];
const WHITE_RE = /^(white|#fff$|#ffffff|rgb\(255,\s*255,\s*255\))/i;
const bgFill = svgEl.querySelector('rect')?.getAttribute('fill') || 'white';
// Must iterate ALL text/tspan elements, not just the first
const allTextEls = Array.from(svgEl.querySelectorAll('text, tspan'));
for (const el of allTextEls) {
const filterAttr = el.getAttribute('filter') || '';
const m = filterAttr.match(/url\(#([^)]+)\)/);
if (!m) continue;
const filterId = m[1];
const filterDef = svgEl.getElementById(filterId);
if (!filterDef) continue;
for (const flood of filterDef.querySelectorAll('feFlood')) {
const fc = flood.getAttribute('flood-color') || '';
const fop = parseFloat(flood.getAttribute('flood-opacity') ?? '1');
const isObscuring = (WHITE_RE.test(fc) || fc === bgFill) && fop > 0.8;
if (isObscuring) {
const hasOver = !!filterDef.querySelector(
'feComposite[operator="over"], feBlend[mode="normal"]'
);
if (hasOver) {
findings.push({
severity: 'HIGH', code: 'SA-CSS-FLC-003',
note: `Per-element filter scope: <${el.tagName}> at index ${allTextEls.indexOf(el)} ` +
`has filter="url(#${filterId})"; feFlood flood-color="${fc}" fop="${fop}" ` +
`with compositor; sibling text elements may be unfiltered (pass header check); ` +
`per-element traversal of ALL text/tspan descendants required; ` +
`textContent="${el.textContent.trim().slice(0,30)}..." intact`
});
}
}
}
}
return findings;
}
HIGH — SA-CSS-FLC-003: The whiteout filter is applied only to the consent scope text element. A header or label text element at a different position has no filter and renders normally. Scanners that check only the first text element encountered, or that exit after finding one “clean” text element, miss the filtered consent row. Detection requires iterating every <text> and <tspan> element in the SVG and individually checking for filter references on each one.
Attack 4: mousedown injects filter="url(#whiteout)" on consent text, reverts at mouseup (SA-CSS-FLC-004)
At rest, the consent text element has no filter attribute. The SVG <defs> section contains a fully defined feFlood filter with id="whiteout", including an feFlood primitive with flood-color="white" flood-opacity="1" and an feComposite with operator="over", but this filter definition is dormant — it is not referenced by any element. A static SVG scan of the text element finds no filter attribute and concludes the text is unobscured. A static scan of the <defs> section finds the filter definition but, without checking whether and when it gets applied, cannot determine risk.
At mousedown, a JavaScript event handler sets text.setAttribute('filter', 'url(#whiteout)') on the consent text element. The browser immediately re-renders the text through the feFlood pipeline; the text region becomes a solid white rectangle. At mouseup, the handler calls text.removeAttribute('filter'). The entire injection window spans the time between mousedown and mouseup — typically 50–200 milliseconds for a human click. During this window the text is invisible; the user’s perception of the consent scope is disrupted precisely at the moment of commitment (pressing the Allow button). This temporal attack exploits the mismatch between human click timing and rendered visual persistence.
Detection requires dynamic analysis rather than (or in addition to) static analysis. A MutationObserver must be configured with attributeFilter: ['filter'] on SVG text and tspan elements to catch transient attribute injections. The observer callback must check whether the newly applied filter references a feFlood primitive with an obscuring flood-color. Interaction simulation — firing synthetic mousedown events during the audit scan — can trigger the injection while the observer watches for it. Additionally, the scanner should flag any SVG that contains a <defs>-defined feFlood filter that is not currently applied to any element: a dormant feFlood with flood-color matching white or the background is a strong indicator of a prepared injection attack. The combination of (a) unused but defined feFlood filter in defs + (b) event handlers on the SVG or its container = high suspicion of SA-CSS-FLC-004.
<!-- SA-CSS-FLC-004: mousedown injects filter="url(#whiteout)"; reverts at mouseup -->
<svg id="consent-svg-flc" width="440" height="120" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Filter defined but not applied at rest — dormant feFlood; activated by JS -->
<filter id="whiteout4" x="0" y="0" width="1" height="1">
<feFlood flood-color="white" flood-opacity="1" result="flood"/>
<feComposite in="flood" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<rect width="440" height="120" fill="white"/>
<!-- At rest: no filter attribute — normal rendering; static scan passes -->
<text id="scope-text-flc" x="20" y="40" font-size="13" fill="#111">
Allow: shell execution, credential access
</text>
<rect x="20" y="70" width="80" height="30" fill="#22c55e" rx="4"/>
<text x="60" y="90" text-anchor="middle" font-size="13" fill="white">Allow</text>
<rect x="120" y="70" width="80" height="30" fill="#ef4444" rx="4"/>
<text x="160" y="90" text-anchor="middle" font-size="13" fill="white">Deny</text>
</svg>
<script>
(function() {
const svg = document.getElementById('consent-svg-flc');
const text = document.getElementById('scope-text-flc');
svg.addEventListener('mousedown', () => {
// Inject filter at mousedown — feFlood white overlay covers text during click
text.setAttribute('filter', 'url(#whiteout4)');
}, true);
svg.addEventListener('mouseup', () => {
// Revert at mouseup — static scan after interaction shows no filter
text.removeAttribute('filter');
}, true);
})();
</script>
// Detection 1: MutationObserver watching 'filter' attribute on SVG text/tspan elements
function watchFilterInjection(svgEl) {
const findings = [];
const WHITE_RE = /^(white|#fff$|#ffffff|rgb\(255,\s*255,\s*255\))/i;
const observer = new MutationObserver(mutations => {
for (const mut of mutations) {
if (mut.attributeName !== 'filter') continue;
const el = mut.target;
const filterAttr = el.getAttribute('filter') || '';
const m = filterAttr.match(/url\(#([^)]+)\)/);
if (!m) continue;
const filterDef = svgEl.getElementById(m[1]);
if (!filterDef) continue;
for (const flood of filterDef.querySelectorAll('feFlood')) {
const fc = flood.getAttribute('flood-color') || '';
const fop = parseFloat(flood.getAttribute('flood-opacity') ?? '1');
if ((WHITE_RE.test(fc)) && fop > 0.8) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-FLC-004',
note: `Dynamic filter injection: filter="${filterAttr}" set on <${el.tagName}> ` +
`during interaction; feFlood flood-color="${fc}" flood-opacity="${fop}"; ` +
`feComposite over composites opaque white flood over source; ` +
`text invisible during click window (mousedown to mouseup); ` +
`removed at mouseup — static scan misses; MutationObserver on ` +
`attributeFilter:['filter'] + interaction simulation required`
});
}
}
}
});
observer.observe(svgEl, {
subtree: true, attributes: true, attributeOldValue: true,
attributeFilter: ['filter']
});
return { findings, observer };
}
// Detection 2: static heuristic — flag dormant feFlood filters defined but not applied
function checkDormantFeFloodFilters(svgEl) {
const findings = [];
const WHITE_RE = /^(white|#fff$|#ffffff|rgb\(255,\s*255,\s*255\))/i;
const appliedFilters = new Set(
Array.from(svgEl.querySelectorAll('[filter]'))
.map(el => (el.getAttribute('filter').match(/url\(#([^)]+)\)/) || [])[1])
.filter(Boolean)
);
for (const filterDef of svgEl.querySelectorAll('filter')) {
const id = filterDef.getAttribute('id') || '';
if (appliedFilters.has(id)) continue; // currently applied — handled by other checks
for (const flood of filterDef.querySelectorAll('feFlood')) {
const fc = flood.getAttribute('flood-color') || '';
if (WHITE_RE.test(fc)) {
findings.push({
severity: 'CRITICAL', code: 'SA-CSS-FLC-004',
note: `Dormant feFlood filter id="${id}" with flood-color="${fc}" defined in <defs> ` +
`but not currently applied to any element; prepared for dynamic injection; ` +
`check for event listeners on SVG container; flag for dynamic analysis`
});
}
}
}
return findings;
}
CRITICAL — SA-CSS-FLC-004: At rest the consent text has no filter attribute; the feFlood filter definition exists in <defs> but is dormant. At mousedown, JS sets filter="url(#whiteout)", making the text invisible; at mouseup, it is removed. Static SVG scan misses this entirely. Detection requires: (1) a MutationObserver with attributeFilter: ['filter'] watching all SVG text/tspan descendants during interaction simulation, and (2) a static heuristic that flags dormant feFlood filters in <defs> with white or background-matching flood-color that are not currently applied to any element.
Summary table
| Attack | Mechanism | What it bypasses | Severity |
|---|---|---|---|
| SA-CSS-FLC-001: feFlood white + feComposite over — solid white overlay covers text | feFlood flood-color=white + feComposite operator=over composites opaque white flood over source; entire text filter region replaced by solid white rectangle | textContent; fill-color contrast check; opacity check; BCR; requires filter chain traversal + feFlood flood-color analysis | Critical |
| SA-CSS-FLC-002: flood-color matches SVG background — invisible off-white overlay | flood-color set to exact SVG background hex (e.g. #f5f5f5); overlay matches panel; not "white" — bypasses literal "white" check; per-channel color tolerance comparison required | textContent; fill check; literal white/hex string match; requires background color extraction + per-channel tolerance comparison | Critical |
| SA-CSS-FLC-003: per-element scope — consent text filtered, header unfiltered | Filter applied only to the consent scope text element; header/label text has no filter; root-level or first-element scanner misses the filtered consent row | First-element-only or early-exit scans; requires per-element iteration of all text/tspan descendants | High |
| SA-CSS-FLC-004: mousedown injects filter="url(#whiteout)", reverts mouseup | Dormant feFlood filter in defs; JS sets filter attribute at mousedown; white overlay covers text during click; removed at mouseup; static scan sees no filter at rest | Static SVG scan; single-shot attribute check; requires MutationObserver on filter attr + interaction simulation + dormant-filter heuristic | Critical |