@dwp/govuk-casa 7.0.8 → 8.0.0-beta2

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.
Files changed (132) hide show
  1. package/README.md +22 -17
  2. package/dist/mjs/esm-wrapper.js +10 -0
  3. package/package.json +55 -80
  4. package/views/casa/components/checkboxes/template.njk +4 -1
  5. package/views/casa/components/date-input/template.njk +3 -3
  6. package/views/casa/components/journey-form/README.md +3 -1
  7. package/views/casa/components/journey-form/template.njk +1 -1
  8. package/views/casa/components/postal-address-object/template.njk +5 -5
  9. package/views/casa/components/radios/template.njk +1 -1
  10. package/views/casa/errors/static.njk +11 -0
  11. package/views/casa/layouts/journey.njk +26 -9
  12. package/views/casa/layouts/main.njk +7 -20
  13. package/views/casa/partials/scripts.njk +8 -3
  14. package/views/casa/partials/styles.njk +2 -2
  15. package/casa.js +0 -208
  16. package/definitions/review-page.js +0 -60
  17. package/dist/casa/css/casa-ie8.css +0 -1
  18. package/dist/casa/css/casa.css +0 -1
  19. package/dist/casa/js/casa.js +0 -1
  20. package/index.d.ts +0 -121
  21. package/lib/ConfigIngestor.js +0 -588
  22. package/lib/GatherModifier.js +0 -14
  23. package/lib/I18n.js +0 -160
  24. package/lib/JourneyContext.d.ts +0 -97
  25. package/lib/JourneyContext.js +0 -552
  26. package/lib/JourneyMap.js +0 -233
  27. package/lib/JourneyRoad.js +0 -330
  28. package/lib/Logger.js +0 -59
  29. package/lib/PageDictionary.d.ts +0 -11
  30. package/lib/PageDirectory.js +0 -77
  31. package/lib/Plan.js +0 -423
  32. package/lib/RoadConverter.js +0 -153
  33. package/lib/UserJourney.js +0 -8
  34. package/lib/Util.js +0 -227
  35. package/lib/Validation.js +0 -20
  36. package/lib/bootstrap/end-session.js +0 -44
  37. package/lib/bootstrap/load-definitions.js +0 -64
  38. package/lib/commonBodyParser.js +0 -15
  39. package/lib/enums.js +0 -6
  40. package/lib/gather-modifiers/index.js +0 -7
  41. package/lib/gather-modifiers/trimPostalAddressObject.js +0 -75
  42. package/lib/gather-modifiers/trimWhitespace.js +0 -16
  43. package/lib/utils/createGetRequest.d.ts +0 -5
  44. package/lib/utils/createGetRequest.js +0 -59
  45. package/lib/utils/index.js +0 -11
  46. package/lib/utils/parseRequest.d.ts +0 -5
  47. package/lib/utils/parseRequest.js +0 -72
  48. package/lib/utils/sanitise.js +0 -74
  49. package/lib/utils/validate.js +0 -32
  50. package/lib/validation/ArrayObjectField.js +0 -49
  51. package/lib/validation/ObjectField.js +0 -53
  52. package/lib/validation/SimpleField.d.ts +0 -12
  53. package/lib/validation/SimpleField.js +0 -46
  54. package/lib/validation/ValidationError.d.ts +0 -14
  55. package/lib/validation/ValidationError.js +0 -170
  56. package/lib/validation/ValidatorFactory.d.ts +0 -32
  57. package/lib/validation/ValidatorFactory.js +0 -91
  58. package/lib/validation/index.js +0 -22
  59. package/lib/validation/processor/flattenErrorArray.js +0 -24
  60. package/lib/validation/processor/queue.js +0 -214
  61. package/lib/validation/processor.js +0 -84
  62. package/lib/validation/rules/README.md +0 -3
  63. package/lib/validation/rules/ValidationRules.d.ts +0 -14
  64. package/lib/validation/rules/dateObject.js +0 -156
  65. package/lib/validation/rules/email.js +0 -44
  66. package/lib/validation/rules/inArray.js +0 -61
  67. package/lib/validation/rules/index.js +0 -23
  68. package/lib/validation/rules/nino.js +0 -48
  69. package/lib/validation/rules/optional.js +0 -14
  70. package/lib/validation/rules/postalAddressObject.js +0 -142
  71. package/lib/validation/rules/regex.js +0 -39
  72. package/lib/validation/rules/required.js +0 -57
  73. package/lib/validation/rules/strlen.js +0 -57
  74. package/lib/validation/rules/wordCount.js +0 -61
  75. package/lib/view-filters/formatDateObject.js +0 -35
  76. package/lib/view-filters/includes.js +0 -10
  77. package/lib/view-filters/index.js +0 -23
  78. package/lib/view-filters/mergeObjectsDeep.js +0 -21
  79. package/lib/view-filters/renderAsAttributes.js +0 -33
  80. package/middleware/errors/404.js +0 -12
  81. package/middleware/errors/catch-all.js +0 -27
  82. package/middleware/errors/index.js +0 -9
  83. package/middleware/headers/config-defaults.js +0 -57
  84. package/middleware/headers/headers.js +0 -40
  85. package/middleware/headers/index.js +0 -9
  86. package/middleware/i18n/i18n.js +0 -56
  87. package/middleware/i18n/index.js +0 -16
  88. package/middleware/index.js +0 -55
  89. package/middleware/mount/index.js +0 -9
  90. package/middleware/mount/mount.js +0 -10
  91. package/middleware/nunjucks/environment.js +0 -57
  92. package/middleware/nunjucks/index.js +0 -8
  93. package/middleware/page/csrf.js +0 -37
  94. package/middleware/page/edit-mode.js +0 -52
  95. package/middleware/page/gather.js +0 -75
  96. package/middleware/page/index.js +0 -103
  97. package/middleware/page/journey-continue.js +0 -157
  98. package/middleware/page/journey-rails.js +0 -102
  99. package/middleware/page/prepare-request.js +0 -77
  100. package/middleware/page/render.js +0 -75
  101. package/middleware/page/skip.js +0 -72
  102. package/middleware/page/utils.js +0 -206
  103. package/middleware/page/validate.js +0 -67
  104. package/middleware/session/expiry.js +0 -95
  105. package/middleware/session/genid.js +0 -18
  106. package/middleware/session/index.js +0 -18
  107. package/middleware/session/init.js +0 -25
  108. package/middleware/session/seed.js +0 -50
  109. package/middleware/session/timeout.js +0 -5
  110. package/middleware/static/asset-versions.js +0 -23
  111. package/middleware/static/index.js +0 -104
  112. package/middleware/static/prepare-assets.js +0 -51
  113. package/middleware/static/serve-assets.js +0 -58
  114. package/middleware/variables/index.js +0 -12
  115. package/middleware/variables/variables.js +0 -35
  116. package/src/browserconfig.xml +0 -5
  117. package/src/js/casa.js +0 -132
  118. package/src/scss/_casaElements.scss +0 -11
  119. package/src/scss/_casaGovukTemplateJinjaPolyfill.scss +0 -39
  120. package/src/scss/_casaMountUrl.scss +0 -8
  121. package/src/scss/casa-ie8.scss +0 -3
  122. package/src/scss/casa.scss +0 -14
  123. package/test/unit/templates/README.md +0 -5
  124. package/test/utils/BaseTestWaypoint.js +0 -106
  125. package/test/utils/concatWaypoints.js +0 -26
  126. package/test/utils/index.js +0 -6
  127. package/test/utils/testTraversal.js +0 -90
  128. package/views/casa/partials/cookie_message.njk +0 -3
  129. package/views/casa/partials/phase_banner_alpha.njk +0 -8
  130. package/views/casa/partials/phase_banner_beta.njk +0 -8
  131. package/views/casa/review/page-block.njk +0 -8
  132. 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
- '<': '&lt;',
24
- '>': '&gt;',
25
- '"': '&quot;',
26
- '\'': '&#039;',
27
- '&': '&amp;',
28
- }[m]));
29
- attrsList.push(`${key}="${value}"`);
30
- });
31
- }
32
- return new nunjucks.runtime.SafeString(attrsList.join(' '));
33
- };
@@ -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,9 +0,0 @@
1
- const logger = require('../../lib/Logger.js')('errors');
2
-
3
- const mw404 = require('./404.js');
4
- const mwCatchAll = require('./catch-all.js');
5
-
6
- module.exports = (app) => {
7
- app.use(mw404(logger));
8
- app.use(mwCatchAll(logger));
9
- };
@@ -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
- };