@dwp/govuk-casa 7.0.6 → 8.0.0-beta1
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 +8 -0
- package/README.md +22 -17
- package/dist/{casa → assets}/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -0
- package/dist/casa.d.ts +11 -0
- package/dist/casa.js +46 -0
- package/dist/lib/CasaTemplateLoader.d.ts +29 -0
- package/dist/lib/CasaTemplateLoader.js +74 -0
- package/dist/lib/JourneyContext.d.ts +297 -0
- package/dist/lib/JourneyContext.js +581 -0
- package/dist/lib/MutableRouter.d.ts +155 -0
- package/dist/lib/MutableRouter.js +277 -0
- package/dist/lib/Plan.d.ts +154 -0
- package/dist/lib/Plan.js +442 -0
- package/dist/lib/ValidationError.d.ts +74 -0
- package/dist/lib/ValidationError.js +159 -0
- package/dist/lib/ValidatorFactory.d.ts +83 -0
- package/dist/lib/ValidatorFactory.js +106 -0
- package/dist/lib/configuration-ingestor.d.ts +262 -0
- package/dist/lib/configuration-ingestor.js +490 -0
- package/dist/lib/configure.d.ts +90 -0
- package/dist/lib/configure.js +192 -0
- package/dist/lib/dirname.cjs +1 -0
- package/dist/lib/dirname.d.cts +2 -0
- package/dist/lib/end-session.d.ts +13 -0
- package/dist/lib/end-session.js +43 -0
- package/dist/lib/field.d.ts +77 -0
- package/dist/lib/field.js +255 -0
- package/dist/lib/index.d.ts +14 -0
- package/dist/lib/index.js +54 -0
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +18 -0
- package/dist/lib/nunjucks-filters.d.ts +26 -0
- package/dist/lib/nunjucks-filters.js +90 -0
- package/dist/lib/nunjucks.d.ts +23 -0
- package/dist/lib/nunjucks.js +49 -0
- package/dist/lib/utils.d.ts +48 -0
- package/dist/lib/utils.js +111 -0
- package/dist/lib/validators/dateObject.d.ts +4 -0
- package/dist/lib/validators/dateObject.js +135 -0
- package/dist/lib/validators/email.d.ts +4 -0
- package/dist/lib/validators/email.js +46 -0
- package/dist/lib/validators/inArray.d.ts +4 -0
- package/dist/lib/validators/inArray.js +60 -0
- package/dist/lib/validators/index.d.ts +21 -0
- package/dist/lib/validators/index.js +47 -0
- package/dist/lib/validators/nino.d.ts +4 -0
- package/dist/lib/validators/nino.js +46 -0
- package/dist/lib/validators/postalAddressObject.d.ts +4 -0
- package/dist/lib/validators/postalAddressObject.js +123 -0
- package/dist/lib/validators/regex.d.ts +4 -0
- package/dist/lib/validators/regex.js +40 -0
- package/dist/lib/validators/required.d.ts +4 -0
- package/dist/lib/validators/required.js +56 -0
- package/dist/lib/validators/strlen.d.ts +4 -0
- package/dist/lib/validators/strlen.js +51 -0
- package/dist/lib/validators/wordCount.d.ts +5 -0
- package/dist/lib/validators/wordCount.js +54 -0
- package/dist/lib/waypoint-url.d.ts +23 -0
- package/dist/lib/waypoint-url.js +52 -0
- package/dist/middleware/body-parser.d.ts +1 -0
- package/dist/middleware/body-parser.js +24 -0
- package/dist/middleware/csrf.d.ts +1 -0
- package/dist/middleware/csrf.js +31 -0
- package/dist/middleware/data.d.ts +5 -0
- package/dist/middleware/data.js +53 -0
- package/dist/middleware/dirname.cjs +1 -0
- package/dist/middleware/dirname.d.cts +2 -0
- package/dist/middleware/gather-fields.d.ts +6 -0
- package/dist/middleware/gather-fields.js +48 -0
- package/dist/middleware/i18n.d.ts +4 -0
- package/dist/middleware/i18n.js +88 -0
- package/dist/middleware/post.d.ts +3 -0
- package/dist/middleware/post.js +57 -0
- package/dist/middleware/pre.d.ts +3 -0
- package/dist/middleware/pre.js +51 -0
- package/dist/middleware/progress-journey.d.ts +6 -0
- package/dist/middleware/progress-journey.js +80 -0
- package/dist/middleware/sanitise-fields.d.ts +5 -0
- package/dist/middleware/sanitise-fields.js +53 -0
- package/dist/middleware/session.d.ts +11 -0
- package/dist/middleware/session.js +121 -0
- package/dist/middleware/skip-waypoint.d.ts +5 -0
- package/dist/middleware/skip-waypoint.js +43 -0
- package/dist/middleware/steer-journey.d.ts +7 -0
- package/dist/middleware/steer-journey.js +62 -0
- package/dist/middleware/validate-fields.d.ts +7 -0
- package/dist/middleware/validate-fields.js +67 -0
- package/dist/mjs/esm-wrapper.js +11 -0
- package/dist/mjs/package.json +3 -0
- package/dist/package.json +3 -0
- package/dist/routes/ancillary.d.ts +11 -0
- package/dist/routes/ancillary.js +27 -0
- package/dist/routes/dirname.cjs +1 -0
- package/dist/routes/dirname.d.cts +2 -0
- package/dist/routes/journey.d.ts +8 -0
- package/dist/routes/journey.js +127 -0
- package/dist/routes/static.d.ts +26 -0
- package/dist/routes/static.js +68 -0
- package/package.json +64 -89
- package/views/casa/components/checkboxes/template.njk +4 -1
- package/views/casa/components/date-input/template.njk +3 -3
- package/views/casa/components/journey-form/README.md +3 -1
- package/views/casa/components/journey-form/template.njk +1 -1
- package/views/casa/components/postal-address-object/template.njk +5 -5
- package/views/casa/components/radios/template.njk +1 -1
- package/views/casa/errors/static.njk +11 -0
- package/views/casa/layouts/journey.njk +26 -9
- package/views/casa/layouts/main.njk +7 -20
- package/views/casa/partials/scripts.njk +8 -3
- package/views/casa/partials/styles.njk +2 -2
- package/casa.js +0 -208
- package/definitions/review-page.js +0 -60
- package/dist/casa/css/casa.css +0 -1
- package/dist/casa/js/casa.js +0 -1
- package/index.d.ts +0 -121
- package/lib/ConfigIngestor.js +0 -588
- package/lib/GatherModifier.js +0 -14
- package/lib/I18n.js +0 -160
- package/lib/JourneyContext.d.ts +0 -97
- package/lib/JourneyContext.js +0 -552
- package/lib/JourneyMap.js +0 -233
- package/lib/JourneyRoad.js +0 -330
- package/lib/Logger.js +0 -59
- package/lib/PageDictionary.d.ts +0 -11
- package/lib/PageDirectory.js +0 -77
- package/lib/Plan.js +0 -423
- package/lib/RoadConverter.js +0 -153
- package/lib/UserJourney.js +0 -8
- package/lib/Util.js +0 -227
- package/lib/Validation.js +0 -20
- package/lib/bootstrap/end-session.js +0 -44
- package/lib/bootstrap/load-definitions.js +0 -64
- package/lib/commonBodyParser.js +0 -15
- package/lib/enums.js +0 -6
- package/lib/gather-modifiers/index.js +0 -7
- package/lib/gather-modifiers/trimPostalAddressObject.js +0 -75
- package/lib/gather-modifiers/trimWhitespace.js +0 -16
- package/lib/utils/createGetRequest.d.ts +0 -5
- package/lib/utils/createGetRequest.js +0 -59
- package/lib/utils/index.js +0 -11
- package/lib/utils/parseRequest.d.ts +0 -5
- package/lib/utils/parseRequest.js +0 -72
- package/lib/utils/sanitise.js +0 -74
- package/lib/utils/validate.js +0 -32
- package/lib/validation/ArrayObjectField.js +0 -49
- package/lib/validation/ObjectField.js +0 -53
- package/lib/validation/SimpleField.d.ts +0 -11
- package/lib/validation/SimpleField.js +0 -46
- package/lib/validation/ValidationError.d.ts +0 -14
- package/lib/validation/ValidationError.js +0 -170
- package/lib/validation/ValidatorFactory.d.ts +0 -32
- package/lib/validation/ValidatorFactory.js +0 -91
- package/lib/validation/index.js +0 -22
- package/lib/validation/processor/flattenErrorArray.js +0 -24
- package/lib/validation/processor/queue.js +0 -214
- package/lib/validation/processor.js +0 -84
- package/lib/validation/rules/README.md +0 -3
- package/lib/validation/rules/ValidationRules.d.ts +0 -22
- package/lib/validation/rules/dateObject.js +0 -156
- package/lib/validation/rules/email.js +0 -44
- package/lib/validation/rules/inArray.js +0 -61
- package/lib/validation/rules/index.js +0 -23
- package/lib/validation/rules/nino.js +0 -48
- package/lib/validation/rules/optional.js +0 -14
- package/lib/validation/rules/postalAddressObject.js +0 -142
- package/lib/validation/rules/regex.js +0 -39
- package/lib/validation/rules/required.js +0 -57
- package/lib/validation/rules/strlen.js +0 -57
- package/lib/validation/rules/wordCount.js +0 -61
- package/lib/view-filters/formatDateObject.js +0 -35
- package/lib/view-filters/includes.js +0 -10
- package/lib/view-filters/index.js +0 -23
- package/lib/view-filters/mergeObjectsDeep.js +0 -21
- package/lib/view-filters/renderAsAttributes.js +0 -33
- package/middleware/errors/404.js +0 -12
- package/middleware/errors/catch-all.js +0 -27
- package/middleware/errors/index.js +0 -9
- package/middleware/headers/config-defaults.js +0 -57
- package/middleware/headers/headers.js +0 -40
- package/middleware/headers/index.js +0 -9
- package/middleware/i18n/i18n.js +0 -56
- package/middleware/i18n/index.js +0 -16
- package/middleware/index.js +0 -55
- package/middleware/mount/index.js +0 -9
- package/middleware/mount/mount.js +0 -10
- package/middleware/nunjucks/environment.js +0 -57
- package/middleware/nunjucks/index.js +0 -8
- package/middleware/page/csrf.js +0 -37
- package/middleware/page/edit-mode.js +0 -52
- package/middleware/page/gather.js +0 -75
- package/middleware/page/index.js +0 -103
- package/middleware/page/journey-continue.js +0 -157
- package/middleware/page/journey-rails.js +0 -102
- package/middleware/page/prepare-request.js +0 -77
- package/middleware/page/render.js +0 -75
- package/middleware/page/skip.js +0 -72
- package/middleware/page/utils.js +0 -206
- package/middleware/page/validate.js +0 -67
- package/middleware/session/expiry.js +0 -95
- package/middleware/session/genid.js +0 -18
- package/middleware/session/index.js +0 -18
- package/middleware/session/init.js +0 -25
- package/middleware/session/seed.js +0 -50
- package/middleware/session/timeout.js +0 -5
- package/middleware/static/asset-versions.js +0 -23
- package/middleware/static/index.js +0 -104
- package/middleware/static/prepare-assets.js +0 -51
- package/middleware/static/serve-assets.js +0 -58
- package/middleware/variables/index.js +0 -12
- package/middleware/variables/variables.js +0 -35
- package/src/browserconfig.xml +0 -5
- package/src/js/casa.js +0 -132
- package/src/scss/_casaElements.scss +0 -11
- package/src/scss/_casaGovukTemplateJinjaPolyfill.scss +0 -39
- package/src/scss/_casaMountUrl.scss +0 -8
- package/src/scss/casa-ie8.scss +0 -3
- package/src/scss/casa.scss +0 -14
- package/test/unit/templates/README.md +0 -5
- package/test/utils/BaseTestWaypoint.js +0 -106
- package/test/utils/concatWaypoints.js +0 -26
- package/test/utils/index.js +0 -6
- package/test/utils/testTraversal.js +0 -90
- package/views/casa/partials/cookie_message.njk +0 -3
- package/views/casa/partials/phase_banner_alpha.njk +0 -8
- package/views/casa/partials/phase_banner_beta.njk +0 -8
- package/views/casa/review/page-block.njk +0 -8
- package/views/casa/review/review.njk +0 -47
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
/**
|
|
3
|
-
* Works hand in hand with the core CASA `postalAddressObject` form macro.
|
|
4
|
-
*
|
|
5
|
-
* The errors sent back from this validator are specific to each subfield. For
|
|
6
|
-
* example, if the field name being tested is "address", any errors related to
|
|
7
|
-
* the "postcode" component would be associated with "address[postcode]".
|
|
8
|
-
*
|
|
9
|
-
* Config options:
|
|
10
|
-
* string|object errorMsg = General error message for the entire address block
|
|
11
|
-
* string|object errorMsgAddress1 = Error message for address1 part
|
|
12
|
-
* string|object errorMsgAddress2 = Error message for address2 part
|
|
13
|
-
* string|object errorMsgAddress3 = Error message for address3 part
|
|
14
|
-
* string|object errorMsgAddress4 = Error message for address4 part
|
|
15
|
-
* string|object errorMsgPostcode = Error message for postcode part
|
|
16
|
-
* int strlenmax = Max. String length for each of the inputs appress[1-4]
|
|
17
|
-
* array requiredFields = Field parts required (others become optional)
|
|
18
|
-
*/
|
|
19
|
-
const ValidationError = require('../ValidationError.js');
|
|
20
|
-
const ValidatorFactory = require('../ValidatorFactory.js');
|
|
21
|
-
const { isObjectType, stringifyInput } = require('../../Util.js');
|
|
22
|
-
|
|
23
|
-
class PostalAddressObject extends ValidatorFactory {
|
|
24
|
-
validate(value, dataContext = {}) {
|
|
25
|
-
const cfg = {
|
|
26
|
-
requiredFields: ['address1', 'address3', 'postcode'],
|
|
27
|
-
strlenmax: undefined,
|
|
28
|
-
errorMsgAddress1: {
|
|
29
|
-
inline: 'validation:rule.postalAddressObject.address1.inline',
|
|
30
|
-
summary: 'validation:rule.postalAddressObject.address1.summary',
|
|
31
|
-
focusSuffix: '[address1]',
|
|
32
|
-
},
|
|
33
|
-
errorMsgAddress2: {
|
|
34
|
-
inline: 'validation:rule.postalAddressObject.address2.inline',
|
|
35
|
-
summary: 'validation:rule.postalAddressObject.address2.summary',
|
|
36
|
-
focusSuffix: '[address2]',
|
|
37
|
-
},
|
|
38
|
-
errorMsgAddress3: {
|
|
39
|
-
inline: 'validation:rule.postalAddressObject.address3.inline',
|
|
40
|
-
summary: 'validation:rule.postalAddressObject.address3.summary',
|
|
41
|
-
focusSuffix: '[address3]',
|
|
42
|
-
},
|
|
43
|
-
errorMsgAddress4: {
|
|
44
|
-
inline: 'validation:rule.postalAddressObject.address4.inline',
|
|
45
|
-
summary: 'validation:rule.postalAddressObject.address4.summary',
|
|
46
|
-
focusSuffix: '[address4]',
|
|
47
|
-
},
|
|
48
|
-
errorMsgPostcode: {
|
|
49
|
-
inline: 'validation:rule.postalAddressObject.postcode.inline',
|
|
50
|
-
summary: 'validation:rule.postalAddressObject.postcode.summary',
|
|
51
|
-
focusSuffix: '[postcode]',
|
|
52
|
-
},
|
|
53
|
-
errorMsg: {
|
|
54
|
-
inline: 'validation:rule.postalAddressObject.group.inline',
|
|
55
|
-
summary: 'validation:rule.postalAddressObject.group.summary',
|
|
56
|
-
focusSuffix: '[address1]',
|
|
57
|
-
},
|
|
58
|
-
...this.config,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/* eslint-disable-next-line require-jsdoc */
|
|
62
|
-
const objectifyError = (err) => (typeof err === 'string' ? {
|
|
63
|
-
inline: err,
|
|
64
|
-
summary: err,
|
|
65
|
-
} : err);
|
|
66
|
-
|
|
67
|
-
// Work out required/optional parts based on config
|
|
68
|
-
const reqF = Object.create(null);
|
|
69
|
-
const reqC = cfg.requiredFields;
|
|
70
|
-
['address1', 'address2', 'address3', 'address4', 'postcode'].forEach((k) => {
|
|
71
|
-
reqF[k] = reqC.indexOf(k) > -1;
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
let valid = true;
|
|
75
|
-
const errorMsgs = [];
|
|
76
|
-
|
|
77
|
-
if (typeof value === 'object') {
|
|
78
|
-
const reAddr = /^[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
|
|
79
|
-
const reAddrLine1 = /^\d+|[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
|
|
80
|
-
// UK Postcode regex taken from the dwp java pc checker
|
|
81
|
-
// https://github.com/dwp/postcode-format-validation
|
|
82
|
-
const pc = /^(?![QVX])[A-Z]((?![IJZ])[A-Z][0-9](([0-9]?)|([ABEHMNPRVWXY]?))|([0-9]([0-9]?|[ABCDEFGHJKPSTUW]?))) ?[0-9]((?![CIKMOV])[A-Z]){2}$|^(BFPO)[ ]?[0-9]{1,4}$/i;
|
|
83
|
-
|
|
84
|
-
const rePostcode = new RegExp(pc, 'i');
|
|
85
|
-
|
|
86
|
-
// [required, regex, strlenmax, error message]
|
|
87
|
-
const attributes = {
|
|
88
|
-
address1: [reqF.address1, reAddrLine1, cfg.strlenmax, cfg.errorMsgAddress1],
|
|
89
|
-
address2: [reqF.address2, reAddr, cfg.strlenmax, cfg.errorMsgAddress2],
|
|
90
|
-
address3: [reqF.address3, reAddr, cfg.strlenmax, cfg.errorMsgAddress3],
|
|
91
|
-
address4: [reqF.address4, reAddr, cfg.strlenmax, cfg.errorMsgAddress4],
|
|
92
|
-
postcode: [reqF.postcode, rePostcode, null, cfg.errorMsgPostcode],
|
|
93
|
-
};
|
|
94
|
-
Object.keys(attributes).forEach((k) => {
|
|
95
|
-
const attr = attributes[k];
|
|
96
|
-
const hasProperty = Object.prototype.hasOwnProperty.call(value, k);
|
|
97
|
-
const hasContent = hasProperty && value[k].length > 0;
|
|
98
|
-
|
|
99
|
-
const condMissingOrRegexMismatch = (attr[0] || hasContent)
|
|
100
|
-
&& (!hasProperty || !value[k].match(attr[1]));
|
|
101
|
-
const condExceedStrlen = attr[2] > 0 && hasContent
|
|
102
|
-
&& String(value[k]).length > attr[2];
|
|
103
|
-
|
|
104
|
-
if (condMissingOrRegexMismatch || condExceedStrlen) {
|
|
105
|
-
valid = false;
|
|
106
|
-
errorMsgs.push(Object.assign(Object.create(null), objectifyError(attr[3]), {
|
|
107
|
-
fieldKeySuffix: `[${k}]`,
|
|
108
|
-
}));
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
} else {
|
|
112
|
-
valid = false;
|
|
113
|
-
errorMsgs.push(cfg.errorMsg);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Build ValidationErrorGroup
|
|
117
|
-
const errorGroup = errorMsgs.map((err) => (
|
|
118
|
-
ValidationError.make({ errorMsg: err, dataContext })));
|
|
119
|
-
|
|
120
|
-
return valid ? Promise.resolve() : Promise.reject(errorGroup);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
sanitise(value) {
|
|
124
|
-
// Only objects are supported
|
|
125
|
-
if (!isObjectType(value)) {
|
|
126
|
-
return Object.create(null);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Prune unrecognised attributes, and coerce to Strings
|
|
130
|
-
const validKeys = ['address1', 'address2', 'address3', 'address4', 'postcode'];
|
|
131
|
-
const pruned = Object.fromEntries(
|
|
132
|
-
Object.entries(value).filter(
|
|
133
|
-
([k]) => (validKeys.includes(k)),
|
|
134
|
-
).map(
|
|
135
|
-
([k, v]) => ([k, stringifyInput(v)]),
|
|
136
|
-
),
|
|
137
|
-
);
|
|
138
|
-
return Object.assign(Object.create(null), pruned);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
module.exports = PostalAddressObject;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
/**
|
|
3
|
-
* Match a string pattern.
|
|
4
|
-
*
|
|
5
|
-
* Config options:
|
|
6
|
-
* string|object errorMsg = Error message to use on validation failure
|
|
7
|
-
* RegExp pattern = Regular expression to test against
|
|
8
|
-
* boolean invert = return reject on positive regex match
|
|
9
|
-
*/
|
|
10
|
-
const ValidatorFactory = require('../ValidatorFactory.js');
|
|
11
|
-
const ValidationError = require('../ValidationError.js');
|
|
12
|
-
const { stringifyInput } = require('../../Util.js');
|
|
13
|
-
|
|
14
|
-
class Regex extends ValidatorFactory {
|
|
15
|
-
validate(value = '', dataContext = {}) {
|
|
16
|
-
const invert = this.config.invert || false;
|
|
17
|
-
const match = value.match(this.config.pattern || /.*/);
|
|
18
|
-
const valid = invert ? !match : match;
|
|
19
|
-
|
|
20
|
-
const errorMsg = this.config.errorMsg || {
|
|
21
|
-
inline: 'validation:rule.regex.inline',
|
|
22
|
-
summary: 'validation:rule.regex.summary',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
return valid ? Promise.resolve() : Promise.reject(ValidationError.make({
|
|
26
|
-
errorMsg,
|
|
27
|
-
dataContext,
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
sanitise(value) {
|
|
32
|
-
if (value !== undefined) {
|
|
33
|
-
return stringifyInput(value);
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = Regex;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
/**
|
|
3
|
-
* Test is value is present.
|
|
4
|
-
*
|
|
5
|
-
* Value is required. The following values will fail this rule:
|
|
6
|
-
* (all values that satisify `Util.isEmpty()`) plus '\s'
|
|
7
|
-
*/
|
|
8
|
-
const {
|
|
9
|
-
isEmpty, isObjectType, isStringable, stringifyInput,
|
|
10
|
-
} = require('../../Util.js');
|
|
11
|
-
const ValidatorFactory = require('../ValidatorFactory.js');
|
|
12
|
-
const ValidationError = require('../ValidationError.js');
|
|
13
|
-
|
|
14
|
-
class Required extends ValidatorFactory {
|
|
15
|
-
validate(value, dataContext = {}) {
|
|
16
|
-
const {
|
|
17
|
-
errorMsg = {
|
|
18
|
-
inline: 'validation:rule.required.inline',
|
|
19
|
-
summary: 'validation:rule.required.summary',
|
|
20
|
-
},
|
|
21
|
-
} = this.config;
|
|
22
|
-
|
|
23
|
-
let result;
|
|
24
|
-
if (!isEmpty(value, {
|
|
25
|
-
regexRemove: /\s/g,
|
|
26
|
-
})) {
|
|
27
|
-
result = Promise.resolve();
|
|
28
|
-
} else {
|
|
29
|
-
result = Promise.reject(ValidationError.make({ errorMsg, dataContext }));
|
|
30
|
-
}
|
|
31
|
-
return result;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
sanitise(value) {
|
|
35
|
-
const coerce = (val) => (stringifyInput(val, undefined));
|
|
36
|
-
|
|
37
|
-
if (isStringable(value)) {
|
|
38
|
-
return stringifyInput(value);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Coerce all elements to Strings.
|
|
42
|
-
// This only supports one dimensional array, with stringable element.
|
|
43
|
-
if (Array.isArray(value)) {
|
|
44
|
-
return value.map(coerce);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Coerce all elements to Strings.
|
|
48
|
-
// This only supports a one dimensional object, with stringable elements.
|
|
49
|
-
if (isObjectType(value)) {
|
|
50
|
-
return Object.fromEntries(Object.entries(value).map(([k, v]) => ([k, coerce(v)])));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
module.exports = Required;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
/**
|
|
3
|
-
* Test the length of a string.
|
|
4
|
-
*
|
|
5
|
-
* Config options:
|
|
6
|
-
* string|object errorMsgMax = Error message to use on max length failure
|
|
7
|
-
* string|object errorMsgMin = Error message to use on min length failure
|
|
8
|
-
* int max = Maximum string length allowed
|
|
9
|
-
* int min = Minimum string length required
|
|
10
|
-
*/
|
|
11
|
-
const ValidatorFactory = require('../ValidatorFactory.js');
|
|
12
|
-
const ValidationError = require('../ValidationError.js');
|
|
13
|
-
const { stringifyInput } = require('../../Util.js');
|
|
14
|
-
|
|
15
|
-
class Strlen extends ValidatorFactory {
|
|
16
|
-
validate(inputValue = '', dataContext = {}) {
|
|
17
|
-
const {
|
|
18
|
-
errorMsgMax = {
|
|
19
|
-
inline: 'validation:rule.strlen.max.inline',
|
|
20
|
-
summary: 'validation:rule.strlen.max.summary',
|
|
21
|
-
},
|
|
22
|
-
errorMsgMin = {
|
|
23
|
-
inline: 'validation:rule.strlen.min.inline',
|
|
24
|
-
summary: 'validation:rule.strlen.min.summary',
|
|
25
|
-
},
|
|
26
|
-
min,
|
|
27
|
-
max,
|
|
28
|
-
} = this.config;
|
|
29
|
-
|
|
30
|
-
let errorMsg;
|
|
31
|
-
let valid = true;
|
|
32
|
-
|
|
33
|
-
if (typeof max !== 'undefined' && inputValue.length > max) {
|
|
34
|
-
valid = false;
|
|
35
|
-
errorMsg = errorMsgMax;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (typeof min !== 'undefined' && inputValue.length < min) {
|
|
39
|
-
valid = false;
|
|
40
|
-
errorMsg = errorMsgMin;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return valid ? Promise.resolve() : Promise.reject(ValidationError.make({
|
|
44
|
-
errorMsg,
|
|
45
|
-
dataContext,
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
sanitise(value) {
|
|
50
|
-
if (value !== undefined) {
|
|
51
|
-
return stringifyInput(value);
|
|
52
|
-
}
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
module.exports = Strlen;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
/**
|
|
3
|
-
* Test the number of words in a string.
|
|
4
|
-
*
|
|
5
|
-
* Config options:
|
|
6
|
-
* string|object errorMsgMax = Error message to use on max length failure
|
|
7
|
-
* string|object errorMsgMin = Error message to use on min length failure
|
|
8
|
-
* int max = Maximum word count allowed
|
|
9
|
-
* int min = Minimum word count required
|
|
10
|
-
*/
|
|
11
|
-
const ValidatorFactory = require('../ValidatorFactory.js');
|
|
12
|
-
const ValidationError = require('../ValidationError.js');
|
|
13
|
-
const { stringifyInput } = require('../../Util.js');
|
|
14
|
-
|
|
15
|
-
class WordCount extends ValidatorFactory {
|
|
16
|
-
count(input) {
|
|
17
|
-
return (input.match(/\S+/g) || []).length;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
validate(inputValue = '', dataContext = {}) {
|
|
21
|
-
const {
|
|
22
|
-
errorMsgMax = {
|
|
23
|
-
inline: 'validation:rule.wordCount.max.inline',
|
|
24
|
-
summary: 'validation:rule.wordCount.max.summary',
|
|
25
|
-
},
|
|
26
|
-
errorMsgMin = {
|
|
27
|
-
inline: 'validation:rule.wordCount.min.inline',
|
|
28
|
-
summary: 'validation:rule.wordCount.min.summary',
|
|
29
|
-
},
|
|
30
|
-
min,
|
|
31
|
-
max,
|
|
32
|
-
} = this.config;
|
|
33
|
-
|
|
34
|
-
let errorMsg;
|
|
35
|
-
let valid = true;
|
|
36
|
-
|
|
37
|
-
if (typeof max !== 'undefined' && (inputValue.match(/\S+/g) || []).length > max) {
|
|
38
|
-
valid = false;
|
|
39
|
-
errorMsg = errorMsgMax;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (typeof min !== 'undefined' && (inputValue.match(/\S+/g) || []).length < min) {
|
|
43
|
-
valid = false;
|
|
44
|
-
errorMsg = errorMsgMin;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return valid ? Promise.resolve() : Promise.reject(ValidationError.make({
|
|
48
|
-
errorMsg,
|
|
49
|
-
dataContext,
|
|
50
|
-
}));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
sanitise(value) {
|
|
54
|
-
if (value !== undefined) {
|
|
55
|
-
return stringifyInput(value);
|
|
56
|
-
}
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
module.exports = WordCount;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Format a given date object into a string.
|
|
3
|
-
*
|
|
4
|
-
* Requires NodeJS >= 14
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { DateTime } = require('luxon');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Format a given date.
|
|
11
|
-
*
|
|
12
|
-
* `date` may be any of the following types:
|
|
13
|
-
* object - {dd:'', mm:'', yyyy:''}
|
|
14
|
-
*
|
|
15
|
-
* @param {object} date Date (see supported formats above)
|
|
16
|
-
* @param {object} config Holds locale
|
|
17
|
-
* @returns {string} Formatted date
|
|
18
|
-
*/
|
|
19
|
-
module.exports = function formatDateObject(date, config = {}) {
|
|
20
|
-
const { locale = 'en' } = config;
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
Object.prototype.toString.call(date) === '[object Object]'
|
|
24
|
-
&& 'yyyy' in date
|
|
25
|
-
&& 'mm' in date
|
|
26
|
-
&& 'dd' in date
|
|
27
|
-
) {
|
|
28
|
-
return DateTime.fromObject({
|
|
29
|
-
year: Math.max(0, parseInt(date.yyyy, 10)),
|
|
30
|
-
month: Math.max(0, parseInt(date.mm, 10)),
|
|
31
|
-
day: Math.max(1, parseInt(date.dd, 10)),
|
|
32
|
-
}).setLocale(locale).toFormat('d MMMM yyyy');
|
|
33
|
-
}
|
|
34
|
-
return 'INVALID DATE OBJECT';
|
|
35
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test if an array includes the given element.
|
|
3
|
-
*
|
|
4
|
-
* @param {Array} source Array to search.
|
|
5
|
-
* @param {any} search Element to search for.
|
|
6
|
-
* @returns {boolean} Result.
|
|
7
|
-
*/
|
|
8
|
-
module.exports = function includes(source = [], search = '') {
|
|
9
|
-
return source.includes(search);
|
|
10
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This is a convenience script that will attach all filters in this directory
|
|
3
|
-
* to the given Nunjucks environment.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const formatDateObject = require('./formatDateObject.js');
|
|
7
|
-
const renderAsAttributes = require('./renderAsAttributes.js');
|
|
8
|
-
const mergeObjectsDeep = require('./mergeObjectsDeep.js');
|
|
9
|
-
const includes = require('./includes.js');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Load filters into the given Nunjucks environment
|
|
13
|
-
*
|
|
14
|
-
* @param {any} env Nunjucks environment
|
|
15
|
-
* @returns {void}
|
|
16
|
-
*/
|
|
17
|
-
module.exports = (env) => {
|
|
18
|
-
env.addFilter('formatDateObject', formatDateObject);
|
|
19
|
-
env.addFilter('renderAsAttributes', renderAsAttributes);
|
|
20
|
-
env.addGlobal('mergeObjects', mergeObjectsDeep);
|
|
21
|
-
env.addGlobal('mergeObjectsDeep', mergeObjectsDeep);
|
|
22
|
-
env.addGlobal('includes', includes);
|
|
23
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const merge = require('lodash.merge');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Merge the given objects. The first object given will _not_ be mutated.
|
|
5
|
-
*
|
|
6
|
-
* @param {...any} objects Objects to be merged.
|
|
7
|
-
* @returns {object} Merged object.
|
|
8
|
-
* @throws {TypeError} When attempting to merge a non-object
|
|
9
|
-
*/
|
|
10
|
-
module.exports = function mergeObjectsDeep(...objects) {
|
|
11
|
-
// Validate
|
|
12
|
-
if (!objects || !objects.length) {
|
|
13
|
-
throw new Error('You must specify some objects to merge');
|
|
14
|
-
}
|
|
15
|
-
objects.forEach((o) => {
|
|
16
|
-
if (Object.prototype.toString.call(o) !== '[object Object]') {
|
|
17
|
-
throw new TypeError('Cannot merge objects of type %s', Object.prototype.toString.call(o));
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
return merge(Object.create(null), ...objects);
|
|
21
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Renders a given attributes object into a format suitable for adding to an
|
|
3
|
-
* HTML element.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const nunjucks = require('nunjucks');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Attribute values will be HTML/attribute escaped.
|
|
10
|
-
*
|
|
11
|
-
* Example:
|
|
12
|
-
* Given: {class: 'basic', 'data-ga': 3}
|
|
13
|
-
* Output: class="basic" data-ga="3"
|
|
14
|
-
*
|
|
15
|
-
* @param {object} attrsObject Attributes object (in name:value pairs)
|
|
16
|
-
* @returns {string} Formatted
|
|
17
|
-
*/
|
|
18
|
-
module.exports = function renderAsAttributes(attrsObject) {
|
|
19
|
-
const attrsList = [];
|
|
20
|
-
if (typeof attrsObject === 'object') {
|
|
21
|
-
Object.keys(attrsObject).forEach((key) => {
|
|
22
|
-
const value = String(attrsObject[key]).replace(/[<>"'&]/g, (m) => ({
|
|
23
|
-
'<': '<',
|
|
24
|
-
'>': '>',
|
|
25
|
-
'"': '"',
|
|
26
|
-
'\'': ''',
|
|
27
|
-
'&': '&',
|
|
28
|
-
}[m]));
|
|
29
|
-
attrsList.push(`${key}="${value}"`);
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return new nunjucks.runtime.SafeString(attrsList.join(' '));
|
|
33
|
-
};
|
package/middleware/errors/404.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 404 handler.
|
|
3
|
-
*
|
|
4
|
-
* This middleware must be the last non-error middleware defined in the
|
|
5
|
-
* stack as it handles the last case when all other middleware have been
|
|
6
|
-
* exhausted.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
module.exports = (logger) => (req, res) => {
|
|
10
|
-
logger.info('[404] %s', req.url);
|
|
11
|
-
res.status(404).render('casa/errors/404.njk');
|
|
12
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Catch-all unhandled routing exceptions.
|
|
3
|
-
*
|
|
4
|
-
* This must be the last handler in the entire middleware chain to ensure it
|
|
5
|
-
* catches errors from all previous middleware functions.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/* eslint-disable-next-line no-unused-vars */
|
|
9
|
-
module.exports = (logger) => (err, req, res, next) => {
|
|
10
|
-
if (err.code === 'EBADCSRFTOKEN') {
|
|
11
|
-
logger.info('[403] CSRF token missing/invalid');
|
|
12
|
-
res.status(403).render('casa/errors/403.njk');
|
|
13
|
-
} else if (err.type === 'entity.verify.failed') {
|
|
14
|
-
logger.info('[403] Request payload blocked');
|
|
15
|
-
res.status(403).render('casa/errors/403.njk');
|
|
16
|
-
} else if (!res.headersSent) {
|
|
17
|
-
logger.error('[500] Internal Server Error (rendered page) - %s - %s', err.message, err.stack.toString());
|
|
18
|
-
res.status(500).render('casa/errors/500.njk');
|
|
19
|
-
} else {
|
|
20
|
-
// This is usually caused by a session save operation failing after the
|
|
21
|
-
// page has already been rendered. So to the end user, the page will appear
|
|
22
|
-
// as normal. It is only when they attempt to POST that they may see the
|
|
23
|
-
// proper `500.njk` error page, because at that point the session is
|
|
24
|
-
// explicitly saved (and error checked) prior to rendering.
|
|
25
|
-
logger.error('[500] Internal Server Error (unrendered; headers already sent) - %s - %s', err.message, err.stack.toString());
|
|
26
|
-
}
|
|
27
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates and returns some default HTTP headers for use in all requests,
|
|
3
|
-
* and sets a few global Express defaults.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
module.exports = (app, cspConfig = {}) => {
|
|
7
|
-
// ETags are disabled by default here. See also "static" middleware, where
|
|
8
|
-
// they are re-enabled on a case-by-case basis.
|
|
9
|
-
app.set('etag', false);
|
|
10
|
-
|
|
11
|
-
// Remove powered by express header
|
|
12
|
-
app.set('x-powered-by', false);
|
|
13
|
-
|
|
14
|
-
// Prepare common CSP directives
|
|
15
|
-
// Content-Security-Policy directives
|
|
16
|
-
const csp = cspConfig;
|
|
17
|
-
const cspKeyScriptSrc = 'script-src';
|
|
18
|
-
let cspDirectives = Object.getOwnPropertyNames(csp).length > 0 ? csp : {
|
|
19
|
-
[cspKeyScriptSrc]: [],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// CASA requires these script-src entries to be included in the CSP
|
|
23
|
-
const requiredScriptSources = [
|
|
24
|
-
'\'self\'',
|
|
25
|
-
// hash of inline GOV.UK template JS to add 'js-enabled' body class
|
|
26
|
-
'\'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU=\'',
|
|
27
|
-
'https://www.google-analytics.com/',
|
|
28
|
-
'https://www.googletagmanager.com/',
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
if (!Object.prototype.hasOwnProperty.call(cspDirectives, cspKeyScriptSrc)) {
|
|
32
|
-
cspDirectives[cspKeyScriptSrc] = [];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
requiredScriptSources.forEach((source) => {
|
|
36
|
-
if (cspDirectives[cspKeyScriptSrc].indexOf(source) === -1) {
|
|
37
|
-
cspDirectives[cspKeyScriptSrc].push(source);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Compile the CSP
|
|
42
|
-
cspDirectives = Object.keys(cspDirectives).map((directive) => `${directive} ${cspDirectives[directive].join(' ')}`);
|
|
43
|
-
|
|
44
|
-
// Prepare default headers
|
|
45
|
-
// added X-Robots-Tag based on https://www.gov.uk/service-manual/technology/get-a-domain-name
|
|
46
|
-
const defaultHeaders = {
|
|
47
|
-
'X-Content-Type-Options': 'nosniff',
|
|
48
|
-
'X-XSS-Protection': '1; mode=block',
|
|
49
|
-
'X-Frame-Options': 'DENY',
|
|
50
|
-
'Content-Security-Policy': cspDirectives.join('; '),
|
|
51
|
-
'X-Robots-Tag': 'noindex, nofollow',
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
defaultHeaders,
|
|
56
|
-
};
|
|
57
|
-
};
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apply HTTP headers to all requests.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const isStaticAsset = /.*\.(js|jpe?g|css|png|svg|woff2?|eot|ttf|otf)/;
|
|
6
|
-
const isIE8 = /MSIE\s*8/i;
|
|
7
|
-
const oneDay = 86400000;
|
|
8
|
-
|
|
9
|
-
module.exports = (logger, defaultHeaders = {}, disabledHeaders = []) => (req, res, next) => {
|
|
10
|
-
logger.trace('apply headers to %s %s', req.method.toUpperCase(), req.url);
|
|
11
|
-
|
|
12
|
-
const headers = Object.assign(Object.create(null), defaultHeaders);
|
|
13
|
-
|
|
14
|
-
// X-XSS-Protection introduces a security bug into IE8, so disable it if IE8
|
|
15
|
-
if (isIE8.test(req.headers['user-agent'])) {
|
|
16
|
-
logger.trace('disabling xss protection for IE8');
|
|
17
|
-
headers['X-XSS-Protection'] = '0';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Caching policy
|
|
21
|
-
// Cache static assets more agressively
|
|
22
|
-
if (isStaticAsset.test(req.url)) {
|
|
23
|
-
headers['Cache-Control'] = 'public';
|
|
24
|
-
headers.Pragma = 'cache';
|
|
25
|
-
headers.Expires = new Date(Date.now() + oneDay).toUTCString();
|
|
26
|
-
} else {
|
|
27
|
-
headers['Cache-Control'] = 'no-cache, no-store, must-revalidate, private';
|
|
28
|
-
headers.Pragma = 'no-cache';
|
|
29
|
-
headers.Expires = 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Write headers
|
|
33
|
-
Object.keys(headers).forEach((k) => {
|
|
34
|
-
if (disabledHeaders.indexOf(k) === -1) {
|
|
35
|
-
res.setHeader(k, headers[k]);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
next();
|
|
40
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const logger = require('../../lib/Logger.js')('headers');
|
|
2
|
-
|
|
3
|
-
const mwHeaders = require('./headers.js');
|
|
4
|
-
const config = require('./config-defaults.js');
|
|
5
|
-
|
|
6
|
-
module.exports = (app, cspConfig = {}, disabledHeadersConfig = []) => {
|
|
7
|
-
const { defaultHeaders } = config(app, cspConfig);
|
|
8
|
-
app.use(mwHeaders(logger, defaultHeaders, disabledHeadersConfig));
|
|
9
|
-
};
|