diff --git a/packages/npm/@amazeelabs/publisher/publisher.config.ts b/packages/npm/@amazeelabs/publisher/publisher.config.ts index 72eed9a3e7..79f51654e7 100644 --- a/packages/npm/@amazeelabs/publisher/publisher.config.ts +++ b/packages/npm/@amazeelabs/publisher/publisher.config.ts @@ -45,4 +45,8 @@ export default defineConfig({ }, }, databaseUrl: './test/database.sqlite', + responseHeaders: new Map() + .set('X-Frame-Options', 'deny') + .set('X-Content-Type-Options', 'nosniff') + .set('Content-Security-Policy', "frame-ancestors 'none'"), }); diff --git a/packages/npm/@amazeelabs/publisher/src/core/tools/config.ts b/packages/npm/@amazeelabs/publisher/src/core/tools/config.ts index 4a54f7655b..d46a95f8e2 100644 --- a/packages/npm/@amazeelabs/publisher/src/core/tools/config.ts +++ b/packages/npm/@amazeelabs/publisher/src/core/tools/config.ts @@ -125,6 +125,13 @@ export type PublisherConfig = { credentials: boolean; origin: Array; }; + /** + * A Map of response headers that should be added to every route. + * + * Example: (new Map()).set('X-Frame-Options', 'deny') + * The above would set the "X-Frame-Options" response header to "deny". + */ + responseHeaders?: Map; /** * Proxy settings. * diff --git a/packages/npm/@amazeelabs/publisher/src/server.ts b/packages/npm/@amazeelabs/publisher/src/server.ts index bca1f30ec9..7188d79a59 100644 --- a/packages/npm/@amazeelabs/publisher/src/server.ts +++ b/packages/npm/@amazeelabs/publisher/src/server.ts @@ -66,6 +66,20 @@ const runServer = async (): Promise => { next(); }); + // Add any configured response headers which should apply on every route. + app.use((req, res, next) => { + // The spread operator applied on a Map generates a 2D key-value array. So + // if we have a Map with two items: key1 => value1, key2 => value2, then + // the spread operator applied on the Map would return + // [["key1", "value1"], ["key2", "value2"]]. + [...(getConfig().responseHeaders || new Map())].map( + (responseHeader) => { + res.set(responseHeader[0], responseHeader[1]); + }, + ); + next(); + }); + core.state.applicationState$.subscribe((state) => { app.locals.isReady = state === ApplicationState.Ready; stateNotify(state);