hof 24.3.2 → 24.4.0-proto-fix-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/controller/base-controller.js +142 -1
- package/frontend/govuk-template/govuk_template_generated.html +118 -0
- package/package.json +1 -1
- package/sandbox/apps/config-driven-navigation/translations/en/default.json +168 -0
- package/sandbox/apps/sandbox/translations/en/default.json +278 -0
- package/sandbox/apps/task-driven-navigation/translations/en/default.json +161 -0
- package/sandbox/public/css/app.css +11714 -0
- package/sandbox/public/images/govuk-logo.svg +25 -0
- package/sandbox/public/images/icons/icon-caret-left.png +0 -0
- package/sandbox/public/images/icons/icon-complete.png +0 -0
- package/sandbox/public/images/icons/icon-cross-remove-sign.png +0 -0
- package/sandbox/public/js/bundle.js +30 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
## 2026-07-01
|
|
1
|
+
## 2026-07-01, Version 24.4.0 (Stable), @vivekkumar-ho
|
|
2
|
+
|
|
3
|
+
### Security
|
|
4
|
+
- Added always-on prototype pollution protection to block dangerous keys and values in request body, query, and route params.
|
|
5
|
+
- Prototype pollution attempts now return `400` with `PROTOTYPE_POLLUTION_DETECTED`.
|
|
6
|
+
|
|
7
|
+
## 2026-07-01 Version 24.3.2 (Stable), @meganjohnHO
|
|
2
8
|
|
|
3
9
|
### Changed
|
|
4
10
|
- Google Analytics domains now added into CSP on presence of `ga4TagId` or `gaTagId` (previously only on `gaTagId`).
|
|
@@ -11,6 +11,17 @@ const ErrorClass = require('./validation-error');
|
|
|
11
11
|
const Helpers = require('../utilities').helpers;
|
|
12
12
|
const sanitisationBlacklistArray = require('../config/sanitisation-rules');
|
|
13
13
|
|
|
14
|
+
// Always-on protection against prototype pollution payloads before request data is used by form configuration.
|
|
15
|
+
const defaultPrototypePollutionProtection = {
|
|
16
|
+
enabled: true,
|
|
17
|
+
blockedKeys: ['__proto__', 'prototype', 'constructor'],
|
|
18
|
+
blockedValues: {
|
|
19
|
+
body: ['__proto__'],
|
|
20
|
+
query: ['__proto__'],
|
|
21
|
+
params: ['__proto__', 'prototype', 'constructor']
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
14
25
|
module.exports = class BaseController extends EventEmitter {
|
|
15
26
|
constructor(options) {
|
|
16
27
|
if (!options) {
|
|
@@ -206,10 +217,140 @@ module.exports = class BaseController extends EventEmitter {
|
|
|
206
217
|
callback();
|
|
207
218
|
}
|
|
208
219
|
|
|
220
|
+
getPrototypePollutionProtectionOptions() {
|
|
221
|
+
return _.cloneDeep(defaultPrototypePollutionProtection);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
hasInheritedEnumerableKey(value, seen) {
|
|
225
|
+
if (!_.isObjectLike(value)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const visited = seen || new Set();
|
|
230
|
+
if (visited.has(value)) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
visited.add(value);
|
|
235
|
+
|
|
236
|
+
for (const key in value) {
|
|
237
|
+
if (
|
|
238
|
+
!Object.prototype.hasOwnProperty.call(value, key) ||
|
|
239
|
+
this.hasInheritedEnumerableKey(value[key], visited)
|
|
240
|
+
) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
hasBlockedKeyOrValue(value, blockedKeys, blockedValues, seen) {
|
|
249
|
+
if (_.isString(value)) {
|
|
250
|
+
return blockedValues.has(value);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!_.isObjectLike(value)) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const visited = seen || new Set();
|
|
258
|
+
if (visited.has(value)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
visited.add(value);
|
|
263
|
+
|
|
264
|
+
const ownKeys = Object.keys(value);
|
|
265
|
+
for (const key of ownKeys) {
|
|
266
|
+
if (blockedKeys.has(key)) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (this.hasBlockedKeyOrValue(value[key], blockedKeys, blockedValues, visited)) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
logPrototypePollutionBlocked(req, details) {
|
|
279
|
+
if (req && typeof req.log === 'function') {
|
|
280
|
+
req.log('warn', 'security.prototype_pollution.blocked', details);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const logger = _.get(this, 'options.logger');
|
|
285
|
+
if (!logger) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (typeof logger.warn === 'function') {
|
|
290
|
+
logger.warn('security.prototype_pollution.blocked', details);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (typeof logger.log === 'function') {
|
|
295
|
+
logger.log('warn', 'security.prototype_pollution.blocked', details);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
checkPrototypePollution(req) {
|
|
300
|
+
const protection = this.getPrototypePollutionProtectionOptions();
|
|
301
|
+
const blockedKeys = new Set(protection.blockedKeys);
|
|
302
|
+
const sources = {
|
|
303
|
+
body: req.body,
|
|
304
|
+
query: req.query,
|
|
305
|
+
params: req.params
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
for (const sourceName of Object.keys(sources)) {
|
|
309
|
+
const source = sources[sourceName];
|
|
310
|
+
if (!source) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
if (this.hasInheritedEnumerableKey(source)) {
|
|
316
|
+
return {
|
|
317
|
+
reason: 'inherited_key',
|
|
318
|
+
source: sourceName
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const blockedValues = new Set(_.get(protection, `blockedValues.${sourceName}`, []));
|
|
323
|
+
if (this.hasBlockedKeyOrValue(source, blockedKeys, blockedValues)) {
|
|
324
|
+
return {
|
|
325
|
+
reason: 'blocked_key_or_value',
|
|
326
|
+
source: sourceName
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
} catch (err) {
|
|
330
|
+
return {
|
|
331
|
+
reason: 'scan_error',
|
|
332
|
+
source: sourceName
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
|
|
209
340
|
_configure(req, res, callback) {
|
|
210
341
|
req.form = req.form || {};
|
|
211
342
|
req.form.options = _.cloneDeep(this.options);
|
|
212
|
-
|
|
343
|
+
|
|
344
|
+
const pollutionDetection = this.checkPrototypePollution(req);
|
|
345
|
+
if (pollutionDetection) {
|
|
346
|
+
this.logPrototypePollutionBlocked(req, pollutionDetection);
|
|
347
|
+
const err = new Error('Prototype pollution detected');
|
|
348
|
+
err.statusCode = 403;
|
|
349
|
+
err.code = 'PROTOTYPE_POLLUTION_DETECTED';
|
|
350
|
+
return callback(err);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return this.configure(req, res, callback);
|
|
213
354
|
}
|
|
214
355
|
|
|
215
356
|
configure(req, res, callback) {
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<!--[if lt IE 9]><html class="lte-ie8" lang="{{htmlLang}}"><![endif]-->
|
|
4
|
+
<!--[if gt IE 8]><!--><html lang="{{htmlLang}}" class="govuk-template--rebranded"><!--<![endif]-->
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="utf-8" />
|
|
7
|
+
<title>{{$pageTitle}}{{/pageTitle}}</title>
|
|
8
|
+
{{$head}}{{/head}}
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
10
|
+
<meta name="theme-color" content="#1d70b8">
|
|
11
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
12
|
+
<link rel="icon" sizes="48x48" href="{{govukAssetPath}}rebrand/images/favicon.ico">
|
|
13
|
+
<link rel="icon" sizes="any" href="{{govukAssetPath}}rebrand/images/favicon.svg" type="image/svg+xml">
|
|
14
|
+
<link rel="mask-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-mask.svg" color="#1d70b8">
|
|
15
|
+
<link rel="apple-touch-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-180.png">
|
|
16
|
+
<link rel="manifest" href="{{govukAssetPath}}rebrand/manifest.json">
|
|
17
|
+
<meta property="og:image" content="{{govukAssetPath}}rebrand/images/govuk-opengraph-image.png">
|
|
18
|
+
</head>
|
|
19
|
+
|
|
20
|
+
<body class="{{$bodyClasses}}{{/bodyClasses}} govuk-template__body js-enabled" >
|
|
21
|
+
<script {{#nonce}}nonce="{{nonce}}"{{/nonce}}>document.body.className += ' js-enabled' + ('noModule' in HTMLScriptElement.prototype ? ' govuk-frontend-supported' : '');</script>
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<div id="global-cookie-message" class="gem-c-cookie-banner govuk-clearfix" data-module="cookie-banner" role="region" aria-label="cookie banner" data-nosnippet="">
|
|
25
|
+
{{$cookieMessage}}{{/cookieMessage}}
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
{{$bodyStart}}{{/bodyStart}}
|
|
29
|
+
|
|
30
|
+
<header role="banner" id="govuk-header" class="{{$headerClass}}{{/headerClass}}">
|
|
31
|
+
<div class="govuk-header__container govuk-width-container">
|
|
32
|
+
|
|
33
|
+
<div class="govuk-header__logo">
|
|
34
|
+
<a href="{{$homepageUrl}}https://www.gov.uk{{/homepageUrl}}" title="{{$logoLinkTitle}}Go to the GOV.UK homepage{{/logoLinkTitle}}" id="logo" class="govuk-header__link govuk-header__link--homepage" target="_blank" data-module="track-click" data-track-category="homeLinkClicked" data-track-action="homeHeader">
|
|
35
|
+
<!--[if gt IE 8]><!-->
|
|
36
|
+
<div id="govuk-header__logo"></div>
|
|
37
|
+
<img src="/public/images/govuk-logo.svg" id="govuk-header__logo" alt="Logo" loading="lazy" />
|
|
38
|
+
<!--<![endif]-->
|
|
39
|
+
<!--[if IE 8]>
|
|
40
|
+
<img src="{{govukAssetPath}}rebrand/images/govuk-logotype-tudor-crown.png" class="govuk-header__logotype-crown-fallback-image" width="32" height="30" alt="">
|
|
41
|
+
<![endif]-->
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
{{$insideHeader}}{{/insideHeader}}
|
|
45
|
+
|
|
46
|
+
{{$propositionHeader}}{{/propositionHeader}}
|
|
47
|
+
</div>
|
|
48
|
+
</header>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
{{$afterHeader}}{{/afterHeader}}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
{{$main}}{{/main}}
|
|
55
|
+
|
|
56
|
+
<footer class="govuk-footer">
|
|
57
|
+
<div class="govuk-width-container">
|
|
58
|
+
<svg
|
|
59
|
+
focusable="false"
|
|
60
|
+
role="presentation"
|
|
61
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
62
|
+
viewBox="0 0 64 60"
|
|
63
|
+
height="30"
|
|
64
|
+
width="32"
|
|
65
|
+
fill="currentcolor" class="govuk-footer__crown">
|
|
66
|
+
<g>
|
|
67
|
+
<circle cx="20" cy="17.6" r="3.7" />
|
|
68
|
+
<circle cx="10.2" cy="23.5" r="3.7" />
|
|
69
|
+
<circle cx="3.7" cy="33.2" r="3.7" />
|
|
70
|
+
<circle cx="31.7" cy="30.6" r="3.7" />
|
|
71
|
+
<circle cx="43.3" cy="17.6" r="3.7" />
|
|
72
|
+
<circle cx="53.2" cy="23.5" r="3.7" />
|
|
73
|
+
<circle cx="59.7" cy="33.2" r="3.7" />
|
|
74
|
+
<circle cx="31.7" cy="30.6" r="3.7" />
|
|
75
|
+
<path d="M33.1,9.8c.2-.1.3-.3.5-.5l4.6,2.4v-6.8l-4.6,1.5c-.1-.2-.3-.3-.5-.5l1.9-5.9h-6.7l1.9,5.9c-.2.1-.3.3-.5.5l-4.6-1.5v6.8l4.6-2.4c.1.2.3.3.5.5l-2.6,8c-.9,2.8,1.2,5.7,4.1,5.7h0c3,0,5.1-2.9,4.1-5.7l-2.6-8ZM37,37.9s-3.4,3.8-4.1,6.1c2.2,0,4.2-.5,6.4-2.8l-.7,8.5c-2-2.8-4.4-4.1-5.7-3.8.1,3.1.5,6.7,5.8,7.2,3.7.3,6.7-1.5,7-3.8.4-2.6-2-4.3-3.7-1.6-1.4-4.5,2.4-6.1,4.9-3.2-1.9-4.5-1.8-7.7,2.4-10.9,3,4,2.6,7.3-1.2,11.1,2.4-1.3,6.2,0,4,4.6-1.2-2.8-3.7-2.2-4.2.2-.3,1.7.7,3.7,3,4.2,1.9.3,4.7-.9,7-5.9-1.3,0-2.4.7-3.9,1.7l2.4-8c.6,2.3,1.4,3.7,2.2,4.5.6-1.6.5-2.8,0-5.3l5,1.8c-2.6,3.6-5.2,8.7-7.3,17.5-7.4-1.1-15.7-1.7-24.5-1.7h0c-8.8,0-17.1.6-24.5,1.7-2.1-8.9-4.7-13.9-7.3-17.5l5-1.8c-.5,2.5-.6,3.7,0,5.3.8-.8,1.6-2.3,2.2-4.5l2.4,8c-1.5-1-2.6-1.7-3.9-1.7,2.3,5,5.2,6.2,7,5.9,2.3-.4,3.3-2.4,3-4.2-.5-2.4-3-3.1-4.2-.2-2.2-4.6,1.6-6,4-4.6-3.7-3.7-4.2-7.1-1.2-11.1,4.2,3.2,4.3,6.4,2.4,10.9,2.5-2.8,6.3-1.3,4.9,3.2-1.8-2.7-4.1-1-3.7,1.6.3,2.3,3.3,4.1,7,3.8,5.4-.5,5.7-4.2,5.8-7.2-1.3-.2-3.7,1-5.7,3.8l-.7-8.5c2.2,2.3,4.2,2.7,6.4,2.8-.7-2.3-4.1-6.1-4.1-6.1h10.6,0Z" />
|
|
76
|
+
</g>
|
|
77
|
+
</svg>
|
|
78
|
+
<div class="govuk-footer__meta">
|
|
79
|
+
<div class="govuk-footer__meta-item govuk-footer__meta-item--grow">
|
|
80
|
+
<h2 class="govuk-visually-hidden">Support links</h2>
|
|
81
|
+
{{$footerSupportLinks}}{{/footerSupportLinks}}
|
|
82
|
+
<svg
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
focusable="false"
|
|
85
|
+
class="govuk-footer__licence-logo"
|
|
86
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
87
|
+
viewBox="0 0 483.2 195.7"
|
|
88
|
+
height="17"
|
|
89
|
+
width="41">
|
|
90
|
+
<path
|
|
91
|
+
fill="currentColor"
|
|
92
|
+
d="M421.5 142.8V.1l-50.7 32.3v161.1h112.4v-50.7zm-122.3-9.6A47.12 47.12 0 0 1 221 97.8c0-26 21.1-47.1 47.1-47.1 16.7 0 31.4 8.7 39.7 21.8l42.7-27.2A97.63 97.63 0 0 0 268.1 0c-36.5 0-68.3 20.1-85.1 49.7A98 98 0 0 0 97.8 0C43.9 0 0 43.9 0 97.8s43.9 97.8 97.8 97.8c36.5 0 68.3-20.1 85.1-49.7a97.76 97.76 0 0 0 149.6 25.4l19.4 22.2h3v-87.8h-80l24.3 27.5zM97.8 145c-26 0-47.1-21.1-47.1-47.1s21.1-47.1 47.1-47.1 47.2 21 47.2 47S123.8 145 97.8 145" />
|
|
93
|
+
</svg>
|
|
94
|
+
<span class="govuk-footer__licence-description">
|
|
95
|
+
{{$licenceMessage}}All content is available under the <a href="https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" id="open-government-licence" class="govuk-footer__link" target="_blank" rel="license">Open Government Licence v3.0</a>, except where otherwise stated{{/licenceMessage}}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="govuk-footer__meta-item">
|
|
99
|
+
<a
|
|
100
|
+
class="govuk-footer__link govuk-footer__copyright-logo"
|
|
101
|
+
href="https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/">
|
|
102
|
+
{{$crownCopyrightMessage}}© Crown copyright{{/crownCopyrightMessage}}
|
|
103
|
+
</a>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</footer>
|
|
108
|
+
|
|
109
|
+
<div id="global-app-error" class="app-error hidden"></div>
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
{{$bodyEnd}}{{/bodyEnd}}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
<script {{#nonce}}nonce="{{nonce}}"{{/nonce}}>if (typeof window.GOVUK === 'undefined') document.body.className = document.body.className.replace('js-enabled', '');</script>
|
|
116
|
+
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
package/package.json
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"buttons": {
|
|
3
|
+
"start": "Start Now",
|
|
4
|
+
"add-another": "Add another"
|
|
5
|
+
},
|
|
6
|
+
"fields": {
|
|
7
|
+
"selected-updates": {
|
|
8
|
+
"legend": "What do you need to change?",
|
|
9
|
+
"hint": "Select all that apply",
|
|
10
|
+
"options": {
|
|
11
|
+
"name": {
|
|
12
|
+
"label": "Name"
|
|
13
|
+
},
|
|
14
|
+
"surname": {
|
|
15
|
+
"label": "Surname"
|
|
16
|
+
},
|
|
17
|
+
"dob": {
|
|
18
|
+
"label": "Date of birth"
|
|
19
|
+
},
|
|
20
|
+
"address": {
|
|
21
|
+
"label": "Address"
|
|
22
|
+
},
|
|
23
|
+
"email": {
|
|
24
|
+
"label": "Email"
|
|
25
|
+
},
|
|
26
|
+
"phone": {
|
|
27
|
+
"label": "Phone"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"name": {
|
|
32
|
+
"label": "Name"
|
|
33
|
+
},
|
|
34
|
+
"surname": {
|
|
35
|
+
"label": "Surname"
|
|
36
|
+
},
|
|
37
|
+
"current-house-number": {
|
|
38
|
+
"label": "House number or name"
|
|
39
|
+
},
|
|
40
|
+
"current-street": {
|
|
41
|
+
"label": "Street"
|
|
42
|
+
},
|
|
43
|
+
"current-townOrCity": {
|
|
44
|
+
"label": "Town or city"
|
|
45
|
+
},
|
|
46
|
+
"current-county": {
|
|
47
|
+
"label": "County (optional)"
|
|
48
|
+
},
|
|
49
|
+
"has-postcode": {
|
|
50
|
+
"legend": "Do you have a postcode for this address?",
|
|
51
|
+
"options": {
|
|
52
|
+
"yes": {
|
|
53
|
+
"label": "Yes"
|
|
54
|
+
},
|
|
55
|
+
"no": {
|
|
56
|
+
"label": "No"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"postcode": {
|
|
61
|
+
"label": "Postcode"
|
|
62
|
+
},
|
|
63
|
+
"dob": {
|
|
64
|
+
"label": "Date of birth"
|
|
65
|
+
},
|
|
66
|
+
"email": {
|
|
67
|
+
"label": "Email address"
|
|
68
|
+
},
|
|
69
|
+
"phone": {
|
|
70
|
+
"label": "Phone number"
|
|
71
|
+
},
|
|
72
|
+
"change-anything-else": {
|
|
73
|
+
"legend": "Do you need to change anything else?",
|
|
74
|
+
"options": {
|
|
75
|
+
"yes": {
|
|
76
|
+
"label": "Yes"
|
|
77
|
+
},
|
|
78
|
+
"no": {
|
|
79
|
+
"label": "No"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"journey": {
|
|
85
|
+
"header": "POC - Config driven navigation",
|
|
86
|
+
"phase": "beta"
|
|
87
|
+
},
|
|
88
|
+
"pages": {
|
|
89
|
+
"surname-summary": {
|
|
90
|
+
"header": "Your previous surnames"
|
|
91
|
+
},
|
|
92
|
+
"address": {
|
|
93
|
+
"header": "What is your home address?"
|
|
94
|
+
},
|
|
95
|
+
"dob": {
|
|
96
|
+
"header": "What is your date of birth?"
|
|
97
|
+
},
|
|
98
|
+
"change-anything-else": {},
|
|
99
|
+
"confirm": {
|
|
100
|
+
"header": "Check your answers",
|
|
101
|
+
"sections": {
|
|
102
|
+
"personalDetails": {
|
|
103
|
+
"header": "Personal details"
|
|
104
|
+
},
|
|
105
|
+
"contactDetails": {
|
|
106
|
+
"header": "Contact details"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"fields": {
|
|
110
|
+
"previoussurnames": {
|
|
111
|
+
"label": "Previous surnames"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"submitted": {
|
|
116
|
+
"header": "Demo complete. If anything looked too smooth, just assume it was highly engineeing luck!",
|
|
117
|
+
"title": "Demo complete"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"validation": {
|
|
121
|
+
"selected-updates": {
|
|
122
|
+
"required": "Select at least one page to continue"
|
|
123
|
+
},
|
|
124
|
+
"name": {
|
|
125
|
+
"required": "Enter a name",
|
|
126
|
+
"maxlength": "Name must be 99 characters or less"
|
|
127
|
+
},
|
|
128
|
+
"surname": {
|
|
129
|
+
"required": "Enter a surname",
|
|
130
|
+
"maxlength": "Surname must be 99 characters or less"
|
|
131
|
+
},
|
|
132
|
+
"current-house-number": {
|
|
133
|
+
"required": "Enter the house number or name",
|
|
134
|
+
"maxlength": "House number or name must be 50 characters or less"
|
|
135
|
+
},
|
|
136
|
+
"current-street": {
|
|
137
|
+
"required": "Enter the street",
|
|
138
|
+
"maxlength": "Street must be 100 characters or less"
|
|
139
|
+
},
|
|
140
|
+
"current-townOrCity": {
|
|
141
|
+
"required": "Enter the town or city",
|
|
142
|
+
"maxlength": "Town or city must be 100 characters or less"
|
|
143
|
+
},
|
|
144
|
+
"current-county": {
|
|
145
|
+
"maxlength": "County must be 100 characters or less"
|
|
146
|
+
},
|
|
147
|
+
"has-postcode": {
|
|
148
|
+
"required": "Select yes if you have a postcode, or no if you do not"
|
|
149
|
+
},
|
|
150
|
+
"postcode": {
|
|
151
|
+
"required": "Enter a UK postcode",
|
|
152
|
+
"postcode": "Enter a real UK postcode"
|
|
153
|
+
},
|
|
154
|
+
"dob": {
|
|
155
|
+
"required": "Enter a date of birth",
|
|
156
|
+
"date": "Enter a date of birth in the correct format",
|
|
157
|
+
"after": "Date of birth must be after 1 January 1900"
|
|
158
|
+
},
|
|
159
|
+
"email": {
|
|
160
|
+
"required": "Enter an email address",
|
|
161
|
+
"email": "Enter an email address in the correct format"
|
|
162
|
+
},
|
|
163
|
+
"phone": {
|
|
164
|
+
"required": "Enter a phone number",
|
|
165
|
+
"internationalPhoneNumber": "Enter a valid phone number"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|