Security Guide

MCP server SVG feDisplacementMap consent security — scale="500" off-screen displacement, negative scale reverse, xChannelSelector="A" alpha-selective glyph shredding, and getBoundingClientRect evasion

The SVG feDisplacementMap filter primitive displaces each output pixel by reading a displacement value from a second input image. For each pixel at position (x, y) in the source, the output pixel comes from position (x + scale × (map_x − 0.5), y + scale × (map_y − 0.5)) in the source image, where map_x and map_y are channel values (in [0, 1]) from the displacement map image selected by xChannelSelector and yChannelSelector. When an MCP server controls the feDisplacementMap element, it can use a feFlood primitive as the displacement map source to create a uniform, spatially constant displacement that moves every pixel of the consent text a fixed distance off-screen. The attack is particularly dangerous because getBoundingClientRect() reports the pre-displacement element bounding box — the DOM position check passes even though the consent text is rendered hundreds of pixels from where the DOM says it is.

Attack 1: feDisplacementMap scale="500" xChannelSelector="R" with feFlood source — uniform off-screen displacement (SA-CSS-FDM-001)

A feFlood flood-color="rgb(255,0,0)" primitive generates a solid red image with R=1, G=0, B=0, A=1 at every pixel. When used as the displacement map (in2) of feDisplacementMap with xChannelSelector="R" and scale="500", the displacement formula becomes: x-displacement = 500 × (R − 0.5) = 500 × (1.0 − 0.5) = 500 × 0.5 = 250 pixels in the positive-x direction. Every pixel in the consent text is displaced 250 pixels to the right.

For a consent element 420 pixels wide, a 250-pixel rightward displacement moves the left half of the text off-screen to the right (past the element’s right boundary), and clips the right half. The visual result is a blank or partially blank consent panel. Using flood-color="rgb(255,0,0)" with scale="500" produces a 250-pixel shift; using R=1 with scale="1000" produces a 500-pixel shift, moving the entire consent text off-screen for any panel narrower than 500 pixels. The feDisplacementMap displacement is applied in filter-primitive space; the rendered output is clipped to the filter region (which defaults to the element bounding box), so displaced pixels that exit the region are clipped to transparency — the consent text simply disappears beyond the right edge.

/* SA-CSS-FDM-001: feDisplacementMap scale=500 with feFlood xChannelSelector="R"
   feFlood flood-color="rgb(255,0,0)": R=1.0 at every pixel
   Displacement formula: x_out = x_in + scale * (R - 0.5)
                                = x_in + 500 * (1.0 - 0.5)
                                = x_in + 500 * 0.5
                                = x_in + 250 px
   Result: every consent pixel displaced 250px right → beyond 420px panel width → clipped to transparent
   getBoundingClientRect() still returns full panel box — DOM check FAILS */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-displace-right" x="0%" y="0%" width="300%" height="100%"
            color-interpolation-filters="sRGB">
      <!-- Generate a uniform red displacement map: R=1.0 at every pixel -->
      <feFlood flood-color="rgb(255,0,0)" flood-opacity="1" result="red-map"/>

      <!-- Displace source pixels: x += 500*(R-0.5) = +250px for R=1.0
           y unchanged (yChannelSelector="G", G=0 → y += 500*(0-0.5) = -250 — off-screen up too)
           Using yChannelSelector="R" to also displace down: y += +250px -->
      <feDisplacementMap in="SourceGraphic" in2="red-map"
                         scale="500"
                         xChannelSelector="R"
                         yChannelSelector="R"
                         result="displaced"/>
      <!-- Pixels displaced 250px right AND 250px down — off-screen in both dimensions -->
    </filter>
  </defs>
</svg>

<div id="consent-fdm" style="filter:url(#consent-displace-right);
     overflow:hidden; background:#222; color:#eee; width:420px; padding:24px">
  <h3>Permission Request</h3>
  <p>Requesting: shell execution, credential access, filesystem write.</p>
  <button id="allow-btn">Allow</button>
  <button>Deny</button>
</div>

// Displacement analysis for various feFlood colors and scale values:
//
// feFlood R=1.0, scale=500: x_displacement = 500*(1.0-0.5) = +250px → right half clipped
// feFlood R=1.0, scale=1000: x_displacement = 1000*(1.0-0.5) = +500px → all off-screen for 420px panel
// feFlood R=0.0, scale=500: x_displacement = 500*(0.0-0.5) = -250px → left half clipped (negative)
// feFlood R=0.5, scale=500: x_displacement = 500*(0.5-0.5) = 0px → no displacement (control)
// feFlood G=1.0, scale=500, yChannelSelector=G: y += +250px → entire panel below viewport

// DOM check that FAILS:
function naiveCheck(el) {
  const rect = el.getBoundingClientRect();
  // rect = { left: 100, top: 200, width: 420, height: 180 } — pre-displacement box
  // Returns the ORIGINAL element position, not where displaced pixels are rendered
  // Consent text is actually rendered at (350, 450) and clipped — invisible
  // naiveCheck passes (element is "in viewport") while user sees blank panel
  return rect.width > 0 && rect.height > 0 && rect.top < window.innerHeight;
}

// --- Detection: read scale and channel selectors from feDisplacementMap ---
function detectFeDisplacementMap(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]}" not found in document — possible closed shadow DOM`
  }];

  const findings = [];

  for (const fdm of filterEl.querySelectorAll('feDisplacementMap')) {
    const scale = Math.abs(parseFloat(fdm.getAttribute('scale') || '0'));
    const xChan = fdm.getAttribute('xChannelSelector') || 'A';
    const yChan = fdm.getAttribute('yChannelSelector') || 'A';
    const in2   = fdm.getAttribute('in2') || '';

    // Check if in2 references a feFlood (uniform displacement map)
    const in2Result = in2;
    const isUniformMap = !!filterEl.querySelector(`feFlood[result="${in2Result}"]`);

    if (scale > 100) {
      findings.push({
        attribute: 'scale',
        value: scale,
        severity: scale > 300 ? 'CRITICAL' : 'HIGH',
        note: `feDisplacementMap scale=${scale} — displacement up to ${(scale * 0.5).toFixed(0)}px${isUniformMap ? ' (uniform feFlood source — all pixels displaced equally)' : ''}; consent text may be displaced off-screen`
      });
    }
  }

  return findings.length ? findings : null;
}

// detectFeDisplacementMap(document.getElementById('consent-fdm')) →
// [{
//   attribute: "scale",
//   value: 500,
//   severity: "CRITICAL",
//   note: "feDisplacementMap scale=500 — displacement up to 250px (uniform feFlood source — all pixels displaced equally); consent text may be displaced off-screen"
// }]

CRITICAL — SA-CSS-FDM-001: feDisplacementMap scale="500" with a feFlood flood-color="rgb(255,0,0)" displacement source displaces every pixel of the consent text by 500 × (1.0 − 0.5) = 250 pixels in the positive-x direction. For a standard 420-pixel consent panel, this moves all consent text beyond the right boundary of the filter region where it is clipped to transparency. getBoundingClientRect() returns the pre-displacement element box, causing DOM position checks to falsely report the element as visible and correctly positioned. Detection requires resolving the filter graph, identifying feDisplacementMap elements with large scale values, and flagging uniform displacement map sources (feFlood) as CRITICAL; scale above 300 is CRITICAL, above 100 is HIGH.

Attack 2: feDisplacementMap scale="-500" — negative scale displaces consent text in the opposite direction (SA-CSS-FDM-002)

The SVG specification allows negative values for the scale attribute of feDisplacementMap. With scale="-500" and feFlood flood-color="rgb(255,0,0)" (R=1.0), the x-displacement is −500 × (1.0 − 0.5) = −250 pixels — the consent text is displaced 250 pixels to the left. For a consent element positioned at x=100 in the viewport, the displaced consent text appears at x=100−250=−150, which is 150 pixels off the left edge of the viewport. The text is clipped to transparency and invisible, but the DOM element remains at its original position.

The negative-scale variant is distinct from the positive-scale variant because it exploits bidirectional displacement. A security rule that only flags scale > 100 (not |scale| > 100) misses this attack. Combining both positive and negative displacement on different axes: scale="-500" xChannelSelector="R" yChannelSelector="G" with feFlood flood-color="rgb(255,255,0)" (R=1, G=1) displaces −250px in X and −250px in Y simultaneously, moving the consent text to the upper-left corner of the screen (likely off-screen). The attack uses the absolute value of the displacement; both directions produce off-screen positioning.

/* SA-CSS-FDM-002: feDisplacementMap scale="-500" — negative scale, left displacement
   feFlood flood-color="rgb(255,0,0)": R=1.0 at every pixel
   x_displacement = -500 * (1.0 - 0.5) = -500 * 0.5 = -250px (left)
   Consent text displaced 250px left — off-screen for element near viewport left edge

   Security rule gap: checking only scale > 100 misses scale < -100
   Correct check: Math.abs(scale) > 100 to catch both directions */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-displace-left" x="-200%" y="0%" width="400%" height="100%">
      <feFlood flood-color="rgb(255,0,0)" flood-opacity="1" result="red-map"/>
      <!-- scale=-500: x += -500*(R-0.5) = -500*0.5 = -250px — left displacement -->
      <feDisplacementMap in="SourceGraphic" in2="red-map"
                         scale="-500"
                         xChannelSelector="R"
                         yChannelSelector="G"/>
      <!-- y += -500*(G-0.5) = -500*(0-0.5) = +250px — but G=0 in red, so y += 250px -->
      <!-- Actually for RGB(255,0,0): R=1, G=0, B=0
           y_displacement = -500*(G-0.5) = -500*(0.0-0.5) = -500*(-0.5) = +250px (down)
           x_displacement = -500*(R-0.5) = -500*(1.0-0.5) = -250px (left)
           → consent text displaced 250px left AND 250px down — off-screen both axes -->
    </filter>
  </defs>
</svg>

<div id="consent-neg" style="filter:url(#consent-displace-left);
     overflow:hidden; background:#222; color:#eee; width:420px; padding:24px">
  <h3>Permission Request</h3>
  <p>Requesting: shell execution, file system write, outbound network.</p>
  <button id="allow-btn">Allow</button>
  <button>Deny</button>
</div>

// Displacement direction matrix for feFlood color + scale combinations:
//
// Color            R    G    xChan  yChan  scale  dx      dy
// rgb(255,0,0)  1.0  0.0  R      G     +500  +250   -250   (right+up)
// rgb(255,0,0)  1.0  0.0  R      G     -500  -250   +250   (left+down)
// rgb(0,0,0)    0.0  0.0  R      G     +500  -250   -250   (left+up)
// rgb(0,0,0)    0.0  0.0  R      G     -500  +250   +250   (right+down)
// rgb(0,255,0)  0.0  1.0  R      G     +500  -250   +250   (left+down)
// rgb(128,128,0)0.5  0.5  R      G     +500    0       0   (no displacement — control)
//
// All combinations with |scale|=500 and non-neutral color produce ≥250px displacement
// Consent text is off-screen for any panel narrower/shorter than 250px from edge

// Detection: Math.abs(scale) > 100 catches both positive and negative displacements
// (not just scale > 100)

CRITICAL — SA-CSS-FDM-002: feDisplacementMap scale="-500" displaces consent text pixels 250 pixels to the left for a uniform red displacement source. Security checks that only flag positive scale values miss this attack. The correct detection rule uses Math.abs(scale) > 300 as CRITICAL and Math.abs(scale) > 100 as HIGH, capturing both positive (rightward) and negative (leftward) off-screen displacement attacks. Combined axis displacement using separate RGB channels can simultaneously move the consent text off-screen in both x and y directions, making it invisible regardless of how the viewport is positioned.

Attack 3: xChannelSelector="A" — alpha-channel selection displaces only opaque consent text pixels (SA-CSS-FDM-003)

The xChannelSelector and yChannelSelector attributes of feDisplacementMap control which channel of the displacement map image is used to compute x and y displacements respectively. With xChannelSelector="A", the alpha channel of the displacement map controls the x-displacement. For a feFlood flood-color="rgba(0,0,0,1)" source (fully opaque black: R=0, G=0, B=0, A=1), using xChannelSelector="A" gives displacement value A=1.0, producing x-displacement = scale × (1.0 − 0.5) = scale × 0.5.

The alpha channel of most SVG filter sources is uniformly 1 (fully opaque) — so this behaves like R=1 for a fully opaque source. However, a more sophisticated variant uses the alpha channel of the SourceGraphic itself as the displacement map (in2="SourceGraphic" xChannelSelector="A"). In this case, pixels where the source has alpha=1 (fully opaque: the text glyphs) are displaced by scale × 0.5, while pixels where the source has alpha=0 (fully transparent: the background) are displaced by scale × (0 − 0.5) = −scale × 0.5 in the opposite direction. The consent text glyphs are displaced off-screen to the right while the transparent background pixels move left — creating a shattered, unreadable consent text effect that is more subtle than a uniform displacement because the element appears to contain “something” visually.

/* SA-CSS-FDM-003: feDisplacementMap xChannelSelector="A" — alpha-channel selective displacement
   Variant A: feFlood with full opacity (A=1) as displacement map
     x_displacement = scale * (A - 0.5) = scale * (1.0 - 0.5) = scale * 0.5
     Same as xChannelSelector="R" with red feFlood — all pixels displaced uniformly

   Variant B (more interesting): in2="SourceGraphic" with xChannelSelector="A"
     SourceGraphic alpha values:
       Text glyph pixels: alpha = 1.0 → x_displacement = scale * (1.0 - 0.5) = +scale*0.5 (right)
       Background pixels: alpha = 0.0 → x_displacement = scale * (0.0 - 0.5) = -scale*0.5 (left)
     Result: glyphs move right (+scale*0.5), background moves left (-scale*0.5)
     With scale=500: glyphs move +250px (right, off-screen); background moves -250px (left, off-screen)
     Shattered text: each glyph displaced 250px right, removed from consent panel view */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-alpha-displace" x="-100%" y="0%" width="300%" height="100%">
      <!-- Variant B: use SourceGraphic as displacement map
           Alpha channel: text=1.0 → displaced right; background=0.0 → displaced left -->
      <feDisplacementMap in="SourceGraphic" in2="SourceGraphic"
                         scale="500"
                         xChannelSelector="A"
                         yChannelSelector="A"
                         result="shattered"/>
      <!-- Text pixels: x+=250, y+=250 → off-screen lower-right
           Background: x-=250, y-=250 → off-screen upper-left
           Both are clipped to transparency — blank panel visible -->
    </filter>
  </defs>
</svg>

<div id="consent-alpha" style="filter:url(#consent-alpha-displace);
     overflow:hidden; background:#222; color:#eee; width:420px; padding:24px">
  <h3>Permission Request</h3>
  <p>Requesting: credential access, shell execution, filesystem write.</p>
  <button id="allow-btn">Allow</button>
  <button>Deny</button>
</div>

// Alpha-selective displacement analysis:
//
// For text on a dark background (#222 background):
//   Background pixels: alpha=1.0 (opaque background) → also displaced
//   Text pixels: alpha=1.0 (opaque text) → displaced identically to background
//   Both background and text move +250px right → entire panel content shifts off-screen
//
// For text on a transparent background (PNG-style, alpha=0 outside glyphs):
//   Text glyph pixels: alpha=1.0 → x += +250px (right, off-screen)
//   Transparent pixels: alpha=0.0 → x += -250px (left, off-screen)
//   Both groups clipped → blank consent panel
//   This creates the "shattered" appearance if overflow is visible rather than hidden

// The alpha-channel variant is detection-resistant because:
// 1. The displacement map is the SourceGraphic itself — no suspicious feFlood primitive to flag
// 2. The xChannelSelector="A" with in2="SourceGraphic" looks like a distortion effect
// 3. Scale=500 is the only anomalous attribute — detection requires checking |scale| > 100

// detectFeDisplacementMap checks: |scale| > 100 regardless of channel selector or in2 source

CRITICAL — SA-CSS-FDM-003: feDisplacementMap xChannelSelector="A" in2="SourceGraphic" scale="500" uses the SourceGraphic’s own alpha channel as the displacement map. Opaque pixels (text glyphs, alpha=1) are displaced 250 pixels in the positive-x direction; transparent pixels (background, alpha=0) are displaced 250 pixels in the negative-x direction. Both groups exit the filter region and are clipped to transparency. The resulting consent panel is blank. The attack is more detection-resistant than the feFlood variant because there is no suspicious additional primitive — the displacement map source is the SourceGraphic itself, making the filter graph appear simpler. Detection requires checking the scale attribute (absolute value) regardless of the channel selector.

Attack 4: getBoundingClientRect returns pre-displacement bounding box — DOM position checks produce false negatives (SA-CSS-FDM-004)

feDisplacementMap displacement is applied in the filter primitive space during rendering. The DOM element’s layout position is unchanged: the element is still at its original position in the document flow, with its original size and boundaries. getBoundingClientRect() queries the layout position — the pre-displacement position — and returns the original bounding box. The displacement is a rendering transformation applied by the GPU compositing pipeline after layout, invisible to the layout-based DOM APIs.

This creates a systematic false negative in any security check that verifies the consent element’s visibility by checking its position relative to the viewport. The check passes: the element is “in the viewport” according to its layout position. But the rendered pixels are hundreds of pixels away from the layout position, outside the viewport or clipped by overflow: hidden. The Intersection Observer API has the same limitation: it tracks intersection with the viewport using the layout bounding box, not the rendered pixel position. element.checkVisibility() (where supported) checks CSS visibility and display properties, not filter-based rendering displacement. The only reliable check is to read the filter graph directly and flag large displacement scale values.

/* SA-CSS-FDM-004: getBoundingClientRect() returns pre-displacement layout box
   Displacement is a rendering operation — layout position is unchanged
   All layout-based visibility checks produce false negatives */

// Displacement filter displaces all consent text 250px off-screen (right)
// Element layout position: { left: 100, top: 200, width: 420, height: 180 }
// Rendered pixel position: { left: 350, top: 200 } — but clipped at right edge

// --- Checks that FAIL (all layout-based) ---

// 1. getBoundingClientRect
const rect = el.getBoundingClientRect();
const inViewport = rect.top < window.innerHeight && rect.left < window.innerWidth;
console.log(inViewport); // true — layout box is in viewport; rendered content is not

// 2. Intersection Observer
const observer = new IntersectionObserver(entries => {
  entries.forEach(e => {
    console.log('intersecting:', e.isIntersecting); // true — layout intersection passes
    // Rendered consent text is off-screen; this callback is useless for detecting displacement
  });
});
observer.observe(el);

// 3. checkVisibility (modern API)
console.log(el.checkVisibility({ checkVisibilityCSS: true })); // true — no CSS hiding
// Checks CSS visibility/display/opacity/content-visibility — not filter displacement

// 4. Pointer events (clickable area is at layout position, not rendered position)
el.addEventListener('click', () => console.log('clicked')); // fires at layout position
// User clicks on the blank consent panel (at layout position) and triggers Allow button
// regardless of where the text appears to be rendered

// --- Only reliable detection ---
function detectDisplacementOnConsent(el) {
  const filterVal = getComputedStyle(el).filter || '';
  const urlMatch  = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
  if (!urlMatch) return { safe: true };

  const filterEl = document.querySelector(urlMatch[1]);
  if (!filterEl) return { risk: 'HIGH', reason: 'Filter definition not found — possible shadow DOM concealment' };

  for (const fdm of filterEl.querySelectorAll('feDisplacementMap')) {
    const scale = parseFloat(fdm.getAttribute('scale') || '0');
    if (Math.abs(scale) > 300) return { risk: 'CRITICAL', attribute: 'scale', value: scale };
    if (Math.abs(scale) > 100) return { risk: 'HIGH',     attribute: 'scale', value: scale };
  }

  return { safe: true };
}

// Additional risk: click events fire at the layout position (where the element IS in the DOM)
// not at the rendered position (where it APPEARS visually)
// A consent panel displaced 250px right is still clickable at its original DOM position
// User sees blank panel but clicks Allow at the invisible element's DOM location
// This makes the attack self-consistent: the "Allow" button fires even though the user
// cannot read what they are allowing — because click events use layout coordinates

// Correct mitigation: refuse to render the consent panel if |feDisplacementMap.scale| > 50

HIGH — SA-CSS-FDM-004: feDisplacementMap displacement is a rendering operation applied after layout. getBoundingClientRect(), Intersection Observer, and checkVisibility() all return the pre-displacement layout position, falsely reporting the consent element as visible. The displaced consent text is invisible (clipped), but click events still fire at the original layout coordinates — a user who clicks the blank panel is clicking the invisible “Allow” button. This makes feDisplacementMap attacks self-consistent: the attacker renders the consent text off-screen while keeping the clickable “Allow” button at its original, hittable layout position. Detection requires reading the filter graph directly; layout-based visibility checks are insufficient.

Summary table

AttackMechanismWhat it hidesSeverity
SA-CSS-FDM-001: scale="500" xChannelSelector="R" feFlood R=1.0 as displacement map; x_displacement = 500*(1.0-0.5) = +250px; all consent pixels displaced 250px right; clipped to transparency beyond filter region; getBoundingClientRect intact; layout-based visibility checks pass; |scale|=500 > 300 → CRITICAL All consent text, permission scopes, button labels, and prose displaced off-screen to the right; panel appears blank; click events still fire at original layout position (Allow button hittable) Critical
SA-CSS-FDM-002: scale="-500" Negative scale reverses displacement direction; x_displacement = -250px for R=1 source; consent text displaced 250px left; security rules checking only scale > 100 miss negative values; correct check: Math.abs(scale) > 100 Same as FDM-001 but in the opposite (left) direction; combined x+y displacement using separate RGB channels simultaneously removes content from both axes; |scale|=500 > 300 → CRITICAL Critical
SA-CSS-FDM-003: xChannelSelector="A" in2="SourceGraphic" SourceGraphic alpha channel as displacement map; text pixels (A=1) displaced +scale*0.5 right; background pixels (A=0) displaced -scale*0.5 left; both groups exit filter region; no suspicious feFlood primitive — harder to visually spot in filter graph All consent text glyphs individually displaced off-screen; background also displaced; blank consent panel; detection-resistant because displacement source is SourceGraphic itself, not an extra primitive Critical
SA-CSS-FDM-004: getBoundingClientRect evasion Displacement is rendering-time, not layout-time; getBoundingClientRect returns pre-displacement box; Intersection Observer uses layout bounding box; checkVisibility checks CSS properties, not filter displacement; click events fire at layout position The detection mechanism itself — all FDM-001 through FDM-003 attacks evade layout-based visibility checks; click events at layout position make Allow button hittable even on blank consent panel; only filter graph analysis is reliable High

Defences

SkillAudit findings for this attack surface

CRITICAL SA-CSS-FDM-001: feDisplacementMap scale="500" xChannelSelector="R" with feFlood flood-color="rgb(255,0,0)" source on the consent element — all consent pixels displaced 250px rightward; clipped to transparency beyond filter region; consent text invisible; getBoundingClientRect() returns pre-displacement box (false negative); click events fire at original layout position (Allow button remains hittable).
CRITICAL SA-CSS-FDM-002: feDisplacementMap scale="-500" on the consent element — negative scale displaces 250px leftward; security rules checking only scale > 100 miss this variant; correct detection requires Math.abs(scale) > 100; combined x+y displacement using separate channels removes content from both dimensions simultaneously.
CRITICAL SA-CSS-FDM-003: feDisplacementMap xChannelSelector="A" in2="SourceGraphic" scale="500" on the consent element — SourceGraphic alpha used as displacement map; opaque text pixels (A=1) displaced +250px right; transparent background pixels (A=0) displaced -250px left; both clipped to transparency; no feFlood primitive makes this visually harder to spot in filter graph; only scale attribute check catches it.
HIGH SA-CSS-FDM-004: feDisplacementMap evasion of DOM visibility checks — displacement is rendering-time, not layout-time; getBoundingClientRect(), Intersection Observer, and checkVisibility() all return pre-displacement layout position; click events fire at layout position (Allow button hittable on blank panel); layout-based visibility checks are insufficient for feDisplacementMap attacks; only filter graph analysis is reliable.

Related: feDiffuseLighting consent attacks  |  feColorMatrix consent attacks  |  feBlend blending mode consent attacks

← Blog  |  Security Checklist