@dwp/govuk-casa 8.0.0-beta2 → 8.0.3

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 (41) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +1 -1
  3. package/dist/assets/css/casa-ie8.css +1 -1
  4. package/dist/assets/css/casa.css +1 -1
  5. package/dist/lib/CasaTemplateLoader.d.ts +1 -3
  6. package/dist/lib/CasaTemplateLoader.js +22 -3
  7. package/dist/lib/JourneyContext.d.ts +1 -0
  8. package/dist/lib/JourneyContext.js +37 -11
  9. package/dist/lib/MutableRouter.js +3 -1
  10. package/dist/lib/Plan.js +12 -5
  11. package/dist/lib/ValidationError.js +4 -0
  12. package/dist/lib/configuration-ingestor.js +11 -37
  13. package/dist/lib/configure.js +1 -2
  14. package/dist/lib/dirname.cjs +1 -1
  15. package/dist/lib/end-session.js +4 -1
  16. package/dist/lib/field.d.ts +2 -2
  17. package/dist/lib/field.js +19 -2
  18. package/dist/lib/logger.d.ts +1 -1
  19. package/dist/lib/logger.js +2 -2
  20. package/dist/lib/nunjucks-filters.js +8 -0
  21. package/dist/lib/utils.js +2 -0
  22. package/dist/lib/validators/inArray.js +1 -1
  23. package/dist/lib/validators/index.js +0 -22
  24. package/dist/lib/validators/postalAddressObject.js +6 -2
  25. package/dist/middleware/body-parser.d.ts +1 -0
  26. package/dist/middleware/body-parser.js +18 -9
  27. package/dist/middleware/data.js +8 -4
  28. package/dist/middleware/dirname.cjs +1 -1
  29. package/dist/middleware/gather-fields.js +3 -5
  30. package/dist/middleware/i18n.js +5 -1
  31. package/dist/middleware/post.js +1 -1
  32. package/dist/middleware/pre.js +10 -2
  33. package/dist/middleware/progress-journey.js +1 -1
  34. package/dist/middleware/sanitise-fields.js +5 -5
  35. package/dist/middleware/session.js +60 -53
  36. package/dist/middleware/skip-waypoint.js +2 -2
  37. package/dist/middleware/validate-fields.js +7 -6
  38. package/dist/routes/ancillary.js +0 -2
  39. package/dist/routes/dirname.cjs +1 -1
  40. package/dist/routes/journey.js +5 -6
  41. package/package.json +37 -30
@@ -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: (req, res, buf, encoding) => {
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
  }
@@ -37,15 +37,19 @@ function dataMiddleware({ plan, mountUrl, events, }) {
37
37
  // CASA and userland templates
38
38
  res.locals.casa = {
39
39
  mountUrl,
40
+ editMode: req.casa.editMode,
41
+ editOrigin: req.casa.editOrigin,
40
42
  };
41
43
  res.locals.locale = req.language;
42
44
  // Used by govuk-frontend template
43
45
  // - req.language is provided by i18n-http-middleware
44
46
  res.locals.htmlLang = req.language;
45
- // Function for building URLs. This will be curried with the `mountUrl`
46
- // and `journeyContext` for convenience
47
- res.locals.waypointUrl = (args) => (0, waypoint_url_js_1.default)(Object.assign({ mountUrl, journeyContext: req.casa.journeyContext }, args));
48
- // req.editSearchParams
47
+ // Function for building URLs. This will be curried with the `mountUrl`,
48
+ // `journeyContext`, `edit` and `editOrigin` for convenience. This means
49
+ // the template author does not have to be concerned about the current
50
+ // "state" when generating URLs, but still has the ability to override
51
+ // these curried defaults if needs be.
52
+ res.locals.waypointUrl = (args) => (0, waypoint_url_js_1.default)(Object.assign({ mountUrl, journeyContext: req.casa.journeyContext, edit: req.casa.editMode, editOrigin: req.casa.editOrigin }, args));
49
53
  next();
50
54
  },
51
55
  ];
@@ -1 +1 @@
1
- module.exports = __dirname;
1
+ module.exports = __dirname;
@@ -28,21 +28,19 @@ exports.default = ({ waypoint, fields = [], }) => [
28
28
  // for any comparison work that may be done in subseqent middleware.
29
29
  req.casa.archivedJourneyContext = JourneyContext_js_1.default.fromContext(req.casa.journeyContext);
30
30
  // Ignore data for any non-persistent fields
31
+ // ESLint disabled as `fields`, `i` and `name` are dev-controlled
32
+ /* eslint-disable security/detect-object-injection */
31
33
  const persistentBody = Object.create(null);
32
34
  for (let i = 0, l = fields.length; i < l; i++) {
33
35
  if (fields[i].meta.persist && fields[i].getValue(req.body) !== undefined) {
34
36
  persistentBody[fields[i].name] = fields[i].getValue(req.body);
35
37
  }
36
38
  }
39
+ /* eslint-enable security/detect-object-injection */
37
40
  // Update data and validation context in the current request, and store
38
41
  req.casa.journeyContext.setDataForPage(waypoint, persistentBody);
39
42
  req.casa.journeyContext.removeValidationStateForPage(waypoint);
40
43
  JourneyContext_js_1.default.putContext(req.session, req.casa.journeyContext);
41
- // TODO: Once feature we might like here is to invalidate specific pages
42
- // based on the change that has just happened, to force those pages to be
43
- // visited again. The main use case is to cater for a change in content on
44
- // those pages that fundamentally alter the context of the question being asked,
45
- // and so should be asked again. For example, "Your address" vs "You and your partner's address"
46
44
  next();
47
45
  },
48
46
  ];
@@ -10,7 +10,7 @@ const fs_1 = require("fs");
10
10
  const deepmerge_1 = __importDefault(require("deepmerge"));
11
11
  const js_yaml_1 = __importDefault(require("js-yaml"));
12
12
  const logger_js_1 = __importDefault(require("../lib/logger.js"));
13
- const log = (0, logger_js_1.default)('middleware.i18n');
13
+ const log = (0, logger_js_1.default)('middleware:i18n');
14
14
  const loadJson = (file) => {
15
15
  // Strip out newlines (this is a legacy feature which we're keeping for
16
16
  // backwards compatibility).
@@ -29,6 +29,9 @@ const extract = (file) => {
29
29
  const loadResources = (languages, directories) => {
30
30
  const store = Object.create(null);
31
31
  languages.forEach((language) => {
32
+ // ESLint disabled as `store`, `language` and `ns` are all dev-controlled,
33
+ // and this function is only called once, at boot-time.
34
+ /* eslint-disable security/detect-object-injection */
32
35
  store[language] = Object.create(null);
33
36
  directories.forEach((basedir) => {
34
37
  const dir = (0, path_1.resolve)(basedir, language);
@@ -44,6 +47,7 @@ const loadResources = (languages, directories) => {
44
47
  store[language][ns] = (0, deepmerge_1.default)(store[language][ns], data);
45
48
  });
46
49
  });
50
+ /* eslint-enable security/detect-object-injection */
47
51
  });
48
52
  return store;
49
53
  };
@@ -9,7 +9,7 @@ const log = (0, logger_js_1.default)('middleware:post');
9
9
  function postMiddleware({ mountUrl, }) {
10
10
  return [
11
11
  (req, res) => {
12
- res.render('casa/errors/404.njk');
12
+ res.status(404).render('casa/errors/404.njk');
13
13
  },
14
14
  /* eslint-disable-next-line no-unused-vars */
15
15
  (err, req, res, next) => {
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const crypto_1 = require("crypto");
7
7
  const helmet_1 = __importDefault(require("helmet"));
8
+ const GA_DOMAIN = 'www.google-analytics.com';
9
+ const GTM_DOMAIN = 'www.googletagmanager.com';
8
10
  exports.default = () => [
9
11
  // Only allow certain request methods
10
12
  (req, res, next) => {
@@ -40,9 +42,15 @@ exports.default = () => [
40
42
  contentSecurityPolicy: {
41
43
  useDefaults: true,
42
44
  directives: {
43
- 'script-src': ["'self'", 'www.google-analytics.com', 'www.googletagmanager.com', (req, res) => `'nonce-${res.locals.cspNonce}'`],
44
- 'style-src': ["'self'", 'https:', (req, res) => `'nonce-${res.locals.cspNonce}'`],
45
+ 'default-src': ["'none'"],
46
+ 'script-src': ["'self'", GA_DOMAIN, GTM_DOMAIN, (req, res) => `'nonce-${res.locals.cspNonce}'`],
47
+ 'img-src': ["'self'", GA_DOMAIN],
48
+ 'connect-src': ["'self'", GA_DOMAIN],
49
+ 'frame-src': ["'self'", GTM_DOMAIN],
50
+ 'frame-ancestors': ["'self'"],
45
51
  'form-action': ["'self'"],
52
+ 'style-src': ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
53
+ 'font-src': ["'self'"],
46
54
  },
47
55
  },
48
56
  // // Require referrer to aid navigation
@@ -26,7 +26,7 @@ exports.default = ({ waypoint, plan, mountUrl, }) => [
26
26
  const traversed = plan.traverse(req.casa.journeyContext);
27
27
  const currentIndex = traversed.indexOf(waypoint);
28
28
  const nextIndex = Math.max(currentIndex < 0 ? traversed.length - 1 : 0, Math.min(currentIndex + 1, traversed.length - 1));
29
- const nextWaypoint = traversed[nextIndex];
29
+ const nextWaypoint = traversed[parseInt(nextIndex, 10)];
30
30
  log.trace(`currentIndex = ${currentIndex}, nextIndex = ${nextIndex}, currentWaypoint = ${waypoint}, nextWaypoint = ${nextWaypoint}`);
31
31
  // Edit mode
32
32
  // Attempt to take the user back to their original URL. We rely on the
@@ -20,23 +20,23 @@ exports.default = ({ waypoint, fields = [], }) => {
20
20
  (req, res, next) => {
21
21
  // First, prune all undefined, or unknown fields from `req.body` (i.e.
22
22
  // those that do not have an entry in `fields`)
23
+ // EsLint disabled as `fields`, `i` & `name` are only controlled by dev
24
+ /* eslint-disable security/detect-object-injection */
23
25
  const prunedBody = Object.create(null);
24
26
  for (let i = 0, l = fields.length; i < l; i++) {
25
27
  if (lodash_1.default.has(req.body, fields[i].name) && req.body[fields[i].name] !== undefined) {
26
28
  prunedBody[fields[i].name] = req.body[fields[i].name];
27
29
  }
28
30
  }
29
- // TODO: the journey context passed to the processors and conditions, with
30
- // data set to the "prunedBody"
31
+ /* eslint-enable security/detect-object-injection */
31
32
  const journeyContext = JourneyContext_js_1.default.fromContext(req.casa.journeyContext);
32
33
  journeyContext.setDataForPage(waypoint, prunedBody);
33
- // const journeyContext = {};
34
34
  // Second, prune any fields that do not pass the validation conditional,
35
35
  // and process those that do.
36
36
  const sanitisedBody = Object.create(null);
37
37
  for (let i = 0, l = fields.length; i < l; i++) {
38
- const field = fields[i];
39
- const fieldValue = field.getValue(prunedBody); // prunedBody[field.name];
38
+ const field = fields[i]; /* eslint-disable-line security/detect-object-injection */
39
+ const fieldValue = field.getValue(prunedBody);
40
40
  if (fieldValue !== undefined && field.testConditions({
41
41
  fieldValue,
42
42
  waypoint,
@@ -28,6 +28,57 @@ Object.defineProperty(exports, "__esModule", { value: true });
28
28
  const express_session_1 = __importStar(require("express-session"));
29
29
  const logger_js_1 = __importDefault(require("../lib/logger.js"));
30
30
  const log = (0, logger_js_1.default)('middleware:session');
31
+ const sessionExpiryMiddleware = (mountUrl, ttl, getCookie, touchCookie, removeCookie) => (req, res, next) => {
32
+ var _a;
33
+ const lastModified = getCookie(req);
34
+ const age = Math.floor(Date.now() * 0.001) - lastModified;
35
+ if (lastModified === 0) {
36
+ // New session, or grace period cookie no longer available after
37
+ // expiring; generate a new session, and create grace-period cookie.
38
+ // This will invalidate any CSRF tokens, so by letting the request POST
39
+ // requests through the user may see a 500 error response.
40
+ log.info('Session is new, or grace period has expired. Regenerating session.');
41
+ req.session.regenerate((err) => {
42
+ if (err) {
43
+ next(err);
44
+ }
45
+ else {
46
+ touchCookie(res);
47
+ if (req.method === 'POST') {
48
+ log.info('The CSRF token for this POST request will now be invalid for this regenerated session. Redirecting to app mount point.');
49
+ res.redirect(302, mountUrl);
50
+ }
51
+ else {
52
+ next();
53
+ }
54
+ }
55
+ });
56
+ }
57
+ else if (age > ttl) {
58
+ // Cookie has become stale and server session will have been removed;
59
+ // redirect to session-timeout
60
+ log.info('Session has timed out within grace period. Destroying session and redirecting to timeout page.');
61
+ const language = (_a = req.session.language) !== null && _a !== void 0 ? _a : 'en';
62
+ req.session.destroy((err) => {
63
+ if (err) {
64
+ next(err);
65
+ }
66
+ else {
67
+ removeCookie(res);
68
+ const params = new URLSearchParams({
69
+ referrer: req.originalUrl,
70
+ lang: language,
71
+ });
72
+ res.redirect(302, `${mountUrl}session-timeout?${params.toString()}`);
73
+ }
74
+ });
75
+ }
76
+ else {
77
+ // Touch cookie and continue
78
+ touchCookie(res);
79
+ next();
80
+ }
81
+ };
31
82
  // 3 middleware:
32
83
  // - set the session cookie
33
84
  // - parse request cookies
@@ -44,8 +95,15 @@ function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl,
44
95
  const ttlGrace = 1800; // user will see session-timeout if session expires within 30mins
45
96
  const touchCookieName = `${name}.t`;
46
97
  const touchCookieOptions = Object.assign(Object.assign({}, commonCookieOptions), { maxAge: (ttl + ttlGrace) * 1000, signed: true });
98
+ const getCookie = (req) => {
99
+ var _a;
100
+ // Disabled eslint as `touchCookieName` is a constant, known value
101
+ /* eslint-disable-next-line security/detect-object-injection */
102
+ const lastModified = Date.parse(String((_a = req.signedCookies[touchCookieName]) !== null && _a !== void 0 ? _a : '1970-01-01T00:00:00+0000'));
103
+ return Number.isNaN(lastModified) ? 0 : Math.floor(lastModified * 0.001);
104
+ };
47
105
  const touchCookie = (res) => {
48
- // Touch cookie expiry is 3a short period after the session ttl. This gives
106
+ // Touch cookie expiry is a short period after the session ttl. This gives
49
107
  // a small period of time where a user will see the session-timeout message,
50
108
  // which is important to avoid the confusion of simply being redirected back
51
109
  // to the start of their journey.
@@ -64,58 +122,7 @@ function sessionMiddleware({ cookieParserMiddleware, secret, name, secure, ttl,
64
122
  store,
65
123
  }),
66
124
  cookieParserMiddleware,
67
- (req, res, next) => {
68
- var _a, _b;
69
- let lastModified = Date.parse(String((_a = req.signedCookies[touchCookieName]) !== null && _a !== void 0 ? _a : '1970-01-01T00:00:00+0000'));
70
- lastModified = Number.isNaN(lastModified) ? 0 : Math.floor(lastModified * 0.001);
71
- const age = Math.floor(Date.now() * 0.001) - lastModified;
72
- if (lastModified === 0) {
73
- // New session, or grace period cookie no longer available after
74
- // expiring; generate a new session, and create grace-period cookie.
75
- // This will invalidate any CSRF tokens, so by letting the request POST
76
- // requests through the user may see a 500 error response.
77
- log.info('Session is new, or grace period has expired. Regenerating session.');
78
- req.session.regenerate((err) => {
79
- if (err) {
80
- next(err);
81
- }
82
- else {
83
- touchCookie(res);
84
- if (req.method === 'POST') {
85
- log.info('The CSRF token for this POST request will now be invalid for this regenerated session. Redirecting to app mount point.');
86
- res.redirect(302, mountUrl);
87
- }
88
- else {
89
- next();
90
- }
91
- }
92
- });
93
- }
94
- else if (age > ttl) {
95
- // Cookie has become stale and server session will have been removed;
96
- // redirect to session-timeout
97
- log.info('Session has timed out within grace period. Destroying session and redirecting to timeout page.');
98
- const language = (_b = req.session.language) !== null && _b !== void 0 ? _b : 'en';
99
- req.session.destroy((err) => {
100
- if (err) {
101
- next(err);
102
- }
103
- else {
104
- removeCookie(res);
105
- const params = new URLSearchParams({
106
- referrer: req.originalUrl,
107
- lang: language,
108
- });
109
- res.redirect(302, `${mountUrl}session-timeout?${params.toString()}`);
110
- }
111
- });
112
- }
113
- else {
114
- // Touch cookie and continue
115
- touchCookie(res);
116
- next();
117
- }
118
- },
125
+ sessionExpiryMiddleware(mountUrl, ttl, getCookie, touchCookie, removeCookie),
119
126
  ];
120
127
  }
121
128
  exports.default = sessionMiddleware;
@@ -26,8 +26,8 @@ exports.default = ({ waypoint, mountUrl, }) => [
26
26
  const redirectUrl = (0, waypoint_url_js_1.default)({
27
27
  mountUrl,
28
28
  waypoint: skipTo,
29
- edit: req.editMode,
30
- editOrigin: req.editOriginUrl,
29
+ edit: req.casa.editMode,
30
+ editOrigin: req.casa.editOrigin,
31
31
  journeyContext: req.casa.journeyContext,
32
32
  });
33
33
  log.debug(`Will redirect to "${redirectUrl}" after skipping "${waypoint}"`);
@@ -21,6 +21,8 @@ exports.default = ({ waypoint, fields = [], mountUrl, plan, }) => [
21
21
  var _a, _b;
22
22
  let errors = [];
23
23
  for (let i = 0, l = fields.length; i < l; i++) {
24
+ /* eslint-disable security/detect-object-injection */
25
+ // Dynamic object keys are only used on known entities (fields, waypoint)
24
26
  const field = fields[i];
25
27
  const fieldName = field.name;
26
28
  const fieldValue = (_b = (_a = req.casa.journeyContext.data) === null || _a === void 0 ? void 0 : _a[waypoint]) === null || _b === void 0 ? void 0 : _b[fieldName];
@@ -30,12 +32,11 @@ exports.default = ({ waypoint, fields = [], mountUrl, plan, }) => [
30
32
  waypoint,
31
33
  journeyContext: req.casa.journeyContext,
32
34
  };
33
- if (field.testConditions(context)) {
34
- errors = [
35
- ...errors,
36
- ...field.runValidators(fieldValue, context),
37
- ];
38
- }
35
+ /* eslint-enable security/detect-object-injection */
36
+ errors = [
37
+ ...errors,
38
+ ...field.runValidators(fieldValue, context),
39
+ ];
39
40
  }
40
41
  // Validation passed with no errors
41
42
  if (!errors.length) {
@@ -15,8 +15,6 @@ function ancillaryRouter({ sessionTtl, }) {
15
15
  // Router
16
16
  const router = new MutableRouter_js_1.default();
17
17
  // Session timeout
18
- // TODO: add a `ancillary.presessiontimeout` hook here? Might be useful for
19
- // those who way to enhance the timeout route, rather than replacing it completely
20
18
  router.all('/session-timeout', (req, res) => {
21
19
  res.render('casa/session-timeout.njk', {
22
20
  sessionTtl: Math.floor(sessionTtl / 60),
@@ -1 +1 @@
1
- module.exports = __dirname;
1
+ module.exports = __dirname;
@@ -14,16 +14,13 @@ const progress_journey_js_1 = __importDefault(require("../middleware/progress-jo
14
14
  const waypoint_url_js_1 = __importDefault(require("../lib/waypoint-url.js"));
15
15
  const logger_js_1 = __importDefault(require("../lib/logger.js"));
16
16
  const utils_js_1 = require("../lib/utils.js");
17
- const log = (0, logger_js_1.default)('router:journey');
17
+ const log = (0, logger_js_1.default)('routes:journey');
18
18
  const renderMiddlewareFactory = (view, contextFactory) => [
19
19
  (req, res, next) => {
20
20
  res.render(view, Object.assign({
21
21
  // Common template variables for both GET and POST requests
22
- inEditMode: req.casa.editMode, editOriginUrl: req.casa.editOrigin,
23
- // editSearchParams: req.editSearchParams,
24
- activeContextId: req.casa.journeyContext.identity.id }, contextFactory(req)), (err, templateString) => {
22
+ inEditMode: req.casa.editMode, editOriginUrl: req.casa.editOrigin, activeContextId: req.casa.journeyContext.identity.id }, contextFactory(req)), (err, templateString) => {
25
23
  if (err) {
26
- // logger.error(err);
27
24
  next(err);
28
25
  }
29
26
  else {
@@ -110,9 +107,11 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
110
107
  // errors in that format.
111
108
  // Where there are multiple errors against a particular field, only the
112
109
  // first one is shown.
110
+ // Disabling security/detect-object-injection rule because both `errors`
111
+ // and the `k` property are known entities
113
112
  const govukErrors = Object.keys(errors).map((k) => ({
114
113
  text: req.t(errors[k][0].summary, errors[k][0].variables),
115
- href: errors[k][0].fieldHref,
114
+ href: errors[k][0].fieldHref, /* eslint-disable-line security/detect-object-injection */
116
115
  }));
117
116
  return {
118
117
  formUrl,
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@dwp/govuk-casa",
3
- "version": "8.0.0-beta2",
3
+ "version": "8.0.3",
4
4
  "description": "A framework for building GOVUK Collect-And-Submit-Applications",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git@github.com:dwp/govuk-casa.git"
8
+ },
5
9
  "main": "dist/casa.js",
6
10
  "module": "dist/mjs/casa.js",
7
11
  "exports": {
@@ -18,18 +22,19 @@
18
22
  "node": ">=14.17.2"
19
23
  },
20
24
  "scripts": {
21
- "pipeline": "npm test && npm run test:e2e && npm run lint",
22
- "test": "mocha './tests/**/*.test.js'",
25
+ "pipeline": "npm run coverage && npm run lint",
26
+ "test:unit": "mocha './tests/**/*.test.js'",
23
27
  "test:e2e": "spiderplan --worker-init ./tests/e2e/worker-init.js --language en ./tests/e2e/personas/**/*.yaml",
24
- "test:combined": "npm test; npm run test:e2e",
28
+ "test": "npm run test:unit && npm run test:e2e",
25
29
  "lint": "eslint .",
26
- "coverage": "c8 npm run test:combined",
30
+ "coverage": "c8 npm test",
27
31
  "build": "npm run build:prepare && npm run build:sources && npm run build:css-assets",
28
32
  "build:prepare": "rm -rf dist/* && mkdir -p dist/assets/js/ && mkdir -p dist/assets/css/",
29
33
  "build:sources": "tsc -p tsconfig-cjs.json && ./scripts/fixup.sh",
30
34
  "build:css-assets": "node scripts/compile-sass.js",
31
35
  "prepare": "npm run build",
32
- "upgrade-deps": "OD=$(npm outdated --long --parseable); echo \"$OD\" | grep ':devDependencies:' | awk -F: '{ print $4 }' | xargs npm i -DE; echo \"$OD\" | grep ':dependencies:' | awk -F: '{ print $4 }' | xargs npm i -E"
36
+ "upgrade-deps": "OD=$(npm outdated --long --parseable); echo \"$OD\" | grep ':devDependencies:' | awk -F: '{ print $4 }' | xargs npm i -DE; echo \"$OD\" | grep ':dependencies:' | awk -F: '{ print $4 }' | xargs npm i -E",
37
+ "standard-version": "node scripts/standard-version.js"
33
38
  },
34
39
  "keywords": [],
35
40
  "author": "DWP Digital",
@@ -40,46 +45,48 @@
40
45
  "csurf": "1.11.0",
41
46
  "debug": "4.3.3",
42
47
  "deepmerge": "4.2.2",
43
- "express": "4.17.1",
48
+ "express": "4.17.2",
44
49
  "express-session": "1.17.2",
45
- "govuk-frontend": "3.14.0",
50
+ "govuk-frontend": "4.0.0",
46
51
  "graphlib": "2.1.8",
47
- "helmet": "4.6.0",
48
- "i18next": "21.6.0",
49
- "i18next-http-middleware": "3.1.4",
52
+ "helmet": "5.0.2",
53
+ "i18next": "21.6.10",
54
+ "i18next-http-middleware": "3.1.5",
50
55
  "js-yaml": "4.1.0",
51
56
  "lodash": "4.17.21",
52
- "luxon": "2.1.1",
57
+ "luxon": "2.3.0",
53
58
  "nunjucks": "3.2.3",
54
59
  "uuid": "8.3.2",
55
- "validator": "^13.7.0"
60
+ "validator": "13.7.0"
56
61
  },
57
62
  "devDependencies": {
58
- "@babel/core": "7.16.0",
59
- "@babel/eslint-parser": "7.16.3",
60
- "@babel/preset-env": "7.16.4",
61
- "@commitlint/config-conventional": "15.0.0",
62
- "@dwp/casa-spiderplan": "2.0.0",
63
+ "@babel/core": "7.16.12",
64
+ "@babel/eslint-parser": "7.16.5",
65
+ "@babel/preset-env": "7.16.11",
66
+ "@commitlint/config-conventional": "16.0.0",
67
+ "@dwp/casa-spiderplan": "2.3.0",
63
68
  "@dwp/casa-spiderplan-a11y-plugin": "0.1.3",
64
69
  "@dwp/casa-spiderplan-zap-plugin": "0.1.1",
65
- "@dwp/eslint-config-base": "5.0.1",
70
+ "@dwp/eslint-config-base": "6.0.0",
66
71
  "@types/express": "4.17.13",
67
- "@types/node": "16.11.12",
68
- "@types/nunjucks": "3.2.0",
72
+ "@types/node": "17.0.12",
73
+ "@types/nunjucks": "3.2.1",
69
74
  "babel-eslint": "10.1.0",
70
- "c8": "7.10.0",
71
- "chai": "4.3.4",
72
- "commitlint": "15.0.0",
73
- "eslint": "7.32.0",
75
+ "c8": "7.11.0",
76
+ "chai": "4.3.6",
77
+ "commitlint": "16.1.0",
78
+ "eslint": "8.7.0",
74
79
  "eslint-plugin-no-unsafe-regex": "1.0.0",
80
+ "eslint-plugin-security": "1.4.0",
75
81
  "eslint-plugin-sonarjs": "0.11.0",
76
- "fast-check": "2.20.0",
82
+ "fast-check": "2.21.0",
77
83
  "husky": "7.0.4",
78
- "mocha": "9.1.3",
79
- "sass": "1.44.0",
84
+ "mocha": "9.2.0",
85
+ "sass": "1.49.0",
80
86
  "sinon": "12.0.1",
81
87
  "sinon-chai": "3.7.0",
82
- "supertest": "6.1.6",
83
- "typescript": "4.5.3"
88
+ "standard-version": "9.3.2",
89
+ "supertest": "6.2.2",
90
+ "typescript": "4.5.5"
84
91
  }
85
92
  }