- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
20 KiB
LiveComponents Security E2E Tests
Comprehensive security testing suite for LiveComponents framework covering CSRF protection, rate limiting, idempotency, input sanitization, authorization, and session security.
Overview
This test suite validates critical security features:
- CSRF Protection: Token validation for state-changing actions
- Rate Limiting: Protection against abuse and DoS attacks
- Idempotency: Preventing duplicate action execution
- Input Sanitization: XSS and injection prevention
- Authorization: Access control and permissions
- Session Security: Session management and hijacking prevention
- Content Security Policy: CSP header enforcement
Quick Start
Prerequisites
# Ensure Playwright is installed
npm install
# Install browser binaries
npx playwright install chromium
# Ensure development server is running
make up
Running Security Tests
# Run all security tests
npm run test:security
# Run specific security test category
npx playwright test security.spec.js --grep "CSRF Protection"
npx playwright test security.spec.js --grep "Rate Limiting"
npx playwright test security.spec.js --grep "Idempotency"
# Run with visible browser (for debugging)
npm run test:security:headed
# Run with debug mode
npm run test:security:debug
Test Categories
1. CSRF Protection (4 tests)
Purpose: Validate Cross-Site Request Forgery protection for all state-changing actions.
Test: CSRF token included in requests
Validates:
- CSRF token present in action requests
- Token included in headers (
X-CSRF-Token) or POST data (_csrf_token) - Framework automatically handles token management
Expected Behavior:
// Request includes CSRF token
headers: {
'X-CSRF-Token': 'generated-token-value'
}
// OR in POST data
{
_csrf_token: 'generated-token-value',
action: 'increment',
params: { ... }
}
Test: Reject action without CSRF token
Validates:
- Requests without CSRF token are rejected
- Appropriate error message returned
- No state changes occur
Expected Response:
Status: 403 Forbidden
Error: "CSRF token validation failed"
Test: Reject action with invalid CSRF token
Validates:
- Invalid/expired tokens are rejected
- Token tampering detected
- Security event logged
Test Approach:
// Replace token with invalid value
headers: { 'X-CSRF-Token': 'invalid-token-12345' }
// Expected: 403 Forbidden
Test: CSRF token rotation
Validates:
- Tokens rotate after usage (optional, framework-dependent)
- New token provided in response
- Old token invalidated
Rotation Strategy:
- Per-request rotation: New token after each action
- Time-based rotation: New token after TTL
- Session-based: Token tied to session lifetime
2. Rate Limiting (5 tests)
Purpose: Prevent abuse through excessive action calls and DoS attacks.
Test: Enforce rate limit on rapid calls
Configuration:
- Default Limit: 10 requests per minute
- Window: 60 seconds sliding window
- Action: Block excess requests
Test Approach:
// Trigger 20 rapid action calls
for (let i = 0; i < 20; i++) {
await triggerAction();
}
// Expected: First 10 succeed, remaining 10 blocked
// Success rate: 50% (10/20)
Expected Error:
Status: 429 Too Many Requests
Error: "Rate limit exceeded"
Retry-After: 45 // seconds
Test: Retry-After header
Validates:
Retry-Afterheader present in 429 responses- Header contains remaining cooldown time
- Client can use header for backoff
Response Format:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"error": "Rate limit exceeded",
"retryAfter": 60,
"limit": 10,
"window": 60
}
Test: Rate limit reset after cooldown
Validates:
- Rate limit resets after window expires
- Actions allowed again after cooldown
- No lingering restrictions
Timeline:
00:00 - First 10 requests succeed
00:01 - Next 10 requests blocked (rate limited)
00:01 - Retry-After: 59 seconds
01:00 - Window expires, limit resets
01:01 - New requests succeed
Test: Separate limits per action type
Validates:
- Different actions have independent rate limits
- Hitting limit on action A doesn't affect action B
- Granular control per action
Configuration:
const RATE_LIMITS = {
'increment': { limit: 10, window: 60 },
'delete': { limit: 5, window: 300 },
'search': { limit: 30, window: 60 }
};
Test: IP-based rate limiting
Validates:
- Rate limits applied per IP address
- Multiple users from same IP share limit
- Different IPs have independent limits
Scenario:
User A (IP: 192.168.1.1) - 10 requests
User B (IP: 192.168.1.1) - 10 requests
Result: Both users share 10-request limit for that IP
3. Idempotency (4 tests)
Purpose: Ensure critical operations execute exactly once, even with duplicate requests.
Test: Handle duplicate calls idempotently
Validates:
- Duplicate action calls with same idempotency key are ignored
- Operation executes exactly once
- State changes applied only once
Test Approach:
// First call with idempotency key
await callAction('increment', { idempotencyKey: 'key-123' });
// Counter: 0 → 1
// Duplicate call (same key)
await callAction('increment', { idempotencyKey: 'key-123' });
// Counter: 1 (unchanged, duplicate ignored)
Test: Allow different idempotency keys
Validates:
- Different idempotency keys allow separate executions
- Each unique key represents distinct operation
- No interference between different keys
Test Approach:
await callAction('increment', { idempotencyKey: 'key-1' }); // Executes
await callAction('increment', { idempotencyKey: 'key-2' }); // Executes
await callAction('increment', { idempotencyKey: 'key-1' }); // Ignored (duplicate)
Test: Idempotency key expiration
Validates:
- Keys expire after TTL (default: 24 hours)
- Expired keys allow re-execution
- Storage cleanup for old keys
Timeline:
00:00 - Action with key-123 executes
00:00 - Duplicate with key-123 ignored
24:00 - Key-123 expires
24:01 - Action with key-123 executes again (new operation)
Test: Cached result for duplicate action
Validates:
- Duplicate calls return cached result
- No server-side re-execution
- Consistent response for same key
Expected Behavior:
const result1 = await callAction('createOrder', {
idempotencyKey: 'order-123'
});
// Returns: { orderId: 'abc', total: 100 }
const result2 = await callAction('createOrder', {
idempotencyKey: 'order-123'
});
// Returns: { orderId: 'abc', total: 100 } (cached, same result)
4. Input Sanitization & XSS Prevention (5 tests)
Purpose: Prevent XSS, injection attacks, and malicious input processing.
Test: Sanitize HTML in action parameters
Validates:
- HTML tags stripped or escaped
- Script tags prevented from execution
- Safe rendering of user input
Attack Vectors Tested:
// XSS attempts
'<script>alert("XSS")</script>'
'<img src=x onerror=alert("XSS")>'
'<svg onload=alert("XSS")>'
'<iframe src="javascript:alert(\'XSS\')"></iframe>'
Expected Output:
<!-- Input: <script>alert("XSS")</script> -->
<!-- Output: <script>alert("XSS")</script> -->
<!-- Displayed as text, not executed -->
Test: Escape HTML entities
Validates:
- Special characters properly escaped
- No raw HTML injection
- Content-Type headers correct
Entity Escaping:
< → <
> → >
& → &
" → "
' → '
Test: Prevent JavaScript injection
Validates:
javascript:protocol blocked in URLs- Event handlers stripped
- No inline script execution
Blocked Patterns:
javascript:alert('XSS')
data:text/html,<script>alert('XSS')</script>
vbscript:msgbox("XSS")
Test: Validate file paths
Validates:
- Path traversal attacks prevented
- Only allowed directories accessible
- Absolute paths rejected
Attack Attempts:
../../../etc/passwd
..\..\windows\system32\config\sam
/etc/passwd
C:\Windows\System32\config\SAM
Expected: All rejected with "Invalid path" error
Test: Prevent SQL injection
Validates:
- Parameterized queries used
- SQL keywords escaped
- No raw query concatenation
Attack Vectors:
'; DROP TABLE users; --
' OR '1'='1
admin'--
' UNION SELECT * FROM users--
Expected: Treated as literal search strings, no SQL execution
5. Authorization (3 tests)
Purpose: Enforce access control and permission checks.
Test: Reject unauthorized action calls
Validates:
- Actions requiring authentication are protected
- Anonymous users blocked
- Clear error message
Protected Action:
#[Action]
#[RequiresAuth]
public function deleteAllData(): array
{
// Only authenticated users
}
Expected Response:
Status: 401 Unauthorized
Error: "Authentication required"
Test: Allow authorized action calls
Validates:
- Authenticated users can access protected actions
- Valid session/token accepted
- Actions execute successfully
Flow:
1. User logs in → Session created
2. Call protected action → Success
3. Action executes → Result returned
Test: Role-based authorization
Validates:
- Different roles have different permissions
- Role checks enforced
- Insufficient permissions rejected
Role Matrix:
Action: deleteAllData
- admin: ✅ Allowed
- moderator: ❌ Forbidden
- user: ❌ Forbidden
Action: editOwnProfile
- admin: ✅ Allowed
- moderator: ✅ Allowed
- user: ✅ Allowed
6. Session Security (3 tests)
Purpose: Secure session management and hijacking prevention.
Test: Invalidate session after logout
Validates:
- Session properly destroyed on logout
- Session cookie removed
- Subsequent requests rejected
Flow:
1. Login → Session active
2. Call protected action → Success
3. Logout → Session destroyed
4. Call protected action → 401 Unauthorized
Test: Detect session hijacking
Validates:
- Session binding to IP/User-Agent
- Suspicious activity detected
- Stolen sessions rejected
Detection Criteria:
- IP address change
- User-Agent change
- Geo-location anomaly
- Concurrent sessions from different locations
Response:
Status: 403 Forbidden
Error: "Session invalid - security violation detected"
Action: Session terminated, user notified
Test: Enforce session timeout
Validates:
- Sessions expire after inactivity
- Timeout configurable (default: 30 minutes)
- Expired sessions rejected
Timeline:
00:00 - Login
00:15 - Action call (session refreshed)
00:45 - No activity for 30 minutes
00:46 - Action call → 401 Session Expired
7. Content Security Policy (2 tests)
Purpose: Enforce CSP headers to prevent injection attacks.
Test: CSP headers present
Validates:
Content-Security-Policyheader set- Proper directives configured
- No unsafe configurations
Expected Header:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-xyz123';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' wss://localhost;
font-src 'self' https://fonts.gstatic.com;
Test: Block inline scripts via CSP
Validates:
- Inline scripts blocked by CSP
- Console errors for violations
- CSP reports generated
Violation Detection:
// Attempt to inject inline script
const script = document.createElement('script');
script.textContent = 'alert("XSS")';
document.body.appendChild(script);
// Expected Console Error:
// "Refused to execute inline script because it violates
// Content Security Policy directive: 'script-src self'"
Test Page Requirements
Tests assume the following test page at https://localhost/livecomponents/test/security:
Required HTML Elements
<div data-component-id="counter:test">
<div id="counter-value">0</div>
<button id="trigger-action">Trigger Action</button>
<div class="action-success" style="display:none">Success</div>
<div class="error-message" style="display:none"></div>
<div class="rate-limit-error" style="display:none" data-retry-after=""></div>
</div>
<div data-component-id="text:test">
<div id="text-display"></div>
</div>
<div data-component-id="admin:test">
<div class="authorization-error" style="display:none"></div>
</div>
<!-- Login Form -->
<form id="login-form">
<input type="text" id="username" name="username" />
<input type="password" id="password" name="password" />
<button type="submit" id="login-btn">Login</button>
</form>
<div class="logged-in-indicator" style="display:none"></div>
<button id="logout-btn" style="display:none">Logout</button>
<!-- CSRF Token -->
<meta name="csrf-token" content="generated-token-value">
Required Component Actions
final readonly class SecurityTestComponent extends LiveComponent
{
#[Action]
#[RateLimit(requests: 10, window: 60)]
public function triggerAction(): array
{
return ['success' => true];
}
#[Action]
public function increment(string $idempotencyKey = null): array
{
// Idempotent increment
if ($idempotencyKey && $this->hasExecuted($idempotencyKey)) {
return $this->getCachedResult($idempotencyKey);
}
$newValue = $this->state->get('counter') + 1;
$this->state->set('counter', $newValue);
$result = ['value' => $newValue];
if ($idempotencyKey) {
$this->cacheResult($idempotencyKey, $result);
}
return $result;
}
#[Action]
public function setText(string $text): array
{
// Sanitize HTML
$sanitizedText = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
$this->state->set('text', $sanitizedText);
return ['text' => $sanitizedText];
}
#[Action]
#[RequiresAuth]
#[RequiresRole('admin')]
public function performAdminAction(): array
{
return ['success' => true];
}
}
Configuration
Environment Variables
# CSRF Protection
CSRF_TOKEN_TTL=7200 # 2 hours
CSRF_ROTATE_ON_ACTION=false # Rotate token after each action
# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_DEFAULT=60 # Requests per window
RATE_LIMIT_WINDOW=60 # Window in seconds
RATE_LIMIT_STORAGE=redis # Storage backend
# Idempotency
IDEMPOTENCY_ENABLED=true
IDEMPOTENCY_TTL=86400 # 24 hours
IDEMPOTENCY_STORAGE=redis
# Session Security
SESSION_LIFETIME=1800 # 30 minutes
SESSION_SECURE=true # HTTPS only
SESSION_HTTP_ONLY=true # No JavaScript access
SESSION_SAME_SITE=strict # SameSite cookie attribute
SESSION_BIND_IP=true # Bind to IP address
SESSION_BIND_USER_AGENT=true # Bind to User-Agent
# Content Security Policy
CSP_ENABLED=true
CSP_REPORT_ONLY=false # Enforce CSP (not just report)
CSP_REPORT_URI=/csp-report # CSP violation reporting endpoint
Troubleshooting
CSRF Tests Failing
Symptoms:
- CSRF token not found in requests
- All CSRF tests failing
Solutions:
- Verify CSRF middleware enabled:
// In middleware stack
new CsrfMiddleware($csrfTokenGenerator)
- Check meta tag presence:
<meta name="csrf-token" content="...">
- Verify JavaScript includes token:
const token = document.querySelector('meta[name="csrf-token"]').content;
// Include in requests
Rate Limiting Not Working
Symptoms:
- All rapid requests succeed
- No 429 responses
Solutions:
- Check rate limit configuration:
# Verify environment variables
docker exec php php -r "echo getenv('RATE_LIMIT_ENABLED');"
- Verify rate limiter initialized:
// Check DI container
$rateLimiter = $container->get(RateLimiter::class);
- Check storage backend:
# For Redis backend
docker exec redis redis-cli KEYS "rate_limit:*"
Idempotency Issues
Symptoms:
- Duplicate requests execute
- Idempotency keys not working
Solutions:
- Check idempotency key format:
// Must be unique per operation
idempotencyKey: `${userId}-${operationId}-${timestamp}`
- Verify storage:
# For Redis
docker exec redis redis-cli KEYS "idempotency:*"
- Check TTL:
# Verify keys not expiring too quickly
docker exec redis redis-cli TTL "idempotency:key-123"
Authorization Failures
Symptoms:
- Authorized users rejected
- Role checks failing
Solutions:
- Verify session active:
const sessionActive = await page.evaluate(() => {
return window.__sessionActive === true;
});
- Check role assignment:
// Verify user roles
$user->hasRole('admin'); // Should return true
- Review authorization middleware:
// Ensure middleware in correct order
[AuthMiddleware, RoleMiddleware, ...]
CI/CD Integration
GitHub Actions
name: Security Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * *' # Daily
jobs:
security-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Start dev server
run: make up
- name: Run security tests
run: npm run test:security
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: security-test-results
path: test-results/
- name: Security alert on failure
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '🚨 Security Tests Failed',
labels: ['security', 'urgent'],
body: 'Security tests failed. Immediate review required.'
});
Best Practices
1. Regular Security Testing
- Run security tests before every deployment
- Include in CI/CD pipeline
- Monitor for new vulnerabilities
- Update tests for new attack vectors
2. Defense in Depth
- Multiple security layers (CSRF + Rate Limit + Auth)
- Fail securely (block by default, allow explicitly)
- Log all security events
- Alert on suspicious patterns
3. Security Monitoring
- Track failed authentication attempts
- Monitor rate limit violations
- Alert on XSS attempts
- Log authorization failures
4. Incident Response
- Defined escalation procedures
- Security event playbooks
- Regular security drills
- Post-incident analysis
Resources
- OWASP Top 10
- OWASP CSRF Prevention Cheat Sheet
- OWASP XSS Prevention Cheat Sheet
- Content Security Policy Reference
- Rate Limiting Best Practices
Support
For security issues or questions:
- Review this documentation
- Check framework security documentation
- Consult OWASP guidelines
- Report security vulnerabilities privately to security@example.com
- Create GitHub issue for non-security test failures