Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Commit

Permalink
Allow scripts to contain a style CSP-nonce
Browse files Browse the repository at this point in the history
This follows on from e377515 which
introduced a script nonce. The same nonce is now used for the inline
styles, allowing a stronger CSP (nonce over unsafe-inline).
  • Loading branch information
pgjones committed May 29, 2020
1 parent 6747275 commit aac4dc4
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 7 deletions.
8 changes: 4 additions & 4 deletions runtime/src/server/middleware/get_page_handler.ts
Expand Up @@ -292,6 +292,9 @@ export function get_page_handler(
script += `</script><script src="${main}">`;
}

// users can set a CSP nonce using res.locals.nonce
const nonce_attr = (res.locals && res.locals.nonce) ? ` nonce="${res.locals.nonce}"` : '';

let styles: string;

// TODO make this consistent across apps
Expand All @@ -314,12 +317,9 @@ export function get_page_handler(
.map(href => `<link rel="stylesheet" href="client/${href}">`)
.join('')
} else {
styles = (css && css.code ? `<style>${css.code}</style>` : '');
styles = (css && css.code ? `<style${nonce_attr}>${css.code}</style>` : '');
}

// users can set a CSP nonce using res.locals.nonce
const nonce_attr = (res.locals && res.locals.nonce) ? ` nonce="${res.locals.nonce}"` : '';

const body = template()
.replace('%sapper.base%', () => `<base href="${req.baseUrl}/">`)
.replace('%sapper.scripts%', () => `<script${nonce_attr}>${script}</script>`)
Expand Down
6 changes: 3 additions & 3 deletions site/content/docs/12-security.md
Expand Up @@ -6,9 +6,9 @@ By default, Sapper does not add security headers to your app, but you may add th

### Content Security Policy (CSP)

Sapper generates inline `<script>`s, which can fail to execute if [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) headers disallow arbitrary script execution (`unsafe-inline`).
Sapper generates inline `<script>`s and `<style>`s, which can fail to execute if [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) headers disallow arbitrary script execution (`unsafe-inline`) or style parsing (`unsafe-inline`).

To work around this, Sapper can inject a [nonce](https://www.troyhunt.com/locking-down-your-website-scripts-with-csp-hashes-nonces-and-report-uri/) which can be configured with middleware to emit the proper CSP headers. Here is an example using [Express][] and [Helmet][]:
To work around this, Sapper can inject a [nonce](https://www.troyhunt.com/locking-down-your-website-scripts-with-csp-hashes-nonces-and-report-uri/) which can be configured with middleware to emit the proper CSP headers. The nonce will be applied to the inline `<script>`s and `<style>`s. Here is an example using [Express][] and [Helmet][]:

```js
// server.js
Expand Down Expand Up @@ -36,4 +36,4 @@ Using `res.locals.nonce` in this way follows the convention set by
[Helmet's CSP docs](https://helmetjs.github.io/docs/csp/#generating-nonces).

[Express]: https://expressjs.com/
[Helmet]: https://helmetjs.github.io/
[Helmet]: https://helmetjs.github.io/
4 changes: 4 additions & 0 deletions test/apps/css/src/server.js
Expand Up @@ -4,6 +4,10 @@ import * as sapper from '@sapper/server';
import { start } from '../../common.js';

const app = polka()
.use((req, res, next) => {
res.locals = { nonce: "nonce"};
next();
})
.use(sapper.middleware());

start(app);
9 changes: 9 additions & 0 deletions test/apps/css/test.ts
Expand Up @@ -25,6 +25,15 @@ describe('css', function() {
);
});

it('includes a style nonce', async () => {
await r.load('/'):

assert.equal(
await r.page.$eval('style', node => node.getAttribute('nonce')),
'nonce'
);
});

it('loads CSS when navigating client-side', async () => {
await r.load('/');

Expand Down

0 comments on commit aac4dc4

Please sign in to comment.