From 9ce345d99d73d2714cda39e826622a7442a03571 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 15 Jul 2020 15:06:13 +0200 Subject: [PATCH] Don't start in production if important security fields are misconfigured --- config/default.ts | 4 ++-- src/Application.ts | 11 +++++++++++ src/ApplicationComponent.ts | 13 ++++++++++++- src/SecurityError.ts | 8 ++++++++ src/components/AutoUpdateComponent.ts | 4 ++++ src/components/MailComponent.ts | 13 +++++++++++++ src/components/SessionComponent.ts | 9 +++++++++ 7 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/SecurityError.ts diff --git a/config/default.ts b/config/default.ts index 9888df4..4e3995c 100644 --- a/config/default.ts +++ b/config/default.ts @@ -8,7 +8,7 @@ export default { public_url: "http://localhost:4899", public_websocket_url: "ws://localhost:4899", port: 4899, - gitlab_webhook_token: 'secret', + gitlab_webhook_token: 'default', mysql: { connectionLimit: 10, host: "localhost", @@ -23,7 +23,7 @@ export default { prefix: 'wms' }, session: { - secret: "very_secret_not_known", + secret: 'default', cookie: { secure: false, maxAge: 30 * 24 * 3600 * 1000, // 30 days diff --git a/src/Application.ts b/src/Application.ts index 17a23f5..a9bb87f 100644 --- a/src/Application.ts +++ b/src/Application.ts @@ -62,6 +62,11 @@ export default abstract class Application { // Register all components and alike await this.init(); + // Security + if (process.env.NODE_ENV === 'production') { + await this.checkSecuritySettings(); + } + // Init express const app = express(); const initRouter = express.Router(); @@ -171,6 +176,12 @@ export default abstract class Application { return false; } + private async checkSecuritySettings(): Promise { + for (const component of this.components) { + await component.checkSecuritySettings(); + } + } + async stop(): Promise { Logger.info('Stopping application...'); diff --git a/src/ApplicationComponent.ts b/src/ApplicationComponent.ts index 200c5fb..1482e73 100644 --- a/src/ApplicationComponent.ts +++ b/src/ApplicationComponent.ts @@ -2,11 +2,16 @@ import {Express, Router} from "express"; import Logger from "./Logger"; import {sleep} from "./Utils"; import Application from "./Application"; +import config from "config"; +import SecurityError from "./SecurityError"; export default abstract class ApplicationComponent { private val?: T; protected app?: Application; + public async checkSecuritySettings(): Promise { + } + public async start(app: Express): Promise { } @@ -60,4 +65,10 @@ export default abstract class ApplicationComponent { Logger.error(e, `An error occurred while closing the ${thingName}.`); } } -} \ No newline at end of file + + protected checkSecurityConfigField(field: string) { + if (!config.has(field) || config.get(field) === 'default') { + throw new SecurityError('field not configured.'); + } + } +} diff --git a/src/SecurityError.ts b/src/SecurityError.ts new file mode 100644 index 0000000..0122dcc --- /dev/null +++ b/src/SecurityError.ts @@ -0,0 +1,8 @@ +export default class SecurityError implements Error { + public readonly name: string = 'SecurityError'; + public readonly message: string; + + public constructor(message: string) { + this.message = message; + } +} \ No newline at end of file diff --git a/src/components/AutoUpdateComponent.ts b/src/components/AutoUpdateComponent.ts index cc275ca..0f82972 100644 --- a/src/components/AutoUpdateComponent.ts +++ b/src/components/AutoUpdateComponent.ts @@ -6,6 +6,10 @@ import {ForbiddenHttpError} from "../HttpError"; import Logger from "../Logger"; export default class AutoUpdateComponent extends ApplicationComponent { + public async checkSecuritySettings(): Promise { + this.checkSecurityConfigField('gitlab_webhook_token'); + } + public async init(router: Router): Promise { router.post('/update/push.json', (req, res) => { const token = req.header('X-Gitlab-Token'); diff --git a/src/components/MailComponent.ts b/src/components/MailComponent.ts index 20f2db7..d42a1d7 100644 --- a/src/components/MailComponent.ts +++ b/src/components/MailComponent.ts @@ -1,8 +1,21 @@ import ApplicationComponent from "../ApplicationComponent"; import {Express} from "express"; import Mail from "../Mail"; +import config from "config"; +import SecurityError from "../SecurityError"; export default class MailComponent extends ApplicationComponent { + + + public async checkSecuritySettings(): Promise { + if (!config.get('mail.secure')) { + throw new SecurityError('Cannot set mail.secure to false'); + } + if (config.get('mail.allow_invalid_tls')) { + throw new SecurityError('Cannot set mail.allow_invalid_tls to true'); + } + } + public async start(app: Express): Promise { await this.prepare('Mail connection', () => Mail.prepare()); } diff --git a/src/components/SessionComponent.ts b/src/components/SessionComponent.ts index d06e975..2409d8c 100644 --- a/src/components/SessionComponent.ts +++ b/src/components/SessionComponent.ts @@ -4,6 +4,7 @@ import config from "config"; import RedisComponent from "./RedisComponent"; import flash from "connect-flash"; import {Router} from "express"; +import SecurityError from "../SecurityError"; export default class SessionComponent extends ApplicationComponent { private readonly storeComponent: RedisComponent; @@ -13,6 +14,14 @@ export default class SessionComponent extends ApplicationComponent { this.storeComponent = storeComponent; } + + public async checkSecuritySettings(): Promise { + this.checkSecurityConfigField('session.secret'); + if (!config.get('session.cookie.secure')) { + throw new SecurityError('Cannot set cookie secure field to false.'); + } + } + public async init(router: Router): Promise { router.use(session({ saveUninitialized: true,