diff --git a/.Jules/palette.md b/.Jules/palette.md index 2b51482..5a5b459 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,7 @@ ## 2024-05-24 - Accessible Input Validation **Learning:** React 19 renders `aria-invalid={false}` as `aria-invalid="false"`, unlike older versions which might have omitted it. Explicitly handling this in tests is crucial. Also, ensuring DOM cleanup (`cleanup()`) in `afterEach` is vital when testing similar components with same labels across tests to avoid "finding the wrong element" false positives/negatives. **Action:** Always include `afterEach(() => cleanup())` in `vitest` setup for DOM tests, and expect `aria-invalid="false"` (or explicitly handle `undefined` if omission is desired) when testing valid states in React 19. + +## 2024-05-24 - Accessible Loading Buttons +**Learning:** Replacing button text with a spinner destroys the accessible name. +**Action:** Use `aria-busy="true"`, keep children in DOM (visually hidden via opacity/class), and overlay spinner absolutely. Ensure wrapper element replicates flex layout (gap/alignment) to prevent layout shifts. diff --git a/.jules/bolt.md b/.jules/bolt.md index f667f2d..6872ec9 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -5,3 +5,6 @@ ## 2025-01-26 - Missing Node Modules **Learning:** The environment might lack `node_modules` completely, preventing `npx vitest` or `pnpm exec tsc` from running even if dependencies are listed in `package.json`. Network restrictions may prevent `pnpm install`. **Action:** When `node_modules` is missing and cannot be installed, rely on static analysis, careful code review, and verifying file contents manually. Do not assume tests can run. +## 2024-05-22 - High-Frequency State Isolation +**Learning:** High-frequency state updates (like typing effects) in large parent components (`Hero`) trigger massive unnecessary re-renders of expensive sub-trees (`GradientBlinds`, `Button`). +**Action:** Isolate high-frequency state into small, leaf-node components (e.g., `TypedText`) and wrap them in `React.memo` if necessary, keeping the heavy parent static. diff --git a/.jules/palette.md b/.jules/palette.md new file mode 100644 index 0000000..9b34d55 --- /dev/null +++ b/.jules/palette.md @@ -0,0 +1,6 @@ +## 2025-02-18 - Missing Alerts for Dynamic Status +**Learning:** The application uses `framer-motion` for dynamic feedback messages but consistently lacks `role="alert"` and `aria-live` attributes, causing screen readers to miss critical status updates. +**Action:** When auditing forms, check all `motion.div/p` elements used for feedback and add `role="alert"` and `aria-live="polite"` (or "assertive" for errors). +## 2024-05-22 - Semantic Required Fields with Custom Validation +**Learning:** To combine custom validation UI with semantic `required` attributes (vital for a11y), add `noValidate` to the `
`. This prevents native browser bubbles while keeping the accessibility benefits. +**Action:** Use `noValidate` on forms when implementing custom validation but keep `required` attributes on inputs. diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 48a437e..4b69a9a 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -2,3 +2,18 @@ **Vulnerability:** The application is served without standard security headers (CSP, X-Frame-Options, etc.), leaving it vulnerable to XSS, Clickjacking, and MIME sniffing. **Learning:** Single Page Applications (SPAs) served via static hosting (like Firebase) rely on infrastructure configuration for security headers, which are often overlooked. Default configurations are rarely secure enough. **Prevention:** Always configure `firebase.json` (or equivalent) with strict security headers (CSP, X-Frame-Options, HSTS, etc.) at project setup. + +## 2026-01-26 - Client-Side Rate Limiting for Serverless Forms +**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('