Compare commits

...

1 Commits

Author SHA1 Message Date
Matti Nannt 1552793151 fix: scope display contact lookup to workspace (ENG-818)
The doesContactExist helper used by POST /api/v2/client/{workspaceId}/displays
checked contact existence by id only, allowing a caller to bind a display
in one workspace to a contact from a different workspace.

PR #7984 previously fixed this by adding an environmentId filter, but the
Formbricks 5 migration (PR #8017) dropped that filter when renaming
environmentId → workspaceId. Re-apply the workspace check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:45:12 +02:00
4 changed files with 12 additions and 10 deletions
@@ -21,24 +21,25 @@ vi.mock("react", async () => {
});
const contactId = "test-contact-id";
const workspaceId = "test-workspace-id";
describe("doesContactExist", () => {
afterEach(() => {
vi.resetAllMocks();
});
test("should return true if contact exists", async () => {
test("should return true if contact exists in the workspace", async () => {
vi.mocked(prisma.contact.findFirst).mockResolvedValue({
id: contactId,
createdAt: new Date(),
updatedAt: new Date(),
} as any);
const result = await doesContactExist(contactId);
const result = await doesContactExist(contactId, workspaceId);
expect(result).toBe(true);
expect(prisma.contact.findFirst).toHaveBeenCalledWith({
where: { id: contactId },
where: { id: contactId, workspaceId },
select: { id: true },
});
});
@@ -46,11 +47,11 @@ describe("doesContactExist", () => {
test("should return false if contact does not exist in the workspace", async () => {
vi.mocked(prisma.contact.findFirst).mockResolvedValue(null);
const result = await doesContactExist(contactId);
const result = await doesContactExist(contactId, workspaceId);
expect(result).toBe(false);
expect(prisma.contact.findFirst).toHaveBeenCalledWith({
where: { id: contactId },
where: { id: contactId, workspaceId },
select: { id: true },
});
});
@@ -1,10 +1,11 @@
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
export const doesContactExist = reactCache(async (id: string): Promise<boolean> => {
export const doesContactExist = reactCache(async (id: string, workspaceId: string): Promise<boolean> => {
const contact = await prisma.contact.findFirst({
where: {
id,
workspaceId,
},
select: {
id: true,
@@ -87,7 +87,7 @@ describe("createDisplay", () => {
const result = await createDisplay(displayInput);
expect(validateInputs).toHaveBeenCalledWith([displayInput, expect.any(Object)]);
expect(doesContactExist).toHaveBeenCalledWith(contactId);
expect(doesContactExist).toHaveBeenCalledWith(contactId, workspaceId);
expect(prisma.display.create).toHaveBeenCalledWith({
data: {
survey: { connect: { id: surveyId } },
@@ -121,7 +121,7 @@ describe("createDisplay", () => {
const result = await createDisplay(displayInput);
expect(validateInputs).toHaveBeenCalledWith([displayInput, expect.any(Object)]);
expect(doesContactExist).toHaveBeenCalledWith(contactId);
expect(doesContactExist).toHaveBeenCalledWith(contactId, workspaceId);
expect(prisma.display.create).toHaveBeenCalledWith({
data: {
survey: { connect: { id: surveyId } },
@@ -148,7 +148,7 @@ describe("createDisplay", () => {
vi.mocked(prisma.survey.findUnique).mockResolvedValue(null);
await expect(createDisplay(displayInput)).rejects.toThrow(new ResourceNotFoundError("Survey", surveyId));
expect(doesContactExist).toHaveBeenCalledWith(contactId);
expect(doesContactExist).toHaveBeenCalledWith(contactId, workspaceId);
expect(prisma.survey.findUnique).toHaveBeenCalledWith({
where: { id: surveyId, workspaceId },
});
@@ -14,7 +14,7 @@ export const createDisplay = async (displayInput: TDisplayCreateInputV2): Promis
const { contactId, surveyId, workspaceId } = displayInput;
try {
const contactExists = contactId ? await doesContactExist(contactId) : false;
const contactExists = contactId ? await doesContactExist(contactId, workspaceId) : false;
const survey = await prisma.survey.findUnique({
where: {