From 57f7c5667f85e311fa1c3580e5fc25656908bcdd Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 01:56:08 +0000
Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Im?=
=?UTF-8?q?plement=20strict=20email=20validation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Implemented `isValidEmail` utility with strict regex validation (rejects `<` and `>`) to prevent XSS vectors.
- Updated `Contact.tsx` to use `isValidEmail` instead of weak regex.
- Added comprehensive tests for `isValidEmail` in `src/utils/security.test.ts`.
- Fixed flaky test in `src/pages/__tests__/Contact.test.tsx` by clearing `localStorage` in `afterEach`.
- Added test case for invalid email submission.
- Documented findings in `.jules/sentinel.md`.
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
---
.jules/sentinel.md | 10 +++++
src/pages/Contact.tsx | 4 +-
src/pages/__tests__/Contact.test.tsx | 23 ++++++++++++
src/utils/security.test.ts | 56 ++++++++++++++++++++++++++++
src/utils/security.ts | 18 +++++++++
5 files changed, 109 insertions(+), 2 deletions(-)
create mode 100644 src/utils/security.test.ts
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('