diff --git a/services/antivirus/pkg/config/config.go b/services/antivirus/pkg/config/config.go index 2049d37e2c0..92230e54895 100644 --- a/services/antivirus/pkg/config/config.go +++ b/services/antivirus/pkg/config/config.go @@ -19,7 +19,7 @@ type Config struct { InfectedFileHandling string `yaml:"infected-file-handling" env:"ANTIVIRUS_INFECTED_FILE_HANDLING" desc:"Defines the behaviour when a virus has been found. Supported options are: 'delete', 'continue' and 'abort '. Delete will delete the file. Continue will mark the file as infected but continues further processing. Abort will keep the file in the uploads folder for further admin inspection and will not move it to its final destination." introductionVersion:"pre5.0"` Events Events Scanner Scanner - MaxScanSize string `yaml:"max-scan-size" env:"ANTIVIRUS_MAX_SCAN_SIZE" desc:"The maximum scan size the virus scanner can handle. Only this many bytes of a file will be scanned. 0 means unlimited and is the default. Usable common abbreviations: [KB, KiB, GB, GiB, TB, TiB, PB, PiB, EB, EiB], example: 2GB." introductionVersion:"pre5.0"` + MaxScanSize string `yaml:"max-scan-size" env:"ANTIVIRUS_MAX_SCAN_SIZE" desc:"The maximum scan size the virus scanner can handle. Only this many bytes of a file will be scanned. 0 means unlimited and is the default. Usable common abbreviations: [KB, KiB, MB, MiB, GB, GiB, TB, TiB, PB, PiB, EB, EiB], example: 2GB." introductionVersion:"pre5.0"` Context context.Context `yaml:"-" json:"-"` diff --git a/services/thumbnails/pkg/config/config.go b/services/thumbnails/pkg/config/config.go index 59d2032cf8f..fa1c3122d3b 100644 --- a/services/thumbnails/pkg/config/config.go +++ b/services/thumbnails/pkg/config/config.go @@ -36,14 +36,15 @@ type FileSystemStorage struct { // Thumbnail defines the available thumbnail related configuration. type Thumbnail struct { - Resolutions []string `yaml:"resolutions" env:"THUMBNAILS_RESOLUTIONS" desc:"The supported list of target resolutions in the format WidthxHeight like 32x32. You can define any resolution as required. See the Environment Variable Types description for more details." introductionVersion:"pre5.0"` - FileSystemStorage FileSystemStorage `yaml:"filesystem_storage"` - WebdavAllowInsecure bool `yaml:"webdav_allow_insecure" env:"OCIS_INSECURE;THUMBNAILS_WEBDAVSOURCE_INSECURE" desc:"Ignore untrusted SSL certificates when connecting to the webdav source." introductionVersion:"pre5.0"` - CS3AllowInsecure bool `yaml:"cs3_allow_insecure" env:"OCIS_INSECURE;THUMBNAILS_CS3SOURCE_INSECURE" desc:"Ignore untrusted SSL certificates when connecting to the CS3 source." introductionVersion:"pre5.0"` - RevaGateway string `yaml:"reva_gateway" env:"OCIS_REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata" introductionVersion:"pre5.0"` - FontMapFile string `yaml:"font_map_file" env:"THUMBNAILS_TXT_FONTMAP_FILE" desc:"The path to a font file for txt thumbnails." introductionVersion:"pre5.0"` - TransferSecret string `yaml:"transfer_secret" env:"THUMBNAILS_TRANSFER_TOKEN" desc:"The secret to sign JWT to download the actual thumbnail file." introductionVersion:"pre5.0"` - DataEndpoint string `yaml:"data_endpoint" env:"THUMBNAILS_DATA_ENDPOINT" desc:"The HTTP endpoint where the actual thumbnail file can be downloaded." introductionVersion:"pre5.0"` - MaxInputWidth int `yaml:"max_input_width" env:"THUMBNAILS_MAX_INPUT_WIDTH" desc:"The maximum width of an input image which is being processed." introductionVersion:"6.0"` - MaxInputHeight int `yaml:"max_input_height" env:"THUMBNAILS_MAX_INPUT_HEIGHT" desc:"The maximum height of an input image which is being processed." introductionVersion:"6.0"` + Resolutions []string `yaml:"resolutions" env:"THUMBNAILS_RESOLUTIONS" desc:"The supported list of target resolutions in the format WidthxHeight like 32x32. You can define any resolution as required. See the Environment Variable Types description for more details." introductionVersion:"pre5.0"` + FileSystemStorage FileSystemStorage `yaml:"filesystem_storage"` + WebdavAllowInsecure bool `yaml:"webdav_allow_insecure" env:"OCIS_INSECURE;THUMBNAILS_WEBDAVSOURCE_INSECURE" desc:"Ignore untrusted SSL certificates when connecting to the webdav source." introductionVersion:"pre5.0"` + CS3AllowInsecure bool `yaml:"cs3_allow_insecure" env:"OCIS_INSECURE;THUMBNAILS_CS3SOURCE_INSECURE" desc:"Ignore untrusted SSL certificates when connecting to the CS3 source." introductionVersion:"pre5.0"` + RevaGateway string `yaml:"reva_gateway" env:"OCIS_REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata" introductionVersion:"pre5.0"` + FontMapFile string `yaml:"font_map_file" env:"THUMBNAILS_TXT_FONTMAP_FILE" desc:"The path to a font file for txt thumbnails." introductionVersion:"pre5.0"` + TransferSecret string `yaml:"transfer_secret" env:"THUMBNAILS_TRANSFER_TOKEN" desc:"The secret to sign JWT to download the actual thumbnail file." introductionVersion:"pre5.0"` + DataEndpoint string `yaml:"data_endpoint" env:"THUMBNAILS_DATA_ENDPOINT" desc:"The HTTP endpoint where the actual thumbnail file can be downloaded." introductionVersion:"pre5.0"` + MaxInputWidth int `yaml:"max_input_width" env:"THUMBNAILS_MAX_INPUT_WIDTH" desc:"The maximum width of an input image which is being processed." introductionVersion:"6.0"` + MaxInputHeight int `yaml:"max_input_height" env:"THUMBNAILS_MAX_INPUT_HEIGHT" desc:"The maximum height of an input image which is being processed." introductionVersion:"6.0"` + MaxInputImageFileSize string `yaml:"max_input_image_file_size" env:"THUMBNAILS_MAX_INPUT_IMAGE_FILE_SIZE" desc:"The maximum file size of an input image which is being processed. Usable common abbreviations: [KB, KiB, MB, MiB, GB, GiB, TB, TiB, PB, PiB, EB, EiB], example: 2GB." introductionVersion:"6.0"` } diff --git a/services/thumbnails/pkg/config/defaults/defaultconfig.go b/services/thumbnails/pkg/config/defaults/defaultconfig.go index a0465a7efc7..81d70775a27 100644 --- a/services/thumbnails/pkg/config/defaults/defaultconfig.go +++ b/services/thumbnails/pkg/config/defaults/defaultconfig.go @@ -44,12 +44,13 @@ func DefaultConfig() *config.Config { FileSystemStorage: config.FileSystemStorage{ RootDirectory: path.Join(defaults.BaseDataPath(), "thumbnails"), }, - WebdavAllowInsecure: false, - RevaGateway: shared.DefaultRevaConfig().Address, - CS3AllowInsecure: false, - DataEndpoint: "http://127.0.0.1:9186/thumbnails/data", - MaxInputWidth: 7680, - MaxInputHeight: 4320, + WebdavAllowInsecure: false, + RevaGateway: shared.DefaultRevaConfig().Address, + CS3AllowInsecure: false, + DataEndpoint: "http://127.0.0.1:9186/thumbnails/data", + MaxInputWidth: 7680, + MaxInputHeight: 4320, + MaxInputImageFileSize: "50MB", }, } } diff --git a/services/thumbnails/pkg/server/grpc/server.go b/services/thumbnails/pkg/server/grpc/server.go index e40ac0c8589..633c7890447 100644 --- a/services/thumbnails/pkg/server/grpc/server.go +++ b/services/thumbnails/pkg/server/grpc/server.go @@ -1,6 +1,7 @@ package grpc import ( + "github.com/cs3org/reva/v2/pkg/bytesize" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc" @@ -54,19 +55,25 @@ func NewService(opts ...Option) grpc.Service { options.Logger.Error().Err(err).Msg("could not get gateway selector") return grpc.Service{} } + b, err := bytesize.Parse(tconf.MaxInputImageFileSize) + if err != nil { + options.Logger.Error().Err(err).Msg("could not parse MaxInputImageFileSize") + return grpc.Service{} + } + var thumbnail decorators.DecoratedService { thumbnail = svc.NewService( svc.Config(options.Config), svc.Logger(options.Logger), - svc.ThumbnailSource(imgsource.NewWebDavSource(tconf)), + svc.ThumbnailSource(imgsource.NewWebDavSource(tconf, b)), svc.ThumbnailStorage( storage.NewFileSystemStorage( tconf.FileSystemStorage, options.Logger, ), ), - svc.CS3Source(imgsource.NewCS3Source(tconf, gatewaySelector)), + svc.CS3Source(imgsource.NewCS3Source(tconf, gatewaySelector, b)), svc.GatewaySelector(gatewaySelector), ) thumbnail = decorators.NewInstrument(thumbnail, options.Metrics) diff --git a/services/thumbnails/pkg/thumbnail/imgsource/cs3.go b/services/thumbnails/pkg/thumbnail/imgsource/cs3.go index 082ed60b0ce..ee698f61e33 100644 --- a/services/thumbnails/pkg/thumbnail/imgsource/cs3.go +++ b/services/thumbnails/pkg/thumbnail/imgsource/cs3.go @@ -10,6 +10,7 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/bytesize" revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp" @@ -27,15 +28,17 @@ const ( // CS3 implements a CS3 image source type CS3 struct { - gatewaySelector pool.Selectable[gateway.GatewayAPIClient] - insecure bool + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + insecure bool + maxImageFileSize uint64 } // NewCS3Source configures a new CS3 image source -func NewCS3Source(cfg config.Thumbnail, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) CS3 { +func NewCS3Source(cfg config.Thumbnail, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], b bytesize.ByteSize) CS3 { return CS3{ - gatewaySelector: gatewaySelector, - insecure: cfg.CS3AllowInsecure, + gatewaySelector: gatewaySelector, + insecure: cfg.CS3AllowInsecure, + maxImageFileSize: b.Bytes(), } } @@ -60,6 +63,17 @@ func (s CS3) Get(ctx context.Context, path string) (io.ReadCloser, error) { if err != nil { return nil, err } + stat, err := gwc.Stat(ctx, &provider.StatRequest{Ref: &ref}) + if err != nil { + return nil, err + } + if stat.GetStatus().GetCode() != rpc.Code_CODE_OK { + return nil, fmt.Errorf("could not stat image: %s", stat.GetStatus().GetMessage()) + } + if stat.GetInfo().GetSize() > s.maxImageFileSize { + return nil, errors.ErrImageTooLarge + } + rsp, err := gwc.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: &ref}) if err != nil { diff --git a/services/thumbnails/pkg/thumbnail/imgsource/webdav.go b/services/thumbnails/pkg/thumbnail/imgsource/webdav.go index 9dda9dd8a1b..d81383cab24 100644 --- a/services/thumbnails/pkg/thumbnail/imgsource/webdav.go +++ b/services/thumbnails/pkg/thumbnail/imgsource/webdav.go @@ -9,21 +9,26 @@ import ( _ "image/png" // Import the png package so that image.Decode can understand pngs "io" "net/http" + "strconv" + "github.com/cs3org/reva/v2/pkg/bytesize" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" + thumbnailerErrors "github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors" "github.com/pkg/errors" ) // NewWebDavSource creates a new webdav instance. -func NewWebDavSource(cfg config.Thumbnail) WebDav { +func NewWebDavSource(cfg config.Thumbnail, b bytesize.ByteSize) WebDav { return WebDav{ - insecure: cfg.WebdavAllowInsecure, + insecure: cfg.WebdavAllowInsecure, + maxImageFileSize: b.Bytes(), } } // WebDav implements the Source interface for webdav services type WebDav struct { - insecure bool + insecure bool + maxImageFileSize uint64 } // Get downloads the file from a webdav service @@ -53,5 +58,18 @@ func (s WebDav) Get(ctx context.Context, url string) (io.ReadCloser, error) { return nil, fmt.Errorf("could not get the image \"%s\". Request returned with statuscode %d ", url, resp.StatusCode) } + contentLength := resp.Header.Get("Content-Length") + if contentLength == "" { + // no size information - let's assume it is too big + return nil, thumbnailerErrors.ErrImageTooLarge + } + c, err := strconv.ParseUint(contentLength, 10, 64) + if err != nil { + return nil, errors.Wrapf(err, `could not parse content length of webdav response "%s"`, url) + } + if c > s.maxImageFileSize { + return nil, thumbnailerErrors.ErrImageTooLarge + } + return resp.Body, nil }