From 34b2f2f7480e045d0ff4f534b1af2b1e4151eeac Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Mon, 14 Aug 2023 00:42:47 +0800 Subject: [PATCH] feat: Hiding stack traces in production env --- packages/backend/src/misc/error.ts | 10 +++++++++- .../src/server/ActivityPubServerService.ts | 3 +++ packages/backend/src/server/FileServerService.ts | 2 ++ .../backend/src/server/NodeinfoServerService.ts | 3 +++ packages/backend/src/server/ServerService.ts | 2 ++ .../backend/src/server/WellKnownServerService.ts | 3 +++ .../backend/src/server/api/ApiServerService.ts | 3 +++ .../server/api/openapi/OpenApiServerService.ts | 2 ++ .../src/server/oauth/OAuth2ProviderService.ts | 5 ++++- .../src/server/web/ClientServerService.ts | 16 ++++++++-------- 10 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/misc/error.ts b/packages/backend/src/misc/error.ts index 000c4862fc..2ea56dd96c 100644 --- a/packages/backend/src/misc/error.ts +++ b/packages/backend/src/misc/error.ts @@ -2,11 +2,19 @@ * SPDX-FileCopyrightText: MomentQYC and other misskey contributors * SPDX-License-Identifier: AGPL-3.0-only */ +import { FastifyReply, FastifyRequest } from 'fastify'; -export function ErrorHandling(message: string): Error { +export function ErrorHandling(message: string, reply?: FastifyReply, statusCode?: number): Error { const error = new Error(message); if (process.env.NODE_ENV === 'production') { error.stack = undefined; } + if (reply) { + reply.code(statusCode ?? 500); + } return error; } + +export function ErrorHandler(error: Error, request: FastifyRequest, reply: FastifyReply): void { + throw ErrorHandling(error.message, reply); +} diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index c9ce1dcaee..6ad1876850 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -26,6 +26,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import { IActivity } from '@/core/activitypub/type.js'; +import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; import type { FindOptionsWhere } from 'typeorm'; @@ -462,6 +463,8 @@ export class ActivityPubServerService { fastify.addContentTypeParser('application/activity+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore')); fastify.addContentTypeParser('application/ld+json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore')); + fastify.setErrorHandler(ErrorHandler); + fastify.addHook('onRequest', (request, reply, done) => { reply.header('Access-Control-Allow-Headers', 'Accept'); reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index c52ce4568e..036d746f37 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { correctFilename } from '@/misc/correct-filename.js'; +import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; const _filename = fileURLToPath(import.meta.url); @@ -59,6 +60,7 @@ export class FileServerService { @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { + fastify.setErrorHandler(ErrorHandler); fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); done(); diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 95cc697768..7fbb11a89e 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -14,6 +14,7 @@ import { bindThis } from '@/decorators.js'; import NotesChart from '@/core/chart/charts/notes.js'; import UsersChart from '@/core/chart/charts/users.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; +import { ErrorHandler } from '@/misc/error.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; const nodeinfo2_1path = '/nodeinfo/2.1'; @@ -118,6 +119,8 @@ export class NodeinfoServerService { const cache = new MemorySingleCache>>(1000 * 60 * 10); + fastify.setErrorHandler(ErrorHandler); + fastify.get(nodeinfo2_1path, async (request, reply) => { const base = await cache.fetch(() => nodeinfo2()); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index b40eeb31b1..1cf7c7e859 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -22,6 +22,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; +import { ErrorHandler } from '@/misc/error.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ApiServerService } from './api/ApiServerService.js'; @@ -76,6 +77,7 @@ export class ServerService implements OnApplicationShutdown { logger: !['production', 'test'].includes(process.env.NODE_ENV ?? ''), }); this.#fastify = fastify; + fastify.setErrorHandler(ErrorHandler); // HSTS // 6months (15552000sec) diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 27139a0d69..bd4bbea372 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -15,6 +15,7 @@ import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandler } from '@/misc/error.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import type { FindOptionsWhere } from 'typeorm'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -51,6 +52,8 @@ export class WellKnownServerService { fastify.register(fastifyAccepts); + fastify.setErrorHandler(ErrorHandler); + fastify.addHook('onRequest', (request, reply, done) => { reply.header('Access-Control-Allow-Headers', 'Accept'); reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 96b6a821d5..6bdb017d83 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -13,6 +13,7 @@ import type { InstancesRepository, AccessTokensRepository } from '@/models/index import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandler } from '@/misc/error.js'; import endpoints from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; @@ -56,6 +57,8 @@ export class ApiServerService { fastify.register(fastifyCookie, {}); + fastify.setErrorHandler(ErrorHandler); + // Prevent cache fastify.addHook('onRequest', (request, reply, done) => { reply.header('Cache-Control', 'private, max-age=0, must-revalidate'); diff --git a/packages/backend/src/server/api/openapi/OpenApiServerService.ts b/packages/backend/src/server/api/openapi/OpenApiServerService.ts index cb22d0f7c9..6ce766e1bf 100644 --- a/packages/backend/src/server/api/openapi/OpenApiServerService.ts +++ b/packages/backend/src/server/api/openapi/OpenApiServerService.ts @@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; +import { ErrorHandler } from '@/misc/error.js'; import { genOpenapiSpec } from './gen-spec.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @@ -23,6 +24,7 @@ export class OpenApiServerService { @bindThis public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) { + fastify.setErrorHandler(ErrorHandler); fastify.get('/api-doc', async (_request, reply) => { reply.header('Cache-Control', 'public, max-age=86400'); return await reply.sendFile('/redoc.html', staticAssets); diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index 9f8e8a3eb1..726c7298bb 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -31,9 +31,9 @@ import { MemoryKVCache } from '@/misc/cache.js'; import { LoggerService } from '@/core/LoggerService.js'; import Logger from '@/logger.js'; import { StatusError } from '@/misc/status-error.js'; +import { ErrorHandler, ErrorHandling } from '@/misc/error.js'; import type { ServerResponse } from 'node:http'; import type { FastifyInstance } from 'fastify'; -import { ErrorHandling } from '@/misc/error.js'; // TODO: Consider migrating to @node-oauth/oauth2-server once // https://github.com/node-oauth/node-oauth2-server/issues/180 is figured out. @@ -354,6 +354,9 @@ export class OAuth2ProviderService { public async createServer(fastify: FastifyInstance): Promise { // https://datatracker.ietf.org/doc/html/rfc8414.html // https://indieauth.spec.indieweb.org/#indieauth-server-metadata + + fastify.setErrorHandler(ErrorHandler); + fastify.get('/.well-known/oauth-authorization-server', async (_request, reply) => { reply.send({ issuer: this.config.url, diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 5ed12000a0..7614b2cf9b 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -42,7 +42,6 @@ import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; import type { FastifyInstance, FastifyPluginOptions, FastifyReply } from 'fastify'; -import { ErrorHandling } from '@/misc/error.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -147,18 +146,18 @@ export class ClientServerService { if (request.url === bullBoardPath || request.url.startsWith(bullBoardPath + '/')) { const token = request.cookies.token; if (token == null) { - reply.code(401); - throw ErrorHandling('login required'); + reply.code(401).send('Login required'); + return; } const user = await this.usersRepository.findOneBy({ token }); if (user == null) { - reply.code(403); - throw ErrorHandling('no such user'); + reply.code(403).send('No such user'); + return; } const isAdministrator = await this.roleService.isAdministrator(user); if (!isAdministrator) { - reply.code(403); - throw ErrorHandling('access denied'); + reply.code(403).send('Access denied'); + return; } } }); @@ -683,12 +682,13 @@ export class ClientServerService { fastify.setErrorHandler(async (error, request, reply) => { const errId = randomUUID(); + const stack = (process.env.NODE_ENV === 'production') ? '' : error.stack; this.clientLoggerService.logger.error(`Internal error occurred in ${request.routerPath}: ${error.message}`, { path: request.routerPath, params: request.params, query: request.query, code: error.name, - stack: error.stack, + stack, id: errId, }); reply.code(500);