fix: prevent reading of binary preview files and improve file type handling (#12984)

This commit is contained in:
2026-06-10 16:28:16 +08:00
committed by GitHub
parent af6a708049
commit fa3e137db8
4 changed files with 135 additions and 21 deletions
+6
View File
@@ -720,6 +720,9 @@ func (f *FileService) GetPreviewContent(op request.FileContentReq) (response.Fil
return response.FileInfo{}, err
}
if files.IsBinaryPreviewFile(info.MimeType, info.Extension) {
return response.FileInfo{}, buserr.New("ErrFileCanNotRead")
}
if files.IsBlockDevice(info.FileMode) {
return response.FileInfo{FileInfo: *info}, nil
}
@@ -932,6 +935,9 @@ func readEditableFileHistoryContent(filePath string) ([]byte, os.FileMode, bool)
if mode.IsDir() || mode&os.ModeSymlink != 0 || !mode.IsRegular() || files.IsBlockDevice(mode) || info.Size() > fileHistorySnapshotMaxSize {
return nil, mode, false
}
if files.IsBinaryPreviewFile(files.GetMimeType(filePath), filepath.Ext(info.Name())) {
return nil, mode, false
}
file, err := os.Open(filePath)
if err != nil {
return nil, mode, false
+104 -1
View File
@@ -21,6 +21,76 @@ import (
"github.com/spf13/afero"
)
var binaryPreviewMimeTypes = map[string]struct{}{
"application/pdf": {},
"application/zip": {},
"application/gzip": {},
"application/x-gzip": {},
"application/x-7z-compressed": {},
"application/x-rar-compressed": {},
"application/x-bzip2": {},
"application/x-xz": {},
"application/x-tar": {},
"application/java-archive": {},
"application/msword": {},
"application/vnd.ms-excel": {},
"application/vnd.ms-powerpoint": {},
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": {},
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {},
"application/vnd.openxmlformats-officedocument.presentationml.presentation": {},
}
var binaryPreviewMimePrefixes = []string{
"image/",
"audio/",
"video/",
"application/vnd.ms-",
"application/vnd.openxmlformats-officedocument.",
"application/vnd.oasis.opendocument.",
}
var binaryPreviewExtensions = map[string]struct{}{
".pdf": {},
".zip": {},
".gz": {},
".bz2": {},
".xz": {},
".tar": {},
".tgz": {},
".rar": {},
".7z": {},
".war": {},
".doc": {},
".docx": {},
".xls": {},
".xlsx": {},
".ppt": {},
".pptx": {},
".jpg": {},
".jpeg": {},
".png": {},
".bmp": {},
".gif": {},
".tiff": {},
".ico": {},
".webp": {},
".svg": {},
".mp3": {},
".wav": {},
".wma": {},
".ape": {},
".acc": {},
".ogg": {},
".flac": {},
".mp4": {},
".webm": {},
".mov": {},
".wmv": {},
".mkv": {},
".avi": {},
".flv": {},
}
type FileInfo struct {
Fs afero.Fs `json:"-"`
Path string `json:"path"`
@@ -418,6 +488,9 @@ func (f *FileInfo) getContent() error {
if f.Size > 10*1024*1024 {
return buserr.New("ErrFileToLarge")
}
if IsBinaryPreviewFile(f.MimeType, f.Extension) {
return buserr.New("ErrFileCanNotRead")
}
afs := &afero.Afero{Fs: f.Fs}
cByte, err := afs.ReadFile(f.Path)
if err != nil {
@@ -430,8 +503,39 @@ func (f *FileInfo) getContent() error {
return nil
}
func IsBinaryPreviewFile(mimeType, extension string) bool {
mimeType = strings.ToLower(strings.TrimSpace(mimeType))
extension = strings.ToLower(strings.TrimSpace(extension))
if mimeType == "" || mimeType == "application/octet-stream" {
_, ok := binaryPreviewExtensions[extension]
return ok
}
if strings.HasPrefix(mimeType, "text/") {
return false
}
for _, prefix := range binaryPreviewMimePrefixes {
if strings.HasPrefix(mimeType, prefix) {
return true
}
}
_, ok := binaryPreviewMimeTypes[mimeType]
if ok {
return true
}
_, ok = binaryPreviewExtensions[extension]
return ok
}
func DetectBinary(buf []byte) bool {
mimeType := http.DetectContentType(buf)
if IsBinaryPreviewFile(mimeType, "") {
return true
}
if !strings.HasPrefix(mimeType, "text/") {
whiteByte := 0
n := min(1024, len(buf))
@@ -445,7 +549,6 @@ func DetectBinary(buf []byte) bool {
return whiteByte < 1
}
return false
}
func min(x, y int) int {
@@ -105,7 +105,7 @@
class="monaco-editor sm:w-48 w-1/3 monaco-editor-background border-0 tree-container"
v-if="isShow"
>
<div class="flex items-center justify-between pl-1 pr-1 py-0.5 h-6">
<div class="flex items-center justify-between px-1 h-7">
<el-text size="small" @click="getUpData()" class="cursor-pointer">
<el-icon>
<Top />
@@ -245,22 +245,24 @@
:on-remove-other-tab="removeOtherTab"
></CodeTabs>
<div ref="codeBox" class="relative" :style="{ height: codeHeight }">
<el-icon
v-if="isShow"
class="cursor-pointer absolute bg-gray-100 py-2 rounded-l-sm block top-1/3 -left-[9px]"
size="9"
@click="toggleShow"
>
<DArrowLeft />
</el-icon>
<el-icon
v-else
class="cursor-pointer absolute bg-gray-100 py-2 rounded-r-sm block top-1/3 z-50"
size="9"
@click="toggleShow"
>
<DArrowRight />
</el-icon>
<div class="absolute top-1/3">
<el-icon
v-if="isShow"
class="cursor-pointer bg-gray-100 py-2 rounded-l-sm block -left-[9px]"
size="9"
@click="toggleShow"
>
<DArrowLeft />
</el-icon>
<el-icon
v-else
class="cursor-pointer bg-gray-100 py-2 rounded-r-sm block z-50"
size="9"
@click="toggleShow"
>
<DArrowRight />
</el-icon>
</div>
<div class="flex justify-center items-center h-full" v-if="fileTabs.length === 0">
<el-empty :image="noUpdateImage" />
</div>
@@ -268,7 +270,7 @@
</div>
</div>
<div
class="hidden code-footer pl-4 h-6 sm:flex justify-end items-center gap-4 rounded-b"
class="hidden code-footer pl-4 h-7 sm:flex justify-end items-center gap-4 rounded-b"
ref="dialogFooter"
>
<el-divider direction="vertical" class="!h-6" v-if="config.theme" />
@@ -305,8 +307,7 @@
</el-dropdown>
<el-divider direction="vertical" class="!h-6" />
<el-text class="cursor-pointer inline-flex items-center gap-1" @click="openHistoryDrawer">
<span>{{ $t('file.history') }}</span>
<span class="text-xs text-gray-500">({{ historyVersionCount }})</span>
<span class="el-dropdown-link">{{ $t('file.history') }} ({{ historyVersionCount }})</span>
</el-text>
<el-divider direction="vertical" class="!h-6" />
<el-dropdown trigger="click" max-height="300" placement="top" @command="changeLanguage">
@@ -1548,6 +1548,10 @@ const getFileExtension = (name: string, extension?: string): string => {
const openView = (item: File.File) => {
const fileType = getFileType(item.extension);
if (fileType == 'pdf') {
MsgWarning(i18n.global.t('file.fileCanNotRead'));
return;
}
if (fileType === 'image') {
imageFiles.value = data.value
.filter((item) => !item.isDir)