# 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 ```bash # Ensure Playwright is installed npm install # Install browser binaries npx playwright install chromium # Ensure development server is running make up ``` ### Running Security Tests ```bash # 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:** ```javascript // 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:** ```javascript // 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:** ```javascript // 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-After` header present in 429 responses - Header contains remaining cooldown time - Client can use header for backoff **Response Format:** ```http 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:** ```javascript 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:** ```javascript // 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:** ```javascript 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:** ```javascript 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:** ```javascript // XSS attempts '' '' '' '' ``` **Expected Output:** ```html ``` #### 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 javascript:alert('XSS') data:text/html, 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:** ```sql '; 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:** ```php #[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-Policy` header set - Proper directives configured - No unsafe configurations **Expected Header:** ```http 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:** ```javascript // 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 ```html
0
``` ### Required Component Actions ```php 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 ```env # 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:** 1. **Verify CSRF middleware enabled:** ```php // In middleware stack new CsrfMiddleware($csrfTokenGenerator) ``` 2. **Check meta tag presence:** ```html ``` 3. **Verify JavaScript includes token:** ```javascript 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:** 1. **Check rate limit configuration:** ```bash # Verify environment variables docker exec php php -r "echo getenv('RATE_LIMIT_ENABLED');" ``` 2. **Verify rate limiter initialized:** ```php // Check DI container $rateLimiter = $container->get(RateLimiter::class); ``` 3. **Check storage backend:** ```bash # For Redis backend docker exec redis redis-cli KEYS "rate_limit:*" ``` ### Idempotency Issues **Symptoms:** - Duplicate requests execute - Idempotency keys not working **Solutions:** 1. **Check idempotency key format:** ```javascript // Must be unique per operation idempotencyKey: `${userId}-${operationId}-${timestamp}` ``` 2. **Verify storage:** ```bash # For Redis docker exec redis redis-cli KEYS "idempotency:*" ``` 3. **Check TTL:** ```bash # 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:** 1. **Verify session active:** ```javascript const sessionActive = await page.evaluate(() => { return window.__sessionActive === true; }); ``` 2. **Check role assignment:** ```php // Verify user roles $user->hasRole('admin'); // Should return true ``` 3. **Review authorization middleware:** ```php // Ensure middleware in correct order [AuthMiddleware, RoleMiddleware, ...] ``` ## CI/CD Integration ### GitHub Actions ```yaml 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](https://owasp.org/www-project-top-ten/) - [OWASP CSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) - [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) - [Content Security Policy Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) - [Rate Limiting Best Practices](https://cloud.google.com/architecture/rate-limiting-strategies-techniques) ## Support For security issues or questions: 1. Review this documentation 2. Check framework security documentation 3. Consult OWASP guidelines 4. **Report security vulnerabilities privately** to security@example.com 5. Create GitHub issue for non-security test failures