diff --git a/inventory/types/types.go b/inventory/types/types.go index 144e77e..479574f 100644 --- a/inventory/types/types.go +++ b/inventory/types/types.go @@ -41,6 +41,12 @@ type ( Token string `json:"token"` // 允许的文件扩展名 FileType []string `json:"file_type"` + // IsFileTypeDenyList Whether above list is a deny list. + IsFileTypeDenyList bool `json:"is_file_type_deny_list,omitempty"` + // FileRegexp 文件扩展名正则表达式 + NameRegexp string `json:"file_regexp,omitempty"` + // IsNameRegexp Whether above regexp is a deny list. + IsNameRegexpDenyList bool `json:"is_name_regexp_deny_list,omitempty"` // OauthRedirect Oauth 重定向地址 OauthRedirect string `json:"od_redirect,omitempty"` // CustomProxy whether to use custom-proxy to get file content diff --git a/pkg/filemanager/fs/dbfs/manage.go b/pkg/filemanager/fs/dbfs/manage.go index 00d5542..914b1f0 100644 --- a/pkg/filemanager/fs/dbfs/manage.go +++ b/pkg/filemanager/fs/dbfs/manage.go @@ -120,6 +120,20 @@ func (f *DBFS) Create(ctx context.Context, path *fs.URI, fileType types.FileType ancestor = newFile(ancestor, newFolder) } else { + // valide file name + policy, err := f.getPreferredPolicy(ctx, ancestor, 0) + if err != nil { + return nil, err + } + + if err := validateExtension(desired[i], policy); err != nil { + return nil, fs.ErrIllegalObjectName.WithError(err) + } + + if err := validateFileNameRegexp(desired[i], policy); err != nil { + return nil, fs.ErrIllegalObjectName.WithError(err) + } + file, err := f.createFile(ctx, ancestor, desired[i], fileType, o) if err != nil { return nil, err @@ -170,6 +184,10 @@ func (f *DBFS) Rename(ctx context.Context, path *fs.URI, newName string) (fs.Fil if err := validateExtension(newName, policy); err != nil { return nil, fs.ErrIllegalObjectName.WithError(err) } + + if err := validateFileNameRegexp(newName, policy); err != nil { + return nil, fs.ErrIllegalObjectName.WithError(err) + } } // Lock target diff --git a/pkg/filemanager/fs/dbfs/validator.go b/pkg/filemanager/fs/dbfs/validator.go index 7133749..34fde54 100644 --- a/pkg/filemanager/fs/dbfs/validator.go +++ b/pkg/filemanager/fs/dbfs/validator.go @@ -3,10 +3,12 @@ package dbfs import ( "context" "fmt" + "regexp" + "strings" + "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/util" - "strings" ) const MaxFileNameLength = 256 @@ -30,18 +32,35 @@ func validateFileName(name string) error { // validateExtension validates the file extension. func validateExtension(name string, policy *ent.StoragePolicy) error { - // 不需要验证 if len(policy.Settings.FileType) == 0 { return nil } - if !util.IsInExtensionList(policy.Settings.FileType, name) { + inList := util.IsInExtensionList(policy.Settings.FileType, name) + if (policy.Settings.IsFileTypeDenyList && inList) || (!policy.Settings.IsFileTypeDenyList && !inList) { return fmt.Errorf("file extension is not allowed") } return nil } +func validateFileNameRegexp(name string, policy *ent.StoragePolicy) error { + if policy.Settings.NameRegexp == "" { + return nil + } + + match, err := regexp.MatchString(policy.Settings.NameRegexp, name) + if err != nil { + return fmt.Errorf("invalid file name regexp: %s", err) + } + + if (policy.Settings.IsNameRegexpDenyList && match) || (!policy.Settings.IsNameRegexpDenyList && !match) { + return fmt.Errorf("file name is not allowed by regexp") + } + + return nil +} + // validateFileSize validates the file size. func validateFileSize(size int64, policy *ent.StoragePolicy) error { if policy.MaxSize == 0 { @@ -56,11 +75,15 @@ func validateFileSize(size int64, policy *ent.StoragePolicy) error { // validateNewFile validates the upload request. func validateNewFile(fileName string, size int64, policy *ent.StoragePolicy) error { if err := validateFileName(fileName); err != nil { - return err + return fs.ErrIllegalObjectName.WithError(err) } if err := validateExtension(fileName, policy); err != nil { - return err + return fs.ErrIllegalObjectName.WithError(err) + } + + if err := validateFileNameRegexp(fileName, policy); err != nil { + return fs.ErrIllegalObjectName.WithError(err) } if err := validateFileSize(size, policy); err != nil { diff --git a/service/explorer/response.go b/service/explorer/response.go index 10afdb0..755e96e 100644 --- a/service/explorer/response.go +++ b/service/explorer/response.go @@ -250,12 +250,15 @@ type DirectLink struct { } type StoragePolicy struct { - ID string `json:"id"` - Name string `json:"name"` - AllowedSuffix []string `json:"allowed_suffix,omitempty"` - Type types.PolicyType `json:"type"` - MaxSize int64 `json:"max_size"` - Relay bool `json:"relay,omitempty"` + ID string `json:"id"` + Name string `json:"name"` + AllowedSuffix []string `json:"allowed_suffix,omitempty"` + DeniedSuffix []string `json:"denied_suffix,omitempty"` + AllowedNameRegexp string `json:"allowed_name_regexp,omitempty"` + DeniedNameRegexp string `json:"denied_name_regexp,omitempty"` + Type types.PolicyType `json:"type"` + MaxSize int64 `json:"max_size"` + Relay bool `json:"relay,omitempty"` } type Entity struct { @@ -442,14 +445,30 @@ func BuildStoragePolicy(sp *ent.StoragePolicy, hasher hashid.Encoder) *StoragePo if sp == nil { return nil } - return &StoragePolicy{ - ID: hashid.EncodePolicyID(hasher, sp.ID), - Name: sp.Name, - Type: types.PolicyType(sp.Type), - MaxSize: sp.MaxSize, - AllowedSuffix: sp.Settings.FileType, - Relay: sp.Settings.Relay, + + res := &StoragePolicy{ + ID: hashid.EncodePolicyID(hasher, sp.ID), + Name: sp.Name, + Type: types.PolicyType(sp.Type), + MaxSize: sp.MaxSize, + Relay: sp.Settings.Relay, } + + if sp.Settings.IsFileTypeDenyList { + res.DeniedSuffix = sp.Settings.FileType + } else { + res.AllowedSuffix = sp.Settings.FileType + } + + if sp.Settings.NameRegexp != "" { + if sp.Settings.IsNameRegexpDenyList { + res.DeniedNameRegexp = sp.Settings.NameRegexp + } else { + res.AllowedNameRegexp = sp.Settings.NameRegexp + } + } + + return res } func WriteEventSourceHeader(c *gin.Context) {