feat(upload): add optional system file filtering for uploads (#1634)

This commit is contained in:
Copilot
2025-11-14 14:45:39 +08:00
committed by GitHub
parent 0866b9075f
commit 9de7561154
9 changed files with 104 additions and 0 deletions

View File

@@ -177,6 +177,7 @@ func InitialSettings() []model.SettingItem {
{Key: conf.ShareArchivePreview, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC}, {Key: conf.ShareArchivePreview, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC},
{Key: conf.ShareForceProxy, Value: "true", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE}, {Key: conf.ShareForceProxy, Value: "true", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE},
{Key: conf.ShareSummaryContent, Value: "@{{creator}} shared {{#each files}}{{#if @first}}\"{{filename this}}\"{{/if}}{{#if @last}}{{#unless (eq @index 0)}} and {{@index}} more files{{/unless}}{{/if}}{{/each}} from {{site_title}}: {{base_url}}/@s/{{id}}{{#if pwd}} , the share code is {{pwd}}{{/if}}{{#if expires}}, please access before {{dateLocaleString expires}}.{{/if}}", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PUBLIC}, {Key: conf.ShareSummaryContent, Value: "@{{creator}} shared {{#each files}}{{#if @first}}\"{{filename this}}\"{{/if}}{{#if @last}}{{#unless (eq @index 0)}} and {{@index}} more files{{/unless}}{{/if}}{{/each}} from {{site_title}}: {{base_url}}/@s/{{id}}{{#if pwd}} , the share code is {{pwd}}{{/if}}{{#if expires}}, please access before {{dateLocaleString expires}}.{{/if}}", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PUBLIC},
{Key: conf.IgnoreSystemFiles, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE, Help: `When enabled, ignores common system files during upload (.DS_Store, desktop.ini, Thumbs.db, and files starting with ._)`},
// single settings // single settings
{Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},

View File

@@ -56,6 +56,7 @@ const (
ShareArchivePreview = "share_archive_preview" ShareArchivePreview = "share_archive_preview"
ShareForceProxy = "share_force_proxy" ShareForceProxy = "share_force_proxy"
ShareSummaryContent = "share_summary_content" ShareSummaryContent = "share_summary_content"
IgnoreSystemFiles = "ignore_system_files"
// index // index
SearchIndex = "search_index" SearchIndex = "search_index"

View File

@@ -11,6 +11,7 @@ var (
ObjectAlreadyExists = errors.New("object already exists") ObjectAlreadyExists = errors.New("object already exists")
NotFolder = errors.New("not a folder") NotFolder = errors.New("not a folder")
NotFile = errors.New("not a file") NotFile = errors.New("not a file")
IgnoredSystemFile = errors.New("system file upload ignored")
) )
func IsObjectNotFound(err error) bool { func IsObjectNotFound(err error) bool {

View File

@@ -185,3 +185,20 @@ const (
GB GB
TB TB
) )
// IsSystemFile checks if a filename is a common system file that should be ignored
// Returns true for files like .DS_Store, desktop.ini, Thumbs.db, and Apple Double files (._*)
func IsSystemFile(filename string) bool {
// Common system files
switch filename {
case ".DS_Store", "desktop.ini", "Thumbs.db":
return true
}
// Apple Double files (._*)
if strings.HasPrefix(filename, "._") {
return true
}
return false
}

42
pkg/utils/file_test.go Normal file
View File

@@ -0,0 +1,42 @@
package utils
import (
"testing"
)
func TestIsSystemFile(t *testing.T) {
testCases := []struct {
filename string
expected bool
}{
// System files that should be filtered
{".DS_Store", true},
{"desktop.ini", true},
{"Thumbs.db", true},
{"._test.txt", true},
{"._", true},
{"._somefile", true},
{"._folder_name", true},
// Regular files that should not be filtered
{"test.txt", false},
{"file.pdf", false},
{"document.docx", false},
{".gitignore", false},
{".env", false},
{"_underscore.txt", false},
{"normal_file.txt", false},
{"", false},
{".hidden", false},
{"..special", false},
}
for _, tc := range testCases {
t.Run(tc.filename, func(t *testing.T) {
result := IsSystemFile(tc.filename)
if result != tc.expected {
t.Errorf("IsSystemFile(%q) = %v, want %v", tc.filename, result, tc.expected)
}
})
}
}

View File

@@ -15,7 +15,9 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/fs" "github.com/OpenListTeam/OpenList/v4/internal/fs"
"github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/internal/setting"
"github.com/OpenListTeam/OpenList/v4/internal/stream" "github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/OpenListTeam/OpenList/v4/server/common" "github.com/OpenListTeam/OpenList/v4/server/common"
ftpserver "github.com/fclairamb/ftpserverlib" ftpserver "github.com/fclairamb/ftpserverlib"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -49,6 +51,11 @@ func OpenUpload(ctx context.Context, path string, trunc bool) (*FileUploadProxy,
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Check if system file should be ignored
_, name := stdpath.Split(path)
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(name) {
return nil, errs.IgnoredSystemFile
}
tmpFile, err := os.CreateTemp(conf.Conf.TempDir, "file-*") tmpFile, err := os.CreateTemp(conf.Conf.TempDir, "file-*")
if err != nil { if err != nil {
return nil, err return nil, err
@@ -150,6 +157,11 @@ func OpenUploadWithLength(ctx context.Context, path string, trunc bool, length i
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Check if system file should be ignored
_, name := stdpath.Split(path)
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(name) {
return nil, errs.IgnoredSystemFile
}
if trunc { if trunc {
_ = fs.Remove(ctx, path) _ = fs.Remove(ctx, path)
} }

View File

@@ -8,8 +8,10 @@ import (
"time" "time"
"github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/fs" "github.com/OpenListTeam/OpenList/v4/internal/fs"
"github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/setting"
"github.com/OpenListTeam/OpenList/v4/internal/stream" "github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/internal/task" "github.com/OpenListTeam/OpenList/v4/internal/task"
"github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils"
@@ -28,6 +30,14 @@ func getLastModified(c *gin.Context) time.Time {
return lastModified return lastModified
} }
// shouldIgnoreSystemFile checks if the filename should be ignored based on settings
func shouldIgnoreSystemFile(filename string) bool {
if setting.GetBool(conf.IgnoreSystemFiles) {
return utils.IsSystemFile(filename)
}
return false
}
func FsStream(c *gin.Context) { func FsStream(c *gin.Context) {
defer func() { defer func() {
if n, _ := io.ReadFull(c.Request.Body, []byte{0}); n == 1 { if n, _ := io.ReadFull(c.Request.Body, []byte{0}); n == 1 {
@@ -56,6 +66,11 @@ func FsStream(c *gin.Context) {
} }
} }
dir, name := stdpath.Split(path) dir, name := stdpath.Split(path)
// Check if system file should be ignored
if shouldIgnoreSystemFile(name) {
common.ErrorStrResp(c, errs.IgnoredSystemFile.Error(), 403)
return
}
// 如果请求头 Content-Length 和 X-File-Size 都没有,则 size=-1表示未知大小的流式上传 // 如果请求头 Content-Length 和 X-File-Size 都没有,则 size=-1表示未知大小的流式上传
size := c.Request.ContentLength size := c.Request.ContentLength
if size < 0 { if size < 0 {
@@ -160,6 +175,11 @@ func FsForm(c *gin.Context) {
} }
defer f.Close() defer f.Close()
dir, name := stdpath.Split(path) dir, name := stdpath.Split(path)
// Check if system file should be ignored
if shouldIgnoreSystemFile(name) {
common.ErrorStrResp(c, errs.IgnoredSystemFile.Error(), 403)
return
}
h := make(map[*utils.HashType]string) h := make(map[*utils.HashType]string)
if md5 := c.GetHeader("X-File-Md5"); md5 != "" { if md5 := c.GetHeader("X-File-Md5"); md5 != "" {
h[utils.MD5] = md5 h[utils.MD5] = md5

View File

@@ -19,6 +19,7 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/fs" "github.com/OpenListTeam/OpenList/v4/internal/fs"
"github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/internal/setting"
"github.com/OpenListTeam/OpenList/v4/internal/stream" "github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/pkg/http_range" "github.com/OpenListTeam/OpenList/v4/pkg/http_range"
"github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils"
@@ -286,6 +287,10 @@ func (b *s3Backend) PutObject(
Modified: ti, Modified: ti,
Ctime: time.Now(), Ctime: time.Now(),
} }
// Check if system file should be ignored
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(obj.Name) {
return result, errs.IgnoredSystemFile
}
stream := &stream.FileStream{ stream := &stream.FileStream{
Obj: &obj, Obj: &obj,
Reader: input, Reader: input,

View File

@@ -20,6 +20,7 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/net" "github.com/OpenListTeam/OpenList/v4/internal/net"
"github.com/OpenListTeam/OpenList/v4/internal/setting"
"github.com/OpenListTeam/OpenList/v4/internal/stream" "github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/internal/errs" "github.com/OpenListTeam/OpenList/v4/internal/errs"
@@ -358,6 +359,10 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int,
Modified: h.getModTime(r), Modified: h.getModTime(r),
Ctime: h.getCreateTime(r), Ctime: h.getCreateTime(r),
} }
// Check if system file should be ignored
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(obj.Name) {
return http.StatusForbidden, errs.IgnoredSystemFile
}
fsStream := &stream.FileStream{ fsStream := &stream.FileStream{
Obj: &obj, Obj: &obj,
Reader: r.Body, Reader: r.Body,