Repository: payloadcms/payload
File: packages/payload/src/uploads/checkFileRestrictions.ts (TypeScript)

Code before:

  const allowRestrictedFileTypes =
    uploadConfig && typeof uploadConfig === 'object' && 'allowRestrictedFileTypes' in uploadConfig
      ? (uploadConfig as { allowRestrictedFileTypes?: boolean }).allowRestrictedFileTypes
      : false

  // Skip validation if `allowRestrictedFileTypes` is true
  if (allowRestrictedFileTypes) {
    return
  }

  // Secondary mimetype check to assess file type from buffer
  if (configMimeTypes.length > 0) {
    let detected = await fileTypeFromBuffer(file.data)

    // Handle SVG files that are detected as XML due to <?xml declarations
    if (
      detected?.mime === 'application/xml' &&
      configMimeTypes.some(
        (type) => type.includes('image/') && (type.includes('svg') || type === 'image/*'),
      ) &&
      detectSvgFromXml(file.data)
    ) {
      detected = { ext: 'svg' as any, mime: 'image/svg+xml' as any }
    }

    const passesMimeTypeCheck = detected?.mime && validateMimeType(detected.mime, configMimeTypes)

    if (detected && !passesMimeTypeCheck) {
      errors.push(`Invalid MIME type: ${detected.mime}.`)
    }
  } else {
    const isRestricted = RESTRICTED_FILE_EXT_AND_TYPES.some((type) => {
      const hasRestrictedExt = type.extensions.some((ext) => file.name.toLowerCase().endsWith(ext))
      const hasRestrictedMime = type.mimeType === file.mimetype
      return hasRestrictedExt || hasRestrictedMime
    })
    if (isRestricted) {
      errors.push(
        `File type '${file.mimetype}' not allowed ${file.name}: Restricted file type detected -- set 'allowRestrictedFileTypes' to true to skip this check for this Collection.`,
      )
    }
  }

  if (errors.length > 0) {
    req.payload.logger.error(errors.join(', '))
    throw new ValidationError({
      errors: [{ message: errors.join(', '), path: 'file' }],
    })
  }
}

Review comment (suggestion):
I am going to approve this, but I do feel like our errors in this file are bad for end users to understand.

Code after:
      : []

  const allowRestrictedFileTypes =
    uploadConfig && typeof uploadConfig === 'object' && 'allowRestrictedFileTypes' in uploadConfig
      ? (uploadConfig as { allowRestrictedFileTypes?: boolean }).allowRestrictedFileTypes
      : false

  const expectsDetectableType = configMimeTypes.some(
    (type) =>
      type.startsWith('image/') ||
      type === 'application/pdf' ||
      type.startsWith('video/') ||
      type.startsWith('audio/'),
  )

  // Skip validation if `allowRestrictedFileTypes` is true
  if (allowRestrictedFileTypes) {
    return
  }

  // Secondary mimetype check to assess file type from buffer
  if (configMimeTypes.length > 0) {
    let detected = await fileTypeFromBuffer(file.data)

    if (!detected && expectsDetectableType) {
      errors.push(`File buffer returned no detectable MIME type.`)
    }

    // Handle SVG files that are detected as XML due to <?xml declarations
    if (
      detected?.mime === 'application/xml' &&
      configMimeTypes.some(
        (type) => type.includes('image/') && (type.includes('svg') || type === 'image/*'),
      ) &&
      detectSvgFromXml(file.data)
    ) {
      detected = { ext: 'svg' as any, mime: 'image/svg+xml' as any }
    }

    const passesMimeTypeCheck = detected?.mime && validateMimeType(detected.mime, configMimeTypes)

    if (passesMimeTypeCheck && detected?.mime === 'application/pdf') {
      const isValidPDF = validatePDF(file?.data)
      if (!isValidPDF) {
        errors.push('Invalid PDF file.')
      }
    }

    if (detected && !passesMimeTypeCheck) {
      errors.push(`Invalid MIME type: ${detected.mime}.`)
    }