From a8c07e07514a85c327e038baf34c8e059a1dfad1 Mon Sep 17 00:00:00 2001 From: Vladimir Kudinov Date: Sat, 8 Mar 2025 17:38:52 +0300 Subject: [PATCH] feat: add nonce to res.locals as well / closes #12 --- packages/express-csp-header/src/index.ts | 10 ++++++---- packages/express-csp-header/tests/index.test.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/express-csp-header/src/index.ts b/packages/express-csp-header/src/index.ts index 8ab0ff9..9c3d753 100644 --- a/packages/express-csp-header/src/index.ts +++ b/packages/express-csp-header/src/index.ts @@ -44,7 +44,7 @@ export function expressCspHeader(params?: ExpressCSPParams): RequestHandler { const { domainOptions } = params; let cspString = getCspString(req, res, params); - cspString = applyNonce(req, cspString); + cspString = applyNonce(req ,res, cspString); cspString = applyAutoTld(req, cspString, domainOptions); res.set(params.reportOnly ? CSP_REPORT_ONLY_HEADER : CSP_HEADER, cspString); @@ -83,11 +83,13 @@ function getCspString(req: Request, res: Response, params: ExpressCSPParams): st return getCSP(cspHeaderParams); } -function applyNonce(req: Request, cspString: string): string { +function applyNonce(req: Request, res: Response, cspString: string): string { if (cspString.includes(NONCE)) { - req.nonce = randomBytes(16).toString('base64'); + const nonceValue = randomBytes(16).toString('base64'); + req.nonce = nonceValue; + res.locals.nonce = nonceValue; - return cspString.replace(new RegExp(NONCE, 'g'), nonce(req.nonce)); + return cspString.replace(new RegExp(NONCE, 'g'), nonce(nonceValue)); } return cspString; diff --git a/packages/express-csp-header/tests/index.test.ts b/packages/express-csp-header/tests/index.test.ts index fa67693..05bdfed 100644 --- a/packages/express-csp-header/tests/index.test.ts +++ b/packages/express-csp-header/tests/index.test.ts @@ -4,6 +4,7 @@ import { expressCspHeader, ExpressCSPParams, SELF, INLINE, NONE, NONCE, TLD } fr function execMiddleware(params?: ExpressCSPParams, req: Partial = {}) { const res = { headers: {} as Record, + locals: {} as Record, set(headerName: string, headerVal: string) { this.headers[headerName] = headerVal; } @@ -36,15 +37,17 @@ test('should not set header with no params', () => { expect(res.headers['Content-Security-Policy']).toStrictEqual(undefined); }); -test('should set req.nonce', () => { - const { req, res} = execMiddleware({ +test('should set req.nonce and res.locals.nonce', () => { + const { req, res } = execMiddleware({ directives: { 'script-src': [NONCE] } }); - expect(res.headers['Content-Security-Policy']).toMatch(/^script-src 'nonce-.+';/); + expect(res.locals).toHaveProperty('nonce'); expect(req).toHaveProperty('nonce'); + expect(req.nonce).toEqual(res.locals.nonce); + expect(res.headers['Content-Security-Policy']).toMatch(new RegExp(`^script-src \'nonce-${req.nonce}\';`)); }); describe('report-uri', () => {