hof 23.0.0-frontend-v4-beta.14 → 23.0.0-vite-sourcemap-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -2
- package/README.md +28 -21
- package/build/tasks/images/index.js +2 -3
- package/build/tasks/index.js +1 -1
- package/build/tasks/vite/index.js +21 -0
- package/build/tasks/vite/vite.config.js +82 -0
- package/components/emailer/emailer.js +3 -3
- package/config/builder-defaults.js +7 -12
- package/config/hof-defaults.js +2 -1
- package/frontend/govuk-template/build/config.js +1 -0
- package/frontend/govuk-template/build/govuk_template.html +9 -9
- package/frontend/govuk-template/govuk_template_generated.html +9 -9
- package/frontend/template-mixins/mixins/template-mixins.js +1 -1
- package/frontend/template-mixins/partials/forms/checkbox.html +1 -1
- package/frontend/themes/gov-uk/styles/govuk.scss +1 -1
- package/frontend/toolkit/index.js +6 -6
- package/index.js +24 -18
- package/lib/encryption.js +43 -17
- package/lib/sessions.js +5 -2
- package/lib/settings.js +1 -1
- package/package.json +9 -13
- package/sandbox/assets/{rebrand/js → js}/index.js +1 -1
- package/sandbox/assets/{rebrand/scss → scss}/app.scss +1 -1
- package/sandbox/package.json +1 -1
- package/sandbox/public/css/app.css +53 -51
- package/sandbox/public/css/app.css.map +1 -0
- package/sandbox/public/js/bundle.js +44 -39242
- package/sandbox/server.js +4 -1
- package/build/tasks/browserify/compress.js +0 -15
- package/build/tasks/browserify/index.js +0 -48
- package/sandbox/public/images/passports/new-window-link-blue.png +0 -0
- package/sandbox/public/images/passports/new-window-link.png +0 -0
- package/sandbox/public/images/spinner.gif +0 -0
- /package/frontend/toolkit/assets/{rebrand/images → images}/passports/new-window-link-blue.png +0 -0
- /package/frontend/toolkit/assets/{rebrand/images → images}/passports/new-window-link.png +0 -0
- /package/frontend/toolkit/assets/{rebrand/images → images}/spinner.gif +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/character-count.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/form-focus.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/helpers.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/progressive-reveal.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/validation.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/vendor/details.polyfill.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/vendor/indexof.polyfill.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/javascript → javascript}/vendor/safari-cachebuster.js +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/_base.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/_helpers.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/_layout.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/_typography.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/_variables.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/app.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_buttons.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_details.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_elements-typography.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_forms.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_helpers.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_layout.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_lists.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_panels.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_reset.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/_tables.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/forms/_form-block-labels.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/forms/_form-date.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/govuk-elements/forms/_form-validation.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/mixins.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/modules/_alerts.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/modules/_buttons.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/modules/_confirm-page.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/modules/_lists.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/modules/_progressive-reveal.scss +0 -0
- /package/frontend/toolkit/assets/{rebrand/stylesheets → stylesheets}/modules/_validation.scss +0 -0
- /package/sandbox/assets/{rebrand/images → images}/icons/icon-caret-left.png +0 -0
- /package/sandbox/assets/{rebrand/images → images}/icons/icon-complete.png +0 -0
- /package/sandbox/assets/{rebrand/images → images}/icons/icon-cross-remove-sign.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
## 2025-
|
|
1
|
+
## 2025-12-16, Version 23.0.0 (Stable), @PaolaDMadd-Pro @Rhodine-orleans-lindsay
|
|
2
|
+
### Changed
|
|
3
|
+
- Replaced Browserify with Vite
|
|
4
|
+
- removed browserify dependencies including in tests
|
|
5
|
+
- added vite configuration and related tests
|
|
6
|
+
- Updated readme.md
|
|
7
|
+
- Potential **breaking changes**:
|
|
8
|
+
- Node version required for Vite is `20.19.0 || >=22.12.0`
|
|
9
|
+
- Updated engine requirements to use node to `>=20.19.0`
|
|
10
|
+
- Vite uses Rollup in production and requires optional rollup dependencies to be loaded. Setting `--ignore-optional flag` in Dockerfiles will result in "Module not found" errors in CI/CD or Docker.
|
|
11
|
+
|
|
12
|
+
## 2025-12-09, Version 22.13.0 (Stable), @Rhodine-orleans-lindsay
|
|
2
13
|
### Changed
|
|
3
14
|
- Updated cookie banner view to follow GOV.UK design system. Updated cookieSettings.js to allow for different confirmation text based on whether cookies were accepted or rejected
|
|
4
15
|
- Added inputmode='numeric' to date component
|
|
@@ -10,7 +21,39 @@
|
|
|
10
21
|
- Added a modifier for phone text input styles that accept sequences of digits
|
|
11
22
|
- Added govuk-template--rebranded class to govuk template html to enable govuk rebrand styles:
|
|
12
23
|
- Updated header and footer to follow GOV.UK design system as part of rebrand
|
|
13
|
-
|
|
24
|
+
|
|
25
|
+
## 2025-11-20, Version 22.12.0 (Stable), @dk4g @jamiecarterHO
|
|
26
|
+
|
|
27
|
+
### Infrastructure
|
|
28
|
+
- Updated CI/CD pipeline to test against Node.js 20.x, 22.x, and 24.x
|
|
29
|
+
- Updated Redis testing versions to 7 and 8
|
|
30
|
+
- Added `NODE_VERSION` environment variable for consistent Node.js version across jobs
|
|
31
|
+
- Updated release process to use Node.js 24 for tagging and publishing operations
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
- Replaced deprecated `crypto.createCipher`/`crypto.createDecipher` with `crypto.createCipheriv`/`crypto.createDecipheriv`
|
|
35
|
+
- Added proper initialisation vector (IV) handling for enhanced security
|
|
36
|
+
- Enforced 32-byte session secret requirement for AES-256 encryption compatibility
|
|
37
|
+
- Removed insecure default session secret ('changethis') - now requires explicit configuration
|
|
38
|
+
|
|
39
|
+
### Migration Notes
|
|
40
|
+
- **Session Reset Required**: Due to enhanced encryption security, existing user sessions will be invalidated and users will need to re-authenticate after this update
|
|
41
|
+
- **Session Secret**: You must now set a unique `SESSION_SECRET` environment variable of exactly 32 bytes for encryption compatibility.
|
|
42
|
+
For testing purposes, you can use the following command to generate a random value. For production environments, consult a security expert or refer to official cryptographic guidelines to generate a secure secret
|
|
43
|
+
`node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"`
|
|
44
|
+
|
|
45
|
+
## 2025-11-15, Version 22.11.0 (Stable), @Rhodine-orleans-lindsay
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- Updated custom session-timeout handling so that custom behaviours are not blocked by a `404` middleware error.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- Added a `CUSTOM_SESSION_EXPIRY` environment variable so that a time other than the redis session ttl can be used for the session timeout. **IMPORTANT**: The `CUSTOM_SESSION_EXPIRY` variable must always be a time before the redis session ttl would expire so that behaviours can run before the `SESSION_TIMEOUT` middleware is triggered.
|
|
52
|
+
- Added a `USE_CUSTOM_SESSION_TIMEOUT` that is `false` by default. When set to `true` the '/session-timeout' page can run before the session expires without triggering a `404` middleware error.
|
|
53
|
+
|
|
54
|
+
- 🎬 Action:
|
|
55
|
+
- For custom session timeout handling that is not linked to the redis session ttl, The following variables must be set: `CUSTOM_SESSION_EXPIRY` to the relevant expiry time e.g.600 and `USE_CUSTOM_SESSION_TIMEOUT` to true.
|
|
56
|
+
- If a behaviour is required on the '/session-timeout` step, the '/session-timeout' step must be set in the project's index.js, along with any relevant behaviours.
|
|
14
57
|
|
|
15
58
|
## 2025-10-10, Version 22.10.4 (Stable), @dk4g
|
|
16
59
|
### Security
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ It is recommended to alias `hof-build` to an npm script in your package.json.
|
|
|
60
60
|
|
|
61
61
|
## Tasks
|
|
62
62
|
|
|
63
|
-
- `
|
|
63
|
+
- `vite` - compiles client-side js with vite in development and rollup in production. Requires node version `^20.19.0 || >=22.12.0`.
|
|
64
64
|
- `sass` - compiles sass
|
|
65
65
|
- `images` - copies images from ./assets/images directory to ./public/images
|
|
66
66
|
- `translate` - compiles translation files
|
|
@@ -69,21 +69,18 @@ Note: For SASS compilation it's possible to additionally configure the following
|
|
|
69
69
|
- `outputStyle` - Controls whether the CSS output is compressed or not, expanded (default) = non compressed and compressed = compressed CSS output.
|
|
70
70
|
- `quietDeps` - This controls whether you get deprecation warning shown in the console output, if set to false (default) SASS deprecation warnings will be shown in the console, if set to true then deprecation warnings will not be shown in the console output.
|
|
71
71
|
- `sourceMaps` - This controls whether the build will output css sourcemaps to help with debugging. These will be output to the same directory as the css output as a .map file. This option is not currently available in production.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Debugging example (in hof.settings or your build config)
|
|
72
|
+
For JavaScript compilation, Vite outputs JavaScript sourcemaps as a .js.map file to the same directory as the js bundle.
|
|
73
|
+
You can change sourcemaps' default settings in hof.settings or your build config as follows:
|
|
76
74
|
```
|
|
77
75
|
"build": {
|
|
78
76
|
"sass": {
|
|
79
77
|
"sourceMaps": true
|
|
80
78
|
},
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
}
|
|
79
|
+
"js": {
|
|
80
|
+
"sourceMaps": true
|
|
81
|
+
},
|
|
84
82
|
}
|
|
85
83
|
```
|
|
86
|
-
|
|
87
84
|
## Watch
|
|
88
85
|
|
|
89
86
|
You can additionally run a `watch` task to start a server instance, which will automatically restart based on changes to files. This will also re-perform the tasks above when relevant files change.
|
|
@@ -109,7 +106,7 @@ hof-build watch --env .envdev
|
|
|
109
106
|
|
|
110
107
|
## Configuration
|
|
111
108
|
|
|
112
|
-
The default settings will match those for an app generated using [`hof-generator`](https://npmjs.com/hof-generator).
|
|
109
|
+
The default settings will match those for an app generated using [`hof-generator`](https://npmjs.com/hof-generator) (⚠️ This package has been deprecated).
|
|
113
110
|
|
|
114
111
|
If a `hof.settings.json` file is found in the application root, then the `build` section of the settings file will be used to override [the default configuration](./defaults.js).
|
|
115
112
|
|
|
@@ -119,17 +116,9 @@ Alternatively you can define a path to a local config file by passing a `--confi
|
|
|
119
116
|
hof-build --config /path/to/my/config.js
|
|
120
117
|
```
|
|
121
118
|
|
|
122
|
-
Any task can be disabled by setting its configuration to `false` (or any falsy value).
|
|
123
|
-
|
|
124
|
-
```js
|
|
125
|
-
module.exports = {
|
|
126
|
-
browserify: false,
|
|
127
|
-
};
|
|
128
|
-
```
|
|
129
|
-
|
|
130
119
|
### Configuration options
|
|
131
120
|
|
|
132
|
-
Each task has a common configuration format with the following options:
|
|
121
|
+
Each task (except Vite) has a common configuration format with the following options:
|
|
133
122
|
|
|
134
123
|
- `src` - defines the input file or files for the build task
|
|
135
124
|
- `out` - defines the output location of the built code where relevant
|
|
@@ -867,7 +856,7 @@ Kubernetes healthcheck URLs are provided as defaults if no overrides are supplie
|
|
|
867
856
|
|----------|-------------|---------|---------|
|
|
868
857
|
| `SHOW_COOKIES_BANNER` | Controls whether the cookies banner is displayed | Auto-detected based on GA tags | `true`, `false` |
|
|
869
858
|
|
|
870
|
-
**
|
|
859
|
+
**Behaviour:**
|
|
871
860
|
- If `SHOW_COOKIES_BANNER` is explicitly set, that value is used
|
|
872
861
|
- If not set, banner is automatically shown when `GA_TAG` or `GA_4_TAG` is present
|
|
873
862
|
- If no GA tags are configured, banner is hidden by default
|
|
@@ -1328,7 +1317,11 @@ This feature allows you to customise the content related to the session timeout
|
|
|
1328
1317
|
|
|
1329
1318
|
### Usage
|
|
1330
1319
|
|
|
1331
|
-
To
|
|
1320
|
+
By default, the session timeout is set to the redis session ttl. To bypass this and display the session timeout message before the redis session ttl the following evironment variables must be set:
|
|
1321
|
+
`CUSTOM_SESSION_EXPIRY` - e.g. `600`. Configure to expire before thte project's redis session ttl.
|
|
1322
|
+
`USE_CUSTOM_SESSION_TIMEOUT` - `false` by default. When set to `true` the '/session-timeout' page can run before the session expires without triggering a `404` middleware error.
|
|
1323
|
+
|
|
1324
|
+
To enable and customise the session timeout behaviour, you need to set the component and translations in your project's `hof.settings.json` file:
|
|
1332
1325
|
```json
|
|
1333
1326
|
"behaviours": [
|
|
1334
1327
|
"hof/components/session-timeout-warning"
|
|
@@ -1348,6 +1341,20 @@ By default, the framework uses the standard content provided by HOF. If you wish
|
|
|
1348
1341
|
"saveExitFormContent": true // allows you to customise the content on the save-and-exit page
|
|
1349
1342
|
```
|
|
1350
1343
|
|
|
1344
|
+
To override the default session-timeout page completely, the path to the session-timeout.html should be set in the views property in the hof.settings.json file e.g.
|
|
1345
|
+
```json
|
|
1346
|
+
"behaviours": [
|
|
1347
|
+
"hof/components/session-timeout-warning"
|
|
1348
|
+
],
|
|
1349
|
+
"translations": "./apps/common/translations",
|
|
1350
|
+
"views": ["./apps/common/views"], // allows you to overide the HOF default session-timeout page and use a custom one from the specified views
|
|
1351
|
+
...
|
|
1352
|
+
```
|
|
1353
|
+
or in the project's `server.js` e.g.
|
|
1354
|
+
```js
|
|
1355
|
+
settings.views = path.resolve(__dirname, './apps/common/views');
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1351
1358
|
### Customising content in `pages.json`
|
|
1352
1359
|
Once the variables are set, you can customise the session timeout warning and exit messages in your project's pages.json:
|
|
1353
1360
|
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
const spawn = require('../../lib/spawn');
|
|
7
|
-
const mkdir = require('../../lib/mkdir');
|
|
8
7
|
|
|
9
8
|
module.exports = config => {
|
|
10
9
|
if (!config.images) {
|
|
@@ -28,11 +27,11 @@ module.exports = config => {
|
|
|
28
27
|
console.log(`${chalk.yellow('warning')}: Skipping missing images folder: ${src}`);
|
|
29
28
|
return Promise.resolve();
|
|
30
29
|
}
|
|
31
|
-
return
|
|
32
|
-
.then(() => spawn('cp', ['-r', `${src}/.`, imagesOutput]));
|
|
30
|
+
return spawn('cp', ['-r', `${src}/.`, imagesOutput]);
|
|
33
31
|
}))
|
|
34
32
|
.catch(e => {
|
|
35
33
|
if (e.code !== 'ENOENT') {
|
|
34
|
+
console.error(`${chalk.red('error')}: Failed to copy images - ${e.message}`);
|
|
36
35
|
throw e;
|
|
37
36
|
} else {
|
|
38
37
|
console.log(`${chalk.yellow('warning')}: no images directory found at ${config.images.src}`);
|
package/build/tasks/index.js
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const vite = require('vite');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const viteConfig = path.resolve(__dirname, './vite.config.js');
|
|
6
|
+
const hofDefaults = require('../../../config/hof-defaults');
|
|
7
|
+
|
|
8
|
+
module.exports = config => {
|
|
9
|
+
process.env.NODE_ENV = hofDefaults.env;
|
|
10
|
+
|
|
11
|
+
if(!config.production) {
|
|
12
|
+
return vite.build({
|
|
13
|
+
configFile: viteConfig,
|
|
14
|
+
mode: 'development'
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return vite.build({
|
|
18
|
+
configFile: viteConfig
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
module.exports.task = 'vite';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
import { defineConfig } from 'vite';
|
|
5
|
+
import { resolve } from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
8
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
9
|
+
import config from '../../../config/builder-defaults';
|
|
10
|
+
|
|
11
|
+
const publicDirectory = resolve(process.cwd(), 'public');
|
|
12
|
+
const entryFile = (() => {
|
|
13
|
+
const src = resolve(process.cwd(), 'assets/js/index.js');
|
|
14
|
+
if (fs.existsSync(src)) return src;
|
|
15
|
+
|
|
16
|
+
throw new Error(`vite: entry file not found. Checked: ${src}`);
|
|
17
|
+
})();
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
plugins: [
|
|
22
|
+
commonjs({
|
|
23
|
+
include: [/node_modules/, /assets\/js/, /frontend/]
|
|
24
|
+
}),
|
|
25
|
+
nodeResolve({ browser: true, preferBuiltins: false }),
|
|
26
|
+
// Intercept all console warnings printed during build
|
|
27
|
+
// Custom plugin to suppress specific resolve warnings.
|
|
28
|
+
// Vite does not process and optimise static images and assets files, it uses a src/ folder.
|
|
29
|
+
// the images and assets folders are therefore loaded at runtime from the public/ folder.
|
|
30
|
+
// This should be resolved with the v5/nunjucks work so we can ignore these warnings for now.
|
|
31
|
+
// TODO: Remove this when v5/nunjucks is implemented.
|
|
32
|
+
{
|
|
33
|
+
name: 'suppress-resolve-warnings',
|
|
34
|
+
apply: 'build',
|
|
35
|
+
configResolved() {
|
|
36
|
+
const originalWarning = console.warn;
|
|
37
|
+
console.warn = (...args) => {
|
|
38
|
+
if (
|
|
39
|
+
args.some(arg =>
|
|
40
|
+
typeof arg === 'string' &&
|
|
41
|
+
arg.includes("didn't resolve at build time")
|
|
42
|
+
)
|
|
43
|
+
) {
|
|
44
|
+
return; // skip this specific warning
|
|
45
|
+
}
|
|
46
|
+
originalWarning(...args);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
base: '/assets/',
|
|
52
|
+
publicDir: 'static', // static files copied as-is
|
|
53
|
+
build: {
|
|
54
|
+
outDir: publicDirectory,
|
|
55
|
+
emptyOutDir: false,
|
|
56
|
+
sourcemap: config.js.sourceMaps, // Enable JS/TS sourcemaps in dev
|
|
57
|
+
rollupOptions: {
|
|
58
|
+
input: {
|
|
59
|
+
index: entryFile
|
|
60
|
+
},
|
|
61
|
+
output: {
|
|
62
|
+
entryFileNames: 'js/bundle.js',
|
|
63
|
+
chunkFileNames: 'js/[name]-[hash].js',
|
|
64
|
+
assetFileNames: assetInfo => {
|
|
65
|
+
const ext = assetInfo.name && assetInfo.name.split('.').pop();
|
|
66
|
+
if (/css/i.test(ext)) return 'css/[name]-[hash][extname]';
|
|
67
|
+
if (/svg|png|jpg|jpeg|gif|webp|ico/i.test(ext)) return 'images/[name]-[hash][extname]';
|
|
68
|
+
return 'assets/[name]-[hash][extname]';
|
|
69
|
+
},
|
|
70
|
+
format: 'iife'
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
css: {
|
|
74
|
+
// devSourcemap: isDev,
|
|
75
|
+
preprocessorOptions: {
|
|
76
|
+
scss: {
|
|
77
|
+
includes: ['node_modules']
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
@@ -34,17 +34,17 @@ module.exports = class Emailer {
|
|
|
34
34
|
html: body,
|
|
35
35
|
attachments: [{
|
|
36
36
|
filename: 'govuk_logotype_email.png',
|
|
37
|
-
path: path.resolve(__dirname, './assets/
|
|
37
|
+
path: path.resolve(__dirname, './assets/images/govuk_logotype_email.png'),
|
|
38
38
|
cid: 'govuk_logotype_email'
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
filename: 'ho_crest_27px.png',
|
|
42
|
-
path: path.resolve(__dirname, './assets/
|
|
42
|
+
path: path.resolve(__dirname, './assets/images/ho_crest_27px.png'),
|
|
43
43
|
cid: 'ho_crest_27px'
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
filename: 'spacer.gif',
|
|
47
|
-
path: path.resolve(__dirname, './assets/
|
|
47
|
+
path: path.resolve(__dirname, './assets/images/spacer.gif'),
|
|
48
48
|
cid: 'spacer_image'
|
|
49
49
|
}]
|
|
50
50
|
}, (err, result) => err ? reject(err) : resolve(result));
|
|
@@ -5,32 +5,27 @@ const toolkitImages =
|
|
|
5
5
|
: 'node_modules/hof/frontend/toolkit/assets/rebrand/images';
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
|
-
browserify: {
|
|
9
|
-
src: 'assets/rebrand/js/index.js',
|
|
10
|
-
out: 'public/js/bundle.js',
|
|
11
|
-
match: 'assets/rebrand/js/**/*.js',
|
|
12
|
-
restart: false,
|
|
13
|
-
compress: false,
|
|
14
|
-
debug: false
|
|
15
|
-
},
|
|
16
8
|
sass: {
|
|
17
|
-
src: 'assets/
|
|
9
|
+
src: 'assets/scss/app.scss',
|
|
18
10
|
out: 'public/css/app.css',
|
|
19
|
-
match: 'assets/
|
|
11
|
+
match: 'assets/scss/**/*.scss',
|
|
20
12
|
restart: false,
|
|
21
13
|
quietDeps: false,
|
|
22
14
|
outputStyle: 'expanded',
|
|
23
15
|
sourceMaps: false
|
|
24
16
|
},
|
|
17
|
+
js: {
|
|
18
|
+
sourceMaps: false
|
|
19
|
+
},
|
|
25
20
|
translate: {
|
|
26
21
|
src: 'apps/**/translations/src',
|
|
27
22
|
match: 'apps/**/translations/src/**/*.json',
|
|
28
23
|
shared: 'apps/common/translations/src'
|
|
29
24
|
},
|
|
30
25
|
images: {
|
|
31
|
-
src: ['assets/rebrand/images', toolkitImages],
|
|
26
|
+
src: ['assets/rebrand/images', 'assets/images', toolkitImages],
|
|
32
27
|
out: 'public/images',
|
|
33
|
-
match:
|
|
28
|
+
match: 'assets/images/**/*',
|
|
34
29
|
restart: false
|
|
35
30
|
},
|
|
36
31
|
server: {
|
package/config/hof-defaults.js
CHANGED
|
@@ -51,7 +51,7 @@ const defaults = {
|
|
|
51
51
|
},
|
|
52
52
|
session: {
|
|
53
53
|
ttl: process.env.SESSION_TTL || 1800,
|
|
54
|
-
secret: process.env.SESSION_SECRET
|
|
54
|
+
secret: process.env.SESSION_SECRET,
|
|
55
55
|
name: process.env.SESSION_NAME || 'hod.sid',
|
|
56
56
|
sanitiseInputs: false
|
|
57
57
|
},
|
|
@@ -59,6 +59,7 @@ const defaults = {
|
|
|
59
59
|
pdfConverter: process.env.PDF_CONVERTER_URL
|
|
60
60
|
},
|
|
61
61
|
serveStatic: process.env.SERVE_STATIC_FILES !== 'false',
|
|
62
|
+
useCustomSessionTimeout: parseBoolean(process.env.USE_CUSTOM_SESSION_TIMEOUT, false, 'USE_CUSTOM_SESSION_TIMEOUT'),
|
|
62
63
|
sessionTimeOutWarning: process.env.SESSION_TIMEOUT_WARNING || 300,
|
|
63
64
|
serviceUnavailable: parseBoolean(process.env.SERVICE_UNAVAILABLE, false, 'SERVICE_UNAVAILABLE')
|
|
64
65
|
};
|
|
@@ -4,6 +4,7 @@ module.exports = {
|
|
|
4
4
|
htmlLang: '{{htmlLang}}',
|
|
5
5
|
assetPath: '{{govukAssetPath}}',
|
|
6
6
|
assetRebrandPath: '{{govukAssetPath}}rebrand/',
|
|
7
|
+
themeColour: '#1d70b8',
|
|
7
8
|
afterHeader: '{{$afterHeader}}{{/afterHeader}}',
|
|
8
9
|
bodyClasses: '{{$bodyClasses}}{{/bodyClasses}}',
|
|
9
10
|
bodyStart: '{{$bodyStart}}{{/bodyStart}}',
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
<meta charset="utf-8" />
|
|
7
7
|
<title>{{ pageTitle }}</title>
|
|
8
8
|
{{{ head }}}
|
|
9
|
-
|
|
10
|
-
<meta name="theme-color" content="
|
|
11
|
-
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
12
|
-
<link rel="icon" sizes="48x48" href="{{assetRebrandPath}}images/favicon.ico">
|
|
13
|
-
<link rel="icon" sizes="any" href="{{assetRebrandPath}}images/favicon.svg" type="image/svg+xml">
|
|
14
|
-
<link rel="mask-icon" href="{{assetRebrandPath}}images/govuk-icon-mask.svg" color="
|
|
15
|
-
<link rel="apple-touch-icon" href="{{assetRebrandPath}}images/govuk-icon-180.png">
|
|
16
|
-
<link rel="manifest" href="{{assetRebrandPath}}manifest.json">
|
|
17
|
-
<meta property="og:image" content="{{assetRebrandPath}}images/govuk-opengraph-image.png">
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
10
|
+
<meta name="theme-color" content="{{themeColour}}">
|
|
11
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
12
|
+
<link rel="icon" sizes="48x48" href="{{assetRebrandPath}}images/favicon.ico">
|
|
13
|
+
<link rel="icon" sizes="any" href="{{assetRebrandPath}}images/favicon.svg" type="image/svg+xml">
|
|
14
|
+
<link rel="mask-icon" href="{{assetRebrandPath}}images/govuk-icon-mask.svg" color="{{themeColour}}">
|
|
15
|
+
<link rel="apple-touch-icon" href="{{assetRebrandPath}}images/govuk-icon-180.png">
|
|
16
|
+
<link rel="manifest" href="{{assetRebrandPath}}manifest.json">
|
|
17
|
+
<meta property="og:image" content="{{assetRebrandPath}}images/govuk-opengraph-image.png">
|
|
18
18
|
</head>
|
|
19
19
|
|
|
20
20
|
<body class="{{ bodyClasses }} govuk-template__body js-enabled" >
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
<meta charset="utf-8" />
|
|
7
7
|
<title>{{$pageTitle}}{{/pageTitle}}</title>
|
|
8
8
|
{{$head}}{{/head}}
|
|
9
|
-
|
|
10
|
-
<meta name="theme-color" content="#1d70b8">
|
|
11
|
-
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
12
|
-
<link rel="icon" sizes="48x48" href="{{govukAssetPath}}rebrand/images/favicon.ico">
|
|
13
|
-
<link rel="icon" sizes="any" href="{{govukAssetPath}}rebrand/images/favicon.svg" type="image/svg+xml">
|
|
14
|
-
<link rel="mask-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-mask.svg" color="#1d70b8">
|
|
15
|
-
<link rel="apple-touch-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-180.png">
|
|
16
|
-
<link rel="manifest" href="{{govukAssetPath}}rebrand/manifest.json">
|
|
17
|
-
<meta property="og:image" content="{{govukAssetPath}}rebrand/images/govuk-opengraph-image.png">
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
10
|
+
<meta name="theme-color" content="#1d70b8">
|
|
11
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
12
|
+
<link rel="icon" sizes="48x48" href="{{govukAssetPath}}rebrand/images/favicon.ico">
|
|
13
|
+
<link rel="icon" sizes="any" href="{{govukAssetPath}}rebrand/images/favicon.svg" type="image/svg+xml">
|
|
14
|
+
<link rel="mask-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-mask.svg" color="#1d70b8">
|
|
15
|
+
<link rel="apple-touch-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-180.png">
|
|
16
|
+
<link rel="manifest" href="{{govukAssetPath}}rebrand/manifest.json">
|
|
17
|
+
<meta property="og:image" content="{{govukAssetPath}}rebrand/images/govuk-opengraph-image.png">
|
|
18
18
|
</head>
|
|
19
19
|
|
|
20
20
|
<body class="{{$bodyClasses}}{{/bodyClasses}} govuk-template__body js-enabled" >
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div id="{{key}}-group" class="govuk-form-group {{#compound}} form-group-compound{{/compound}}{{#formGroupClassName}} {{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
|
|
2
2
|
{{#error}}
|
|
3
|
-
<p class="govuk-error-message" aria-hidden="true">
|
|
3
|
+
<p id="{{key}}-error" class="govuk-error-message" aria-hidden="true">
|
|
4
4
|
<span class="govuk-visually-hidden">Error:</span> {{error.message}}
|
|
5
5
|
</p>
|
|
6
6
|
{{/error}}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
helpers: require('./assets/
|
|
3
|
-
formFocus: require('./assets/
|
|
4
|
-
progressiveReveal: require('./assets/
|
|
5
|
-
validation: require('./assets/
|
|
2
|
+
helpers: require('./assets/javascript/helpers'),
|
|
3
|
+
formFocus: require('./assets/javascript/form-focus'),
|
|
4
|
+
progressiveReveal: require('./assets/javascript/progressive-reveal'),
|
|
5
|
+
validation: require('./assets/javascript/validation'),
|
|
6
6
|
detailsSummary: function () {
|
|
7
|
-
require('./assets/
|
|
7
|
+
require('./assets/javascript/vendor/details.polyfill');
|
|
8
8
|
},
|
|
9
|
-
characterCount: require('./assets/
|
|
9
|
+
characterCount: require('./assets/javascript/character-count')
|
|
10
10
|
};
|
package/index.js
CHANGED
|
@@ -158,7 +158,7 @@ function bootstrap(options) {
|
|
|
158
158
|
res.locals.htmlLang = config.htmlLang;
|
|
159
159
|
res.locals.cookieName = config.session.name;
|
|
160
160
|
// session timeout warning configs
|
|
161
|
-
res.locals.sessionTimeOut = config.session.ttl;
|
|
161
|
+
res.locals.sessionTimeOut = process.env.CUSTOM_SESSION_EXPIRY || config.session.ttl;
|
|
162
162
|
res.locals.sessionTimeOutWarning = config.sessionTimeOutWarning;
|
|
163
163
|
res.locals.sessionTimeoutWarningContent = config.sessionTimeoutWarningContent;
|
|
164
164
|
res.locals.exitFormContent = config.exitFormContent;
|
|
@@ -238,8 +238,8 @@ function bootstrap(options) {
|
|
|
238
238
|
app.use(hofMiddleware.rateLimiter(config, 'requests'));
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
// Set up routing so <YOUR-SITE-URL>/assets are served from /node_modules/govuk-frontend/govuk/assets
|
|
242
|
-
app.use('/assets', express.static(path.join(__dirname, '/node_modules/govuk-frontend/govuk/assets
|
|
241
|
+
// Set up routing so <YOUR-SITE-URL>/assets are served from /node_modules/govuk-frontend/govuk/assets
|
|
242
|
+
app.use('/assets', express.static(path.join(__dirname, '/node_modules/govuk-frontend/govuk/assets')));
|
|
243
243
|
// Check if service has been paused and redirect accordingly
|
|
244
244
|
const bypassPaths = ['/assets', '/healthcheck', '/service-unavailable'];
|
|
245
245
|
|
|
@@ -254,26 +254,32 @@ function bootstrap(options) {
|
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
256
|
* Handles requests to the session timeout page.
|
|
257
|
+
* For custom session timeout handling that is not linked to the redis session ttl,
|
|
258
|
+
* set `CUSTOM_SESSION_EXPIRY` variables to the relevant time and `USE_CUSTOM_SESSION_TIMEOUT`variables to true.
|
|
259
|
+
* If `CUSTOM_SESSION_EXPIRY` and `USE_CUSTOM_SESSION_TIMEOUT` envs are set,
|
|
260
|
+
* include '/session-timeout' step in the project's index.js.
|
|
257
261
|
* - If the user has a session cookie but their session is missing or inactive,
|
|
258
262
|
* this triggers a session timeout error handled by error middleware.
|
|
259
263
|
* - Otherwise, responds with a 404 "Page Not Found" error.
|
|
260
264
|
* This route ensures the timeout page only appears after an actual session expiry.
|
|
261
265
|
*/
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
266
|
+
if (!config.useCustomSessionTimeout) {
|
|
267
|
+
app.get('/session-timeout', (req, res, next) => {
|
|
268
|
+
if ((req.cookies['hof-wizard-sc']) && (!req.session || req.session.exists !== true)) {
|
|
269
|
+
const err = new Error('Session expired');
|
|
270
|
+
err.code = 'SESSION_TIMEOUT';
|
|
271
|
+
return next(err);
|
|
272
|
+
}
|
|
273
|
+
const err = new Error('Not Found');
|
|
274
|
+
err.status = 404;
|
|
275
|
+
const locals = Object.assign({}, req.translate('errors'));
|
|
276
|
+
if (locals && locals['404']) {
|
|
277
|
+
return res.status(404).render('404', locals['404']);
|
|
278
|
+
}
|
|
279
|
+
// Fallback: render a basic 404 page if translation is missing
|
|
280
|
+
return res.status(404).send('Page Not Found');
|
|
281
|
+
});
|
|
282
|
+
}
|
|
277
283
|
|
|
278
284
|
if (config.getAccessibility === true) {
|
|
279
285
|
deprecate(
|
package/lib/encryption.js
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
4
|
-
const crypto = require('crypto');
|
|
3
|
+
const crypto = require('node:crypto');
|
|
5
4
|
const algorithm = 'aes-256-cbc';
|
|
5
|
+
const ivLength = 16;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return dec;
|
|
7
|
+
/**
|
|
8
|
+
* Creates an encryption utility with AES-256-CBC algorithm.
|
|
9
|
+
* Provides encrypt and decrypt methods that use a random IV for each encryption operation.
|
|
10
|
+
*
|
|
11
|
+
* @module encryption
|
|
12
|
+
* @param {string|Buffer} secret - Must be exactly 32 bytes
|
|
13
|
+
* @returns {Object} Encryption utility object
|
|
14
|
+
* @throws {Error} If secret is not exactly 32 bytes
|
|
15
|
+
*/
|
|
16
|
+
module.exports = secret => {
|
|
17
|
+
const encryptionKey = Buffer.from(secret, 'utf8');
|
|
18
|
+
if (encryptionKey.byteLength !== 32) {
|
|
19
|
+
throw new Error(`Encryption secret must be exactly 32 bytes. Provided: ${encryptionKey.byteLength} bytes.`);
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
return {
|
|
23
|
+
encrypt: text => {
|
|
24
|
+
try {
|
|
25
|
+
const iv = crypto.randomBytes(ivLength);
|
|
26
|
+
const cipher = crypto.createCipheriv(algorithm, encryptionKey, iv);
|
|
27
|
+
let encrypted = cipher.update(text, 'utf8');
|
|
28
|
+
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
|
29
|
+
return iv.toString('hex') + ':' + encrypted.toString('hex');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new Error(`Encryption failed: ${error.message}`);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
decrypt: text => {
|
|
36
|
+
try {
|
|
37
|
+
const textParts = text.split(':');
|
|
38
|
+
const iv = Buffer.from(textParts.shift(), 'hex');
|
|
39
|
+
const encryptedText = Buffer.from(textParts.join(':'), 'hex');
|
|
40
|
+
const decipher = crypto.createDecipheriv(algorithm, encryptionKey, iv);
|
|
41
|
+
let decrypted = decipher.update(encryptedText);
|
|
42
|
+
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
43
|
+
return decrypted.toString('utf8');
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error(`Decryption failed: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
};
|
package/lib/sessions.js
CHANGED
|
@@ -10,8 +10,11 @@ const secureHttps = config => config.protocol === 'https' || config.env === 'pro
|
|
|
10
10
|
module.exports = (app, config) => {
|
|
11
11
|
const logger = config.logger || console;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const secretBuffer = Buffer.from(config.session.secret, 'utf8');
|
|
14
|
+
if (secretBuffer.byteLength !== 32) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Session secret must be exactly 32 bytes. Current: ${secretBuffer.byteLength} bytes.`
|
|
17
|
+
);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
app.use(cookieParser(config.session.secret, {
|