mirror of
https://github.com/strapi/strapi.git
synced 2025-09-27 09:25:46 +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 helmet, { KoaHelmet } from 'koa-helmet';
|
||||||
|
|
||||||
import type { Common } from '@strapi/types';
|
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> =
|
export const security: Common.MiddlewareFactory<Config> =
|
||||||
(config, { strapi }) =>
|
(config, { strapi }) =>
|
||||||
(ctx, next) => {
|
(ctx, next) => {
|
||||||
@ -42,7 +50,7 @@ export const security: Common.MiddlewareFactory<Config> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.method === 'GET' && specialPaths.some((str) => ctx.path.startsWith(str))) {
|
if (ctx.method === 'GET' && specialPaths.some((str) => ctx.path.startsWith(str))) {
|
||||||
helmetConfig = merge(helmetConfig, {
|
helmetConfig = mergeConfig(helmetConfig, {
|
||||||
contentSecurityPolicy: {
|
contentSecurityPolicy: {
|
||||||
directives: {
|
directives: {
|
||||||
'script-src': ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net'],
|
'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))) {
|
if (ctx.method === 'GET' && ['/admin'].some((str) => ctx.path.startsWith(str))) {
|
||||||
helmetConfig = merge(helmetConfig, {
|
helmetConfig = mergeConfig(helmetConfig, {
|
||||||
contentSecurityPolicy: {
|
contentSecurityPolicy: {
|
||||||
directives: {
|
directives: {
|
||||||
'script-src': ["'self'", "'unsafe-inline'"],
|
'script-src': ["'self'", "'unsafe-inline'"],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user