refactor(server)!: sanitize error messages to avoid leaking resource and permission details

This commit is contained in:
timonrieger
2026-04-29 14:45:46 +02:00
parent 65bd0a9320
commit b96421a083
31 changed files with 129 additions and 128 deletions
+4 -4
View File
@@ -7,9 +7,9 @@ export const errorDto = {
forbidden: {
message: expect.any(String),
},
missingPermission: (permission: string) => ({
message: `Missing required permission: ${permission}`,
}),
missingPermission: {
message: 'Access denied',
},
wrongPassword: {
message: 'Wrong password',
},
@@ -26,7 +26,7 @@ export const errorDto = {
message: message ?? expect.anything(),
}),
noPermission: {
message: expect.stringContaining('Not found or no'),
message: 'Access denied',
},
incorrectLogin: {
message: 'Incorrect email or password',
@@ -598,7 +598,7 @@ describe(AssetService.name, () => {
const auth = factory.auth({ user });
const { asset } = await ctx.newAsset({ ownerId: user2.id });
await expect(sut.getOcr(auth, asset.id)).rejects.toThrow('Not found or no asset.read access');
await expect(sut.getOcr(auth, asset.id)).rejects.toThrow('Access denied');
});
it('should work', async () => {
@@ -649,7 +649,7 @@ describe(AssetService.name, () => {
const auth = factory.auth({ user });
const { asset } = await ctx.newAsset({ ownerId: user2.id });
await expect(sut.getOcr(auth, asset.id)).rejects.toThrow('Not found or no asset.read access');
await expect(sut.getOcr(auth, asset.id)).rejects.toThrow('Access denied');
});
it('should work', async () => {
@@ -875,7 +875,7 @@ describe(AssetService.name, () => {
await expect(
sut.editAsset(auth, asset.id, { edits: [{ action: AssetEditAction.Rotate, parameters: { angle: 90 } }] }),
).rejects.toThrow('Not found or no asset.edit.create access');
).rejects.toThrow('Access denied');
});
it('should work', async () => {
@@ -35,7 +35,7 @@ describe(PersonService.name, () => {
const { sut } = setup();
const auth = factory.auth();
const personId = factory.uuid();
await expect(sut.delete(auth, personId)).rejects.toThrow('Not found or no person.delete access');
await expect(sut.delete(auth, personId)).rejects.toThrow('Access denied');
});
it('should delete the person', async () => {
@@ -60,7 +60,7 @@ describe(PersonService.name, () => {
const { sut } = setup();
const auth = factory.auth();
const personId = factory.uuid();
await expect(sut.deleteAll(auth, { ids: [personId] })).rejects.toThrow('Not found or no person.delete access');
await expect(sut.deleteAll(auth, { ids: [personId] })).rejects.toThrow('Access denied');
});
it('should delete the person', async () => {
@@ -1,4 +1,4 @@
import { BadRequestException } from '@nestjs/common';
import { BadRequestException, ForbiddenException } from '@nestjs/common';
import { Kysely } from 'kysely';
import { AssetVisibility } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository';
@@ -90,8 +90,8 @@ describe(TimelineService.name, () => {
const { sut } = setup();
const auth = factory.auth({ sharedLink: {} });
const response = sut.getTimeBuckets(auth, {});
await expect(response).rejects.toBeInstanceOf(BadRequestException);
await expect(response).rejects.toThrow('Not found or no timeline.read access');
await expect(response).rejects.toBeInstanceOf(ForbiddenException);
await expect(response).rejects.toThrow('Access denied');
});
});
@@ -724,7 +724,7 @@ describe(WorkflowService.name, () => {
await sut.delete(auth, workflow.id);
await expect(sut.get(auth, workflow.id)).rejects.toThrow('Not found or no workflow.read access');
await expect(sut.get(auth, workflow.id)).rejects.toThrow('Access denied');
});
it('should delete workflow with filters and actions', async () => {
@@ -743,7 +743,7 @@ describe(WorkflowService.name, () => {
await sut.delete(auth, workflow.id);
await expect(sut.get(auth, workflow.id)).rejects.toThrow('Not found or no workflow.read access');
await expect(sut.get(auth, workflow.id)).rejects.toThrow('Access denied');
});
it('should throw error when deleting non-existent workflow', async () => {