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 found1. 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, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/')
.replace(/\{/g, '{')
.replace(/\}/g, '}')
}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