diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a98cbd0..4b69a9a 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -7,3 +7,13 @@ **Vulnerability:** Contact forms using client-side services (like EmailJS) without backend middleware are vulnerable to spam and quota exhaustion. **Learning:** While true rate limiting requires a backend, client-side throttling via `localStorage` provides a necessary friction layer for legitimate users and simple bots, protecting external service quotas. **Prevention:** Implement reusable rate-limit hooks for all public-facing form submissions in static/serverless applications. + +## 2026-02-13 - State Leakage in Tests masking Security Failures +**Vulnerability:** Flaky tests caused by `localStorage` state leakage (e.g. rate limits persisting between tests) can prevent security features from being properly verified, leading to false negatives or untested paths. +**Learning:** Global state like `localStorage` must be explicitly cleared in `afterEach` blocks in test environments (jsdom). Failing to do so can cause subsequent tests to fail or behave unpredictably, especially for rate-limiting logic. +**Prevention:** Always include `localStorage.clear()` in `afterEach` (or `beforeEach`) when testing components that rely on local storage. + +## 2026-02-13 - Strict Email Validation vs HTML5 Validation +**Vulnerability:** Standard email regexes and HTML5 validation are often too permissive, allowing XSS vectors (like `'; + const expected = '<script>alert("XSS")</script>'; + expect(sanitizeInput(input)).toBe(expected); + }); + }); + + describe('isValidEmail', () => { + it('accepts valid email addresses', () => { + expect(isValidEmail('test@example.com')).toBe(true); + expect(isValidEmail('john.doe@sub.domain.co.uk')).toBe(true); + expect(isValidEmail('user+tag@example.com')).toBe(true); + }); + + it('rejects invalid email formats', () => { + expect(isValidEmail('plainaddress')).toBe(false); + expect(isValidEmail('@example.com')).toBe(false); + expect(isValidEmail('user@')).toBe(false); + expect(isValidEmail('user@.com')).toBe(false); + expect(isValidEmail('user@com')).toBe(false); // Missing dot in domain part (simple regex might allow, but strict one requires dot) + }); + + it('rejects emails with dangerous characters (<, >)', () => { + expect(isValidEmail('