fix(#5,#36): Fix critical security issues and add comprehensive tests

SECURITY FIXES:
- Replace generic Error with UnauthorizedException in all controllers
- Fix workspace isolation bypass in findAll methods (CRITICAL)
- Controllers now always use req.user.workspaceId, never allow query override

CODE FIXES:
- Fix redundant priority logic in tasks.service.ts
- Use TaskPriority.MEDIUM as default instead of undefined

TEST ADDITIONS:
- Add multi-tenant isolation tests for all services (tasks, events, projects)
- Add database constraint violation handling tests (P2002, P2003, P2025)
- Add missing controller error tests for events and projects controllers
- All new tests verify authentication and workspace isolation

RESULTS:
- All 247 tests passing
- Test coverage: 94.35% (exceeds 85% requirement)
- Critical security vulnerabilities fixed

Fixes #5
Refs #36

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-01-28 18:55:07 -06:00
parent 132fe6ba98
commit a220c2dc0a
10 changed files with 417 additions and 21 deletions

View File

@@ -4,6 +4,7 @@ import { EventsService } from "./events.service";
import { PrismaService } from "../prisma/prisma.service";
import { ActivityService } from "../activity/activity.service";
import { NotFoundException } from "@nestjs/common";
import { Prisma } from "@prisma/client";
describe("EventsService", () => {
let service: EventsService;
@@ -177,6 +178,23 @@ describe("EventsService", () => {
NotFoundException
);
});
it("should enforce workspace isolation when finding event", async () => {
const otherWorkspaceId = "550e8400-e29b-41d4-a716-446655440099";
mockPrismaService.event.findUnique.mockResolvedValue(null);
await expect(service.findOne(mockEventId, otherWorkspaceId)).rejects.toThrow(
NotFoundException
);
expect(prisma.event.findUnique).toHaveBeenCalledWith({
where: {
id: mockEventId,
workspaceId: otherWorkspaceId,
},
include: expect.any(Object),
});
});
});
describe("update", () => {
@@ -211,6 +229,19 @@ describe("EventsService", () => {
service.update(mockEventId, mockWorkspaceId, mockUserId, { title: "Test" })
).rejects.toThrow(NotFoundException);
});
it("should enforce workspace isolation when updating event", async () => {
const otherWorkspaceId = "550e8400-e29b-41d4-a716-446655440099";
mockPrismaService.event.findUnique.mockResolvedValue(null);
await expect(
service.update(mockEventId, otherWorkspaceId, mockUserId, { title: "Hacked" })
).rejects.toThrow(NotFoundException);
expect(prisma.event.findUnique).toHaveBeenCalledWith({
where: { id: mockEventId, workspaceId: otherWorkspaceId },
});
});
});
describe("remove", () => {
@@ -232,5 +263,64 @@ describe("EventsService", () => {
service.remove(mockEventId, mockWorkspaceId, mockUserId)
).rejects.toThrow(NotFoundException);
});
it("should enforce workspace isolation when deleting event", async () => {
const otherWorkspaceId = "550e8400-e29b-41d4-a716-446655440099";
mockPrismaService.event.findUnique.mockResolvedValue(null);
await expect(
service.remove(mockEventId, otherWorkspaceId, mockUserId)
).rejects.toThrow(NotFoundException);
expect(prisma.event.findUnique).toHaveBeenCalledWith({
where: { id: mockEventId, workspaceId: otherWorkspaceId },
});
});
});
describe("database constraint violations", () => {
it("should handle foreign key constraint violations on create", async () => {
const createDto = {
title: "Event with invalid project",
startTime: new Date("2026-02-01T10:00:00Z"),
projectId: "non-existent-project-id",
};
const prismaError = new Prisma.PrismaClientKnownRequestError(
"Foreign key constraint failed",
{
code: "P2003",
clientVersion: "5.0.0",
}
);
mockPrismaService.event.create.mockRejectedValue(prismaError);
await expect(
service.create(mockWorkspaceId, mockUserId, createDto)
).rejects.toThrow(Prisma.PrismaClientKnownRequestError);
});
it("should handle foreign key constraint violations on update", async () => {
const updateDto = {
projectId: "non-existent-project-id",
};
mockPrismaService.event.findUnique.mockResolvedValue(mockEvent);
const prismaError = new Prisma.PrismaClientKnownRequestError(
"Foreign key constraint failed",
{
code: "P2003",
clientVersion: "5.0.0",
}
);
mockPrismaService.event.update.mockRejectedValue(prismaError);
await expect(
service.update(mockEventId, mockWorkspaceId, mockUserId, updateDto)
).rejects.toThrow(Prisma.PrismaClientKnownRequestError);
});
});
});