Skip to content

Commit c7974af

Browse files
committed
fix: enforce mimeTypes restriction when useTempFiles is enabled
When useTempFiles is enabled, file.data is an empty buffer because the actual file content is stored on disk at tempFilePath. This caused fileTypeFromBuffer to return undefined, and the subsequent fallback validation was guarded by !useTempFiles, effectively skipping all mimeTypes validation for temp file uploads. The fix reads the file buffer from the temp file path when useTempFiles is enabled and file.data is empty, ensuring that both magic-byte detection and extension-based fallback validation are applied consistently regardless of the upload mode. Closes #16233
1 parent 9d6bf0b commit c7974af

1 file changed

Lines changed: 15 additions & 6 deletions

File tree

packages/payload/src/uploads/checkFileRestrictions.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { fileTypeFromBuffer } from 'file-type'
2+
import fs from 'fs/promises'
23

34
import type { checkFileRestrictionsParams, FileAllowList } from './types.js'
45

@@ -89,7 +90,15 @@ export const checkFileRestrictions = async ({
8990

9091
// Secondary mimetype check to assess file type from buffer
9192
if (configMimeTypes.length > 0) {
92-
let detected = await fileTypeFromBuffer(file.data)
93+
// When useTempFiles is enabled, file.data is an empty buffer because the
94+
// actual content lives on disk. Read from the temp file so that
95+
// fileTypeFromBuffer can perform real magic-byte detection.
96+
const fileBuffer =
97+
useTempFiles && file.tempFilePath && (!file.data || file.data.length === 0)
98+
? await fs.readFile(file.tempFilePath)
99+
: file.data
100+
101+
let detected = await fileTypeFromBuffer(fileBuffer)
93102
const typeFromExtension = file.name.split('.').pop() || ''
94103

95104
// Handle SVG files that are detected as XML due to <?xml declarations
@@ -99,13 +108,13 @@ export const checkFileRestrictions = async ({
99108
(type) => type.includes('image/') && (type.includes('svg') || type === 'image/*'),
100109
)
101110
) {
102-
const isSvg = detectSvgFromXml(file.data)
111+
const isSvg = detectSvgFromXml(fileBuffer)
103112
if (isSvg) {
104113
detected = { ext: 'svg' as any, mime: 'image/svg+xml' as any }
105114
}
106115
}
107116

108-
if (!detected && !useTempFiles) {
117+
if (!detected) {
109118
const mimeTypeFromExtension = getFileTypeFallback(file.name).mime
110119
const extIsValid = validateMimeType(mimeTypeFromExtension, configMimeTypes)
111120

@@ -116,15 +125,15 @@ export const checkFileRestrictions = async ({
116125
} else {
117126
// SVG security check (text-based files not detectable by buffer)
118127
if (typeFromExtension.toLowerCase() === 'svg') {
119-
const isSafeSvg = validateSvg(file.data)
128+
const isSafeSvg = validateSvg(fileBuffer)
120129
if (!isSafeSvg) {
121130
errors.push('SVG file contains potentially harmful content.')
122131
}
123132
}
124133

125134
// PDF validation
126135
if (mimeTypeFromExtension === 'application/pdf') {
127-
const isValidPDF = validatePDF(file.data)
136+
const isValidPDF = validatePDF(fileBuffer)
128137
if (!isValidPDF) {
129138
errors.push('Invalid or corrupted PDF file.')
130139
}
@@ -141,7 +150,7 @@ export const checkFileRestrictions = async ({
141150
const passesMimeTypeCheck = detected?.mime && validateMimeType(detected.mime, configMimeTypes)
142151

143152
if (passesMimeTypeCheck && detected?.mime === 'application/pdf') {
144-
const isValidPDF = validatePDF(file?.data)
153+
const isValidPDF = validatePDF(fileBuffer)
145154
if (!isValidPDF) {
146155
errors.push('Invalid PDF file.')
147156
}

0 commit comments

Comments
 (0)