Skip to content

Commit a4c90a9

Browse files
committed
Handles invalid unicode in email fields
1 parent 980dcc6 commit a4c90a9

File tree

5 files changed

+46
-6
lines changed

5 files changed

+46
-6
lines changed

src/server/plugins/engine/components/EmailAddressField.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { type EmailAddressFieldComponent } from '@defra/forms-model'
2-
import joi from 'joi'
1+
import {
2+
preventUnicodeInEmail,
3+
type EmailAddressFieldComponent
4+
} from '@defra/forms-model'
5+
import joi, { type CustomHelpers } from 'joi'
36

47
import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'
58
import { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'
@@ -20,7 +23,15 @@ export class EmailAddressField extends FormComponent {
2023

2124
const { options } = def
2225

23-
let formSchema = joi.string().email().trim().label(this.label).required()
26+
let formSchema = joi
27+
.string()
28+
.email()
29+
.trim()
30+
.custom((value, helpers: CustomHelpers<string>) =>
31+
preventUnicodeInEmail(value, helpers)
32+
)
33+
.label(this.label)
34+
.required()
2435

2536
if (options.required === false) {
2637
formSchema = formSchema.allow('')
@@ -69,7 +80,8 @@ export class EmailAddressField extends FormComponent {
6980
return {
7081
baseErrors: [
7182
{ type: 'required', template: messageTemplate.required },
72-
{ type: 'format', template: messageTemplate.format }
83+
{ type: 'format', template: messageTemplate.format },
84+
{ type: 'unicode', template: messageTemplate.unicode }
7385
],
7486
advancedSettingsErrors: []
7587
}

src/server/plugins/engine/pageControllers/helpers/state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { ControllerType, getHiddenFields } from '@defra/forms-model'
2-
import { validate as isValidUUID } from 'uuid'
32

43
import { getCacheService } from '~/src/server/plugins/engine/helpers.js'
54
import {
@@ -16,6 +15,7 @@ import {
1615
} from '~/src/server/plugins/engine/types.js'
1716
import { type FormQuery } from '~/src/server/routes/types.js'
1817
import { type Services } from '~/src/server/types.js'
18+
import { isValidUUID } from '~/src/server/utils/utils.js'
1919

2020
const GUID_LENGTH = 36
2121

src/server/plugins/engine/pageControllers/validationOptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const messageTemplate: Record<string, JoiExpression> = {
4444
'Enter {{lowerFirst(#label)}} in the correct format',
4545
opts
4646
) as JoiExpression,
47+
unicode: '{{#label}} includes invalid characters, for example, long dashes',
4748
number: '{{#label}} must be a number',
4849
numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',
4950
numberInteger: '{{#label}} must be a whole number',
@@ -69,6 +70,7 @@ export const messages: LanguageMessagesExt = {
6970
'string.empty': messageTemplate.required,
7071
'string.max': messageTemplate.max,
7172
'string.email': messageTemplate.format,
73+
'string.unicode': messageTemplate.unicode,
7274
'string.pattern.base': messageTemplate.pattern,
7375
'string.maxWords': messageTemplate.maxWords,
7476

src/server/utils/utils.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getTraceId } from '@defra/hapi-tracing'
2+
import Joi from 'joi'
23

34
import { config } from '~/src/config/index.js'
45

@@ -22,3 +23,13 @@ export function applyTraceHeaders(
2223

2324
return existingHeaders ? Object.assign(existingHeaders, headers) : headers
2425
}
26+
27+
/**
28+
* Validates if a string conforms to the uuid structure
29+
* @param {string} str
30+
* @returns
31+
*/
32+
export function isValidUUID(str) {
33+
const { error } = Joi.string().uuid().validate(str)
34+
return error === undefined
35+
}

src/server/utils/utils.test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getTraceId } from '@defra/hapi-tracing'
22

33
import { config } from '~/src/config/index.js'
4-
import { applyTraceHeaders } from '~/src/server/utils/utils.js'
4+
import { applyTraceHeaders, isValidUUID } from '~/src/server/utils/utils.js'
55

66
jest.mock('@defra/hapi-tracing')
77

@@ -51,4 +51,19 @@ describe('Header helper functions', () => {
5151

5252
expect(result).toBe(existingHeaders)
5353
})
54+
55+
it.each([
56+
{ uuid: '1f457a37-7b99-452e-8324-df9e041abff2', valid: true },
57+
{ uuid: '0c9a2690-9a0c-4a2c-98d7-e9ef95615ac9', valid: true },
58+
{ uuid: 'f223de3b-5ae5-44b2-8cee-ea8439adc335', valid: true },
59+
{ uuid: '82ecc90c-bc47-4ec5-80af-1a9fc1c4c08c', valid: true },
60+
{ uuid: 'd99ff582-ecce-474f-a44b-bc5961d977c5', valid: true },
61+
{ uuid: '7afffc8a-81ab-4aa6-a8f5-ecf6a600a781', valid: true },
62+
{ uuid: '7afffc8a81ab4aa6a8f5ecf6a600a781', valid: true },
63+
{ uuid: '', valid: false },
64+
{ uuid: 'uuid', valid: false },
65+
{ uuid: 'h4f84ef8-b5e1-4544-94aa-1b671d50d8cb', valid: false }
66+
])('should validate uuid appropriately %s', ({ uuid, valid }) => {
67+
expect(isValidUUID(uuid)).toBe(valid)
68+
})
5469
})

0 commit comments

Comments
 (0)