Skip to content

Commit

Permalink
Add static file configuration to use the govuk npm directory and upda…
Browse files Browse the repository at this point in the history
…te related tests files
  • Loading branch information
Mouhajer-CO committed Aug 6, 2024
1 parent 3ed1c5c commit 6c522e0
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ AUTH_SIGN_OUT_URL=${AUTH_PATH}/?logout=true
AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE
AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY
BASE_URL=http://localhost:3000
CDN_HOST=test
CDN_HOST="d3ums2fp3imnyp.cloudfront.net"
COOKIE_ID_NAME=github-requests
COOKIE_PARSER_SECRET=test
COOKIE_SESSION_SECRET=test
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,13 @@ The issue is then reviewed by the team, and further comments may be requested if

### Running local development environment with Docker

Docker is used to run the application in **development** mode, with tooling setup to detect changes in local `src` directory and reload the container's node server.
Docker is used to run the application in **development** mode, with tooling setup to detect changes in local `src` directory and reload the container's node server.

- Ensure that `NODE_ENV=development` is set in the `.env` file.

- In order for static assets to be loaded, the `CDN_HOST` must be set to a CDN domain which serves the GOV.UK static assets.

### Building the application

To build the project, run:
To build the project, run:

```sh
make build
Expand Down
7 changes: 5 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ const app = express();

app.disable('x-powered-by');

const assetsPath = path.join(__dirname, '../assets');
app.use('/assets', express.static(assetsPath));
const govukPath = path.join(__dirname, '../node_modules/govuk-frontend/dist/govuk');

app.use('/assets', express.static(`${govukPath}/assets`));
app.use('/govuk-frontend.min.css', express.static(`${govukPath}/govuk-frontend.min.css`));
app.use('/govuk-frontend.min.js', express.static(`${govukPath}/govuk-frontend.min.js`));

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Expand Down
59 changes: 34 additions & 25 deletions src/config/helmet.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import express from 'express';
import helmet from 'helmet';
import helmet, { HelmetOptions } from 'helmet';

import * as config from '../config';

export const configureHelmet = (app: express.Application) => {
app.use(helmet({
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
export const HELMET_OPTIONS: HelmetOptions = {
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
},
contentSecurityPolicy: {
useDefaults: true,
directives: {
defaultSrc: ["'self'"],
fontSrc: ["'self'", config.CDN_HOST],
styleSrc: [
"'self'",
"'unsafe-hashes'",
"'sha256-6FWIojjtZwiNizws7ImlHjGH3DA5yMh5x4c+/4UVpXk='",
config.CDN_HOST
],
scriptSrc: [
"'self'",
"'sha256-pOe+O2hOZnTvxPXzgAfiocCoHgEZxBBgg+a66TylmHw='",
"'sha256-GUQ5ad8JK5KmEWmROf3LZd9ge94daqNvd8xy9YS1iDw='",
"'sha256-rDMP7u4Lf+tIufrYmUZIhcf2T3WBD4Pweu0EXe+qaLA='",
"'sha256-wJphCpPdstup3Ojta3HnXJ3EOilATTTWg5oi4S9oi4s='",
config.CDN_HOST
],
imgSrc: ["'self'", 'data:', config.CDN_HOST],
connectSrc: ["'self'"],
formAction: ["'self'"],
objectSrc: ["'none'"]
},
contentSecurityPolicy: {
useDefaults: true,
directives: {
defaultSrc: ["'self'"],
fontSrc: ["'self'", config.CDN_HOST],
styleSrc: ["'self'", "'unsafe-hashes'", "'sha256-6FWIojjtZwiNizws7ImlHjGH3DA5yMh5x4c+/4UVpXk='", config.CDN_HOST],
scriptSrc: [
"'self'",
"'sha256-GUQ5ad8JK5KmEWmROf3LZd9ge94daqNvd8xy9YS1iDw='",
"'sha256-rDMP7u4Lf+tIufrYmUZIhcf2T3WBD4Pweu0EXe+qaLA='",
config.CDN_HOST
],
imgSrc: ["'self'", 'data:', config.CDN_HOST],
connectSrc: ["'self'"],
formAction: ["'self'"],
objectSrc: ["'none'"]
},
reportOnly: false
}
}));
reportOnly: false
}
};

export const configureHelmet = (app: express.Application) => {
app.use(helmet(HELMET_OPTIONS));
};
2 changes: 1 addition & 1 deletion src/config/nunjucks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const configureNunjucks = (app: express.Application, viewsPath: string) =
log.info(`Set nunjucks configurations and where nunjucks templates should resolve to: ${viewsPath}`);

const nunjucksEnv = nunjucks.configure(
[viewsPath, 'node_modules/govuk-frontend/dist', 'node_modules/govuk-frontend/dist/components'],
[viewsPath, 'node_modules/govuk-frontend/dist'],
{
autoescape: true,
express: app
Expand Down
13 changes: 6 additions & 7 deletions src/views/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
{% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %}

{% block head %}
<link rel="stylesheet" type="text/css" media="all" href="//{{CDN_HOST}}/govuk-frontend/v5.3.0/govuk-frontend-5.3.0.min.css">
<link rel="stylesheet" type="text/css" media="all" href="/govuk-frontend.min.css">

<link rel="SHORTCUT ICON" href="//{{CDN_HOST}}/images/favicon.ico"/>
<link rel="icon" href="//{{CDN_HOST}}/images/favicon.ico" type="image/x-icon"/>
<link rel="SHORTCUT ICON" href="/assets/images/favicon.ico"/>
<link rel="icon" href="/assets/images/favicon.ico" type="image/x-icon"/>
{% endblock %}

{% block header %}
Expand Down Expand Up @@ -85,11 +85,10 @@

{% block bodyEnd %}
{% if FEATURE_FLAG_ENABLE_COOKIE_BANNER == "true" %}
<script src="/assets/javascripts/cookies.js"></script>
<script src="//{{CDN_HOST}}/app/github-requests-app/javascripts/cookies.js"></script>
{% endif %}
<script type="module" src="//{{CDN_HOST}}/govuk-frontend/v5.3.0/govuk-frontend-5.3.0.min.js"></script>
<script type="module">
import { initAll } from '//{{CDN_HOST}}/govuk-frontend/v5.3.0/govuk-frontend.min.js'
import { initAll } from '/govuk-frontend.min.js'
initAll()
</script>
{% endblock %}
{% endblock %}
25 changes: 0 additions & 25 deletions test/mock/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,31 +98,6 @@ export const MOCK_ERROR = {
message: 'Error message'
} as Error;

export const MOCK_HELMET_VALUE = {
referrerPolicy: {
policy: 'strict-origin-when-cross-origin'
},
contentSecurityPolicy: {
useDefaults: true,
directives: {
defaultSrc: ["'self'"],
fontSrc: ["'self'", config.CDN_HOST],
styleSrc: ["'self'", "'unsafe-hashes'", "'sha256-6FWIojjtZwiNizws7ImlHjGH3DA5yMh5x4c+/4UVpXk='", config.CDN_HOST],
scriptSrc: [
"'self'",
"'sha256-GUQ5ad8JK5KmEWmROf3LZd9ge94daqNvd8xy9YS1iDw='",
"'sha256-rDMP7u4Lf+tIufrYmUZIhcf2T3WBD4Pweu0EXe+qaLA='",
config.CDN_HOST
],
imgSrc: ["'self'", 'data:', config.CDN_HOST],
connectSrc: ["'self'"],
formAction: ["'self'"],
objectSrc: ["'none'"]
},
reportOnly: false
}
};

export const MOCK_RATE_LIMIT_VALUE = {
windowMs: 15 * 60 * 1000,
limit: 100,
Expand Down
6 changes: 3 additions & 3 deletions test/unit/config/helmet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ jest.mock('helmet');
import { describe, expect, test, jest, afterEach } from '@jest/globals';
import helmet from 'helmet';

import { configureHelmet } from '../../../src/config/helmet';
import { MOCK_HELMET_VALUE, MOCK_EXPRESS_APP } from '../../mock/data';
import { configureHelmet, HELMET_OPTIONS } from '../../../src/config/helmet';
import { MOCK_EXPRESS_APP } from '../../mock/data';

describe('Helmet Config test suites', () => {
afterEach(() => {
Expand All @@ -17,7 +17,7 @@ describe('Helmet Config test suites', () => {
configureHelmet(MOCK_EXPRESS_APP);

expect(mockHelmet).toHaveBeenCalledTimes(1);
expect(mockHelmet).toHaveBeenCalledWith(MOCK_HELMET_VALUE);
expect(mockHelmet).toHaveBeenCalledWith(HELMET_OPTIONS);
expect(MOCK_EXPRESS_APP.use).toHaveBeenCalled();
});
});
4 changes: 2 additions & 2 deletions test/unit/config/nunjucks.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
jest.mock('nunjucks');
jest.mock('../../../src/utils/logger');

import { describe, expect, test, jest } from '@jest/globals';
import { describe, expect, afterEach, test, jest } from '@jest/globals';

import * as nunjucks from 'nunjucks';

Expand Down Expand Up @@ -32,7 +32,7 @@ describe('Nunjucks Configuration test suites', () => {
configureNunjucks(MOCK_EXPRESS_APP, MOCK_VIEWS_PATH);

expect(nunjucks.configure).toHaveBeenCalledWith(
[MOCK_VIEWS_PATH, 'node_modules/govuk-frontend/dist', 'node_modules/govuk-frontend/dist/components'],
[MOCK_VIEWS_PATH, 'node_modules/govuk-frontend/dist'],
{
autoescape: true,
express: MOCK_EXPRESS_APP
Expand Down

0 comments on commit 6c522e0

Please sign in to comment.