From 3aaa1cde0dd69f42bb523f6e5db8f2f31d947c32 Mon Sep 17 00:00:00 2001
From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Date: Tue, 28 Mar 2023 11:40:13 +0200
Subject: [PATCH] Fixes formatted description in email + sanitize html
everywhere (#7928)
* use sanitize-html instead
* add list formatting
* add lost changes from merging main
* fixes caused by merge
* remove dompurify
* Revert "remove dompurify"
This reverts commit 583bb623c7d3c0c7daa28e63ab2b1ac2da68c0d8.
* sanitize already done in editor
* Update yarn.lock
---------
Co-authored-by: CarinaWolli
Co-authored-by: Alex van Andel
---
.../EventTypeDescriptionSafeHTML.tsx | 6 +-
apps/web/components/team/screens/Team.tsx | 11 +--
apps/web/package.json | 2 +
apps/web/pages/[user].tsx | 8 +-
apps/web/pages/[user]/[type].tsx | 8 +-
apps/web/pages/[user]/book.tsx | 2 +
apps/web/pages/d/[link]/[slug].tsx | 2 +
apps/web/pages/d/[link]/book.tsx | 2 +
apps/web/pages/event-types/index.tsx | 2 +-
apps/web/pages/team/[slug].tsx | 13 ++-
apps/web/pages/team/[slug]/[type].tsx | 2 +
apps/web/pages/team/[slug]/book.tsx | 2 +
packages/emails/src/components/Info.tsx | 21 ++++-
.../src/templates/BaseScheduledEmail.tsx | 2 +-
.../ee/teams/pages/team-profile-view.tsx | 2 +-
.../components/EventTypeDescription.tsx | 4 +-
packages/lib/markdownIt.ts | 9 ---
packages/lib/markdownToSafeHTML.ts | 23 ++++++
packages/prisma/index.ts | 6 +-
.../eventTypeDescriptionParseAndSanitize.ts | 43 ----------
packages/prisma/middleware/index.ts | 1 -
packages/prisma/seed-app-store.config.json | 3 +-
.../trpc/server/routers/viewer/eventTypes.ts | 2 +
packages/trpc/server/routers/viewer/teams.tsx | 3 +-
.../editor/plugins/ToolbarPlugin.tsx | 2 +-
yarn.lock | 81 ++++++++++++++++++-
26 files changed, 175 insertions(+), 87 deletions(-)
create mode 100644 packages/lib/markdownToSafeHTML.ts
delete mode 100644 packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts
diff --git a/apps/web/components/eventtype/EventTypeDescriptionSafeHTML.tsx b/apps/web/components/eventtype/EventTypeDescriptionSafeHTML.tsx
index d60a64019c..29f283b2ba 100644
--- a/apps/web/components/eventtype/EventTypeDescriptionSafeHTML.tsx
+++ b/apps/web/components/eventtype/EventTypeDescriptionSafeHTML.tsx
@@ -1,11 +1,11 @@
export type EventTypeDescriptionSafeProps = {
- eventType: { description: string | null };
+ eventType: { description: string | null; descriptionAsSafeHTML: string | null };
};
export const EventTypeDescriptionSafeHTML = ({ eventType }: EventTypeDescriptionSafeProps) => {
const props: JSX.IntrinsicElements["div"] = { suppressHydrationWarning: true };
- // @ts-expect-error: @see packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts
- if (eventType.description) props.dangerouslySetInnerHTML = { __html: eventType.descriptionAsSafeHTML };
+ if (eventType.description)
+ props.dangerouslySetInnerHTML = { __html: eventType.descriptionAsSafeHTML || "" };
return ;
};
diff --git a/apps/web/components/team/screens/Team.tsx b/apps/web/components/team/screens/Team.tsx
index 4da45f819b..76ce292fe2 100644
--- a/apps/web/components/team/screens/Team.tsx
+++ b/apps/web/components/team/screens/Team.tsx
@@ -2,13 +2,14 @@ import Link from "next/link";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { md } from "@calcom/lib/markdownIt";
import type { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { Avatar } from "@calcom/ui";
type TeamType = NonNullable;
type MembersType = TeamType["members"];
-type MemberType = MembersType[number];
+type MemberType = MembersType[number] & { safeBio: string | null };
+
+type TeamTypeWithSafeHtml = Omit & { members: MemberType[] };
const Member = ({ member, teamName }: { member: MemberType; teamName: string | null }) => {
const { t } = useLocale();
@@ -30,7 +31,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
<>
>
) : (
@@ -43,7 +44,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
);
};
-const Members = ({ members, teamName }: { members: MembersType; teamName: string | null }) => {
+const Members = ({ members, teamName }: { members: MemberType[]; teamName: string | null }) => {
if (!members || members.length === 0) {
return null;
}
@@ -57,7 +58,7 @@ const Members = ({ members, teamName }: { members: MembersType; teamName: string
);
};
-const Team = ({ team }: { team: TeamType }) => {
+const Team = ({ team }: { team: TeamTypeWithSafeHtml }) => {
return (
diff --git a/apps/web/package.json b/apps/web/package.json
index 9e80820ddb..0f9ae44fd3 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -119,6 +119,7 @@
"react-window": "^1.8.7",
"remark": "^14.0.2",
"rrule": "^2.7.1",
+ "sanitize-html": "^2.10.0",
"schema-dts": "^1.1.0",
"short-uuid": "^4.2.0",
"strip-markdown": "^5.0.0",
@@ -156,6 +157,7 @@
"@types/react-phone-number-input": "^3.0.14",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
+ "@types/sanitize-html": "^2.9.0",
"@types/stripe": "^8.0.417",
"@types/uuid": "8.3.1",
"autoprefixer": "^10.4.12",
diff --git a/apps/web/pages/[user].tsx b/apps/web/pages/[user].tsx
index 8e032088f8..c24af1651c 100644
--- a/apps/web/pages/[user].tsx
+++ b/apps/web/pages/[user].tsx
@@ -24,7 +24,7 @@ import defaultEvents, {
} from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
-import { md } from "@calcom/lib/markdownIt";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import prisma from "@calcom/prisma";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
@@ -147,7 +147,7 @@ export default function User(props: inferSSRProps
& E
<>
>
)}
@@ -343,6 +343,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const eventTypes = eventTypesRaw.map((eventType) => ({
...eventType,
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
+ descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
}));
const isSingleUser = users.length === 1;
@@ -352,9 +353,12 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
})
: [];
+ const safeBio = markdownToSafeHTML(user.bio) || "";
+
return {
props: {
users,
+ safeBio,
profile,
user: {
emailMd5: crypto.createHash("md5").update(user.email).digest("hex"),
diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx
index b0879a49ce..4e5c27aed2 100644
--- a/apps/web/pages/[user]/[type].tsx
+++ b/apps/web/pages/[user]/[type].tsx
@@ -5,7 +5,7 @@ import type { LocationObject } from "@calcom/app-store/locations";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { addListFormatting } from "@calcom/lib/markdownIt";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import type { User } from "@calcom/prisma/client";
import { isBrandingHidden } from "@lib/isBrandingHidden";
@@ -59,7 +59,6 @@ Type.isThemeSupported = true;
const paramsSchema = z.object({ type: z.string(), user: z.string() });
async function getUserPageProps(context: GetStaticPropsContext) {
// load server side dependencies
- const MarkdownIt = await import("markdown-it").then((mod) => mod.default);
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
@@ -124,9 +123,6 @@ async function getUserPageProps(context: GetStaticPropsContext) {
},
},
});
-
- const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
-
if (!user || !user.eventTypes.length) return { notFound: true };
const [eventType]: ((typeof user.eventTypes)[number] & {
@@ -153,7 +149,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
locations: privacyFilteredLocations(locations),
- descriptionAsSafeHTML: eventType.description ? addListFormatting(md.render(eventType.description)) : null,
+ descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
});
// Check if the user you are logging into has any active teams or premium user name
const hasActiveTeam =
diff --git a/apps/web/pages/[user]/book.tsx b/apps/web/pages/[user]/book.tsx
index 764332c1fc..ab729bd3ff 100644
--- a/apps/web/pages/[user]/book.tsx
+++ b/apps/web/pages/[user]/book.tsx
@@ -14,6 +14,7 @@ import {
getUsernameList,
} from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import prisma, { bookEventTypeSelect } from "@calcom/prisma";
import {
customInputSchema,
@@ -189,6 +190,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
slug: u.username,
theme: u.theme,
})),
+ descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
};
})[0];
diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx
index 29ec5b3f80..bbb3c7eef4 100644
--- a/apps/web/pages/d/[link]/[slug].tsx
+++ b/apps/web/pages/d/[link]/[slug].tsx
@@ -5,6 +5,7 @@ import type { LocationObject } from "@calcom/core/location";
import { privacyFilteredLocations } from "@calcom/core/location";
import { parseRecurringEvent } from "@calcom/lib";
import { getWorkingHours } from "@calcom/lib/availability";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { availiblityPageEventTypeSelect } from "@calcom/prisma";
import prisma from "@calcom/prisma";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
@@ -119,6 +120,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
hideBranding: u.hideBranding,
timeZone: u.timeZone,
})),
+ descriptionAsSafeHTML: markdownToSafeHTML(hashedLink.eventType.description),
});
const [user] = users;
diff --git a/apps/web/pages/d/[link]/book.tsx b/apps/web/pages/d/[link]/book.tsx
index d71e7457e1..6e148844f9 100644
--- a/apps/web/pages/d/[link]/book.tsx
+++ b/apps/web/pages/d/[link]/book.tsx
@@ -1,6 +1,7 @@
import type { GetServerSidePropsContext } from "next";
import { parseRecurringEvent } from "@calcom/lib";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import prisma from "@calcom/prisma";
import { bookEventTypeSelect } from "@calcom/prisma/selects";
import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
@@ -93,6 +94,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
brandColor: u.brandColor,
darkBrandColor: u.darkBrandColor,
})),
+ descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
};
})[0];
diff --git a/apps/web/pages/event-types/index.tsx b/apps/web/pages/event-types/index.tsx
index ecacf53eb8..aa52db80c7 100644
--- a/apps/web/pages/event-types/index.tsx
+++ b/apps/web/pages/event-types/index.tsx
@@ -141,7 +141,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx
index 0a9741b179..1695750ab7 100644
--- a/apps/web/pages/team/[slug].tsx
+++ b/apps/web/pages/team/[slug].tsx
@@ -10,7 +10,7 @@ import { CAL_URL } from "@calcom/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
-import { md } from "@calcom/lib/markdownIt";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import prisma from "@calcom/prisma";
@@ -113,7 +113,7 @@ function TeamPage({ team, isUnpublished }: TeamPageProps) {
<>
>
)}
@@ -187,11 +187,18 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
...user,
avatar: CAL_URL + "/" + user.username + "/avatar.png",
})),
+ descriptionAsSafeHTML: markdownToSafeHTML(type.description),
}));
+ const safeBio = markdownToSafeHTML(team.bio) || "";
+
+ const members = team.members.map((member) => {
+ return { ...member, safeBio: markdownToSafeHTML(member.bio || "") };
+ });
+
return {
props: {
- team,
+ team: { ...team, safeBio, members },
trpcState: ssr.dehydrate(),
},
} as const;
diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx
index 455e6e4de0..5249962186 100644
--- a/apps/web/pages/team/[slug]/[type].tsx
+++ b/apps/web/pages/team/[slug]/[type].tsx
@@ -4,6 +4,7 @@ import type { LocationObject } from "@calcom/core/location";
import { privacyFilteredLocations } from "@calcom/core/location";
import { parseRecurringEvent } from "@calcom/lib";
import { getWorkingHours } from "@calcom/lib/availability";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import prisma from "@calcom/prisma";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
@@ -170,6 +171,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
hideBranding,
timeZone,
})),
+ descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
});
eventTypeObject.availability = [];
diff --git a/apps/web/pages/team/[slug]/book.tsx b/apps/web/pages/team/[slug]/book.tsx
index 6ed37f89a1..afce8a1438 100644
--- a/apps/web/pages/team/[slug]/book.tsx
+++ b/apps/web/pages/team/[slug]/book.tsx
@@ -5,6 +5,7 @@ import type { LocationObject } from "@calcom/app-store/locations";
import { privacyFilteredLocations } from "@calcom/app-store/locations";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { parseRecurringEvent } from "@calcom/lib";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import prisma from "@calcom/prisma";
import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
@@ -121,6 +122,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
image: u.avatar,
slug: u.username,
})),
+ descriptionAsSafeHTML: markdownToSafeHTML(eventType.description),
};
})[0];
diff --git a/packages/emails/src/components/Info.tsx b/packages/emails/src/components/Info.tsx
index a737e3177b..f91e4da20b 100644
--- a/packages/emails/src/components/Info.tsx
+++ b/packages/emails/src/components/Info.tsx
@@ -1,3 +1,5 @@
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
+
const Spacer = () => ;
export const Info = (props: {
@@ -6,8 +8,14 @@ export const Info = (props: {
extraInfo?: React.ReactNode;
withSpacer?: boolean;
lineThrough?: boolean;
+ formatted?: boolean;
}) => {
if (!props.description || props.description === "") return null;
+
+ const descriptionCSS = "color: '#101010'; font-weight: 400; line-height: 24px; margin: 0;";
+
+ const safeDescription = markdownToSafeHTML(props.description.toString()) || "";
+
return (
<>
{props.withSpacer && }
@@ -21,7 +29,18 @@ export const Info = (props: {
whiteSpace: "pre-wrap",
textDecoration: props.lineThrough ? "line-through" : undefined,
}}>
- {props.description}
+ {props.formatted ? (
+ ", `
`)
+ .replaceAll("
", ``),
+ }}
+ />
+ ) : (
+ props.description
+ )}
{props.extraInfo}
diff --git a/packages/emails/src/templates/BaseScheduledEmail.tsx b/packages/emails/src/templates/BaseScheduledEmail.tsx
index 5d084731b3..654c0378a5 100644
--- a/packages/emails/src/templates/BaseScheduledEmail.tsx
+++ b/packages/emails/src/templates/BaseScheduledEmail.tsx
@@ -76,7 +76,7 @@ export const BaseScheduledEmail = (
-
+
{props.includeAppsStatus && }
diff --git a/packages/features/ee/teams/pages/team-profile-view.tsx b/packages/features/ee/teams/pages/team-profile-view.tsx
index 419aec51a0..8215d46cff 100644
--- a/packages/features/ee/teams/pages/team-profile-view.tsx
+++ b/packages/features/ee/teams/pages/team-profile-view.tsx
@@ -256,7 +256,7 @@ const ProfileView = () => {
>
)}
diff --git a/packages/features/eventtypes/components/EventTypeDescription.tsx b/packages/features/eventtypes/components/EventTypeDescription.tsx
index 6b4953e3ce..705588b7e7 100644
--- a/packages/features/eventtypes/components/EventTypeDescription.tsx
+++ b/packages/features/eventtypes/components/EventTypeDescription.tsx
@@ -7,7 +7,6 @@ import type { z } from "zod";
import { classNames, parseRecurringEvent } from "@calcom/lib";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { useLocale } from "@calcom/lib/hooks/useLocale";
-import { addListFormatting, md } from "@calcom/lib/markdownIt";
import type { baseEventTypeSelect } from "@calcom/prisma";
import type { EventTypeModel } from "@calcom/prisma/zod";
import { Badge } from "@calcom/ui";
@@ -26,6 +25,7 @@ export type EventTypeDescriptionProps = {
z.infer,
Exclude | "metadata"
> & {
+ descriptionAsSafeHTML?: string | null;
recurringEvent: Prisma.JsonValue;
seatsPerTimeSlot?: number;
};
@@ -57,7 +57,7 @@ export const EventTypeDescription = ({
shortenDescription ? "line-clamp-4" : ""
)}
dangerouslySetInnerHTML={{
- __html: addListFormatting(md.render(eventType.description)),
+ __html: eventType.descriptionAsSafeHTML || "",
}}
/>
)}
diff --git a/packages/lib/markdownIt.ts b/packages/lib/markdownIt.ts
index 45df9a5fdd..2ac2daa290 100644
--- a/packages/lib/markdownIt.ts
+++ b/packages/lib/markdownIt.ts
@@ -1,12 +1,3 @@
import MarkdownIt from "markdown-it";
export const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
-
-export function addListFormatting(html: string) {
- return html
- .replaceAll("", "")
- .replaceAll(
- "",
- ""
- );
-}
diff --git a/packages/lib/markdownToSafeHTML.ts b/packages/lib/markdownToSafeHTML.ts
new file mode 100644
index 0000000000..f4c993377b
--- /dev/null
+++ b/packages/lib/markdownToSafeHTML.ts
@@ -0,0 +1,23 @@
+import sanitizeHtml from "sanitize-html";
+
+import { md } from "@calcom/lib/markdownIt";
+
+export function markdownToSafeHTML(markdown: string | null) {
+ if (!markdown) return null;
+
+ const html = md.render(markdown);
+
+ const safeHTML = sanitizeHtml(html);
+
+ const safeHTMLWithListFormatting = safeHTML
+ .replace(
+ //g,
+ ""
+ )
+ .replace(
+ //g,
+ ""
+ );
+
+ return safeHTMLWithListFormatting;
+}
diff --git a/packages/prisma/index.ts b/packages/prisma/index.ts
index 6782dbf9c3..2f91849dfe 100644
--- a/packages/prisma/index.ts
+++ b/packages/prisma/index.ts
@@ -1,6 +1,7 @@
-import { Prisma, PrismaClient } from "@prisma/client";
+import type { Prisma } from "@prisma/client";
+import { PrismaClient } from "@prisma/client";
-import { bookingReferenceMiddleware, eventTypeDescriptionParseAndSanitizeMiddleware } from "./middleware";
+import { bookingReferenceMiddleware } from "./middleware";
declare global {
// eslint-disable-next-line no-var
@@ -21,7 +22,6 @@ if (process.env.NODE_ENV !== "production") {
}
// If any changed on middleware server restart is required
bookingReferenceMiddleware(prisma);
-eventTypeDescriptionParseAndSanitizeMiddleware(prisma);
export default prisma;
diff --git a/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts b/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts
deleted file mode 100644
index ab561e1f20..0000000000
--- a/packages/prisma/middleware/eventTypeDescriptionParseAndSanitize.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import type { PrismaClient, EventType } from "@prisma/client";
-
-import { md } from "@calcom/lib/markdownIt";
-
-function parseAndSanitize(description: string) {
- const parsedMarkdown = md.render(description);
- return parsedMarkdown;
-}
-
-function getParsedResults(eventTypes: EventType | EventType[]) {
- const results = Array.isArray(eventTypes) ? eventTypes : [eventTypes];
- const parsedResults = results.map((record) => ({
- ...record,
- descriptionAsSafeHTML: record.description ? parseAndSanitize(record.description) : null,
- }));
- /* If the original result was an array, return the parsed array, otherwise is a single record */
- return Array.isArray(eventTypes) ? parsedResults : parsedResults[0];
-}
-
-/**
- * Parses event type descriptions and treat them as markdown,
- * then sanitizes the resulting HTML and adds a new `descriptionAsSafeHTML` property.
- */
-function eventTypeDescriptionParseAndSanitize(prisma: PrismaClient) {
- prisma.$use(async (params, next) => {
- if (params.model === "Team") {
- const result = await next(params);
- if (result?.eventTypes) {
- result.eventTypes = getParsedResults(result.eventTypes);
- }
- return result;
- } else if (params.model === "EventType") {
- const result = await next(params);
- if (result) {
- return getParsedResults(result);
- }
- return result;
- }
- return next(params);
- });
-}
-
-export default eventTypeDescriptionParseAndSanitize;
diff --git a/packages/prisma/middleware/index.ts b/packages/prisma/middleware/index.ts
index fea8946dd6..90097ed181 100644
--- a/packages/prisma/middleware/index.ts
+++ b/packages/prisma/middleware/index.ts
@@ -1,2 +1 @@
export { default as bookingReferenceMiddleware } from "./bookingReference";
-export { default as eventTypeDescriptionParseAndSanitizeMiddleware } from "./eventTypeDescriptionParseAndSanitize";
diff --git a/packages/prisma/seed-app-store.config.json b/packages/prisma/seed-app-store.config.json
index cda49aa8b7..47cec99b9c 100644
--- a/packages/prisma/seed-app-store.config.json
+++ b/packages/prisma/seed-app-store.config.json
@@ -203,7 +203,8 @@
"categories": ["video"],
"slug": "facetime",
"type": "facetime_video",
- "isTemplate": false},
+ "isTemplate": false
+ },
{
"dirName": "zohocrm",
"categories": ["other"],
diff --git a/packages/trpc/server/routers/viewer/eventTypes.ts b/packages/trpc/server/routers/viewer/eventTypes.ts
index 2cd6f7be00..b0cb38a703 100644
--- a/packages/trpc/server/routers/viewer/eventTypes.ts
+++ b/packages/trpc/server/routers/viewer/eventTypes.ts
@@ -12,6 +12,7 @@ import getApps, { getAppFromLocationValue, getAppFromSlug } from "@calcom/app-st
import { validateIntervalLimitOrder } from "@calcom/lib";
import { CAL_URL } from "@calcom/lib/constants";
import getEventTypeById from "@calcom/lib/getEventTypeById";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { baseEventTypeSelect, baseUserSelect } from "@calcom/prisma";
import { _DestinationCalendarModel, _EventTypeModel } from "@calcom/prisma/zod";
import type { CustomInputSchema } from "@calcom/prisma/zod-utils";
@@ -283,6 +284,7 @@ export const eventTypesRouter = router({
const mapEventType = (eventType: (typeof user.eventTypes)[number]) => ({
...eventType,
+ safeDescription: markdownToSafeHTML(eventType.description),
users: !!eventType.hosts?.length ? eventType.hosts.map((host) => host.user) : eventType.users,
// @FIXME: cc @hariombalhara This is failing with production data
// metadata: EventTypeMetaDataSchema.parse(eventType.metadata),
diff --git a/packages/trpc/server/routers/viewer/teams.tsx b/packages/trpc/server/routers/viewer/teams.tsx
index 7982919810..98affd758b 100644
--- a/packages/trpc/server/routers/viewer/teams.tsx
+++ b/packages/trpc/server/routers/viewer/teams.tsx
@@ -11,6 +11,7 @@ import {
updateQuantitySubscriptionFromStripe,
} from "@calcom/features/ee/teams/lib/payments";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
+import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { getTranslation } from "@calcom/lib/server/i18n";
import { getTeamWithMembers, isTeamAdmin, isTeamMember, isTeamOwner } from "@calcom/lib/server/queries/teams";
import slugify from "@calcom/lib/slugify";
@@ -42,9 +43,9 @@ export const viewerTeamsRouter = router({
throw new TRPCError({ code: "NOT_FOUND", message: "Team not found." });
}
const membership = team?.members.find((membership) => membership.id === ctx.user.id);
-
return {
...team,
+ safeBio: markdownToSafeHTML(team.bio),
membership: {
role: membership?.role as MembershipRole,
accepted: membership?.accepted,
diff --git a/packages/ui/components/editor/plugins/ToolbarPlugin.tsx b/packages/ui/components/editor/plugins/ToolbarPlugin.tsx
index 1c4fe0eb9b..ed6efb33c7 100644
--- a/packages/ui/components/editor/plugins/ToolbarPlugin.tsx
+++ b/packages/ui/components/editor/plugins/ToolbarPlugin.tsx
@@ -351,7 +351,7 @@ export default function ToolbarPlugin(props: TextEditorProps) {
editor.registerUpdateListener(({ editorState, prevEditorState }) => {
editorState.read(() => {
- const textInHtml = $generateHtmlFromNodes(editor);
+ const textInHtml = $generateHtmlFromNodes(editor).replace(/</g, "<").replace(/>/g, ">");
props.setText(textInHtml);
});
if (!prevEditorState._selection) editor.blur();
diff --git a/yarn.lock b/yarn.lock
index b0cd77a507..e2ca01c027 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4883,6 +4883,7 @@ __metadata:
"@types/react-phone-number-input": ^3.0.14
"@types/react-virtualized-auto-sizer": ^1.0.1
"@types/react-window": ^1.8.5
+ "@types/sanitize-html": ^2.9.0
"@types/stripe": ^8.0.417
"@types/turndown": ^5.0.1
"@types/uuid": 8.3.1
@@ -4952,6 +4953,7 @@ __metadata:
react-window: ^1.8.7
remark: ^14.0.2
rrule: ^2.7.1
+ sanitize-html: ^2.10.0
schema-dts: ^1.1.0
short-uuid: ^4.2.0
strip-markdown: ^5.0.0
@@ -13028,6 +13030,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/sanitize-html@npm:^2.9.0":
+ version: 2.9.0
+ resolution: "@types/sanitize-html@npm:2.9.0"
+ dependencies:
+ htmlparser2: ^8.0.0
+ checksum: b60f42b740bbfb1b1434ce8b43925a38ecc608b60aa654fd009d2e22e33f324b61d370768c55bd2fd98e03de08518ffa8911d61606c483526fb931bb8b59d1b0
+ languageName: node
+ linkType: hard
+
"@types/scheduler@npm:*":
version: 0.16.2
resolution: "@types/scheduler@npm:0.16.2"
@@ -19131,6 +19142,17 @@ __metadata:
languageName: node
linkType: hard
+"dom-serializer@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "dom-serializer@npm:2.0.0"
+ dependencies:
+ domelementtype: ^2.3.0
+ domhandler: ^5.0.2
+ entities: ^4.2.0
+ checksum: cd1810544fd8cdfbd51fa2c0c1128ec3a13ba92f14e61b7650b5de421b88205fd2e3f0cc6ace82f13334114addb90ed1c2f23074a51770a8e9c1273acbc7f3e6
+ languageName: node
+ linkType: hard
+
"dom-walk@npm:^0.1.0":
version: 0.1.2
resolution: "dom-walk@npm:0.1.2"
@@ -19145,7 +19167,7 @@ __metadata:
languageName: node
linkType: hard
-"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0":
+"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0":
version: 2.3.0
resolution: "domelementtype@npm:2.3.0"
checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6
@@ -19179,6 +19201,15 @@ __metadata:
languageName: node
linkType: hard
+"domhandler@npm:^5.0.1, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3":
+ version: 5.0.3
+ resolution: "domhandler@npm:5.0.3"
+ dependencies:
+ domelementtype: ^2.3.0
+ checksum: 0f58f4a6af63e6f3a4320aa446d28b5790a009018707bce2859dcb1d21144c7876482b5188395a188dfa974238c019e0a1e610d2fc269a12b2c192ea2b0b131c
+ languageName: node
+ linkType: hard
+
"domino@npm:^2.1.6":
version: 2.1.6
resolution: "domino@npm:2.1.6"
@@ -19211,6 +19242,17 @@ __metadata:
languageName: node
linkType: hard
+"domutils@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "domutils@npm:3.0.1"
+ dependencies:
+ dom-serializer: ^2.0.0
+ domelementtype: ^2.3.0
+ domhandler: ^5.0.1
+ checksum: 23aa7a840572d395220e173cb6263b0d028596e3950100520870a125af33ff819e6f609e1606d6f7d73bd9e7feb03bb404286e57a39063b5384c62b724d987b3
+ languageName: node
+ linkType: hard
+
"dot-case@npm:^1.1.0":
version: 1.1.2
resolution: "dot-case@npm:1.1.2"
@@ -19602,7 +19644,7 @@ __metadata:
languageName: node
linkType: hard
-"entities@npm:^4.4.0":
+"entities@npm:^4.2.0, entities@npm:^4.4.0":
version: 4.4.0
resolution: "entities@npm:4.4.0"
checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2
@@ -23760,6 +23802,18 @@ __metadata:
languageName: node
linkType: hard
+"htmlparser2@npm:^8.0.0":
+ version: 8.0.2
+ resolution: "htmlparser2@npm:8.0.2"
+ dependencies:
+ domelementtype: ^2.3.0
+ domhandler: ^5.0.3
+ domutils: ^3.0.1
+ entities: ^4.4.0
+ checksum: 29167a0f9282f181da8a6d0311b76820c8a59bc9e3c87009e21968264c2987d2723d6fde5a964d4b7b6cba663fca96ffb373c06d8223a85f52a6089ced942700
+ languageName: node
+ linkType: hard
+
"http-cache-semantics@npm:3.8.1":
version: 3.8.1
resolution: "http-cache-semantics@npm:3.8.1"
@@ -30736,6 +30790,13 @@ __metadata:
languageName: node
linkType: hard
+"parse-srcset@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "parse-srcset@npm:1.0.2"
+ checksum: 3a0380380c6082021fcce982f0b89fb8a493ce9dfd7d308e5e6d855201e80db8b90438649b31fdd82a3d6089a8ca17dccddaa2b730a718389af4c037b8539ebf
+ languageName: node
+ linkType: hard
+
"parse5-htmlparser2-tree-adapter@npm:^6.0.0":
version: 6.0.1
resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1"
@@ -31515,7 +31576,7 @@ __metadata:
languageName: node
linkType: hard
-"postcss@npm:^8.4.13, postcss@npm:^8.4.21":
+"postcss@npm:^8.3.11, postcss@npm:^8.4.13, postcss@npm:^8.4.21":
version: 8.4.21
resolution: "postcss@npm:8.4.21"
dependencies:
@@ -34466,6 +34527,20 @@ __metadata:
languageName: node
linkType: hard
+"sanitize-html@npm:^2.10.0":
+ version: 2.10.0
+ resolution: "sanitize-html@npm:2.10.0"
+ dependencies:
+ deepmerge: ^4.2.2
+ escape-string-regexp: ^4.0.0
+ htmlparser2: ^8.0.0
+ is-plain-object: ^5.0.0
+ parse-srcset: ^1.0.2
+ postcss: ^8.3.11
+ checksum: 0cb2bb330ed966a4d667b1890322dd868a67f527f87c04d7e3be1688fcfda20f7452a9a7744870751f51e255742e7264a287d9bcfcd64d4cd74a3c99f99c73d2
+ languageName: node
+ linkType: hard
+
"saslprep@npm:^1.0.3":
version: 1.0.3
resolution: "saslprep@npm:1.0.3"