Skip to content

Commit 9de7561

Browse files
authored
feat(upload): add optional system file filtering for uploads (#1634)
1 parent 0866b90 commit 9de7561

File tree

9 files changed

+104
-0
lines changed

9 files changed

+104
-0
lines changed

internal/bootstrap/data/setting.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ func InitialSettings() []model.SettingItem {
177177
{Key: conf.ShareArchivePreview, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC},
178178
{Key: conf.ShareForceProxy, Value: "true", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE},
179179
{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},
180+
{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 ._)`},
180181

181182
// single settings
182183
{Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},

internal/conf/const.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const (
5656
ShareArchivePreview = "share_archive_preview"
5757
ShareForceProxy = "share_force_proxy"
5858
ShareSummaryContent = "share_summary_content"
59+
IgnoreSystemFiles = "ignore_system_files"
5960

6061
// index
6162
SearchIndex = "search_index"

internal/errs/object.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var (
1111
ObjectAlreadyExists = errors.New("object already exists")
1212
NotFolder = errors.New("not a folder")
1313
NotFile = errors.New("not a file")
14+
IgnoredSystemFile = errors.New("system file upload ignored")
1415
)
1516

1617
func IsObjectNotFound(err error) bool {

pkg/utils/file.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,20 @@ const (
185185
GB
186186
TB
187187
)
188+
189+
// IsSystemFile checks if a filename is a common system file that should be ignored
190+
// Returns true for files like .DS_Store, desktop.ini, Thumbs.db, and Apple Double files (._*)
191+
func IsSystemFile(filename string) bool {
192+
// Common system files
193+
switch filename {
194+
case ".DS_Store", "desktop.ini", "Thumbs.db":
195+
return true
196+
}
197+
198+
// Apple Double files (._*)
199+
if strings.HasPrefix(filename, "._") {
200+
return true
201+
}
202+
203+
return false
204+
}

pkg/utils/file_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package utils
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestIsSystemFile(t *testing.T) {
8+
testCases := []struct {
9+
filename string
10+
expected bool
11+
}{
12+
// System files that should be filtered
13+
{".DS_Store", true},
14+
{"desktop.ini", true},
15+
{"Thumbs.db", true},
16+
{"._test.txt", true},
17+
{"._", true},
18+
{"._somefile", true},
19+
{"._folder_name", true},
20+
21+
// Regular files that should not be filtered
22+
{"test.txt", false},
23+
{"file.pdf", false},
24+
{"document.docx", false},
25+
{".gitignore", false},
26+
{".env", false},
27+
{"_underscore.txt", false},
28+
{"normal_file.txt", false},
29+
{"", false},
30+
{".hidden", false},
31+
{"..special", false},
32+
}
33+
34+
for _, tc := range testCases {
35+
t.Run(tc.filename, func(t *testing.T) {
36+
result := IsSystemFile(tc.filename)
37+
if result != tc.expected {
38+
t.Errorf("IsSystemFile(%q) = %v, want %v", tc.filename, result, tc.expected)
39+
}
40+
})
41+
}
42+
}

server/ftp/fsup.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import (
1515
"github.com/OpenListTeam/OpenList/v4/internal/fs"
1616
"github.com/OpenListTeam/OpenList/v4/internal/model"
1717
"github.com/OpenListTeam/OpenList/v4/internal/op"
18+
"github.com/OpenListTeam/OpenList/v4/internal/setting"
1819
"github.com/OpenListTeam/OpenList/v4/internal/stream"
20+
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
1921
"github.com/OpenListTeam/OpenList/v4/server/common"
2022
ftpserver "github.com/fclairamb/ftpserverlib"
2123
"github.com/pkg/errors"
@@ -49,6 +51,11 @@ func OpenUpload(ctx context.Context, path string, trunc bool) (*FileUploadProxy,
4951
if err != nil {
5052
return nil, err
5153
}
54+
// Check if system file should be ignored
55+
_, name := stdpath.Split(path)
56+
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(name) {
57+
return nil, errs.IgnoredSystemFile
58+
}
5259
tmpFile, err := os.CreateTemp(conf.Conf.TempDir, "file-*")
5360
if err != nil {
5461
return nil, err
@@ -150,6 +157,11 @@ func OpenUploadWithLength(ctx context.Context, path string, trunc bool, length i
150157
if err != nil {
151158
return nil, err
152159
}
160+
// Check if system file should be ignored
161+
_, name := stdpath.Split(path)
162+
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(name) {
163+
return nil, errs.IgnoredSystemFile
164+
}
153165
if trunc {
154166
_ = fs.Remove(ctx, path)
155167
}

server/handles/fsup.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"time"
99

1010
"github.com/OpenListTeam/OpenList/v4/internal/conf"
11+
"github.com/OpenListTeam/OpenList/v4/internal/errs"
1112
"github.com/OpenListTeam/OpenList/v4/internal/fs"
1213
"github.com/OpenListTeam/OpenList/v4/internal/model"
14+
"github.com/OpenListTeam/OpenList/v4/internal/setting"
1315
"github.com/OpenListTeam/OpenList/v4/internal/stream"
1416
"github.com/OpenListTeam/OpenList/v4/internal/task"
1517
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
@@ -28,6 +30,14 @@ func getLastModified(c *gin.Context) time.Time {
2830
return lastModified
2931
}
3032

33+
// shouldIgnoreSystemFile checks if the filename should be ignored based on settings
34+
func shouldIgnoreSystemFile(filename string) bool {
35+
if setting.GetBool(conf.IgnoreSystemFiles) {
36+
return utils.IsSystemFile(filename)
37+
}
38+
return false
39+
}
40+
3141
func FsStream(c *gin.Context) {
3242
defer func() {
3343
if n, _ := io.ReadFull(c.Request.Body, []byte{0}); n == 1 {
@@ -56,6 +66,11 @@ func FsStream(c *gin.Context) {
5666
}
5767
}
5868
dir, name := stdpath.Split(path)
69+
// Check if system file should be ignored
70+
if shouldIgnoreSystemFile(name) {
71+
common.ErrorStrResp(c, errs.IgnoredSystemFile.Error(), 403)
72+
return
73+
}
5974
// 如果请求头 Content-Length 和 X-File-Size 都没有,则 size=-1,表示未知大小的流式上传
6075
size := c.Request.ContentLength
6176
if size < 0 {
@@ -160,6 +175,11 @@ func FsForm(c *gin.Context) {
160175
}
161176
defer f.Close()
162177
dir, name := stdpath.Split(path)
178+
// Check if system file should be ignored
179+
if shouldIgnoreSystemFile(name) {
180+
common.ErrorStrResp(c, errs.IgnoredSystemFile.Error(), 403)
181+
return
182+
}
163183
h := make(map[*utils.HashType]string)
164184
if md5 := c.GetHeader("X-File-Md5"); md5 != "" {
165185
h[utils.MD5] = md5

server/s3/backend.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/OpenListTeam/OpenList/v4/internal/fs"
2020
"github.com/OpenListTeam/OpenList/v4/internal/model"
2121
"github.com/OpenListTeam/OpenList/v4/internal/op"
22+
"github.com/OpenListTeam/OpenList/v4/internal/setting"
2223
"github.com/OpenListTeam/OpenList/v4/internal/stream"
2324
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
2425
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
@@ -286,6 +287,10 @@ func (b *s3Backend) PutObject(
286287
Modified: ti,
287288
Ctime: time.Now(),
288289
}
290+
// Check if system file should be ignored
291+
if setting.GetBool(conf.IgnoreSystemFiles) && utils.IsSystemFile(obj.Name) {
292+
return result, errs.IgnoredSystemFile
293+
}
289294
stream := &stream.FileStream{
290295
Obj: &obj,
291296
Reader: input,

server/webdav/webdav.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/OpenListTeam/OpenList/v4/internal/conf"
2222
"github.com/OpenListTeam/OpenList/v4/internal/net"
23+
"github.com/OpenListTeam/OpenList/v4/internal/setting"
2324
"github.com/OpenListTeam/OpenList/v4/internal/stream"
2425

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

0 commit comments

Comments
 (0)