@dwp/govuk-casa 8.0.0-beta2 → 8.0.0
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 +12 -0
- package/README.md +1 -1
- package/dist/assets/css/casa-ie8.css +1 -1
- package/dist/assets/css/casa.css +1 -1
- package/dist/lib/CasaTemplateLoader.d.ts +1 -3
- package/dist/lib/CasaTemplateLoader.js +22 -3
- package/dist/lib/JourneyContext.d.ts +1 -0
- package/dist/lib/JourneyContext.js +37 -11
- package/dist/lib/MutableRouter.js +3 -1
- package/dist/lib/Plan.js +12 -5
- package/dist/lib/ValidationError.js +4 -0
- package/dist/lib/configuration-ingestor.js +11 -37
- package/dist/lib/configure.js +1 -2
- package/dist/lib/dirname.cjs +1 -1
- package/dist/lib/end-session.js +4 -1
- package/dist/lib/field.js +10 -0
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.js +2 -2
- package/dist/lib/nunjucks-filters.js +8 -0
- package/dist/lib/utils.js +2 -0
- package/dist/lib/validators/inArray.js +1 -1
- package/dist/lib/validators/index.js +0 -22
- package/dist/lib/validators/postalAddressObject.js +6 -2
- package/dist/middleware/body-parser.d.ts +1 -0
- package/dist/middleware/body-parser.js +18 -9
- package/dist/middleware/data.js +8 -4
- package/dist/middleware/dirname.cjs +1 -1
- package/dist/middleware/gather-fields.js +3 -5
- package/dist/middleware/i18n.js +5 -1
- package/dist/middleware/progress-journey.js +1 -1
- package/dist/middleware/sanitise-fields.js +5 -5
- package/dist/middleware/session.js +60 -53
- package/dist/middleware/skip-waypoint.js +2 -2
- package/dist/middleware/validate-fields.js +3 -0
- package/dist/routes/ancillary.js +0 -2
- package/dist/routes/dirname.cjs +1 -1
- package/dist/routes/journey.js +5 -6
- package/package.json +19 -16
|
@@ -26,6 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
};
|
|
27
27
|
var _JourneyContext_data, _JourneyContext_validation, _JourneyContext_nav, _JourneyContext_identity, _JourneyContext_eventListeners, _JourneyContext_eventListenerPreState;
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.validateObjectKey = void 0;
|
|
29
30
|
/**
|
|
30
31
|
* Represents the state of a user's journey through the Plan. It contains
|
|
31
32
|
* information about:
|
|
@@ -39,7 +40,7 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
39
40
|
const ValidationError_js_1 = __importDefault(require("./ValidationError.js"));
|
|
40
41
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
41
42
|
const { cloneDeep, isPlainObject, isObject, has, isEqual, } = lodash_1.default; // CommonJS
|
|
42
|
-
const log = (0, logger_js_1.default)('
|
|
43
|
+
const log = (0, logger_js_1.default)('lib:journey-context');
|
|
43
44
|
/**
|
|
44
45
|
* @typedef {import('./configuration-ingestor').Page} Page
|
|
45
46
|
*/
|
|
@@ -55,6 +56,14 @@ const log = (0, logger_js_1.default)('class:journey-context');
|
|
|
55
56
|
* @property {string} [field] Field to watch for changes
|
|
56
57
|
* @property {ContextEventHandler} handler Handler to invoke when change happens
|
|
57
58
|
*/
|
|
59
|
+
function validateObjectKey(key = '') {
|
|
60
|
+
const keyLower = String.prototype.toLowerCase.call(key);
|
|
61
|
+
if (keyLower === 'prototype' || keyLower === '__proto__' || keyLower === 'constructor') {
|
|
62
|
+
throw new SyntaxError(`Invalid object key used, ${key}`);
|
|
63
|
+
}
|
|
64
|
+
return String(key);
|
|
65
|
+
}
|
|
66
|
+
exports.validateObjectKey = validateObjectKey;
|
|
58
67
|
class JourneyContext {
|
|
59
68
|
/**
|
|
60
69
|
* Constructor.
|
|
@@ -144,10 +153,10 @@ class JourneyContext {
|
|
|
144
153
|
*/
|
|
145
154
|
getDataForPage(page) {
|
|
146
155
|
if (typeof page === 'string') {
|
|
147
|
-
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[page];
|
|
156
|
+
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page)];
|
|
148
157
|
}
|
|
149
158
|
if (isPlainObject(page)) {
|
|
150
|
-
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[page.waypoint];
|
|
159
|
+
return __classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page.waypoint)];
|
|
151
160
|
}
|
|
152
161
|
throw new TypeError(`Page must be a string or Page object. Got ${typeof page}`);
|
|
153
162
|
}
|
|
@@ -179,10 +188,10 @@ class JourneyContext {
|
|
|
179
188
|
*/
|
|
180
189
|
setDataForPage(page, webFormData) {
|
|
181
190
|
if (typeof page === 'string') {
|
|
182
|
-
__classPrivateFieldGet(this, _JourneyContext_data, "f")[page] = webFormData;
|
|
191
|
+
__classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page)] = webFormData;
|
|
183
192
|
}
|
|
184
193
|
else if (isPlainObject(page)) {
|
|
185
|
-
__classPrivateFieldGet(this, _JourneyContext_data, "f")[page.waypoint] = webFormData;
|
|
194
|
+
__classPrivateFieldGet(this, _JourneyContext_data, "f")[validateObjectKey(page.waypoint)] = webFormData;
|
|
186
195
|
}
|
|
187
196
|
else {
|
|
188
197
|
throw new TypeError(`Page must be a string or Page object. Got ${typeof page}`);
|
|
@@ -220,7 +229,7 @@ class JourneyContext {
|
|
|
220
229
|
* @returns {JourneyContext} Chain.
|
|
221
230
|
*/
|
|
222
231
|
clearValidationErrorsForPage(pageId) {
|
|
223
|
-
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[pageId] = null;
|
|
232
|
+
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)] = null;
|
|
224
233
|
return this;
|
|
225
234
|
}
|
|
226
235
|
/**
|
|
@@ -240,7 +249,7 @@ class JourneyContext {
|
|
|
240
249
|
throw new SyntaxError('Field errors must be a ValidationError');
|
|
241
250
|
}
|
|
242
251
|
});
|
|
243
|
-
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[pageId] = errors;
|
|
252
|
+
__classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)] = errors;
|
|
244
253
|
return this;
|
|
245
254
|
}
|
|
246
255
|
/**
|
|
@@ -251,17 +260,21 @@ class JourneyContext {
|
|
|
251
260
|
* @returns {ValidationError[]} An array of errors
|
|
252
261
|
*/
|
|
253
262
|
getValidationErrorsForPage(pageId) {
|
|
254
|
-
|
|
263
|
+
var _a;
|
|
264
|
+
return (_a = __classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)]) !== null && _a !== void 0 ? _a : [];
|
|
255
265
|
}
|
|
256
266
|
getValidationErrorsForPageByField(pageId) {
|
|
257
267
|
const errors = this.getValidationErrorsForPage(pageId);
|
|
258
268
|
const obj = Object.create(null);
|
|
269
|
+
// ESLint disabled as `i` is an integer
|
|
270
|
+
/* eslint-disable security/detect-object-injection */
|
|
259
271
|
for (let i = 0, l = errors.length; i < l; i++) {
|
|
260
272
|
if (!obj[errors[i].field]) {
|
|
261
273
|
obj[errors[i].field] = [];
|
|
262
274
|
}
|
|
263
275
|
obj[errors[i].field].push(errors[i]);
|
|
264
276
|
}
|
|
277
|
+
/* eslint-enable security/detect-object-injection */
|
|
265
278
|
return obj;
|
|
266
279
|
}
|
|
267
280
|
/**
|
|
@@ -273,7 +286,7 @@ class JourneyContext {
|
|
|
273
286
|
*/
|
|
274
287
|
hasValidationErrorsForPage(pageId) {
|
|
275
288
|
var _a, _b;
|
|
276
|
-
return ((_b = (_a = __classPrivateFieldGet(this, _JourneyContext_validation, "f")) === null || _a === void 0 ? void 0 : _a[pageId]) === null || _b === void 0 ? void 0 : _b.length) > 0;
|
|
289
|
+
return ((_b = (_a = __classPrivateFieldGet(this, _JourneyContext_validation, "f")) === null || _a === void 0 ? void 0 : _a[validateObjectKey(pageId)]) === null || _b === void 0 ? void 0 : _b.length) > 0;
|
|
277
290
|
}
|
|
278
291
|
/**
|
|
279
292
|
* Set language of the context.
|
|
@@ -292,7 +305,7 @@ class JourneyContext {
|
|
|
292
305
|
* @returns {boolean} True if the page is valid.
|
|
293
306
|
*/
|
|
294
307
|
isPageValid(pageId) {
|
|
295
|
-
return __classPrivateFieldGet(this, _JourneyContext_validation, "f")[pageId] === null;
|
|
308
|
+
return __classPrivateFieldGet(this, _JourneyContext_validation, "f")[validateObjectKey(pageId)] === null;
|
|
296
309
|
}
|
|
297
310
|
/**
|
|
298
311
|
* Remove information about these waypoints.
|
|
@@ -303,10 +316,13 @@ class JourneyContext {
|
|
|
303
316
|
const newData = Object.create(null);
|
|
304
317
|
const newValidation = Object.create(null);
|
|
305
318
|
const toKeep = Object.keys(this.data).filter((w) => !waypoints.includes(w));
|
|
319
|
+
// ESLint disabled as `i` is an integer
|
|
320
|
+
/* eslint-disable security/detect-object-injection */
|
|
306
321
|
for (let i = 0, l = toKeep.length; i < l; i++) {
|
|
307
322
|
newData[toKeep[i]] = __classPrivateFieldGet(this, _JourneyContext_data, "f")[toKeep[i]];
|
|
308
323
|
newValidation[toKeep[i]] = __classPrivateFieldGet(this, _JourneyContext_validation, "f")[toKeep[i]];
|
|
309
324
|
}
|
|
325
|
+
/* eslint-enable security/detect-object-injection */
|
|
310
326
|
__classPrivateFieldSet(this, _JourneyContext_data, Object.assign({}, newData), "f");
|
|
311
327
|
__classPrivateFieldSet(this, _JourneyContext_validation, Object.assign({}, newValidation), "f");
|
|
312
328
|
}
|
|
@@ -319,6 +335,8 @@ class JourneyContext {
|
|
|
319
335
|
*/
|
|
320
336
|
invalidate(waypoints = []) {
|
|
321
337
|
for (let i = 0, l = waypoints.length; i < l; i++) {
|
|
338
|
+
// ESLint disabled as `i` is an integer
|
|
339
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
322
340
|
this.removeValidationStateForPage(waypoints[i]);
|
|
323
341
|
}
|
|
324
342
|
}
|
|
@@ -346,6 +364,10 @@ class JourneyContext {
|
|
|
346
364
|
}
|
|
347
365
|
const previousContext = JourneyContext.fromObject(__classPrivateFieldGet(this, _JourneyContext_eventListenerPreState, "f"));
|
|
348
366
|
const listeners = __classPrivateFieldGet(this, _JourneyContext_eventListeners, "f").filter((l) => l.event === event);
|
|
367
|
+
// ESLint disabled as `listeners[i]` uses an integer key, and the other keys
|
|
368
|
+
// are derived from the list of `listeners`, which are not manipulated at
|
|
369
|
+
// runtime (only set by dev in code).
|
|
370
|
+
/* eslint-disable security/detect-object-injection */
|
|
349
371
|
for (let i = 0, l = listeners.length; i < l; i++) {
|
|
350
372
|
const { waypoint, field, handler } = listeners[i];
|
|
351
373
|
let logMessage;
|
|
@@ -367,6 +389,7 @@ class JourneyContext {
|
|
|
367
389
|
handler({ journeyContext: this, previousContext });
|
|
368
390
|
}
|
|
369
391
|
}
|
|
392
|
+
/* eslint-enable security/detect-object-injection */
|
|
370
393
|
return this;
|
|
371
394
|
}
|
|
372
395
|
/* ----------------------------------------------- session context handling */
|
|
@@ -440,6 +463,8 @@ class JourneyContext {
|
|
|
440
463
|
*/
|
|
441
464
|
static getContextById(session, id) {
|
|
442
465
|
if (has(session === null || session === void 0 ? void 0 : session.journeyContextList, id)) {
|
|
466
|
+
// ESLint disabled as `id` has been verified as an "own" property
|
|
467
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
443
468
|
return JourneyContext.fromObject(session.journeyContextList[id]);
|
|
444
469
|
}
|
|
445
470
|
return undefined;
|
|
@@ -533,7 +558,8 @@ class JourneyContext {
|
|
|
533
558
|
}
|
|
534
559
|
static removeContextById(session, id) {
|
|
535
560
|
if (session && has(session.journeyContextList, id)) {
|
|
536
|
-
|
|
561
|
+
// ESLint disabled as `id` has been verified as an "own" property
|
|
562
|
+
/* eslint-disable-next-line security/detect-object-injection, no-param-reassign */
|
|
537
563
|
delete session.journeyContextList[id];
|
|
538
564
|
}
|
|
539
565
|
}
|
|
@@ -48,6 +48,9 @@ class MutableRouter {
|
|
|
48
48
|
return __classPrivateFieldGet(this, _MutableRouter_router, "f");
|
|
49
49
|
}
|
|
50
50
|
__classPrivateFieldGet(this, _MutableRouter_stack, "f").forEach(({ method, args }) => {
|
|
51
|
+
// ESLint disabled as `#router` is dev-controlled, and `seal()` is only
|
|
52
|
+
// run at boot-time before any user interaction
|
|
53
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
51
54
|
__classPrivateFieldGet(this, _MutableRouter_router, "f")[method].call(__classPrivateFieldGet(this, _MutableRouter_router, "f"), ...args);
|
|
52
55
|
});
|
|
53
56
|
__classPrivateFieldSet(this, _MutableRouter_sealed, true, "f");
|
|
@@ -115,7 +118,6 @@ class MutableRouter {
|
|
|
115
118
|
__classPrivateFieldGet(this, _MutableRouter_instances, "m", _MutableRouter_prepend).call(this, 'use', path, ...callbacks);
|
|
116
119
|
}
|
|
117
120
|
/* -------------------------------------------------------------- replacers */
|
|
118
|
-
// TODO: How do we handle multiple routes on the same path?
|
|
119
121
|
/**
|
|
120
122
|
* Replace middleware function(s) that were mounted using the `all()` method.
|
|
121
123
|
*
|
package/dist/lib/Plan.js
CHANGED
|
@@ -18,7 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
const graphlib_1 = require("graphlib");
|
|
19
19
|
const JourneyContext_js_1 = __importDefault(require("./JourneyContext.js"));
|
|
20
20
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
21
|
-
const log = (0, logger_js_1.default)('
|
|
21
|
+
const log = (0, logger_js_1.default)('lib:plan');
|
|
22
22
|
/**
|
|
23
23
|
* Will check if the source waypoint has specifically passed validation, i.e
|
|
24
24
|
* there is a "null" validation entry for the route source.
|
|
@@ -58,6 +58,7 @@ function validateRouteName(val) {
|
|
|
58
58
|
else if (!['next', 'prev'].includes(val)) {
|
|
59
59
|
throw new ReferenceError(`Expected route name to be one of next or prev. Got ${val}`);
|
|
60
60
|
}
|
|
61
|
+
return val;
|
|
61
62
|
}
|
|
62
63
|
function validateRouteCondition(val) {
|
|
63
64
|
if (!(val instanceof Function)) {
|
|
@@ -177,7 +178,7 @@ class Plan {
|
|
|
177
178
|
return self.dgraph.edges().map((edge) => makeRouteObject(self.dgraph, edge));
|
|
178
179
|
}
|
|
179
180
|
getRouteCondition(src, tgt, name) {
|
|
180
|
-
return priv.get(this).follows[name][`${src}/${tgt}`];
|
|
181
|
+
return priv.get(this).follows[validateRouteName(name)][`${src}/${tgt}`];
|
|
181
182
|
}
|
|
182
183
|
/**
|
|
183
184
|
* Return all outward routes (out-edges) from the given waypoint, to the
|
|
@@ -205,6 +206,8 @@ class Plan {
|
|
|
205
206
|
addSequence(...waypoints) {
|
|
206
207
|
// Setup simple double routes (next/prev) between all waypoints in this list
|
|
207
208
|
for (let i = 0, l = waypoints.length - 1; i < l; i += 1) {
|
|
209
|
+
// ESLint disabled as `i` is an integer
|
|
210
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
208
211
|
this.setRoute(waypoints[i], waypoints[i + 1]);
|
|
209
212
|
}
|
|
210
213
|
}
|
|
@@ -309,6 +312,8 @@ class Plan {
|
|
|
309
312
|
else {
|
|
310
313
|
followFunc = defaultPrevFollow;
|
|
311
314
|
}
|
|
315
|
+
// ESLint disabled as `name` has been validated further above
|
|
316
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
312
317
|
self.follows[name][`${src}/${tgt}`] = followFunc;
|
|
313
318
|
return this;
|
|
314
319
|
}
|
|
@@ -360,9 +365,7 @@ class Plan {
|
|
|
360
365
|
if (!self.dgraph.hasNode(startWaypoint)) {
|
|
361
366
|
throw new ReferenceError(`Plan does not contain waypoint '${startWaypoint}'`);
|
|
362
367
|
}
|
|
363
|
-
|
|
364
|
-
throw new ReferenceError('Route name must be provided');
|
|
365
|
-
}
|
|
368
|
+
validateRouteName(routeName);
|
|
366
369
|
const history = new Map();
|
|
367
370
|
const traverse = (startWP) => {
|
|
368
371
|
let target = self.dgraph.outEdges(startWP).filter((e) => {
|
|
@@ -371,6 +374,8 @@ class Plan {
|
|
|
371
374
|
}
|
|
372
375
|
const route = makeRouteObject(self.dgraph, e);
|
|
373
376
|
try {
|
|
377
|
+
// ESLint disabled as `routeName` has been validated further above
|
|
378
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
374
379
|
return self.follows[routeName][`${e.v}/${e.w}`](route, context);
|
|
375
380
|
}
|
|
376
381
|
catch (ex) {
|
|
@@ -413,6 +418,8 @@ class Plan {
|
|
|
413
418
|
const results = new Array(totalTrav + 1);
|
|
414
419
|
results[0] = route;
|
|
415
420
|
for (let i = 0; i < totalTrav; i++) {
|
|
421
|
+
// ESLint disabled as `i` is an integer
|
|
422
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
416
423
|
results[i + 1] = traversed[i];
|
|
417
424
|
}
|
|
418
425
|
return results;
|
|
@@ -114,11 +114,15 @@ class ValidationError {
|
|
|
114
114
|
// the values that will be readable, and reflect any context that may have
|
|
115
115
|
// been applied
|
|
116
116
|
Object.keys(originals).forEach((o) => {
|
|
117
|
+
// ESLint disabled as `o` is an "own" property of `originals`, which is
|
|
118
|
+
// dev-controlled
|
|
119
|
+
/* eslint-disable security/detect-object-injection */
|
|
117
120
|
Object.defineProperty(this, o, {
|
|
118
121
|
value: originals[o],
|
|
119
122
|
enumerable: true,
|
|
120
123
|
writable: true,
|
|
121
124
|
});
|
|
125
|
+
/* eslint-enable security/detect-object-injection */
|
|
122
126
|
});
|
|
123
127
|
}
|
|
124
128
|
/**
|
|
@@ -193,10 +193,10 @@ exports.validateViews = validateViews;
|
|
|
193
193
|
*/
|
|
194
194
|
function validateSessionSecret(secret) {
|
|
195
195
|
if (typeof secret === 'undefined') {
|
|
196
|
-
throw ReferenceError('Session secret is missing (
|
|
196
|
+
throw ReferenceError('Session secret is missing (session.secret)');
|
|
197
197
|
}
|
|
198
198
|
else if (typeof secret !== 'string') {
|
|
199
|
-
throw new TypeError('Session secret must be a string (
|
|
199
|
+
throw new TypeError('Session secret must be a string (session.secret)');
|
|
200
200
|
}
|
|
201
201
|
return secret;
|
|
202
202
|
}
|
|
@@ -211,10 +211,10 @@ exports.validateSessionSecret = validateSessionSecret;
|
|
|
211
211
|
*/
|
|
212
212
|
function validateSessionTtl(ttl) {
|
|
213
213
|
if (typeof ttl === 'undefined') {
|
|
214
|
-
throw ReferenceError('Session ttl is missing (
|
|
214
|
+
throw ReferenceError('Session ttl is missing (session.ttl)');
|
|
215
215
|
}
|
|
216
216
|
else if (typeof ttl !== 'number') {
|
|
217
|
-
throw new TypeError('Session ttl must be an integer (
|
|
217
|
+
throw new TypeError('Session ttl must be an integer (session.ttl)');
|
|
218
218
|
}
|
|
219
219
|
return ttl;
|
|
220
220
|
}
|
|
@@ -229,10 +229,10 @@ exports.validateSessionTtl = validateSessionTtl;
|
|
|
229
229
|
*/
|
|
230
230
|
function validateSessionName(name) {
|
|
231
231
|
if (typeof name === 'undefined') {
|
|
232
|
-
throw ReferenceError('Session name is missing (
|
|
232
|
+
throw ReferenceError('Session name is missing (session.name)');
|
|
233
233
|
}
|
|
234
234
|
else if (typeof name !== 'string') {
|
|
235
|
-
throw new TypeError('Session name must be a string (
|
|
235
|
+
throw new TypeError('Session name must be a string (session.name)');
|
|
236
236
|
}
|
|
237
237
|
return name;
|
|
238
238
|
}
|
|
@@ -247,10 +247,10 @@ exports.validateSessionName = validateSessionName;
|
|
|
247
247
|
*/
|
|
248
248
|
function validateSessionSecure(secure) {
|
|
249
249
|
if (typeof secure === 'undefined') {
|
|
250
|
-
throw ReferenceError('Session secure flag is missing (
|
|
250
|
+
throw ReferenceError('Session secure flag is missing (session.secure)');
|
|
251
251
|
}
|
|
252
252
|
else if (typeof secure !== 'boolean') {
|
|
253
|
-
throw new TypeError('Session secure flag must be boolean (
|
|
253
|
+
throw new TypeError('Session secure flag must be boolean (session.secure)');
|
|
254
254
|
}
|
|
255
255
|
return secure;
|
|
256
256
|
}
|
|
@@ -302,11 +302,11 @@ function validateSessionCookieSameSite(cookieSameSite, defaultFlag) {
|
|
|
302
302
|
throw new TypeError('validateSessionCookieSameSite() requires an explicit default flag');
|
|
303
303
|
}
|
|
304
304
|
else if (!validValues.includes(defaultFlag)) {
|
|
305
|
-
throw new TypeError('validateSessionCookieSameSite() default flag must be set to one of true, false, Strict, Lax or None (
|
|
305
|
+
throw new TypeError('validateSessionCookieSameSite() default flag must be set to one of true, false, Strict, Lax or None (session.cookieSameSite)');
|
|
306
306
|
}
|
|
307
307
|
const value = cookieSameSite !== undefined ? cookieSameSite : defaultFlag;
|
|
308
308
|
if (!validValues.includes(value)) {
|
|
309
|
-
throw new TypeError('SameSite flag must be set to one of true, false, Strict, Lax or None (
|
|
309
|
+
throw new TypeError('SameSite flag must be set to one of true, false, Strict, Lax or None (session.cookieSameSite)');
|
|
310
310
|
}
|
|
311
311
|
return value;
|
|
312
312
|
}
|
|
@@ -435,7 +435,7 @@ function ingest(config = {}) {
|
|
|
435
435
|
// Public URL from which the app will be served
|
|
436
436
|
mountUrl: validateMountUrl(config.mountUrl),
|
|
437
437
|
// Session
|
|
438
|
-
|
|
438
|
+
session: validateSessionObject(config.session, (session) => ({
|
|
439
439
|
name: validateSessionName(session.name),
|
|
440
440
|
secret: validateSessionSecret(session.secret),
|
|
441
441
|
secure: validateSessionSecure(session.secure),
|
|
@@ -462,29 +462,3 @@ function ingest(config = {}) {
|
|
|
462
462
|
return parsed;
|
|
463
463
|
}
|
|
464
464
|
exports.default = ingest;
|
|
465
|
-
// module.exports = {
|
|
466
|
-
// ingest,
|
|
467
|
-
// validateAllowPageEdit,
|
|
468
|
-
// validateUseStickyEdit,
|
|
469
|
-
// validateContentSecurityPolicies,
|
|
470
|
-
// validateHeadersObject,
|
|
471
|
-
// validateHeadersDisabled,
|
|
472
|
-
// validateI18nObject,
|
|
473
|
-
// validateI18nDirs,
|
|
474
|
-
// validateI18nLocales,
|
|
475
|
-
// validateMountController,
|
|
476
|
-
// validateMountUrl,
|
|
477
|
-
// validatePhase,
|
|
478
|
-
// validateServiceName,
|
|
479
|
-
// validateSessionExpiryController,
|
|
480
|
-
// validateSessionObject,
|
|
481
|
-
// validateSessionCookiePath,
|
|
482
|
-
// validateSessionCookieSameSite,
|
|
483
|
-
// validateSessionName,
|
|
484
|
-
// validateSessionSecret,
|
|
485
|
-
// validateSessionSecure,
|
|
486
|
-
// validateSessionStore,
|
|
487
|
-
// validateSessionTtl,
|
|
488
|
-
// validateViewsObject,
|
|
489
|
-
// validateViews,
|
|
490
|
-
// };
|
package/dist/lib/configure.js
CHANGED
|
@@ -61,12 +61,11 @@ function configure(config = {}) {
|
|
|
61
61
|
plugin.configure(config);
|
|
62
62
|
});
|
|
63
63
|
// Extract config
|
|
64
|
-
// TODO: Validate/sanitise and deep-freeze object
|
|
65
64
|
const { mountUrl = '/', views = [], session = {
|
|
66
65
|
secret: 'secret',
|
|
67
66
|
name: 'casasession',
|
|
68
67
|
secure: false,
|
|
69
|
-
ttl:
|
|
68
|
+
ttl: 3600,
|
|
70
69
|
cookieSameSite: true,
|
|
71
70
|
cookiePath: '/',
|
|
72
71
|
store: undefined,
|
package/dist/lib/dirname.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = __dirname;
|
|
1
|
+
module.exports = __dirname;
|
package/dist/lib/end-session.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const logger_js_1 = __importDefault(require("./logger.js"));
|
|
7
|
-
const log = (0, logger_js_1.default)('
|
|
7
|
+
const log = (0, logger_js_1.default)('lib:end-session');
|
|
8
8
|
/**
|
|
9
9
|
* A convenience for ending the current session, but retaining some data in it,
|
|
10
10
|
* like the current language. It persists an empty session before regenerating
|
|
@@ -21,6 +21,9 @@ function endSession(req, next) {
|
|
|
21
21
|
const { language } = req.session;
|
|
22
22
|
Object.entries(req.session).forEach(([k]) => {
|
|
23
23
|
if (!['cookie'].includes(k)) {
|
|
24
|
+
// ESLint disabled as `Object.entries()` returns "own" properties, and
|
|
25
|
+
// all values are being null'd, so not assigned any user-controlled values
|
|
26
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
24
27
|
req.session[k] = null;
|
|
25
28
|
}
|
|
26
29
|
});
|
package/dist/lib/field.js
CHANGED
|
@@ -181,7 +181,10 @@ class PageField {
|
|
|
181
181
|
}
|
|
182
182
|
let errors = [];
|
|
183
183
|
for (let i = 0, l = __classPrivateFieldGet(this, _PageField_validators, "f").length; i < l; i++) {
|
|
184
|
+
// ESLint disabled as `i` is an integer
|
|
185
|
+
/* eslint-disable security/detect-object-injection */
|
|
184
186
|
const fieldErrors = __classPrivateFieldGet(this, _PageField_validators, "f")[i].validate(value, context).map((e) => e.withContext(Object.assign(Object.assign({}, context), { validator: __classPrivateFieldGet(this, _PageField_validators, "f")[i].name })));
|
|
187
|
+
/* eslint-enable security/detect-object-injection */
|
|
185
188
|
errors = [
|
|
186
189
|
...errors,
|
|
187
190
|
...(fieldErrors !== null && fieldErrors !== void 0 ? fieldErrors : []),
|
|
@@ -199,12 +202,17 @@ class PageField {
|
|
|
199
202
|
let processedValue = value;
|
|
200
203
|
// Some of the validators may have their own "sanitise()" methods. These
|
|
201
204
|
// should be run before any other processors
|
|
205
|
+
// ESLint disabled as `i` is an integer
|
|
206
|
+
/* eslint-disable security/detect-object-injection */
|
|
202
207
|
for (let i = 0, l = __classPrivateFieldGet(this, _PageField_validators, "f").length; i < l; i++) {
|
|
203
208
|
if (isFunction(__classPrivateFieldGet(this, _PageField_validators, "f")[i].sanitise)) {
|
|
204
209
|
processedValue = __classPrivateFieldGet(this, _PageField_validators, "f")[i].sanitise(processedValue);
|
|
205
210
|
}
|
|
206
211
|
}
|
|
212
|
+
/* eslint-enable security/detect-object-injection */
|
|
207
213
|
for (let i = 0, l = __classPrivateFieldGet(this, _PageField_processors, "f").length; i < l; i++) {
|
|
214
|
+
// ESLint disabled as `i` is an integer
|
|
215
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
208
216
|
processedValue = __classPrivateFieldGet(this, _PageField_processors, "f")[i](processedValue);
|
|
209
217
|
}
|
|
210
218
|
return processedValue;
|
|
@@ -228,6 +236,8 @@ class PageField {
|
|
|
228
236
|
};
|
|
229
237
|
let result = true;
|
|
230
238
|
for (let i = 0, l = __classPrivateFieldGet(this, _PageField_conditions, "f").length; i < l; i++) {
|
|
239
|
+
// ESLint disabled as `i` is an integer
|
|
240
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
231
241
|
result = result && __classPrivateFieldGet(this, _PageField_conditions, "f")[i](context);
|
|
232
242
|
}
|
|
233
243
|
return result;
|
package/dist/lib/logger.d.ts
CHANGED
package/dist/lib/logger.js
CHANGED
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const debug_1 = __importDefault(require("debug"));
|
|
7
7
|
const casaDebugger = (0, debug_1.default)('casa');
|
|
8
|
-
exports.default = (
|
|
9
|
-
const logger = casaDebugger.extend(
|
|
8
|
+
exports.default = (namespace) => {
|
|
9
|
+
const logger = casaDebugger.extend(namespace);
|
|
10
10
|
return {
|
|
11
11
|
trace: logger.extend('trace'),
|
|
12
12
|
debug: logger.extend('debug'),
|
|
@@ -14,6 +14,8 @@ const { all: deepmergeAll } = deepmerge_1.default;
|
|
|
14
14
|
const combineMerge = (target, source, options) => {
|
|
15
15
|
const destination = target.slice();
|
|
16
16
|
source.forEach((item, index) => {
|
|
17
|
+
// ESLint disabled as `index` is only an integer
|
|
18
|
+
/* eslint-disable security/detect-object-injection */
|
|
17
19
|
if (typeof destination[index] === 'undefined') {
|
|
18
20
|
destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
|
|
19
21
|
}
|
|
@@ -23,6 +25,7 @@ const combineMerge = (target, source, options) => {
|
|
|
23
25
|
else if (target.indexOf(item) === -1) {
|
|
24
26
|
destination.push(item);
|
|
25
27
|
}
|
|
28
|
+
/* eslint-enable security/detect-object-injection */
|
|
26
29
|
});
|
|
27
30
|
return destination;
|
|
28
31
|
};
|
|
@@ -75,6 +78,10 @@ function renderAsAttributes(attrsObject) {
|
|
|
75
78
|
const attrsList = [];
|
|
76
79
|
if (typeof attrsObject === 'object') {
|
|
77
80
|
Object.keys(attrsObject).forEach((key) => {
|
|
81
|
+
// ESLint disable as `attrsObject` is dev-controlled, `Object.keys()` has
|
|
82
|
+
// been used (to get "own" properties) and `m` is one of the characters
|
|
83
|
+
// found by the regex.
|
|
84
|
+
/* eslint-disable security/detect-object-injection */
|
|
78
85
|
const value = String(attrsObject[key]).replace(/[<>"'&]/g, (m) => ({
|
|
79
86
|
'<': '<',
|
|
80
87
|
'>': '>',
|
|
@@ -82,6 +89,7 @@ function renderAsAttributes(attrsObject) {
|
|
|
82
89
|
'\'': ''',
|
|
83
90
|
'&': '&',
|
|
84
91
|
}[m]));
|
|
92
|
+
/* eslint-enable security/detect-object-injection */
|
|
85
93
|
attrsList.push(`${key}="${value}"`);
|
|
86
94
|
});
|
|
87
95
|
}
|
package/dist/lib/utils.js
CHANGED
|
@@ -46,6 +46,8 @@ function isEmpty(val) {
|
|
|
46
46
|
return true;
|
|
47
47
|
}
|
|
48
48
|
if (Array.isArray(val) || typeof val === 'object') {
|
|
49
|
+
// ESLint disabled as `k` is an "own property" (thanks to `Object.keys()`)
|
|
50
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
49
51
|
return Object.keys(val).filter((k) => !isEmpty(val[k])).length === 0;
|
|
50
52
|
}
|
|
51
53
|
return false;
|
|
@@ -31,7 +31,7 @@ class InArray extends ValidatorFactory_js_1.default {
|
|
|
31
31
|
if (value !== null && typeof value !== 'undefined') {
|
|
32
32
|
const search = Array.isArray(value) ? value : [value];
|
|
33
33
|
for (let i = 0, l = search.length; i < l; i += 1) {
|
|
34
|
-
if (source.indexOf(search[i]) > -1) {
|
|
34
|
+
if (source.indexOf(search[parseInt(i, 10)]) > -1) {
|
|
35
35
|
valid = true;
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
@@ -23,25 +23,3 @@ exports.default = {
|
|
|
23
23
|
strlen: strlen_js_1.default,
|
|
24
24
|
wordCount: wordCount_js_1.default,
|
|
25
25
|
};
|
|
26
|
-
// const dateObject = require('./dateObject.js');
|
|
27
|
-
// const email = require('./email.js');
|
|
28
|
-
// const inArray = require('./inArray.js');
|
|
29
|
-
// const nino = require('./nino.js');
|
|
30
|
-
// const optional = require('./optional.js');
|
|
31
|
-
// const postalAddressObject = require('./postalAddressObject.js');
|
|
32
|
-
// const regex = require('./regex.js');
|
|
33
|
-
// const required = require('./required.js');
|
|
34
|
-
// const strlen = require('./strlen.js');
|
|
35
|
-
// const wordCount = require('./wordCount.js');
|
|
36
|
-
// module.exports = {
|
|
37
|
-
// dateObject,
|
|
38
|
-
// email,
|
|
39
|
-
// inArray,
|
|
40
|
-
// nino,
|
|
41
|
-
// optional,
|
|
42
|
-
// postalAddressObject,
|
|
43
|
-
// regex,
|
|
44
|
-
// required,
|
|
45
|
-
// strlen,
|
|
46
|
-
// wordCount,
|
|
47
|
-
// };
|
|
@@ -66,6 +66,8 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
66
66
|
const reqF = Object.create(null);
|
|
67
67
|
const reqC = cfg.requiredFields;
|
|
68
68
|
['address1', 'address2', 'address3', 'address4', 'postcode'].forEach((k) => {
|
|
69
|
+
// ESLint disabled as `k` is a known value from a constant list
|
|
70
|
+
/* eslint-disable-next-line security/detect-object-injection */
|
|
69
71
|
reqF[k] = reqC.indexOf(k) > -1;
|
|
70
72
|
});
|
|
71
73
|
let valid = true;
|
|
@@ -75,8 +77,7 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
75
77
|
const reAddrLine1 = /^\d+|[^\s]+[a-z0-9\-,.&#()/\\:;'" ]+$/i;
|
|
76
78
|
// UK Postcode regex taken from the dwp java pc checker
|
|
77
79
|
// https://github.com/dwp/postcode-format-validation
|
|
78
|
-
const
|
|
79
|
-
const rePostcode = new RegExp(pc, 'i');
|
|
80
|
+
const rePostcode = new RegExp(/^(?![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, 'i');
|
|
80
81
|
// [required, regex, strlenmax, error message]
|
|
81
82
|
const attributes = {
|
|
82
83
|
address1: [reqF.address1, reAddrLine1, cfg.strlenmax, cfg.errorMsgAddress1],
|
|
@@ -85,6 +86,8 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
85
86
|
address4: [reqF.address4, reAddr, cfg.strlenmax, cfg.errorMsgAddress4],
|
|
86
87
|
postcode: [reqF.postcode, rePostcode, null, cfg.errorMsgPostcode],
|
|
87
88
|
};
|
|
89
|
+
// ESLint disabled as `k` is a known value from the constant list above
|
|
90
|
+
/* eslint-disable security/detect-object-injection */
|
|
88
91
|
Object.keys(attributes).forEach((k) => {
|
|
89
92
|
const attr = attributes[k];
|
|
90
93
|
const hasProperty = Object.prototype.hasOwnProperty.call(value, k);
|
|
@@ -100,6 +103,7 @@ class PostalAddressObject extends ValidatorFactory_js_1.default {
|
|
|
100
103
|
}));
|
|
101
104
|
}
|
|
102
105
|
});
|
|
106
|
+
/* eslint-enable security/detect-object-injection */
|
|
103
107
|
}
|
|
104
108
|
else {
|
|
105
109
|
valid = false;
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyBody = void 0;
|
|
3
4
|
const express_1 = require("express");
|
|
5
|
+
const rProto = /__proto__/i;
|
|
6
|
+
const rPrototype = /prototype[='"[\]]/i;
|
|
7
|
+
const rConstructor = /constructor[='"[\]]/i;
|
|
8
|
+
function verifyBody(req, res, buf, encoding) {
|
|
9
|
+
const body = decodeURI(buf.toString(encoding));
|
|
10
|
+
if (rProto.test(body)) {
|
|
11
|
+
throw new Error('Request body verification failed (__proto__)');
|
|
12
|
+
}
|
|
13
|
+
if (rPrototype.test(body)) {
|
|
14
|
+
throw new Error('Request body verification failed (prototype)');
|
|
15
|
+
}
|
|
16
|
+
if (rConstructor.test(body)) {
|
|
17
|
+
throw new Error('Request body verification failed (constructor)');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.verifyBody = verifyBody;
|
|
4
21
|
function bodyParserMiddleware() {
|
|
5
|
-
const rProto = /__proto__/i;
|
|
6
|
-
const rPrototype = /prototype[='"[\]]/i;
|
|
7
|
-
const rConstructor = /constructor[='"[\]]/i;
|
|
8
22
|
return [
|
|
9
23
|
(0, express_1.urlencoded)({
|
|
10
24
|
extended: true,
|
|
@@ -12,12 +26,7 @@ function bodyParserMiddleware() {
|
|
|
12
26
|
inflate: true,
|
|
13
27
|
parameterLimit: 25,
|
|
14
28
|
limit: 1024 * 50,
|
|
15
|
-
verify:
|
|
16
|
-
const body = decodeURI(buf.toString(encoding));
|
|
17
|
-
if (rProto.test(body) || rPrototype.test(body) || rConstructor.test(body)) {
|
|
18
|
-
throw new Error('Request body verification failed');
|
|
19
|
-
}
|
|
20
|
-
},
|
|
29
|
+
verify: verifyBody,
|
|
21
30
|
}),
|
|
22
31
|
];
|
|
23
32
|
}
|