feat: implement file extension blacklist for upload security (#27540)

This commit is contained in:
Novice
2025-11-04 15:45:22 +08:00
committed by GitHub
parent f9c67621ca
commit ef1db35f80
19 changed files with 277 additions and 23 deletions

View File

@ -2,7 +2,7 @@ import { useCallback, useMemo, useRef, useState } from 'react'
import type { ClipboardEvent } from 'react'
import { useParams } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import { imageUpload } from './utils'
import { getImageUploadErrorMessage, imageUpload } from './utils'
import { useToastContext } from '@/app/components/base/toast'
import { ALLOW_FILE_EXTENSIONS, TransferMethod } from '@/types/app'
import type { ImageFile, VisionSettings } from '@/types/app'
@ -81,8 +81,9 @@ export const useImageFiles = () => {
filesRef.current = newFiles
setFiles(newFiles)
},
onErrorCallback: () => {
notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') })
onErrorCallback: (error?: any) => {
const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t)
notify({ type: 'error', message: errorMessage })
const newFiles = [...files.slice(0, index), { ...currentImageFile, progress: -1 }, ...files.slice(index + 1)]
filesRef.current = newFiles
setFiles(newFiles)
@ -158,8 +159,9 @@ export const useLocalFileUploader = ({ limit, disabled = false, onUpload }: useL
onSuccessCallback: (res) => {
onUpload({ ...imageFile, fileId: res.id, progress: 100 })
},
onErrorCallback: () => {
notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') })
onErrorCallback: (error?: any) => {
const errorMessage = getImageUploadErrorMessage(error, t('common.imageUploader.uploadFromComputerUploadError'), t)
notify({ type: 'error', message: errorMessage })
onUpload({ ...imageFile, progress: -1 })
},
}, !!params.token)

View File

@ -1,10 +1,29 @@
import { upload } from '@/service/base'
/**
* Get appropriate error message for image upload errors
* @param error - The error object from upload failure
* @param defaultMessage - Default error message to use if no specific error is matched
* @param t - Translation function
* @returns Localized error message
*/
export const getImageUploadErrorMessage = (error: any, defaultMessage: string, t: (key: string) => string): string => {
const errorCode = error?.response?.code
if (errorCode === 'forbidden')
return error?.response?.message
if (errorCode === 'file_extension_blocked')
return t('common.fileUploader.fileExtensionBlocked')
return defaultMessage
}
type ImageUploadParams = {
file: File
onProgressCallback: (progress: number) => void
onSuccessCallback: (res: { id: string }) => void
onErrorCallback: () => void
onErrorCallback: (error?: any) => void
}
type ImageUpload = (v: ImageUploadParams, isPublic?: boolean, url?: string) => void
export const imageUpload: ImageUpload = ({
@ -30,7 +49,7 @@ export const imageUpload: ImageUpload = ({
.then((res: { id: string }) => {
onSuccessCallback(res)
})
.catch(() => {
onErrorCallback()
.catch((error) => {
onErrorCallback(error)
})
}