forked from Cutlery/immich
		
	fix(server): read file permission checks (#3046)
This commit is contained in:
		
							parent
							
								
									ad343b7b32
								
							
						
					
					
						commit
						455a36b0fc
					
				@ -82,7 +82,7 @@ describe(MetadataService.name, () => {
 | 
			
		||||
      assetMock.save.mockResolvedValue(assetEntityStub.image);
 | 
			
		||||
      storageMock.checkFileExists.mockResolvedValue(true);
 | 
			
		||||
      await sut.handleSidecarDiscovery({ id: assetEntityStub.image.id });
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.W_OK);
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK);
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        sidecarPath: '/original/path.ext.xmp',
 | 
			
		||||
@ -94,7 +94,7 @@ describe(MetadataService.name, () => {
 | 
			
		||||
      assetMock.save.mockResolvedValue(assetEntityStub.video);
 | 
			
		||||
      storageMock.checkFileExists.mockResolvedValue(true);
 | 
			
		||||
      await sut.handleSidecarDiscovery({ id: assetEntityStub.video.id });
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.W_OK);
 | 
			
		||||
      expect(storageMock.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK);
 | 
			
		||||
      expect(assetMock.save).toHaveBeenCalledWith({
 | 
			
		||||
        id: assetEntityStub.image.id,
 | 
			
		||||
        sidecarPath: '/original/path.ext.xmp',
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ export class MetadataService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const sidecarPath = `${asset.originalPath}.xmp`;
 | 
			
		||||
    const exists = await this.storageRepository.checkFileExists(sidecarPath, constants.W_OK);
 | 
			
		||||
    const exists = await this.storageRepository.checkFileExists(sidecarPath, constants.R_OK);
 | 
			
		||||
    if (!exists) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -112,7 +112,7 @@ export class UserCore {
 | 
			
		||||
    if (!user.profileImagePath) {
 | 
			
		||||
      throw new NotFoundException('User does not have a profile image');
 | 
			
		||||
    }
 | 
			
		||||
    await fs.access(user.profileImagePath, constants.R_OK | constants.W_OK);
 | 
			
		||||
    await fs.access(user.profileImagePath, constants.R_OK);
 | 
			
		||||
    return createReadStream(user.profileImagePath);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -24,9 +24,8 @@ import {
 | 
			
		||||
  StreamableFile,
 | 
			
		||||
} from '@nestjs/common';
 | 
			
		||||
import { InjectRepository } from '@nestjs/typeorm';
 | 
			
		||||
import { R_OK, W_OK } from 'constants';
 | 
			
		||||
import { Response as Res } from 'express';
 | 
			
		||||
import { createReadStream, stat } from 'fs';
 | 
			
		||||
import { constants, createReadStream, stat } from 'fs';
 | 
			
		||||
import fs from 'fs/promises';
 | 
			
		||||
import mime from 'mime-types';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
@ -156,7 +155,7 @@ export class AssetService {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const exists = await this.storageRepository.checkFileExists(filepath, R_OK);
 | 
			
		||||
      const exists = await this.storageRepository.checkFileExists(filepath, constants.R_OK);
 | 
			
		||||
      if (!exists) {
 | 
			
		||||
        throw new BadRequestException('File does not exist');
 | 
			
		||||
      }
 | 
			
		||||
@ -307,7 +306,7 @@ export class AssetService {
 | 
			
		||||
        let videoPath = asset.originalPath;
 | 
			
		||||
        let mimeType = asset.mimeType;
 | 
			
		||||
 | 
			
		||||
        await fs.access(videoPath, R_OK | W_OK);
 | 
			
		||||
        await fs.access(videoPath, constants.R_OK);
 | 
			
		||||
 | 
			
		||||
        if (asset.encodedVideoPath) {
 | 
			
		||||
          videoPath = asset.encodedVideoPath == '' ? String(asset.originalPath) : String(asset.encodedVideoPath);
 | 
			
		||||
@ -355,8 +354,8 @@ export class AssetService {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.streamFile(videoPath, res, headers, mimeType);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        this.logger.error(`Error serving VIDEO asset=${asset.id}`);
 | 
			
		||||
      } catch (e: Error | any) {
 | 
			
		||||
        this.logger.error(`Error serving VIDEO asset=${asset.id}`, e?.stack);
 | 
			
		||||
        throw new InternalServerErrorException(`Failed to serve video asset ${e}`, 'ServeFile');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -632,7 +631,7 @@ export class AssetService {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await fs.access(filepath, R_OK);
 | 
			
		||||
    await fs.access(filepath, constants.R_OK);
 | 
			
		||||
 | 
			
		||||
    return new StreamableFile(createReadStream(filepath));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ export class FilesystemProvider implements IStorageRepository {
 | 
			
		||||
 | 
			
		||||
  async createReadStream(filepath: string, mimeType?: string | null): Promise<ImmichReadStream> {
 | 
			
		||||
    const { size } = await fs.stat(filepath);
 | 
			
		||||
    await fs.access(filepath, constants.R_OK | constants.W_OK);
 | 
			
		||||
    await fs.access(filepath, constants.R_OK);
 | 
			
		||||
    return {
 | 
			
		||||
      stream: createReadStream(filepath),
 | 
			
		||||
      length: size,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user