UI/UX Security Testing

Specialized testing for security vulnerabilities that can be exploited through the user interface while maintaining usability and user experience.

Overview

UI/UX security testing focuses on vulnerabilities specific to user interfaces, including injection attacks, clickjacking, form tampering, and accessibility-related security issues.

  • UI Injection Testing — XSS and template injection prevention
  • UI Manipulation Testing — Clickjacking and form tampering protection
  • UI Timing Attacks — Timing-based information disclosure
  • UI Memory Leaks — Memory management in UI components
  • Accessibility Security — Screen reader and ARIA attribute security

Running the Tests

bun run security:ui

# Output:
[UI/UX] Starting UI/UX Security Testing Suite...
[UI/UX] Testing UI Injection Vulnerabilities...
[UI/UX] Testing UI Manipulation Vulnerabilities...
[UI/UX] Testing UI Timing Attacks...
[UI/UX] Testing UI Memory Leaks...
[UI/UX] Testing Accessibility Security...
[UI/UX] UI/UX Security Testing Results:
[PASS] No critical or high severity issues found

1. UI Injection Testing

Tests for XSS, template injection, and other injection vulnerabilities.

Malicious Input Patterns

const maliciousInputs = [
  '<script>alert("xss")</script>',
  '<img src=x onerror=alert("xss")>',
  'javascript:alert("xss")',
  'data:text/html,<script>alert("xss")</script>',
  '">"<script>alert("xss")</script>',
  "'>"<script>alert('xss')</script>",
  '${alert("xss")}',
  '{{alert("xss")}}',
  '{{7*7}}',
  '{{constructor.constructor("alert(1)")()}}',
]

for (const malicious of maliciousInputs) {
  const sanitized = sanitizeForDisplay(malicious)

  if (sanitized !== malicious) {
    findings.push({
      testType: 'ui-injection',
      target: 'Input Sanitization',
      input: { malicious, sanitized },
      observation: 'Input sanitization working correctly',
      severity: 'info'
    })
  } else {
    findings.push({
      testType: 'ui-injection',
      observation: 'Malicious input not sanitized',
      severity: 'high'
    })
  }
}

HTML Sanitization Function

function sanitizeForDisplay(input: string): string {
  return input
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;')
    .replace(/\//g, '&#x2F;')
    .replace(/\{/g, '&#123;')
    .replace(/\}/g, '&#125;')
}

Test Results

All XSS vectors are properly sanitized in the Keythings Wallet, with characters being HTML-encoded before display. This prevents script execution and template injection attacks.

2. UI Manipulation Testing

Tests for clickjacking, button spoofing, and form tampering vulnerabilities.

Clickjacking Protection

// Test for overlay mitigation
function hasOverlayMitigation(config) {
  const hasFrameAncestors =
    config.frameAncestors?.length > 0
  const hasOverlayStrategy =
    config.antiOverlayMeasures?.length > 0

  return hasFrameAncestors && hasOverlayStrategy
}

// CSP Headers
Content-Security-Policy:
  frame-ancestors 'none';
  require-trusted-types-for 'script';

Button Integrity Protection

function hasButtonSpoofingProtection(config) {
  return (
    config.trustedSelectorAllowlist?.length > 0 &&
    config.mutationObserverEnabled &&
    config.enforcedTrustedTypes
  )
}

// Mutation Observer for button tampering
const observer = new MutationObserver(mutations => {
  for (const mutation of mutations) {
    if (mutation.type === 'childList' ||
        mutation.type === 'attributes') {
      detectButtonTampering(mutation.target)
    }
  }
})

observer.observe(document.body, {
  childList: true,
  subtree: true,
  attributes: true,
  attributeFilter: ['onclick', 'href']
})

Form Tampering Protection

function hasFormTamperProtection(config) {
  return (
    config.revalidateOnSubmit &&
    config.tamperDetectionStrategy &&
    config.protectedFields?.length > 0 &&
    config.serverValidationMirror
  )
}

// Server-side validation mirror
function validateFormSubmission(payload) {
  const expectedHash = signPayload(payload)
  if (payload.hash !== expectedHash) {
    throw new Error('FORM_TAMPERED')
  }
}

Test Results

The wallet enforces strict UI manipulation protections, ensuring buttons and forms cannot be tampered with without detection.

3. UI Timing Attacks

Timing attacks exploit response delays to infer sensitive information.

Timing Measurement

const timingTests = [
  { name: 'Modal Render', action: renderApprovalModal },
  { name: 'Button Hover', action: simulateHover },
  { name: 'Form Submit', action: submitTransactionForm },
  { name: 'Error Toast', action: triggerErrorToast },
]

const timings = timingTests.map(test => {
  const start = performance.now()
  test.action()
  return { name: test.name, duration: performance.now() - start }
})

const variation = Math.max(...timings.map(t => t.duration)) -
  Math.min(...timings.map(t => t.duration))

if (variation > 16) {
  findings.push({
    testType: 'ui-timing',
    observation: `Timing variation: ${variation.toFixed(2)}ms`,
    severity: 'medium'
  })
}

4. UI Memory Leak Detection

Ensures UI components release resources correctly.

Leak Detection Harness

async function detectUiLeaks() {
  const initial = performance.memory.usedJSHeapSize

  for (let i = 0; i < 500; i++) {
    const { unmount } = renderApprovalModal()
    await waitForAnimationFrame()
    unmount()
  }

  if (globalThis.gc) globalThis.gc()

  const finalSize = performance.memory.usedJSHeapSize
  const growth = finalSize - initial

  if (growth > initial * 0.05) {
    findings.push({
      testType: 'ui-memory',
      observation: `Memory growth: ${(growth / initial * 100).toFixed(2)}%`,
      severity: 'medium'
    })
  }
}

5. Accessibility Security

Validates that accessibility features do not introduce security vulnerabilities.

Screen Reader Output

function analyzeScreenReaderOutput(element: HTMLElement) {
  const output = []
  const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT)

  while (walker.nextNode()) {
    const node = walker.currentNode as HTMLElement
    if (node.getAttribute('aria-hidden') === 'true') continue

    const label = node.getAttribute('aria-label') || node.innerText
    if (label && /private key|seed phrase/i.test(label)) {
      findings.push({
        testType: 'accessibility',
        observation: 'Sensitive content exposed to assistive tech',
        severity: 'high'
      })
    }
  }

  return output
}

Keyboard Trap Detection

function detectKeyboardTraps(root: HTMLElement) {
  const focusable = root.querySelectorAll('[tabindex], button, input, select, textarea, a[href]')
  const visited = new Set()

  for (const element of focusable) {
    element.addEventListener('focus', () => visited.add(element))
  }

  simulateTabNavigation(root)

  if (visited.size !== focusable.length) {
    findings.push({
      testType: 'accessibility',
      observation: 'Potential keyboard trap detected',
      severity: 'medium'
    })
  }
}

Reporting

UI/UX security tests produce structured findings with severity, affected components, and remediation guidance.

Example Finding

{
  "testType": "ui-injection",
  "target": "Input Sanitization",
  "input": {"malicious": "<img src=x onerror=alert(\"xss\")>", "sanitized": "<img src=x onerror=alert(\"xss\")>"},
  "observation": "Input sanitization working correctly",
  "severity": "info",
  "timestamp": "2025-10-09T18:36:14.210Z"
}

Best Practices

  • Review UI sanitization rules whenever new components are added
  • Ensure CSP headers are enforced for all user-generated content
  • Test keyboard navigation paths to prevent focus traps
  • Monitor memory usage of interactive components for leaks
  • Audit accessibility labels to avoid leaking sensitive data