feat(security): enhance email validation and sanitization

- Updates `isValidEmail` to strictly reject double quotes and backticks while allowing apostrophes.
- Applies `sanitizeInput` to email fields in Contact form payload (Defense in Depth).
- Adds tests for email validation edge cases.
- Updates Sentinel journal.

Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot]
2026-01-30 01:48:47 +00:00
parent f3866fc2de
commit 15c4b88535
4 changed files with 17 additions and 2 deletions

View File

@@ -17,3 +17,8 @@
**Vulnerability:** Standard email regexes and HTML5 validation are often too permissive, allowing XSS vectors (like `<script>`) in email fields if not properly sanitized/rejected.
**Learning:** While HTML5 browsers block some invalid emails, relying solely on them is insufficient for defense-in-depth. Application-level validation should explicitly reject dangerous characters (`<`, `>`) to prevent stored XSS or injection if the data is processed by less-secure backends.
**Prevention:** Implement strict, reusable validation functions (`isValidEmail`) that reject XSS vectors, and ensure tests verify this logic by bypassing browser validation if necessary.
## 2026-02-14 - Strict Email Regex vs RFC Compliance
**Vulnerability:** Standard email validation often allows characters like quotes (`"`) or backticks (`` ` ``) which can be vectors for XSS in HTML attribute contexts.
**Learning:** While RFCs allow quoted local parts (e.g., `"user"@example.com`), these are rare in practice and pose significant security risks in web applications. It is often safer to explicitly reject them. However, apostrophes (`'`) are common in names (O'Connor) and should be allowed, relying on sanitization (Defense in Depth) rather than validation to handle them safely.
**Prevention:** Use a hybrid approach: Strict validation (reject quotes/backticks) for high-risk characters, coupled with output sanitization (`sanitizeInput`) for characters that must be allowed but are still risky (apostrophes).

View File

@@ -93,7 +93,7 @@ export function Contact() {
try {
const templateParams = {
name: sanitizeInput(formData.name),
email: formData.email, // Email doesn't typically need HTML sanitization if validated by regex, but good practice to handle it if used in HTML context.
email: sanitizeInput(formData.email),
title: sanitizeInput(formData.subject),
message: sanitizeInput(formData.message),
reply_to: formData.email,

View File

@@ -48,6 +48,16 @@ describe('Security Utils', () => {
expect(isValidEmail('user<name>@example.com')).toBe(false);
});
it('rejects emails with double quotes and backticks', () => {
expect(isValidEmail('user"name@example.com')).toBe(false);
expect(isValidEmail('user`name@example.com')).toBe(false);
});
it('accepts emails with apostrophes', () => {
expect(isValidEmail("user'name@example.com")).toBe(true);
expect(isValidEmail("o'connor@example.com")).toBe(true);
});
it('rejects emails with whitespace', () => {
expect(isValidEmail('user @example.com')).toBe(false);
expect(isValidEmail('user@ example.com')).toBe(false);

View File

@@ -31,6 +31,6 @@ export function isValidEmail(email: string): boolean {
// [^\s@<>]+ : Domain part - no whitespace, @, <, or >
// \. : Literal .
// [^\s@<>]+ : TLD part - no whitespace, @, <, or >
const emailRegex = /^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/;
const emailRegex = /^[^\s@<>,"`]+@[^\s@<>,"`]+\.[^\s@<>,"`]+$/;
return emailRegex.test(email);
}