mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor(server)!: sanitize error messages to avoid leaking resource and permission details
This commit is contained in:
@@ -10,9 +10,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',
|
||||
},
|
||||
@@ -29,7 +29,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',
|
||||
|
||||
@@ -323,8 +323,8 @@ describe('/activities', () => {
|
||||
.delete(`/activities/${reaction.id}`)
|
||||
.set('Authorization', `Bearer ${nonOwner.accessToken}`);
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no activity.delete access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should let a non-owner remove their own comment', async () => {
|
||||
|
||||
@@ -480,8 +480,8 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||
.send({ ids: [asset.id] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no albumAsset.create access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should add duplicate assets only once', async () => {
|
||||
@@ -526,8 +526,8 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||
.send({ albumName: 'New album name' });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no album.update access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should be able to update as an editor', async () => {
|
||||
@@ -553,7 +553,7 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||
.send({ ids: [user1Asset1.id] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -614,8 +614,8 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||
.send({ ids: [user1Asset1.id] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no albumAsset.delete access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should remove duplicate assets only once', async () => {
|
||||
@@ -728,8 +728,8 @@ describe('/albums', () => {
|
||||
.set('Authorization', `Bearer ${user2.accessToken}`)
|
||||
.send({ role: AlbumUserRole.Editor });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no album.share access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('/api-keys', () => {
|
||||
const { secret } = await create(user.accessToken, [Permission.ApiKeyRead]);
|
||||
const { status, body } = await request(app).post('/api-keys').set('x-api-key', secret).send({ name: 'API Key' });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('apiKey.create'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should work with apiKey.create', async () => {
|
||||
|
||||
@@ -157,7 +157,7 @@ describe('/asset', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/assets/${user2Assets[0].id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -385,7 +385,7 @@ describe('/asset', () => {
|
||||
.put(`/assets/${user2Assets[0].id}`)
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({});
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -609,8 +609,8 @@ describe('/asset', () => {
|
||||
.send({ ids: [uuidDto.notFound] })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no asset.delete access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should move an asset to trash', async () => {
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('/memories', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/memories/${userMemory.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('/memories', () => {
|
||||
.put(`/memories/${userMemory.id}`)
|
||||
.send({ isSaved: true })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('/memories', () => {
|
||||
.put(`/memories/${userMemory.id}/assets`)
|
||||
.send({ ids: [userAsset1.id] })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -126,7 +126,7 @@ describe('/memories', () => {
|
||||
.delete(`/memories/${userMemory.id}/assets`)
|
||||
.send({ ids: [userAsset1.id] })
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -160,7 +160,7 @@ describe('/memories', () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/memories/${userMemory.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ describe('/sessions', () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/sessions/${uuidDto.notFound}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no authDevice.delete access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should logout a device', async () => {
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('/stacks', () => {
|
||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||
.send({ assetIds: [asset.id, user2Asset.id] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('/tags', () => {
|
||||
const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]);
|
||||
const { status, body } = await request(app).post('/tags').set('x-api-key', secret).send({ name: 'TagA' });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.create'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should work with tag.create', async () => {
|
||||
@@ -127,7 +127,7 @@ describe('/tags', () => {
|
||||
const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]);
|
||||
const { status, body } = await request(app).get('/tags').set('x-api-key', secret);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.read'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should start off empty', async () => {
|
||||
@@ -179,7 +179,7 @@ describe('/tags', () => {
|
||||
const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]);
|
||||
const { status, body } = await request(app).put('/tags').set('x-api-key', secret).send({ name: 'TagA' });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.create'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should upsert tags', async () => {
|
||||
@@ -226,7 +226,7 @@ describe('/tags', () => {
|
||||
.set('x-api-key', secret)
|
||||
.send({ assetIds: [], tagIds: [] });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.asset'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should skip assets that are not owned by the user', async () => {
|
||||
@@ -290,7 +290,7 @@ describe('/tags', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get(`/tags/${tag.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -301,7 +301,7 @@ describe('/tags', () => {
|
||||
.set('x-api-key', secret)
|
||||
.send({ assetIds: [], tagIds: [] });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.read'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
@@ -362,7 +362,7 @@ describe('/tags', () => {
|
||||
.put(`/tags/${tag.id}`)
|
||||
.send({ color: '#000000' })
|
||||
.set('Authorization', `Bearer ${user.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -374,7 +374,7 @@ describe('/tags', () => {
|
||||
.set('x-api-key', secret)
|
||||
.send({ color: '#000000' });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.update'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should update a tag', async () => {
|
||||
@@ -410,7 +410,7 @@ describe('/tags', () => {
|
||||
const { status, body } = await request(app)
|
||||
.delete(`/tags/${tag.id}`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -419,7 +419,7 @@ describe('/tags', () => {
|
||||
const { secret } = await utils.createApiKey(user.accessToken, [Permission.AssetRead]);
|
||||
const { status, body } = await request(app).delete(`/tags/${tag.id}`).set('x-api-key', secret);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.delete'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should require a valid uuid', async () => {
|
||||
@@ -478,7 +478,7 @@ describe('/tags', () => {
|
||||
.put(`/tags/${tag.id}/assets`)
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ ids: [userAsset.id] });
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -490,7 +490,7 @@ describe('/tags', () => {
|
||||
.set('x-api-key', secret)
|
||||
.send({ ids: [userAsset.id] });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.asset'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should be able to tag own asset', async () => {
|
||||
@@ -511,8 +511,8 @@ describe('/tags', () => {
|
||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||
.send({ ids: [userAsset.id] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorDto.badRequest('Not found or no tag.asset access'));
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
it('should add duplicate assets only once', async () => {
|
||||
@@ -552,7 +552,7 @@ describe('/tags', () => {
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ ids: [userAsset.id] });
|
||||
|
||||
expect(status).toBe(400);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.noPermission);
|
||||
});
|
||||
|
||||
@@ -564,7 +564,7 @@ describe('/tags', () => {
|
||||
.set('x-api-key', secret)
|
||||
.send({ ids: [userAsset.id] });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.missingPermission('tag.asset'));
|
||||
expect(body).toEqual(errorDto.missingPermission);
|
||||
});
|
||||
|
||||
it('should be able to remove own asset from own tag', async () => {
|
||||
|
||||
Reference in New Issue
Block a user