From b51f0118933b19f35241b6bd280c5002ba9de4de Mon Sep 17 00:00:00 2001 From: Zeyu Zhang <39144422+zeyu2001@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:12:35 -0700 Subject: [PATCH] fix: block encoded word emails https://portswigger.net/research/splitting-the-email-atom#encoded-word --- packages/validators/package.json | 2 +- packages/validators/src/__tests__/email.test.ts | 14 ++++++++++++++ packages/validators/src/email/consts.ts | 1 + packages/validators/src/email/utils.ts | 11 ++++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/validators/package.json b/packages/validators/package.json index 94c1df3..2dc4757 100644 --- a/packages/validators/package.json +++ b/packages/validators/package.json @@ -1,6 +1,6 @@ { "name": "@opengovsg/starter-kitty-validators", - "version": "1.1.0", + "version": "1.1.1", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ diff --git a/packages/validators/src/__tests__/email.test.ts b/packages/validators/src/__tests__/email.test.ts index 80c8232..07d1ed6 100644 --- a/packages/validators/src/__tests__/email.test.ts +++ b/packages/validators/src/__tests__/email.test.ts @@ -76,6 +76,20 @@ describe('EmailValidator with default options', () => { expect(() => schema.parse('postmaster@[123.123.123.123]')).toThrowError( ZodError, ) + + // Encoded-word + // https://portswigger.net/research/splitting-the-email-atom + expect(() => + schema.parse('=?iso-8859-1?q?=61=62=63?=collab@psres.net'), + ).toThrowError(ZodError) + + expect(() => + schema.parse('=?utf-7?b?JkFHWUFid0J2QUdJQVlRQnkt?=@psres.net'), + ).toThrowError(ZodError) + + expect(() => + schema.parse('=?x?q?collab=40invalid.com=3e=00?=open.gov.sg'), + ).toThrowError(ZodError) }) it('should clean up unnecessary whitespace', () => { diff --git a/packages/validators/src/email/consts.ts b/packages/validators/src/email/consts.ts index be1b1ca..b00b6d2 100644 --- a/packages/validators/src/email/consts.ts +++ b/packages/validators/src/email/consts.ts @@ -1,2 +1,3 @@ export const MAX_LOCAL_LENGTH = 64 export const MAX_DOMAIN_LENGTH = 255 +export const ENCODED_WORD_REGEX = /=[?].+[?]=/ diff --git a/packages/validators/src/email/utils.ts b/packages/validators/src/email/utils.ts index 9cae6a9..5faab50 100644 --- a/packages/validators/src/email/utils.ts +++ b/packages/validators/src/email/utils.ts @@ -1,6 +1,10 @@ import { ParsedMailbox } from 'email-addresses' -import { MAX_DOMAIN_LENGTH, MAX_LOCAL_LENGTH } from './consts' +import { + ENCODED_WORD_REGEX, + MAX_DOMAIN_LENGTH, + MAX_LOCAL_LENGTH, +} from './consts' import { ParsedEmailValidatorOptions } from './options' export const isValidEmail = ( @@ -17,6 +21,11 @@ export const isValidEmail = ( if (whitelisted.length === 0) { return true } + + if (ENCODED_WORD_REGEX.test(email.address)) { + return false + } + return isWhitelistedDomain(domain, whitelisted) }