mirror of
https://github.com/strapi/strapi.git
synced 2025-09-26 17:00:55 +00:00
Merge pull request #19590 from med8bra/fix/strapi-csp-merge
fix(security-middleware): fix config merging with defaults
This commit is contained in:
commit
1b03d7e2c0
@ -0,0 +1,65 @@
|
||||
import Koa from 'koa';
|
||||
import request from 'supertest';
|
||||
import { security } from '../security';
|
||||
|
||||
const parseCspHeader = (csp: string) =>
|
||||
Object.fromEntries(
|
||||
csp
|
||||
.split(';')
|
||||
.map((directive) => directive.split(' '))
|
||||
.map(([k, ...v]) => [k, v])
|
||||
);
|
||||
|
||||
describe('Security middleware', () => {
|
||||
describe('Content security policy', () => {
|
||||
// GIVEN
|
||||
const app = new Koa();
|
||||
const securityMiddleware = security(
|
||||
{
|
||||
contentSecurityPolicy: {
|
||||
useDefaults: true,
|
||||
directives: {
|
||||
'script-src': ["'self'", 'https://cdn.custom.com'],
|
||||
upgradeInsecureRequests: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
strapi: {
|
||||
plugin: () => null,
|
||||
} as any,
|
||||
}
|
||||
)!;
|
||||
|
||||
// WHEN
|
||||
app.use(securityMiddleware);
|
||||
const agent = request.agent(app.callback());
|
||||
|
||||
// THEN
|
||||
it.each(['/', '/admin', '/api'])(
|
||||
'includes user custom CSP directives in GET %s response',
|
||||
async (path) => {
|
||||
await agent.get(path).expect((req) => {
|
||||
const csp = parseCspHeader(req.header['content-security-policy']);
|
||||
expect(csp['script-src']).toContain('https://cdn.custom.com');
|
||||
});
|
||||
}
|
||||
);
|
||||
it('includes required default CSP directives in GET /admin response', async () => {
|
||||
await agent.get('/admin').expect((req) => {
|
||||
const csp = parseCspHeader(req.header['content-security-policy']);
|
||||
expect(csp['script-src']).toContain("'unsafe-inline'");
|
||||
expect(csp['connect-src']).toContain('ws:');
|
||||
});
|
||||
});
|
||||
it('includes required default CSP directives in GET /documentation response', async () => {
|
||||
await agent.get('/documentation').expect((req) => {
|
||||
const csp = parseCspHeader(req.header['content-security-policy']);
|
||||
expect(csp['script-src']).toContain("'unsafe-inline'");
|
||||
expect(csp['script-src']).toContain('cdn.jsdelivr.net');
|
||||
expect(csp['img-src']).toContain('strapi.io');
|
||||
expect(csp['img-src']).toContain('cdn.jsdelivr.net');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import { defaultsDeep, merge } from 'lodash/fp';
|
||||
import { defaultsDeep, mergeWith } from 'lodash/fp';
|
||||
import helmet, { KoaHelmet } from 'koa-helmet';
|
||||
|
||||
import type { Common } from '@strapi/types';
|
||||
@ -29,6 +29,14 @@ const defaults: Config = {
|
||||
},
|
||||
};
|
||||
|
||||
const mergeConfig = (existingConfig: Config, newConfig: Config) => {
|
||||
return mergeWith(
|
||||
(obj, src) => (Array.isArray(obj) && Array.isArray(src) ? obj.concat(src) : undefined),
|
||||
existingConfig,
|
||||
newConfig
|
||||
);
|
||||
};
|
||||
|
||||
export const security: Common.MiddlewareFactory<Config> =
|
||||
(config, { strapi }) =>
|
||||
(ctx, next) => {
|
||||
@ -42,7 +50,7 @@ export const security: Common.MiddlewareFactory<Config> =
|
||||
}
|
||||
|
||||
if (ctx.method === 'GET' && specialPaths.some((str) => ctx.path.startsWith(str))) {
|
||||
helmetConfig = merge(helmetConfig, {
|
||||
helmetConfig = mergeConfig(helmetConfig, {
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
'script-src': ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net'],
|
||||
@ -53,7 +61,7 @@ export const security: Common.MiddlewareFactory<Config> =
|
||||
}
|
||||
|
||||
if (ctx.method === 'GET' && ['/admin'].some((str) => ctx.path.startsWith(str))) {
|
||||
helmetConfig = merge(helmetConfig, {
|
||||
helmetConfig = mergeConfig(helmetConfig, {
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
'script-src': ["'self'", "'unsafe-inline'"],
|
||||
|
Loading…
x
Reference in New Issue
Block a user