fix: Redact prisma errors (#10536)

This commit is contained in:
Hariom Balhara 2023-08-03 20:52:38 +05:30 committed by GitHub
parent 669065cebd
commit 5491821baf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 96 additions and 16 deletions

View File

@ -1,6 +1,8 @@
import * as Sentry from "@sentry/nextjs";
import type { NextMiddleware } from "next-api-middleware";
import { redactError } from "@calcom/lib/redactError";
export const captureErrors: NextMiddleware = async (_req, res, next) => {
try {
// Catch any errors that are thrown in remaining
@ -8,7 +10,11 @@ export const captureErrors: NextMiddleware = async (_req, res, next) => {
await next();
} catch (error) {
Sentry.captureException(error);
console.log(error);
const redactedError = redactError(error);
if (redactedError instanceof Error) {
res.status(400).json({ message: redactedError.message, error: redactedError });
return;
}
res.status(400).json({ message: "Something went wrong", error });
}
};

View File

@ -28,10 +28,17 @@ const withMiddleware = label(
customPrismaClient,
extendRequest,
pagination: withPagination,
sentry: captureErrors,
captureErrors,
},
// The order here, determines the order of execution, put customPrismaClient before verifyApiKey always.
["extendRequest", "sentry", "customPrismaClient", "verifyApiKey", "addRequestId"] // <-- Provide a list of middleware to call automatically
// The order here, determines the order of execution
[
"extendRequest",
"captureErrors",
// - Put customPrismaClient before verifyApiKey always.
"customPrismaClient",
"verifyApiKey",
"addRequestId",
] // <-- Provide a list of middleware to call automatically
);
export { withMiddleware };

View File

@ -10,6 +10,7 @@ import React from "react";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { redactError } from "@calcom/lib/redactError";
import { ErrorPage } from "@components/error/error-page";
@ -59,10 +60,17 @@ CustomError.getInitialProps = async (ctx: AugmentedNextPageContext) => {
// If a HttpError message, let's override defaults
if (err instanceof HttpError) {
const redactedError = redactError(err);
errorInitialProps.statusCode = err.statusCode;
errorInitialProps.title = err.name;
errorInitialProps.message = err.message;
errorInitialProps.err = err;
errorInitialProps.title = redactedError.name;
errorInitialProps.message = redactedError.message;
errorInitialProps.err = {
...redactedError,
url: err.url,
statusCode: err.statusCode,
cause: err.cause,
method: err.method,
};
}
if (res) {

View File

@ -0,0 +1,26 @@
import { Prisma } from "@prisma/client";
import logger from "@calcom/lib/logger";
const log = logger.getChildLogger({ prefix: [`[[redactError]`] });
function shouldRedact<T extends Error>(error: T) {
return (
error instanceof Prisma.PrismaClientInitializationError ||
error instanceof Prisma.PrismaClientKnownRequestError ||
error instanceof Prisma.PrismaClientUnknownRequestError ||
error instanceof Prisma.PrismaClientValidationError
);
}
export const redactError = <T extends Error | unknown>(error: T) => {
if (!(error instanceof Error)) {
return error;
}
log.debug("Type of Error: ", error.constructor);
if (shouldRedact(error)) {
log.error("Error: ", JSON.stringify(error));
return new Error("An error occured while querying the database.");
}
return error;
};

View File

@ -4,6 +4,7 @@ import type { ZodIssue } from "zod";
import { ZodError } from "zod";
import { HttpError } from "../http-error";
import { redactError } from "../redactError";
function hasName(cause: unknown): cause is { name: string } {
return !!cause && typeof cause === "object" && "name" in cause;
@ -40,20 +41,28 @@ export function getServerErrorFromUnknown(cause: unknown): HttpError {
message: "Unexpected error, please reach out for our customer support.",
});
}
if (cause instanceof PrismaClientKnownRequestError) {
return new HttpError({ statusCode: 400, message: cause.message, cause });
return getHttpError({ statusCode: 400, cause });
}
if (cause instanceof NotFoundError) {
return new HttpError({ statusCode: 404, message: cause.message, cause });
return getHttpError({ statusCode: 404, cause });
}
if (cause instanceof Stripe.errors.StripeInvalidRequestError) {
return new HttpError({ statusCode: 400, message: cause.message, cause });
return getHttpError({ statusCode: 400, cause });
}
if (cause instanceof HttpError) {
return cause;
const redactedCause = redactError(cause);
return {
...redactedCause,
cause: cause.cause,
url: cause.url,
statusCode: cause.statusCode,
method: cause.method,
};
}
if (cause instanceof Error) {
return new HttpError({ statusCode: 500, message: cause.message, cause });
return getHttpError({ statusCode: 500, cause });
}
if (typeof cause === "string") {
// @ts-expect-error https://github.com/tc39/proposal-error-cause
@ -65,3 +74,8 @@ export function getServerErrorFromUnknown(cause: unknown): HttpError {
message: `Unhandled error of type '${typeof cause}'. Please reach out for our customer support.`,
});
}
function getHttpError<T extends Error>({ statusCode, cause }: { statusCode: number; cause: T }) {
const redacted = redactError(cause);
return new HttpError({ statusCode, message: redacted.message, cause: redacted });
}

View File

@ -0,0 +1,17 @@
import { redactError } from "@calcom/lib/redactError";
import { middleware } from "../trpc";
const captureErrorsMiddleware = middleware(async ({ next }) => {
const result = await next();
if (result && !result.ok) {
const cause = result.error.cause;
if (!cause) {
return result;
}
throw redactError(cause);
}
return result;
});
export default captureErrorsMiddleware;

View File

@ -1,3 +1,4 @@
import captureErrorsMiddleware from "../middlewares/captureErrorsMiddleware";
import perfMiddleware from "../middlewares/perfMiddleware";
import { isAdminMiddleware, isAuthed, isOrgAdminMiddleware } from "../middlewares/sessionMiddleware";
import { procedure } from "../trpc";
@ -23,10 +24,10 @@ const isRateLimitedByUserIdMiddleware = ({ intervalInMs, limit }: IRateLimitOpti
return next({ ctx: { user: ctx.user, session: ctx.session } });
});
*/
const authedProcedure = procedure.use(perfMiddleware).use(isAuthed);
const authedProcedure = procedure.use(captureErrorsMiddleware).use(perfMiddleware).use(isAuthed);
/*export const authedRateLimitedProcedure = ({ intervalInMs, limit }: IRateLimitOptions) =>
authedProcedure.use(isRateLimitedByUserIdMiddleware({ intervalInMs, limit }));*/
export const authedAdminProcedure = publicProcedure.use(isAdminMiddleware);
export const authedOrgAdminProcedure = publicProcedure.use(isOrgAdminMiddleware);
export const authedAdminProcedure = publicProcedure.use(captureErrorsMiddleware).use(isAdminMiddleware);
export const authedOrgAdminProcedure = publicProcedure.use(captureErrorsMiddleware).use(isOrgAdminMiddleware);
export default authedProcedure;

View File

@ -1,6 +1,7 @@
import captureErrorsMiddleware from "../middlewares/captureErrorsMiddleware";
import perfMiddleware from "../middlewares/perfMiddleware";
import { tRPCContext } from "../trpc";
const publicProcedure = tRPCContext.procedure.use(perfMiddleware);
const publicProcedure = tRPCContext.procedure.use(captureErrorsMiddleware).use(perfMiddleware);
export default publicProcedure;