1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-21 18:16:55 +02:00

fix(server): only allow absolute import paths (#13642)

fix: only allow absolute paths
This commit is contained in:
Jonathan Jogenfors 2024-10-21 16:12:12 +02:00 committed by GitHub
parent 56bebd01df
commit b411e30796
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 7 deletions

View File

@ -633,6 +633,29 @@ describe('/libraries', () => {
});
});
it("should fail if path isn't absolute", async () => {
const pathToTest = `relative/path`;
const cwd = process.cwd();
// Create directory in cwd
utils.createDirectory(`${cwd}/${pathToTest}`);
const response = await utils.validateLibrary(admin.accessToken, library.id, {
importPaths: [pathToTest],
});
utils.removeDirectory(`${cwd}/${pathToTest}`);
expect(response.importPaths?.length).toEqual(1);
const pathResponse = response?.importPaths?.at(0);
expect(pathResponse).toEqual({
importPath: pathToTest,
isValid: false,
message: expect.stringMatching('Import path must be absolute, try /usr/src/app/relative/path'),
});
});
it('should fail if path is a file', async () => {
const pathToTest = `${testAssetDirInternal}/albums/nature/el_torcal_rocks.jpg`;

View File

@ -907,7 +907,9 @@ describe(LibraryService.name, () => {
storageMock.stat.mockResolvedValue({ isDirectory: () => true } as Stats);
storageMock.checkFileExists.mockResolvedValue(true);
await expect(sut.update('library-id', { importPaths: ['foo/bar'] })).resolves.toEqual(
const cwd = process.cwd();
await expect(sut.update('library-id', { importPaths: [`${cwd}/foo/bar`] })).resolves.toEqual(
mapLibrary(libraryStub.externalLibrary1),
);
expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' }));
@ -1300,14 +1302,31 @@ describe(LibraryService.name, () => {
});
});
it('should detect when import path is not absolute', async () => {
const cwd = process.cwd();
await expect(sut.validate('library-id', { importPaths: ['relative/path'] })).resolves.toEqual({
importPaths: [
{
importPath: 'relative/path',
isValid: false,
message: `Import path must be absolute, try ${cwd}/relative/path`,
},
],
});
});
it('should detect when import path is in immich media folder', async () => {
storageMock.stat.mockResolvedValue({ isDirectory: () => true } as Stats);
const validImport = libraryStub.hasImmichPaths.importPaths[1];
const cwd = process.cwd();
const validImport = `${cwd}/${libraryStub.hasImmichPaths.importPaths[1]}`;
storageMock.checkFileExists.mockImplementation((importPath) => Promise.resolve(importPath === validImport));
await expect(
sut.validate('library-id', { importPaths: libraryStub.hasImmichPaths.importPaths }),
).resolves.toEqual({
const pathStubs = libraryStub.hasImmichPaths.importPaths;
const importPaths = [pathStubs[0], validImport, pathStubs[2]];
await expect(sut.validate('library-id', { importPaths })).resolves.toEqual({
importPaths: [
{
importPath: libraryStub.hasImmichPaths.importPaths[0],

View File

@ -1,6 +1,6 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { R_OK } from 'node:constants';
import path, { basename, parse } from 'node:path';
import path, { basename, isAbsolute, parse } from 'node:path';
import picomatch from 'picomatch';
import { StorageCore } from 'src/cores/storage.core';
import { OnEvent } from 'src/decorators';
@ -268,6 +268,11 @@ export class LibraryService extends BaseService {
return validation;
}
if (!isAbsolute(importPath)) {
validation.message = `Import path must be absolute, try ${path.resolve(importPath)}`;
return validation;
}
try {
const stat = await this.storageRepository.stat(importPath);
if (!stat.isDirectory()) {

View File

@ -68,7 +68,7 @@ export const libraryStub = {
assets: [],
owner: userStub.admin,
ownerId: 'user-id',
importPaths: ['upload/thumbs', '/xyz', 'upload/library'],
importPaths: ['upload/thumbs', 'xyz', 'upload/library'],
createdAt: new Date('2023-01-01'),
updatedAt: new Date('2023-01-01'),
refreshedAt: null,