mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +00:00 
			
		
		
		
	Merge branch 'develop' into sec/updateMySQL2
This commit is contained in:
		
						commit
						94739c5a56
					
				
							
								
								
									
										2
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							| @ -295,7 +295,6 @@ jobs: | |||||||
|           MYSQL_USER: strapi |           MYSQL_USER: strapi | ||||||
|           MYSQL_PASSWORD: strapi |           MYSQL_PASSWORD: strapi | ||||||
|           MYSQL_DATABASE: strapi_test |           MYSQL_DATABASE: strapi_test | ||||||
|           MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password |  | ||||||
|         options: >- |         options: >- | ||||||
|           --health-cmd="mysqladmin ping" |           --health-cmd="mysqladmin ping" | ||||||
|           --health-interval=10s |           --health-interval=10s | ||||||
| @ -450,7 +449,6 @@ jobs: | |||||||
|           MYSQL_USER: strapi |           MYSQL_USER: strapi | ||||||
|           MYSQL_PASSWORD: strapi |           MYSQL_PASSWORD: strapi | ||||||
|           MYSQL_DATABASE: strapi_test |           MYSQL_DATABASE: strapi_test | ||||||
|           MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password |  | ||||||
|         options: >- |         options: >- | ||||||
|           --health-cmd="mysqladmin ping" |           --health-cmd="mysqladmin ping" | ||||||
|           --health-interval=10s |           --health-interval=10s | ||||||
|  | |||||||
| @ -16,7 +16,6 @@ services: | |||||||
|   mysql: |   mysql: | ||||||
|     image: mysql |     image: mysql | ||||||
|     restart: always |     restart: always | ||||||
|     command: --default-authentication-plugin=mysql_native_password |  | ||||||
|     environment: |     environment: | ||||||
|       MYSQL_DATABASE: strapi_test |       MYSQL_DATABASE: strapi_test | ||||||
|       MYSQL_USER: strapi |       MYSQL_USER: strapi | ||||||
|  | |||||||
| @ -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
	 Alexandre BODIN
						Alexandre BODIN