feat: adds restricted file check (#12989)

Adds `restrictedFileTypes` (default: `false`) to upload collections
which prevents files on a restricted list from being uploaded.

To skip this check:
- set `[Collection].upload.restrictedFileTypes` to `true`
- set `[Collection].upload.mimeType` to any type(s)
This commit is contained in:
Kendell
2025-07-07 16:04:34 -04:00
committed by GitHub
parent af9837de44
commit ba660fdea2
7 changed files with 219 additions and 3 deletions

View File

@@ -31,9 +31,12 @@ import {
mediaWithoutCacheTagsSlug,
mediaWithoutRelationPreviewSlug,
mediaWithRelationPreviewSlug,
noRestrictFileMimeTypesSlug,
noRestrictFileTypesSlug,
reduceSlug,
relationPreviewSlug,
relationSlug,
restrictFileTypesSlug,
skipAllowListSafeFetchMediaSlug,
skipSafeFetchMediaSlug,
threeDimensionalSlug,
@@ -468,6 +471,27 @@ export default buildConfigWithDefaults({
staticDir: path.resolve(dirname, './media'),
},
},
{
slug: restrictFileTypesSlug,
fields: [],
upload: {
allowRestrictedFileTypes: false,
},
},
{
slug: noRestrictFileTypesSlug,
fields: [],
upload: {
allowRestrictedFileTypes: true,
},
},
{
slug: noRestrictFileMimeTypesSlug,
fields: [],
upload: {
mimeTypes: ['text/html'],
},
},
{
slug: animatedTypeMedia,
fields: [],

View File

@@ -1,5 +1,6 @@
import type { CollectionSlug, Payload } from 'payload'
import { randomUUID } from 'crypto'
import fs from 'fs'
import path from 'path'
import { getFileByPath } from 'payload'
@@ -17,8 +18,11 @@ import {
focalNoSizesSlug,
focalOnlySlug,
mediaSlug,
noRestrictFileMimeTypesSlug,
noRestrictFileTypesSlug,
reduceSlug,
relationSlug,
restrictFileTypesSlug,
skipAllowListSafeFetchMediaSlug,
skipSafeFetchMediaSlug,
unstoredMediaSlug,
@@ -623,6 +627,49 @@ describe('Collections - Uploads', () => {
)
})
})
describe('file restrictions', () => {
const file: File = {
name: `test-${randomUUID()}.html`,
data: Buffer.from('<html><script>alert("test")</script></html>'),
mimetype: 'text/html',
size: 100,
}
it('should not allow files with restricted file types', async () => {
await expect(async () =>
payload.create({
collection: restrictFileTypesSlug as CollectionSlug,
data: {},
file,
}),
).rejects.toThrow(
expect.objectContaining({
name: 'APIError',
message: `File type 'text/html' not allowed ${file.name}: Restricted file type detected -- set 'allowRestrictedFileTypes' to true to skip this check for this Collection.`,
}),
)
})
it('should allow files with restricted file types when allowRestrictedFileTypes is true', async () => {
await expect(
payload.create({
collection: noRestrictFileTypesSlug as CollectionSlug,
data: {},
file,
}),
).resolves.not.toThrow()
})
it('should allow files with restricted file types when mimeTypes are set', async () => {
await expect(
payload.create({
collection: noRestrictFileMimeTypesSlug as CollectionSlug,
data: {},
file,
}),
).resolves.not.toThrow()
})
})
})
describe('focal point', () => {

View File

@@ -26,6 +26,9 @@ export const withoutMetadataSlug = 'without-meta-data'
export const withOnlyJPEGMetadataSlug = 'with-only-jpeg-meta-data'
export const customFileNameMediaSlug = 'custom-file-name-media'
export const allowListMediaSlug = 'allow-list-media'
export const restrictFileTypesSlug = 'restrict-file-types'
export const noRestrictFileTypesSlug = 'no-restrict-file-types'
export const noRestrictFileMimeTypesSlug = 'no-restrict-file-mime-types'
export const skipSafeFetchMediaSlug = 'skip-safe-fetch-media'
export const skipAllowListSafeFetchMediaSlug = 'skip-allow-list-safe-fetch-media'
export const listViewPreviewSlug = 'list-view-preview'