@strapi/upload 5.38.0 → 5.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs +1 -0
- package/dist/admin/components/EditAssetDialog/PreviewBox/AssetPreview.mjs.map +1 -1
- package/dist/admin/translations/es.json.js +25 -1
- package/dist/admin/translations/es.json.js.map +1 -1
- package/dist/admin/translations/es.json.mjs +25 -1
- package/dist/admin/translations/es.json.mjs.map +1 -1
- package/dist/server/services/upload.js +6 -1
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +6 -1
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/server/src/utils/mime-validation.d.ts +1 -0
- package/dist/server/src/utils/mime-validation.d.ts.map +1 -1
- package/dist/server/utils/mime-validation.js +184 -47
- package/dist/server/utils/mime-validation.js.map +1 -1
- package/dist/server/utils/mime-validation.mjs +185 -47
- package/dist/server/utils/mime-validation.mjs.map +1 -1
- package/package.json +17 -8
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { extname } from 'node:path';
|
|
3
|
+
import { lookup } from 'mime-types';
|
|
2
4
|
import { errors } from '@strapi/utils';
|
|
3
5
|
|
|
4
6
|
async function readFileChunk(filePath, chunkSize = 4100) {
|
|
@@ -7,7 +9,8 @@ async function readFileChunk(filePath, chunkSize = 4100) {
|
|
|
7
9
|
}
|
|
8
10
|
async function detectMimeType(file) {
|
|
9
11
|
let buffer;
|
|
10
|
-
|
|
12
|
+
// Support multiple property names used by different multipart parsers (formidable uses filepath)
|
|
13
|
+
const filePath = file.filepath ?? file.path ?? file.tempFilePath ?? file.filePath;
|
|
11
14
|
if (filePath) {
|
|
12
15
|
try {
|
|
13
16
|
buffer = await readFileChunk(filePath, 4100);
|
|
@@ -17,7 +20,7 @@ async function detectMimeType(file) {
|
|
|
17
20
|
} else if (file.buffer) {
|
|
18
21
|
buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;
|
|
19
22
|
} else {
|
|
20
|
-
// No file
|
|
23
|
+
// No file path or buffer; cannot detect MIME. Validation will use declared/extension or reject.
|
|
21
24
|
return undefined;
|
|
22
25
|
}
|
|
23
26
|
try {
|
|
@@ -47,19 +50,80 @@ function matchesMimePattern(mimeType, patterns) {
|
|
|
47
50
|
return exactMatch;
|
|
48
51
|
});
|
|
49
52
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Single gate for allow/deny. Returns ValidationResult.
|
|
55
|
+
* Doc: docs/docs/01-core/upload/01-backend/01-mime-validation.md
|
|
56
|
+
*/ function validateAllowBanLists(mimetype, fileName, declaredType, detectedType, config) {
|
|
57
|
+
if (!mimetype) {
|
|
58
|
+
return {
|
|
59
|
+
isValid: false,
|
|
60
|
+
error: {
|
|
61
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
62
|
+
message: 'MIME type is not allowed',
|
|
63
|
+
details: {
|
|
64
|
+
fileName,
|
|
65
|
+
reason: 'No MIME type to validate',
|
|
66
|
+
declaredType,
|
|
67
|
+
detectedType,
|
|
68
|
+
allowedTypes: config.allowedTypes,
|
|
69
|
+
deniedTypes: config.deniedTypes
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
55
73
|
}
|
|
56
|
-
if (
|
|
57
|
-
return
|
|
74
|
+
if (config.deniedTypes?.length && matchesMimePattern(mimetype, config.deniedTypes)) {
|
|
75
|
+
return buildNotAllowedError(fileName, declaredType, detectedType, mimetype, config);
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(config.allowedTypes)) {
|
|
78
|
+
return {
|
|
79
|
+
isValid: true,
|
|
80
|
+
detectedMime: mimetype
|
|
81
|
+
};
|
|
58
82
|
}
|
|
59
|
-
|
|
83
|
+
if (config.allowedTypes.length === 0) {
|
|
84
|
+
return {
|
|
85
|
+
isValid: false,
|
|
86
|
+
error: {
|
|
87
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
88
|
+
message: 'MIME type is not allowed',
|
|
89
|
+
details: {
|
|
90
|
+
fileName,
|
|
91
|
+
reason: 'Allow list is empty',
|
|
92
|
+
declaredType,
|
|
93
|
+
detectedType,
|
|
94
|
+
allowedTypes: config.allowedTypes,
|
|
95
|
+
deniedTypes: config.deniedTypes
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (matchesMimePattern(mimetype, config.allowedTypes)) {
|
|
101
|
+
return {
|
|
102
|
+
isValid: true,
|
|
103
|
+
detectedMime: mimetype
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
isValid: false,
|
|
108
|
+
error: {
|
|
109
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
110
|
+
message: `File type '${mimetype}' is not allowed`,
|
|
111
|
+
details: {
|
|
112
|
+
fileName,
|
|
113
|
+
declaredType,
|
|
114
|
+
detectedType,
|
|
115
|
+
finalType: mimetype,
|
|
116
|
+
allowedTypes: config.allowedTypes,
|
|
117
|
+
deniedTypes: config.deniedTypes
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function isDeclaredGeneric(declaredMimeType) {
|
|
123
|
+
return !declaredMimeType || declaredMimeType === 'application/octet-stream';
|
|
60
124
|
}
|
|
61
125
|
function extractFileInfo(file) {
|
|
62
|
-
const fileName = file.originalFilename || file.name || file.filename || file.newFilename || 'unknown';
|
|
126
|
+
const fileName = file.originalFilename || file.name || file.filename || file.newFilename || file.originalname || 'unknown';
|
|
63
127
|
const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';
|
|
64
128
|
return {
|
|
65
129
|
fileName,
|
|
@@ -68,60 +132,125 @@ function extractFileInfo(file) {
|
|
|
68
132
|
}
|
|
69
133
|
async function validateFile(file, config, strapi) {
|
|
70
134
|
const { allowedTypes, deniedTypes } = config;
|
|
71
|
-
if (!allowedTypes && !deniedTypes) {
|
|
72
|
-
return {
|
|
73
|
-
isValid: true
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
135
|
const { fileName, declaredMimeType } = extractFileInfo(file);
|
|
136
|
+
const fileExt = extname(fileName).toLowerCase();
|
|
137
|
+
const expectedMimeFromExt = fileExt ? lookup(fileExt) || null : null;
|
|
138
|
+
// Reject if declared type is denied.
|
|
139
|
+
if (!isDeclaredGeneric(declaredMimeType) && deniedTypes?.length && matchesMimePattern(declaredMimeType, deniedTypes)) {
|
|
140
|
+
return buildNotAllowedError(fileName, declaredMimeType, undefined, declaredMimeType, config);
|
|
141
|
+
}
|
|
142
|
+
// Reject if extension's type is denied.
|
|
143
|
+
if (expectedMimeFromExt && deniedTypes?.length && matchesMimePattern(expectedMimeFromExt, deniedTypes)) {
|
|
144
|
+
return buildNotAllowedError(fileName, declaredMimeType, undefined, expectedMimeFromExt, config);
|
|
145
|
+
}
|
|
146
|
+
// Run content detection.
|
|
77
147
|
let detectedMime;
|
|
78
|
-
let mimeDetectionFailed = false;
|
|
79
148
|
try {
|
|
80
149
|
detectedMime = await detectMimeType(file);
|
|
81
150
|
} catch (error) {
|
|
82
|
-
|
|
83
|
-
strapi.log.warn(
|
|
151
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
152
|
+
strapi.log.warn(`Failed to detect MIME type from file: ${errMsg}`, {
|
|
84
153
|
fileName,
|
|
85
|
-
error:
|
|
154
|
+
error: errMsg
|
|
86
155
|
});
|
|
87
156
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
declaredType: declaredMimeType,
|
|
100
|
-
mimeDetectionFailed
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
}
|
|
157
|
+
const declaredMatchesExtension = !isDeclaredGeneric(declaredMimeType) && expectedMimeFromExt !== null && expectedMimeFromExt !== undefined && (matchesMimePattern(declaredMimeType, [
|
|
158
|
+
expectedMimeFromExt
|
|
159
|
+
]) || matchesMimePattern(expectedMimeFromExt, [
|
|
160
|
+
declaredMimeType
|
|
161
|
+
]));
|
|
162
|
+
const detectedMatchesDeclared = detectedMime && declaredMimeType && matchesMimePattern(detectedMime, [
|
|
163
|
+
declaredMimeType
|
|
164
|
+
]);
|
|
165
|
+
// Trusted declaration: declared matches extension and detection confirms.
|
|
166
|
+
if (declaredMatchesExtension && detectedMatchesDeclared) {
|
|
167
|
+
return validateAllowBanLists(declaredMimeType, fileName, declaredMimeType, detectedMime, config);
|
|
105
168
|
}
|
|
106
|
-
|
|
169
|
+
// Reject if detected type is denied.
|
|
170
|
+
if (detectedMime && deniedTypes?.length && matchesMimePattern(detectedMime, deniedTypes)) {
|
|
171
|
+
return buildNotAllowedError(fileName, declaredMimeType, detectedMime, detectedMime, config);
|
|
172
|
+
}
|
|
173
|
+
// Reject if detected is not in allow list (extension/declared cannot override).
|
|
174
|
+
// Exception: file-type often returns application/zip for Office formats (docx, xlsx); skip so the next block can allow via extension type.
|
|
175
|
+
const isZipWithAllowedExt = detectedMime === 'application/zip' && expectedMimeFromExt && expectedMimeFromExt !== 'application/zip' && Array.isArray(allowedTypes) && allowedTypes.length > 0 && matchesMimePattern(expectedMimeFromExt, allowedTypes);
|
|
176
|
+
if (detectedMime && Array.isArray(allowedTypes) && allowedTypes.length > 0 && !matchesMimePattern(detectedMime, allowedTypes) && !isZipWithAllowedExt) {
|
|
107
177
|
return {
|
|
108
178
|
isValid: false,
|
|
109
179
|
error: {
|
|
110
180
|
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
111
|
-
message:
|
|
181
|
+
message: 'MIME type is not allowed',
|
|
112
182
|
details: {
|
|
113
183
|
fileName,
|
|
114
|
-
|
|
184
|
+
reason: 'File content was detected as a type not in the allow list; extension or declared type cannot override',
|
|
115
185
|
declaredType: declaredMimeType,
|
|
116
|
-
|
|
186
|
+
detectedType: detectedMime,
|
|
187
|
+
expectedMimeFromExtension: expectedMimeFromExt,
|
|
117
188
|
allowedTypes,
|
|
118
189
|
deniedTypes
|
|
119
190
|
}
|
|
120
191
|
}
|
|
121
192
|
};
|
|
122
193
|
}
|
|
194
|
+
// Use detected type when defined and (no extension, or detected matches extension, or declared is generic).
|
|
195
|
+
const detectedMatchesExtension = detectedMime && expectedMimeFromExt !== null && expectedMimeFromExt !== undefined && matchesMimePattern(detectedMime, [
|
|
196
|
+
expectedMimeFromExt
|
|
197
|
+
]);
|
|
198
|
+
if (detectedMime && (!expectedMimeFromExt || detectedMatchesExtension || isDeclaredGeneric(declaredMimeType))) {
|
|
199
|
+
// Office/zip exception: use extension type when detection returned application/zip and extension is in allow list.
|
|
200
|
+
if (detectedMime === 'application/zip' && expectedMimeFromExt && expectedMimeFromExt !== 'application/zip') {
|
|
201
|
+
const extResult = validateAllowBanLists(expectedMimeFromExt, fileName, declaredMimeType, detectedMime, config);
|
|
202
|
+
if (extResult.isValid) {
|
|
203
|
+
strapi.log.warn('MIME type detection returned application/zip; trusting extension for allow list', {
|
|
204
|
+
fileName,
|
|
205
|
+
fileExtension: fileExt,
|
|
206
|
+
expectedMimeFromExtension: expectedMimeFromExt
|
|
207
|
+
});
|
|
208
|
+
return extResult;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return validateAllowBanLists(detectedMime, fileName, declaredMimeType, detectedMime, config);
|
|
212
|
+
}
|
|
213
|
+
// Use extension's type when present.
|
|
214
|
+
if (expectedMimeFromExt) {
|
|
215
|
+
return validateAllowBanLists(expectedMimeFromExt, fileName, declaredMimeType, detectedMime, config);
|
|
216
|
+
}
|
|
217
|
+
// Use declared type as last resort.
|
|
218
|
+
if (declaredMimeType) {
|
|
219
|
+
return validateAllowBanLists(declaredMimeType, fileName, declaredMimeType, detectedMime, config);
|
|
220
|
+
}
|
|
221
|
+
// Reject when no type can be chosen.
|
|
222
|
+
return {
|
|
223
|
+
isValid: false,
|
|
224
|
+
error: {
|
|
225
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
226
|
+
message: 'Cannot verify file type for security reasons',
|
|
227
|
+
details: {
|
|
228
|
+
fileName,
|
|
229
|
+
reason: 'No MIME type to validate (no declared type, no extension, no detection)',
|
|
230
|
+
declaredType: declaredMimeType,
|
|
231
|
+
detectedType: detectedMime,
|
|
232
|
+
allowedTypes,
|
|
233
|
+
deniedTypes
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function buildNotAllowedError(fileName, declaredType, detectedType, rejectedType, config) {
|
|
123
239
|
return {
|
|
124
|
-
isValid:
|
|
240
|
+
isValid: false,
|
|
241
|
+
error: {
|
|
242
|
+
code: 'MIME_TYPE_NOT_ALLOWED',
|
|
243
|
+
message: `File type '${rejectedType}' is not allowed`,
|
|
244
|
+
details: {
|
|
245
|
+
fileName,
|
|
246
|
+
declaredType,
|
|
247
|
+
detectedType,
|
|
248
|
+
finalType: rejectedType,
|
|
249
|
+
allowedTypes: config.allowedTypes,
|
|
250
|
+
deniedTypes: config.deniedTypes,
|
|
251
|
+
reason: 'MIME type is in the denied list'
|
|
252
|
+
}
|
|
253
|
+
}
|
|
125
254
|
};
|
|
126
255
|
}
|
|
127
256
|
async function validateFiles(files, strapi) {
|
|
@@ -131,7 +260,14 @@ async function validateFiles(files, strapi) {
|
|
|
131
260
|
if (!filesArray.length) {
|
|
132
261
|
return [];
|
|
133
262
|
}
|
|
134
|
-
|
|
263
|
+
// Use array path so 'plugin::upload' is a single key (lodash string path splits on '.')
|
|
264
|
+
let config = strapi.config.get([
|
|
265
|
+
'plugin::upload',
|
|
266
|
+
'security'
|
|
267
|
+
], {});
|
|
268
|
+
if (!config || typeof config !== 'object') {
|
|
269
|
+
config = {};
|
|
270
|
+
}
|
|
135
271
|
if (config.allowedTypes && (!Array.isArray(config.allowedTypes) || !config.allowedTypes.every((item)=>typeof item === 'string'))) {
|
|
136
272
|
throw new errors.ApplicationError('Invalid configuration: allowedTypes must be an array of strings.');
|
|
137
273
|
}
|
|
@@ -140,9 +276,7 @@ async function validateFiles(files, strapi) {
|
|
|
140
276
|
}
|
|
141
277
|
if (!config.allowedTypes && !config.deniedTypes) {
|
|
142
278
|
strapi.log.warn('No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.');
|
|
143
|
-
|
|
144
|
-
isValid: true
|
|
145
|
-
}));
|
|
279
|
+
// Do not return; we still run validation so MIME detection runs and stored file gets detected type when possible
|
|
146
280
|
}
|
|
147
281
|
const validationPromises = filesArray.map(async (file, index)=>{
|
|
148
282
|
try {
|
|
@@ -180,6 +314,10 @@ async function enforceUploadSecurity(files, strapi) {
|
|
|
180
314
|
for (const [index, result] of validationResults.entries()){
|
|
181
315
|
if (result.isValid) {
|
|
182
316
|
const file = filesArray[index];
|
|
317
|
+
// Enrich file with detected MIME type for use in storage
|
|
318
|
+
if (result.detectedMime) {
|
|
319
|
+
file.detectedMimeType = result.detectedMime;
|
|
320
|
+
}
|
|
183
321
|
validFiles.push(file);
|
|
184
322
|
validFileNames.push(file.originalFilename || file.name);
|
|
185
323
|
} else if (result.error) {
|
|
@@ -257,5 +395,5 @@ async function enforceUploadSecurity(files, strapi) {
|
|
|
257
395
|
};
|
|
258
396
|
}
|
|
259
397
|
|
|
260
|
-
export { detectMimeType, enforceUploadSecurity, extractFileInfo,
|
|
398
|
+
export { detectMimeType, enforceUploadSecurity, extractFileInfo, prepareUploadRequest, validateFile, validateFiles };
|
|
261
399
|
//# sourceMappingURL=mime-validation.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime-validation.mjs","sources":["../../../server/src/utils/mime-validation.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Core } from '@strapi/types';\nimport { errors } from '@strapi/utils';\n\nexport type SecurityConfig = {\n allowedTypes?: string[];\n deniedTypes?: string[];\n};\ntype UploadValidationError = {\n code: 'MIME_TYPE_NOT_ALLOWED' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR';\n message: string;\n details: Record<string, any>;\n};\n\ntype ValidationResult = {\n isValid: boolean;\n error?: UploadValidationError;\n};\n\ntype ErrorDetail = {\n file: any;\n originalIndex: number;\n error: UploadValidationError;\n};\n\nasync function readFileChunk(filePath: string, chunkSize: number = 4100): Promise<Buffer> {\n const buffer = await readFile(filePath);\n return buffer.length > chunkSize ? buffer.subarray(0, chunkSize) : buffer;\n}\n\nexport async function detectMimeType(file: any): Promise<string | undefined> {\n let buffer: Buffer;\n\n const filePath = file.path || file.filepath || file.tempFilePath;\n\n if (filePath) {\n try {\n buffer = await readFileChunk(filePath, 4100);\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else if (file.buffer) {\n buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;\n } else {\n // No file data available\n return undefined;\n }\n\n try {\n /**\n * Use dynamic import to support file-type which is ESM-only\n * Static imports fail during CommonJS build since bundler can't transform ESM-only packages\n * @see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n */\n const { fileTypeFromBuffer } = await import('file-type');\n\n const result = await fileTypeFromBuffer(new Uint8Array(buffer));\n return result?.mime;\n } catch (error) {\n throw new Error(\n `Failed to detect MIME type: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nfunction matchesMimePattern(mimeType: string, patterns: string[]): boolean {\n if (!patterns?.length) return false;\n\n return patterns.some((pattern) => {\n const normalizedPattern = pattern.toLowerCase();\n const normalizedMimeType = mimeType.toLowerCase();\n\n if (normalizedPattern.includes('*')) {\n const regexPattern = normalizedPattern.replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n const matches = regex.test(normalizedMimeType);\n return matches;\n }\n\n const exactMatch = normalizedPattern === normalizedMimeType;\n return exactMatch;\n });\n}\n\nexport function isMimeTypeAllowed(mimeType: string, config: SecurityConfig): boolean {\n const { allowedTypes, deniedTypes } = config;\n\n if (!mimeType) return false;\n\n if (deniedTypes?.length && matchesMimePattern(mimeType, deniedTypes)) {\n return false;\n }\n\n if (allowedTypes?.length) {\n return matchesMimePattern(mimeType, allowedTypes);\n }\n\n return true;\n}\n\nexport function extractFileInfo(file: any) {\n const fileName =\n file.originalFilename || file.name || file.filename || file.newFilename || 'unknown';\n const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';\n\n return { fileName, declaredMimeType };\n}\n\nexport async function validateFile(\n file: any,\n config: SecurityConfig,\n strapi: Core.Strapi\n): Promise<ValidationResult> {\n const { allowedTypes, deniedTypes } = config;\n\n if (!allowedTypes && !deniedTypes) {\n return { isValid: true };\n }\n\n const { fileName, declaredMimeType } = extractFileInfo(file);\n\n let detectedMime: string | undefined;\n let mimeDetectionFailed = false;\n\n try {\n detectedMime = await detectMimeType(file);\n } catch (error) {\n mimeDetectionFailed = true;\n strapi.log.warn('Failed to detect MIME type from file', {\n fileName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n const mimeToValidate = detectedMime || declaredMimeType;\n\n if (\n !detectedMime &&\n (declaredMimeType === 'application/octet-stream' || !declaredMimeType || mimeDetectionFailed)\n ) {\n if (allowedTypes?.length || deniedTypes?.length) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `Cannot verify file type for security reasons`,\n details: {\n fileName,\n reason: 'Unable to detect MIME type from file content',\n declaredType: declaredMimeType,\n mimeDetectionFailed,\n },\n },\n };\n }\n }\n\n if (\n mimeToValidate &&\n (allowedTypes || deniedTypes) &&\n !isMimeTypeAllowed(mimeToValidate, config)\n ) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${mimeToValidate}' is not allowed`,\n details: {\n fileName,\n detectedType: detectedMime,\n declaredType: declaredMimeType,\n finalType: mimeToValidate,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n }\n\n return { isValid: true };\n}\n\nexport async function validateFiles(files: any, strapi: Core.Strapi): Promise<ValidationResult[]> {\n const filesArray = Array.isArray(files) ? files : [files];\n\n if (!filesArray.length) {\n return [];\n }\n\n const config: SecurityConfig = strapi.config.get('plugin::upload.security', {});\n if (\n config.allowedTypes &&\n (!Array.isArray(config.allowedTypes) ||\n !config.allowedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: allowedTypes must be an array of strings.'\n );\n }\n\n if (\n config.deniedTypes &&\n (!Array.isArray(config.deniedTypes) ||\n !config.deniedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: deniedTypes must be an array of strings.'\n );\n }\n\n if (!config.allowedTypes && !config.deniedTypes) {\n strapi.log.warn(\n 'No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.'\n );\n return filesArray.map(() => ({ isValid: true }));\n }\n\n const validationPromises = filesArray.map(async (file, index) => {\n try {\n return await validateFile(file, config, strapi);\n } catch (error) {\n strapi.log.error('Unexpected error during file validation', {\n fileIndex: index,\n fileName: file?.name || file?.originalname,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n isValid: false,\n error: {\n code: 'VALIDATION_ERROR' as const,\n message: `Validation failed for file at index ${index}`,\n details: {\n index,\n fileName: file?.name || file?.originalname,\n originalError: error instanceof Error ? error.message : String(error),\n },\n },\n };\n }\n });\n\n return Promise.all(validationPromises);\n}\n\nexport async function enforceUploadSecurity(\n files: any,\n strapi: Core.Strapi\n): Promise<{\n validFiles: any[];\n validFileNames: string[];\n errors: Array<ErrorDetail>;\n}> {\n const validationResults = await validateFiles(files, strapi);\n const filesArray = Array.isArray(files) ? files : [files];\n\n const validFiles: any[] = [];\n const validFileNames: string[] = [];\n const errors: Array<ErrorDetail> = [];\n\n for (const [index, result] of validationResults.entries()) {\n if (result.isValid) {\n const file = filesArray[index];\n validFiles.push(file);\n validFileNames.push(file.originalFilename || file.name);\n } else if (result.error) {\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: result.error,\n });\n } else {\n // Handle case where validation failed but no error details are provided\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: {\n code: 'UNKNOWN_ERROR' as const,\n message: 'File validation failed for unknown reason',\n details: {\n index,\n fileName: filesArray[index]?.name || filesArray[index]?.originalname,\n },\n },\n });\n }\n }\n\n return { validFiles, validFileNames, errors };\n}\n\nexport type FileUploadError = {\n name: string;\n message: string;\n};\n\nexport type PrepareUploadResult = {\n validFiles: any[];\n filteredBody: any;\n errors: FileUploadError[];\n};\n\n/**\n * Prepare files and body for upload by enforcing security and parsing fileInfo\n */\nexport async function prepareUploadRequest(\n filesInput: any,\n body: any,\n strapi: Core.Strapi\n): Promise<PrepareUploadResult> {\n const securityResults = await enforceUploadSecurity(filesInput, strapi);\n\n let filteredBody = body;\n if (body?.fileInfo) {\n // Parse JSON strings in fileInfo\n let parsedFileInfo = body.fileInfo;\n if (Array.isArray(body.fileInfo)) {\n parsedFileInfo = body.fileInfo.map((fi: any) =>\n typeof fi === 'string' ? JSON.parse(fi) : fi\n );\n } else if (typeof body.fileInfo === 'string') {\n parsedFileInfo = JSON.parse(body.fileInfo);\n }\n\n // Filter fileInfo by index - only keep entries for files that passed validation\n if (Array.isArray(parsedFileInfo)) {\n const invalidIndices = new Set(securityResults.errors.map((e) => e.originalIndex));\n const filteredFileInfo = parsedFileInfo.filter(\n (_: any, index: number) => !invalidIndices.has(index)\n );\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n } else {\n filteredBody = {\n ...body,\n fileInfo: parsedFileInfo,\n };\n }\n }\n\n // Map errors to simplified format\n const uploadErrors: FileUploadError[] = securityResults.errors.map((e) => ({\n name: e.file?.originalFilename || e.file?.name || 'unknown',\n message: e.error.message,\n }));\n\n return {\n validFiles: securityResults.validFiles,\n filteredBody,\n errors: uploadErrors,\n };\n}\n"],"names":["readFileChunk","filePath","chunkSize","buffer","readFile","length","subarray","detectMimeType","file","path","filepath","tempFilePath","error","Error","message","String","undefined","fileTypeFromBuffer","result","Uint8Array","mime","matchesMimePattern","mimeType","patterns","some","pattern","normalizedPattern","toLowerCase","normalizedMimeType","includes","regexPattern","replace","regex","RegExp","matches","test","exactMatch","isMimeTypeAllowed","config","allowedTypes","deniedTypes","extractFileInfo","fileName","originalFilename","name","filename","newFilename","declaredMimeType","mimetype","type","validateFile","strapi","isValid","detectedMime","mimeDetectionFailed","log","warn","mimeToValidate","code","details","reason","declaredType","detectedType","finalType","validateFiles","files","filesArray","Array","isArray","get","every","item","errors","ApplicationError","map","validationPromises","index","fileIndex","originalname","originalError","Promise","all","enforceUploadSecurity","validationResults","validFiles","validFileNames","entries","push","originalIndex","prepareUploadRequest","filesInput","body","securityResults","filteredBody","fileInfo","parsedFileInfo","fi","JSON","parse","invalidIndices","Set","e","filteredFileInfo","filter","_","has","uploadErrors"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mime-validation.mjs","sources":["../../../server/src/utils/mime-validation.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport { lookup } from 'mime-types';\nimport type { Core } from '@strapi/types';\nimport { errors } from '@strapi/utils';\n\nexport type SecurityConfig = {\n allowedTypes?: string[];\n deniedTypes?: string[];\n};\ntype UploadValidationError = {\n code: 'MIME_TYPE_NOT_ALLOWED' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR';\n message: string;\n details: Record<string, any>;\n};\n\ntype ValidationResult = {\n isValid: boolean;\n error?: UploadValidationError;\n detectedMime?: string;\n};\n\ntype ErrorDetail = {\n file: any;\n originalIndex: number;\n error: UploadValidationError;\n};\n\nasync function readFileChunk(filePath: string, chunkSize: number = 4100): Promise<Buffer> {\n const buffer = await readFile(filePath);\n return buffer.length > chunkSize ? buffer.subarray(0, chunkSize) : buffer;\n}\n\nexport async function detectMimeType(file: any): Promise<string | undefined> {\n let buffer: Buffer;\n\n // Support multiple property names used by different multipart parsers (formidable uses filepath)\n const filePath = file.filepath ?? file.path ?? file.tempFilePath ?? file.filePath;\n\n if (filePath) {\n try {\n buffer = await readFileChunk(filePath, 4100);\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else if (file.buffer) {\n buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;\n } else {\n // No file path or buffer; cannot detect MIME. Validation will use declared/extension or reject.\n return undefined;\n }\n\n try {\n /**\n * Use dynamic import to support file-type which is ESM-only\n * Static imports fail during CommonJS build since bundler can't transform ESM-only packages\n * @see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n */\n const { fileTypeFromBuffer } = await import('file-type');\n\n const result = await fileTypeFromBuffer(new Uint8Array(buffer));\n return result?.mime;\n } catch (error) {\n throw new Error(\n `Failed to detect MIME type: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nfunction matchesMimePattern(mimeType: string, patterns: string[]): boolean {\n if (!patterns?.length) return false;\n\n return patterns.some((pattern) => {\n const normalizedPattern = pattern.toLowerCase();\n const normalizedMimeType = mimeType.toLowerCase();\n\n if (normalizedPattern.includes('*')) {\n const regexPattern = normalizedPattern.replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n const matches = regex.test(normalizedMimeType);\n return matches;\n }\n\n const exactMatch = normalizedPattern === normalizedMimeType;\n return exactMatch;\n });\n}\n\nexport function isMimeTypeAllowed(mimeType: string, config: SecurityConfig): boolean {\n const { allowedTypes, deniedTypes } = config;\n\n if (!mimeType) return false;\n\n if (deniedTypes?.length && matchesMimePattern(mimeType, deniedTypes)) {\n return false;\n }\n\n if (Array.isArray(allowedTypes)) {\n return allowedTypes.length > 0 && matchesMimePattern(mimeType, allowedTypes);\n }\n\n return true;\n}\n\n/**\n * Single gate for allow/deny. Returns ValidationResult.\n * Doc: docs/docs/01-core/upload/01-backend/01-mime-validation.md\n */\nfunction validateAllowBanLists(\n mimetype: string,\n fileName: string,\n declaredType: string,\n detectedType: string | undefined,\n config: SecurityConfig\n): ValidationResult {\n if (!mimetype) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: 'MIME type is not allowed',\n details: {\n fileName,\n reason: 'No MIME type to validate',\n declaredType,\n detectedType,\n allowedTypes: config.allowedTypes,\n deniedTypes: config.deniedTypes,\n },\n },\n };\n }\n if (config.deniedTypes?.length && matchesMimePattern(mimetype, config.deniedTypes)) {\n return buildNotAllowedError(fileName, declaredType, detectedType, mimetype, config);\n }\n if (!Array.isArray(config.allowedTypes)) {\n return { isValid: true, detectedMime: mimetype };\n }\n if (config.allowedTypes.length === 0) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: 'MIME type is not allowed',\n details: {\n fileName,\n reason: 'Allow list is empty',\n declaredType,\n detectedType,\n allowedTypes: config.allowedTypes,\n deniedTypes: config.deniedTypes,\n },\n },\n };\n }\n if (matchesMimePattern(mimetype, config.allowedTypes)) {\n return { isValid: true, detectedMime: mimetype };\n }\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${mimetype}' is not allowed`,\n details: {\n fileName,\n declaredType,\n detectedType,\n finalType: mimetype,\n allowedTypes: config.allowedTypes,\n deniedTypes: config.deniedTypes,\n },\n },\n };\n}\n\nfunction isDeclaredGeneric(declaredMimeType: string): boolean {\n return !declaredMimeType || declaredMimeType === 'application/octet-stream';\n}\n\nexport function extractFileInfo(file: any) {\n const fileName =\n file.originalFilename ||\n file.name ||\n file.filename ||\n file.newFilename ||\n file.originalname ||\n 'unknown';\n const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';\n\n return { fileName, declaredMimeType };\n}\n\nexport async function validateFile(\n file: any,\n config: SecurityConfig,\n strapi: Core.Strapi\n): Promise<ValidationResult> {\n const { allowedTypes, deniedTypes } = config;\n\n const { fileName, declaredMimeType } = extractFileInfo(file);\n\n const fileExt = extname(fileName).toLowerCase();\n const expectedMimeFromExt = fileExt ? lookup(fileExt) || null : null;\n\n // Reject if declared type is denied.\n if (\n !isDeclaredGeneric(declaredMimeType) &&\n deniedTypes?.length &&\n matchesMimePattern(declaredMimeType, deniedTypes)\n ) {\n return buildNotAllowedError(fileName, declaredMimeType, undefined, declaredMimeType, config);\n }\n\n // Reject if extension's type is denied.\n if (\n expectedMimeFromExt &&\n deniedTypes?.length &&\n matchesMimePattern(expectedMimeFromExt, deniedTypes)\n ) {\n return buildNotAllowedError(fileName, declaredMimeType, undefined, expectedMimeFromExt, config);\n }\n\n // Run content detection.\n let detectedMime: string | undefined;\n try {\n detectedMime = await detectMimeType(file);\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n strapi.log.warn(`Failed to detect MIME type from file: ${errMsg}`, {\n fileName,\n error: errMsg,\n });\n }\n\n const declaredMatchesExtension =\n !isDeclaredGeneric(declaredMimeType) &&\n expectedMimeFromExt !== null &&\n expectedMimeFromExt !== undefined &&\n (matchesMimePattern(declaredMimeType, [expectedMimeFromExt]) ||\n matchesMimePattern(expectedMimeFromExt, [declaredMimeType]));\n const detectedMatchesDeclared =\n detectedMime && declaredMimeType && matchesMimePattern(detectedMime, [declaredMimeType]);\n\n // Trusted declaration: declared matches extension and detection confirms.\n if (declaredMatchesExtension && detectedMatchesDeclared) {\n return validateAllowBanLists(\n declaredMimeType,\n fileName,\n declaredMimeType,\n detectedMime,\n config\n );\n }\n\n // Reject if detected type is denied.\n if (detectedMime && deniedTypes?.length && matchesMimePattern(detectedMime, deniedTypes)) {\n return buildNotAllowedError(fileName, declaredMimeType, detectedMime, detectedMime, config);\n }\n\n // Reject if detected is not in allow list (extension/declared cannot override).\n // Exception: file-type often returns application/zip for Office formats (docx, xlsx); skip so the next block can allow via extension type.\n const isZipWithAllowedExt =\n detectedMime === 'application/zip' &&\n expectedMimeFromExt &&\n expectedMimeFromExt !== 'application/zip' &&\n Array.isArray(allowedTypes) &&\n allowedTypes.length > 0 &&\n matchesMimePattern(expectedMimeFromExt, allowedTypes);\n if (\n detectedMime &&\n Array.isArray(allowedTypes) &&\n allowedTypes.length > 0 &&\n !matchesMimePattern(detectedMime, allowedTypes) &&\n !isZipWithAllowedExt\n ) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: 'MIME type is not allowed',\n details: {\n fileName,\n reason:\n 'File content was detected as a type not in the allow list; extension or declared type cannot override',\n declaredType: declaredMimeType,\n detectedType: detectedMime,\n expectedMimeFromExtension: expectedMimeFromExt,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n }\n\n // Use detected type when defined and (no extension, or detected matches extension, or declared is generic).\n const detectedMatchesExtension =\n detectedMime &&\n expectedMimeFromExt !== null &&\n expectedMimeFromExt !== undefined &&\n matchesMimePattern(detectedMime, [expectedMimeFromExt]);\n if (\n detectedMime &&\n (!expectedMimeFromExt || detectedMatchesExtension || isDeclaredGeneric(declaredMimeType))\n ) {\n // Office/zip exception: use extension type when detection returned application/zip and extension is in allow list.\n if (\n detectedMime === 'application/zip' &&\n expectedMimeFromExt &&\n expectedMimeFromExt !== 'application/zip'\n ) {\n const extResult = validateAllowBanLists(\n expectedMimeFromExt,\n fileName,\n declaredMimeType,\n detectedMime,\n config\n );\n if (extResult.isValid) {\n strapi.log.warn(\n 'MIME type detection returned application/zip; trusting extension for allow list',\n { fileName, fileExtension: fileExt, expectedMimeFromExtension: expectedMimeFromExt }\n );\n return extResult;\n }\n }\n return validateAllowBanLists(detectedMime, fileName, declaredMimeType, detectedMime, config);\n }\n\n // Use extension's type when present.\n if (expectedMimeFromExt) {\n return validateAllowBanLists(\n expectedMimeFromExt,\n fileName,\n declaredMimeType,\n detectedMime,\n config\n );\n }\n\n // Use declared type as last resort.\n if (declaredMimeType) {\n return validateAllowBanLists(\n declaredMimeType,\n fileName,\n declaredMimeType,\n detectedMime,\n config\n );\n }\n\n // Reject when no type can be chosen.\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: 'Cannot verify file type for security reasons',\n details: {\n fileName,\n reason: 'No MIME type to validate (no declared type, no extension, no detection)',\n declaredType: declaredMimeType,\n detectedType: detectedMime,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n}\n\nfunction buildNotAllowedError(\n fileName: string,\n declaredType: string,\n detectedType: string | undefined,\n rejectedType: string,\n config: SecurityConfig\n): ValidationResult {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${rejectedType}' is not allowed`,\n details: {\n fileName,\n declaredType,\n detectedType,\n finalType: rejectedType,\n allowedTypes: config.allowedTypes,\n deniedTypes: config.deniedTypes,\n reason: 'MIME type is in the denied list',\n },\n },\n };\n}\n\nexport async function validateFiles(files: any, strapi: Core.Strapi): Promise<ValidationResult[]> {\n const filesArray = Array.isArray(files) ? files : [files];\n\n if (!filesArray.length) {\n return [];\n }\n\n // Use array path so 'plugin::upload' is a single key (lodash string path splits on '.')\n let config: SecurityConfig = strapi.config.get(\n ['plugin::upload', 'security'],\n {}\n ) as SecurityConfig;\n if (!config || typeof config !== 'object') {\n config = {};\n }\n if (\n config.allowedTypes &&\n (!Array.isArray(config.allowedTypes) ||\n !config.allowedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: allowedTypes must be an array of strings.'\n );\n }\n\n if (\n config.deniedTypes &&\n (!Array.isArray(config.deniedTypes) ||\n !config.deniedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: deniedTypes must be an array of strings.'\n );\n }\n\n if (!config.allowedTypes && !config.deniedTypes) {\n strapi.log.warn(\n 'No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.'\n );\n // Do not return; we still run validation so MIME detection runs and stored file gets detected type when possible\n }\n\n const validationPromises = filesArray.map(async (file, index) => {\n try {\n return await validateFile(file, config, strapi);\n } catch (error) {\n strapi.log.error('Unexpected error during file validation', {\n fileIndex: index,\n fileName: file?.name || file?.originalname,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n isValid: false,\n error: {\n code: 'VALIDATION_ERROR' as const,\n message: `Validation failed for file at index ${index}`,\n details: {\n index,\n fileName: file?.name || file?.originalname,\n originalError: error instanceof Error ? error.message : String(error),\n },\n },\n };\n }\n });\n\n return Promise.all(validationPromises);\n}\n\nexport async function enforceUploadSecurity(\n files: any,\n strapi: Core.Strapi\n): Promise<{\n validFiles: any[];\n validFileNames: string[];\n errors: Array<ErrorDetail>;\n}> {\n const validationResults = await validateFiles(files, strapi);\n const filesArray = Array.isArray(files) ? files : [files];\n\n const validFiles: any[] = [];\n const validFileNames: string[] = [];\n const errors: Array<ErrorDetail> = [];\n\n for (const [index, result] of validationResults.entries()) {\n if (result.isValid) {\n const file = filesArray[index];\n // Enrich file with detected MIME type for use in storage\n if (result.detectedMime) {\n file.detectedMimeType = result.detectedMime;\n }\n validFiles.push(file);\n validFileNames.push(file.originalFilename || file.name);\n } else if (result.error) {\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: result.error,\n });\n } else {\n // Handle case where validation failed but no error details are provided\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: {\n code: 'UNKNOWN_ERROR' as const,\n message: 'File validation failed for unknown reason',\n details: {\n index,\n fileName: filesArray[index]?.name || filesArray[index]?.originalname,\n },\n },\n });\n }\n }\n\n return { validFiles, validFileNames, errors };\n}\n\nexport type FileUploadError = {\n name: string;\n message: string;\n};\n\nexport type PrepareUploadResult = {\n validFiles: any[];\n filteredBody: any;\n errors: FileUploadError[];\n};\n\n/**\n * Prepare files and body for upload by enforcing security and parsing fileInfo\n */\nexport async function prepareUploadRequest(\n filesInput: any,\n body: any,\n strapi: Core.Strapi\n): Promise<PrepareUploadResult> {\n const securityResults = await enforceUploadSecurity(filesInput, strapi);\n\n let filteredBody = body;\n if (body?.fileInfo) {\n // Parse JSON strings in fileInfo\n let parsedFileInfo = body.fileInfo;\n if (Array.isArray(body.fileInfo)) {\n parsedFileInfo = body.fileInfo.map((fi: any) =>\n typeof fi === 'string' ? JSON.parse(fi) : fi\n );\n } else if (typeof body.fileInfo === 'string') {\n parsedFileInfo = JSON.parse(body.fileInfo);\n }\n\n // Filter fileInfo by index - only keep entries for files that passed validation\n if (Array.isArray(parsedFileInfo)) {\n const invalidIndices = new Set(securityResults.errors.map((e) => e.originalIndex));\n const filteredFileInfo = parsedFileInfo.filter(\n (_: any, index: number) => !invalidIndices.has(index)\n );\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n } else {\n filteredBody = {\n ...body,\n fileInfo: parsedFileInfo,\n };\n }\n }\n\n // Map errors to simplified format\n const uploadErrors: FileUploadError[] = securityResults.errors.map((e) => ({\n name: e.file?.originalFilename || e.file?.name || 'unknown',\n message: e.error.message,\n }));\n\n return {\n validFiles: securityResults.validFiles,\n filteredBody,\n errors: uploadErrors,\n };\n}\n"],"names":["readFileChunk","filePath","chunkSize","buffer","readFile","length","subarray","detectMimeType","file","filepath","path","tempFilePath","error","Error","message","String","undefined","fileTypeFromBuffer","result","Uint8Array","mime","matchesMimePattern","mimeType","patterns","some","pattern","normalizedPattern","toLowerCase","normalizedMimeType","includes","regexPattern","replace","regex","RegExp","matches","test","exactMatch","validateAllowBanLists","mimetype","fileName","declaredType","detectedType","config","isValid","code","details","reason","allowedTypes","deniedTypes","buildNotAllowedError","Array","isArray","detectedMime","finalType","isDeclaredGeneric","declaredMimeType","extractFileInfo","originalFilename","name","filename","newFilename","originalname","type","validateFile","strapi","fileExt","extname","expectedMimeFromExt","lookup","errMsg","log","warn","declaredMatchesExtension","detectedMatchesDeclared","isZipWithAllowedExt","expectedMimeFromExtension","detectedMatchesExtension","extResult","fileExtension","rejectedType","validateFiles","files","filesArray","get","every","item","errors","ApplicationError","validationPromises","map","index","fileIndex","originalError","Promise","all","enforceUploadSecurity","validationResults","validFiles","validFileNames","entries","detectedMimeType","push","originalIndex","prepareUploadRequest","filesInput","body","securityResults","filteredBody","fileInfo","parsedFileInfo","fi","JSON","parse","invalidIndices","Set","e","filteredFileInfo","filter","_","has","uploadErrors"],"mappings":";;;;;AA4BA,eAAeA,aAAAA,CAAcC,QAAgB,EAAEC,SAAAA,GAAoB,IAAI,EAAA;IACrE,MAAMC,MAAAA,GAAS,MAAMC,QAAAA,CAASH,QAAAA,CAAAA;IAC9B,OAAOE,MAAAA,CAAOE,MAAM,GAAGH,SAAAA,GAAYC,OAAOG,QAAQ,CAAC,GAAGJ,SAAAA,CAAAA,GAAaC,MAAAA;AACrE;AAEO,eAAeI,eAAeC,IAAS,EAAA;IAC5C,IAAIL,MAAAA;;IAGJ,MAAMF,QAAAA,GAAWO,IAAAA,CAAKC,QAAQ,IAAID,IAAAA,CAAKE,IAAI,IAAIF,IAAAA,CAAKG,YAAY,IAAIH,IAAAA,CAAKP,QAAQ;AAEjF,IAAA,IAAIA,QAAAA,EAAU;QACZ,IAAI;YACFE,MAAAA,GAAS,MAAMH,cAAcC,QAAAA,EAAU,IAAA,CAAA;AACzC,QAAA,CAAA,CAAE,OAAOW,KAAAA,EAAO;YACd,MAAM,IAAIC,KAAAA,CACR,CAAC,qBAAqB,EAAED,KAAAA,YAAiBC,KAAAA,GAAQD,KAAAA,CAAME,OAAO,GAAGC,MAAAA,CAAOH,KAAAA,CAAAA,CAAAA,CAAQ,CAAA;AAEpF,QAAA;IACF,CAAA,MAAO,IAAIJ,IAAAA,CAAKL,MAAM,EAAE;AACtBA,QAAAA,MAAAA,GAASK,IAAAA,CAAKL,MAAM,CAACE,MAAM,GAAG,IAAA,GAAOG,IAAAA,CAAKL,MAAM,CAACG,QAAQ,CAAC,CAAA,EAAG,IAAA,CAAA,GAAQE,KAAKL,MAAM;IAClF,CAAA,MAAO;;QAEL,OAAOa,SAAAA;AACT,IAAA;IAEA,IAAI;AACF;;;;AAIC,QACD,MAAM,EAAEC,kBAAkB,EAAE,GAAG,MAAM,OAAO,WAAA,CAAA;AAE5C,QAAA,MAAMC,MAAAA,GAAS,MAAMD,kBAAAA,CAAmB,IAAIE,UAAAA,CAAWhB,MAAAA,CAAAA,CAAAA;AACvD,QAAA,OAAOe,MAAAA,EAAQE,IAAAA;AACjB,IAAA,CAAA,CAAE,OAAOR,KAAAA,EAAO;QACd,MAAM,IAAIC,KAAAA,CACR,CAAC,4BAA4B,EAAED,KAAAA,YAAiBC,KAAAA,GAAQD,KAAAA,CAAME,OAAO,GAAGC,MAAAA,CAAOH,KAAAA,CAAAA,CAAAA,CAAQ,CAAA;AAE3F,IAAA;AACF;AAEA,SAASS,kBAAAA,CAAmBC,QAAgB,EAAEC,QAAkB,EAAA;IAC9D,IAAI,CAACA,QAAAA,EAAUlB,MAAAA,EAAQ,OAAO,KAAA;IAE9B,OAAOkB,QAAAA,CAASC,IAAI,CAAC,CAACC,OAAAA,GAAAA;QACpB,MAAMC,iBAAAA,GAAoBD,QAAQE,WAAW,EAAA;QAC7C,MAAMC,kBAAAA,GAAqBN,SAASK,WAAW,EAAA;QAE/C,IAAID,iBAAAA,CAAkBG,QAAQ,CAAC,GAAA,CAAA,EAAM;AACnC,YAAA,MAAMC,YAAAA,GAAeJ,iBAAAA,CAAkBK,OAAO,CAAC,KAAA,EAAO,IAAA,CAAA;YAEtD,MAAMC,KAAAA,GAAQ,IAAIC,MAAAA,CAAO,CAAC,CAAC,EAAEH,YAAAA,CAAa,CAAC,CAAC,CAAA;YAC5C,MAAMI,OAAAA,GAAUF,KAAAA,CAAMG,IAAI,CAACP,kBAAAA,CAAAA;YAC3B,OAAOM,OAAAA;AACT,QAAA;AAEA,QAAA,MAAME,aAAaV,iBAAAA,KAAsBE,kBAAAA;QACzC,OAAOQ,UAAAA;AACT,IAAA,CAAA,CAAA;AACF;AAkBA;;;IAIA,SAASC,qBAAAA,CACPC,QAAgB,EAChBC,QAAgB,EAChBC,YAAoB,EACpBC,YAAgC,EAChCC,MAAsB,EAAA;AAEtB,IAAA,IAAI,CAACJ,QAAAA,EAAU;QACb,OAAO;YACLK,OAAAA,EAAS,KAAA;YACT/B,KAAAA,EAAO;gBACLgC,IAAAA,EAAM,uBAAA;gBACN9B,OAAAA,EAAS,0BAAA;gBACT+B,OAAAA,EAAS;AACPN,oBAAAA,QAAAA;oBACAO,MAAAA,EAAQ,0BAAA;AACRN,oBAAAA,YAAAA;AACAC,oBAAAA,YAAAA;AACAM,oBAAAA,YAAAA,EAAcL,OAAOK,YAAY;AACjCC,oBAAAA,WAAAA,EAAaN,OAAOM;AACtB;AACF;AACF,SAAA;AACF,IAAA;IACA,IAAIN,MAAAA,CAAOM,WAAW,EAAE3C,MAAAA,IAAUgB,mBAAmBiB,QAAAA,EAAUI,MAAAA,CAAOM,WAAW,CAAA,EAAG;AAClF,QAAA,OAAOC,oBAAAA,CAAqBV,QAAAA,EAAUC,YAAAA,EAAcC,YAAAA,EAAcH,QAAAA,EAAUI,MAAAA,CAAAA;AAC9E,IAAA;AACA,IAAA,IAAI,CAACQ,KAAAA,CAAMC,OAAO,CAACT,MAAAA,CAAOK,YAAY,CAAA,EAAG;QACvC,OAAO;YAAEJ,OAAAA,EAAS,IAAA;YAAMS,YAAAA,EAAcd;AAAS,SAAA;AACjD,IAAA;AACA,IAAA,IAAII,MAAAA,CAAOK,YAAY,CAAC1C,MAAM,KAAK,CAAA,EAAG;QACpC,OAAO;YACLsC,OAAAA,EAAS,KAAA;YACT/B,KAAAA,EAAO;gBACLgC,IAAAA,EAAM,uBAAA;gBACN9B,OAAAA,EAAS,0BAAA;gBACT+B,OAAAA,EAAS;AACPN,oBAAAA,QAAAA;oBACAO,MAAAA,EAAQ,qBAAA;AACRN,oBAAAA,YAAAA;AACAC,oBAAAA,YAAAA;AACAM,oBAAAA,YAAAA,EAAcL,OAAOK,YAAY;AACjCC,oBAAAA,WAAAA,EAAaN,OAAOM;AACtB;AACF;AACF,SAAA;AACF,IAAA;AACA,IAAA,IAAI3B,kBAAAA,CAAmBiB,QAAAA,EAAUI,MAAAA,CAAOK,YAAY,CAAA,EAAG;QACrD,OAAO;YAAEJ,OAAAA,EAAS,IAAA;YAAMS,YAAAA,EAAcd;AAAS,SAAA;AACjD,IAAA;IACA,OAAO;QACLK,OAAAA,EAAS,KAAA;QACT/B,KAAAA,EAAO;YACLgC,IAAAA,EAAM,uBAAA;AACN9B,YAAAA,OAAAA,EAAS,CAAC,WAAW,EAAEwB,QAAAA,CAAS,gBAAgB,CAAC;YACjDO,OAAAA,EAAS;AACPN,gBAAAA,QAAAA;AACAC,gBAAAA,YAAAA;AACAC,gBAAAA,YAAAA;gBACAY,SAAAA,EAAWf,QAAAA;AACXS,gBAAAA,YAAAA,EAAcL,OAAOK,YAAY;AACjCC,gBAAAA,WAAAA,EAAaN,OAAOM;AACtB;AACF;AACF,KAAA;AACF;AAEA,SAASM,kBAAkBC,gBAAwB,EAAA;IACjD,OAAO,CAACA,oBAAoBA,gBAAAA,KAAqB,0BAAA;AACnD;AAEO,SAASC,gBAAgBhD,IAAS,EAAA;AACvC,IAAA,MAAM+B,QAAAA,GACJ/B,IAAAA,CAAKiD,gBAAgB,IACrBjD,KAAKkD,IAAI,IACTlD,IAAAA,CAAKmD,QAAQ,IACbnD,IAAAA,CAAKoD,WAAW,IAChBpD,IAAAA,CAAKqD,YAAY,IACjB,SAAA;AACF,IAAA,MAAMN,gBAAAA,GAAmB/C,IAAAA,CAAK8B,QAAQ,IAAI9B,IAAAA,CAAKsD,IAAI,IAAItD,IAAAA,CAAKc,QAAQ,IAAId,IAAAA,CAAKY,IAAI,IAAI,EAAA;IAErF,OAAO;AAAEmB,QAAAA,QAAAA;AAAUgB,QAAAA;AAAiB,KAAA;AACtC;AAEO,eAAeQ,YAAAA,CACpBvD,IAAS,EACTkC,MAAsB,EACtBsB,MAAmB,EAAA;AAEnB,IAAA,MAAM,EAAEjB,YAAY,EAAEC,WAAW,EAAE,GAAGN,MAAAA;AAEtC,IAAA,MAAM,EAAEH,QAAQ,EAAEgB,gBAAgB,EAAE,GAAGC,eAAAA,CAAgBhD,IAAAA,CAAAA;IAEvD,MAAMyD,OAAAA,GAAUC,OAAAA,CAAQ3B,QAAAA,CAAAA,CAAUZ,WAAW,EAAA;AAC7C,IAAA,MAAMwC,mBAAAA,GAAsBF,OAAAA,GAAUG,MAAAA,CAAOH,OAAAA,CAAAA,IAAY,IAAA,GAAO,IAAA;;AAGhE,IAAA,IACE,CAACX,iBAAAA,CAAkBC,gBAAAA,CAAAA,IACnBP,aAAa3C,MAAAA,IACbgB,kBAAAA,CAAmBkC,kBAAkBP,WAAAA,CAAAA,EACrC;AACA,QAAA,OAAOC,oBAAAA,CAAqBV,QAAAA,EAAUgB,gBAAAA,EAAkBvC,SAAAA,EAAWuC,gBAAAA,EAAkBb,MAAAA,CAAAA;AACvF,IAAA;;AAGA,IAAA,IACEyB,mBAAAA,IACAnB,WAAAA,EAAa3C,MAAAA,IACbgB,kBAAAA,CAAmB8C,qBAAqBnB,WAAAA,CAAAA,EACxC;AACA,QAAA,OAAOC,oBAAAA,CAAqBV,QAAAA,EAAUgB,gBAAAA,EAAkBvC,SAAAA,EAAWmD,mBAAAA,EAAqBzB,MAAAA,CAAAA;AAC1F,IAAA;;IAGA,IAAIU,YAAAA;IACJ,IAAI;AACFA,QAAAA,YAAAA,GAAe,MAAM7C,cAAAA,CAAeC,IAAAA,CAAAA;AACtC,IAAA,CAAA,CAAE,OAAOI,KAAAA,EAAO;AACd,QAAA,MAAMyD,SAASzD,KAAAA,YAAiBC,KAAAA,GAAQD,KAAAA,CAAME,OAAO,GAAGC,MAAAA,CAAOH,KAAAA,CAAAA;QAC/DoD,MAAAA,CAAOM,GAAG,CAACC,IAAI,CAAC,CAAC,sCAAsC,EAAEF,QAAQ,EAAE;AACjE9B,YAAAA,QAAAA;YACA3B,KAAAA,EAAOyD;AACT,SAAA,CAAA;AACF,IAAA;IAEA,MAAMG,wBAAAA,GACJ,CAAClB,iBAAAA,CAAkBC,gBAAAA,CAAAA,IACnBY,mBAAAA,KAAwB,QACxBA,mBAAAA,KAAwBnD,SAAAA,KACvBK,kBAAAA,CAAmBkC,gBAAAA,EAAkB;AAACY,QAAAA;AAAoB,KAAA,CAAA,IACzD9C,mBAAmB8C,mBAAAA,EAAqB;AAACZ,QAAAA;KAAiB,CAAA,CAAA;AAC9D,IAAA,MAAMkB,uBAAAA,GACJrB,YAAAA,IAAgBG,gBAAAA,IAAoBlC,kBAAAA,CAAmB+B,YAAAA,EAAc;AAACG,QAAAA;AAAiB,KAAA,CAAA;;AAGzF,IAAA,IAAIiB,4BAA4BC,uBAAAA,EAAyB;AACvD,QAAA,OAAOpC,qBAAAA,CACLkB,gBAAAA,EACAhB,QAAAA,EACAgB,gBAAAA,EACAH,YAAAA,EACAV,MAAAA,CAAAA;AAEJ,IAAA;;AAGA,IAAA,IAAIU,YAAAA,IAAgBJ,WAAAA,EAAa3C,MAAAA,IAAUgB,kBAAAA,CAAmB+B,cAAcJ,WAAAA,CAAAA,EAAc;AACxF,QAAA,OAAOC,oBAAAA,CAAqBV,QAAAA,EAAUgB,gBAAAA,EAAkBH,YAAAA,EAAcA,YAAAA,EAAcV,MAAAA,CAAAA;AACtF,IAAA;;;AAIA,IAAA,MAAMgC,mBAAAA,GACJtB,YAAAA,KAAiB,iBAAA,IACjBe,mBAAAA,IACAA,wBAAwB,iBAAA,IACxBjB,KAAAA,CAAMC,OAAO,CAACJ,iBACdA,YAAAA,CAAa1C,MAAM,GAAG,CAAA,IACtBgB,mBAAmB8C,mBAAAA,EAAqBpB,YAAAA,CAAAA;AAC1C,IAAA,IACEK,YAAAA,IACAF,KAAAA,CAAMC,OAAO,CAACJ,iBACdA,YAAAA,CAAa1C,MAAM,GAAG,CAAA,IACtB,CAACgB,kBAAAA,CAAmB+B,YAAAA,EAAcL,YAAAA,CAAAA,IAClC,CAAC2B,mBAAAA,EACD;QACA,OAAO;YACL/B,OAAAA,EAAS,KAAA;YACT/B,KAAAA,EAAO;gBACLgC,IAAAA,EAAM,uBAAA;gBACN9B,OAAAA,EAAS,0BAAA;gBACT+B,OAAAA,EAAS;AACPN,oBAAAA,QAAAA;oBACAO,MAAAA,EACE,uGAAA;oBACFN,YAAAA,EAAce,gBAAAA;oBACdd,YAAAA,EAAcW,YAAAA;oBACduB,yBAAAA,EAA2BR,mBAAAA;AAC3BpB,oBAAAA,YAAAA;AACAC,oBAAAA;AACF;AACF;AACF,SAAA;AACF,IAAA;;AAGA,IAAA,MAAM4B,2BACJxB,YAAAA,IACAe,mBAAAA,KAAwB,QACxBA,mBAAAA,KAAwBnD,SAAAA,IACxBK,mBAAmB+B,YAAAA,EAAc;AAACe,QAAAA;AAAoB,KAAA,CAAA;AACxD,IAAA,IACEf,iBACC,CAACe,uBAAuBS,wBAAAA,IAA4BtB,iBAAAA,CAAkBC,iBAAgB,CAAA,EACvF;;AAEA,QAAA,IACEH,YAAAA,KAAiB,iBAAA,IACjBe,mBAAAA,IACAA,mBAAAA,KAAwB,iBAAA,EACxB;AACA,YAAA,MAAMU,SAAAA,GAAYxC,qBAAAA,CAChB8B,mBAAAA,EACA5B,QAAAA,EACAgB,kBACAH,YAAAA,EACAV,MAAAA,CAAAA;YAEF,IAAImC,SAAAA,CAAUlC,OAAO,EAAE;AACrBqB,gBAAAA,MAAAA,CAAOM,GAAG,CAACC,IAAI,CACb,iFAAA,EACA;AAAEhC,oBAAAA,QAAAA;oBAAUuC,aAAAA,EAAeb,OAAAA;oBAASU,yBAAAA,EAA2BR;AAAoB,iBAAA,CAAA;gBAErF,OAAOU,SAAAA;AACT,YAAA;AACF,QAAA;AACA,QAAA,OAAOxC,qBAAAA,CAAsBe,YAAAA,EAAcb,QAAAA,EAAUgB,gBAAAA,EAAkBH,YAAAA,EAAcV,MAAAA,CAAAA;AACvF,IAAA;;AAGA,IAAA,IAAIyB,mBAAAA,EAAqB;AACvB,QAAA,OAAO9B,qBAAAA,CACL8B,mBAAAA,EACA5B,QAAAA,EACAgB,gBAAAA,EACAH,YAAAA,EACAV,MAAAA,CAAAA;AAEJ,IAAA;;AAGA,IAAA,IAAIa,gBAAAA,EAAkB;AACpB,QAAA,OAAOlB,qBAAAA,CACLkB,gBAAAA,EACAhB,QAAAA,EACAgB,gBAAAA,EACAH,YAAAA,EACAV,MAAAA,CAAAA;AAEJ,IAAA;;IAGA,OAAO;QACLC,OAAAA,EAAS,KAAA;QACT/B,KAAAA,EAAO;YACLgC,IAAAA,EAAM,uBAAA;YACN9B,OAAAA,EAAS,8CAAA;YACT+B,OAAAA,EAAS;AACPN,gBAAAA,QAAAA;gBACAO,MAAAA,EAAQ,yEAAA;gBACRN,YAAAA,EAAce,gBAAAA;gBACdd,YAAAA,EAAcW,YAAAA;AACdL,gBAAAA,YAAAA;AACAC,gBAAAA;AACF;AACF;AACF,KAAA;AACF;AAEA,SAASC,oBAAAA,CACPV,QAAgB,EAChBC,YAAoB,EACpBC,YAAgC,EAChCsC,YAAoB,EACpBrC,MAAsB,EAAA;IAEtB,OAAO;QACLC,OAAAA,EAAS,KAAA;QACT/B,KAAAA,EAAO;YACLgC,IAAAA,EAAM,uBAAA;AACN9B,YAAAA,OAAAA,EAAS,CAAC,WAAW,EAAEiE,YAAAA,CAAa,gBAAgB,CAAC;YACrDlC,OAAAA,EAAS;AACPN,gBAAAA,QAAAA;AACAC,gBAAAA,YAAAA;AACAC,gBAAAA,YAAAA;gBACAY,SAAAA,EAAW0B,YAAAA;AACXhC,gBAAAA,YAAAA,EAAcL,OAAOK,YAAY;AACjCC,gBAAAA,WAAAA,EAAaN,OAAOM,WAAW;gBAC/BF,MAAAA,EAAQ;AACV;AACF;AACF,KAAA;AACF;AAEO,eAAekC,aAAAA,CAAcC,KAAU,EAAEjB,MAAmB,EAAA;AACjE,IAAA,MAAMkB,UAAAA,GAAahC,KAAAA,CAAMC,OAAO,CAAC8B,SAASA,KAAAA,GAAQ;AAACA,QAAAA;AAAM,KAAA;IAEzD,IAAI,CAACC,UAAAA,CAAW7E,MAAM,EAAE;AACtB,QAAA,OAAO,EAAE;AACX,IAAA;;AAGA,IAAA,IAAIqC,MAAAA,GAAyBsB,MAAAA,CAAOtB,MAAM,CAACyC,GAAG,CAC5C;AAAC,QAAA,gBAAA;AAAkB,QAAA;AAAW,KAAA,EAC9B,EAAC,CAAA;AAEH,IAAA,IAAI,CAACzC,MAAAA,IAAU,OAAOA,MAAAA,KAAW,QAAA,EAAU;AACzCA,QAAAA,MAAAA,GAAS,EAAC;AACZ,IAAA;IACA,IACEA,MAAAA,CAAOK,YAAY,KAClB,CAACG,KAAAA,CAAMC,OAAO,CAACT,MAAAA,CAAOK,YAAY,CAAA,IACjC,CAACL,MAAAA,CAAOK,YAAY,CAACqC,KAAK,CAAC,CAACC,IAAAA,GAAS,OAAOA,IAAAA,KAAS,QAAA,CAAQ,CAAA,EAC/D;QACA,MAAM,IAAIC,MAAAA,CAAOC,gBAAgB,CAC/B,kEAAA,CAAA;AAEJ,IAAA;IAEA,IACE7C,MAAAA,CAAOM,WAAW,KACjB,CAACE,KAAAA,CAAMC,OAAO,CAACT,MAAAA,CAAOM,WAAW,CAAA,IAChC,CAACN,MAAAA,CAAOM,WAAW,CAACoC,KAAK,CAAC,CAACC,IAAAA,GAAS,OAAOA,IAAAA,KAAS,QAAA,CAAQ,CAAA,EAC9D;QACA,MAAM,IAAIC,MAAAA,CAAOC,gBAAgB,CAC/B,iEAAA,CAAA;AAEJ,IAAA;AAEA,IAAA,IAAI,CAAC7C,MAAAA,CAAOK,YAAY,IAAI,CAACL,MAAAA,CAAOM,WAAW,EAAE;QAC/CgB,MAAAA,CAAOM,GAAG,CAACC,IAAI,CACb,mHAAA,CAAA;;AAGJ,IAAA;AAEA,IAAA,MAAMiB,kBAAAA,GAAqBN,UAAAA,CAAWO,GAAG,CAAC,OAAOjF,IAAAA,EAAMkF,KAAAA,GAAAA;QACrD,IAAI;YACF,OAAO,MAAM3B,YAAAA,CAAavD,IAAAA,EAAMkC,MAAAA,EAAQsB,MAAAA,CAAAA;AAC1C,QAAA,CAAA,CAAE,OAAOpD,KAAAA,EAAO;AACdoD,YAAAA,MAAAA,CAAOM,GAAG,CAAC1D,KAAK,CAAC,yCAAA,EAA2C;gBAC1D+E,SAAAA,EAAWD,KAAAA;gBACXnD,QAAAA,EAAU/B,IAAAA,EAAMkD,QAAQlD,IAAAA,EAAMqD,YAAAA;AAC9BjD,gBAAAA,KAAAA,EAAOA,KAAAA,YAAiBC,KAAAA,GAAQD,KAAAA,CAAME,OAAO,GAAGC,MAAAA,CAAOH,KAAAA;AACzD,aAAA,CAAA;YAEA,OAAO;gBACL+B,OAAAA,EAAS,KAAA;gBACT/B,KAAAA,EAAO;oBACLgC,IAAAA,EAAM,kBAAA;oBACN9B,OAAAA,EAAS,CAAC,oCAAoC,EAAE4E,KAAAA,CAAAA,CAAO;oBACvD7C,OAAAA,EAAS;AACP6C,wBAAAA,KAAAA;wBACAnD,QAAAA,EAAU/B,IAAAA,EAAMkD,QAAQlD,IAAAA,EAAMqD,YAAAA;AAC9B+B,wBAAAA,aAAAA,EAAehF,KAAAA,YAAiBC,KAAAA,GAAQD,KAAAA,CAAME,OAAO,GAAGC,MAAAA,CAAOH,KAAAA;AACjE;AACF;AACF,aAAA;AACF,QAAA;AACF,IAAA,CAAA,CAAA;IAEA,OAAOiF,OAAAA,CAAQC,GAAG,CAACN,kBAAAA,CAAAA;AACrB;AAEO,eAAeO,qBAAAA,CACpBd,KAAU,EACVjB,MAAmB,EAAA;IAMnB,MAAMgC,iBAAAA,GAAoB,MAAMhB,aAAAA,CAAcC,KAAAA,EAAOjB,MAAAA,CAAAA;AACrD,IAAA,MAAMkB,UAAAA,GAAahC,KAAAA,CAAMC,OAAO,CAAC8B,SAASA,KAAAA,GAAQ;AAACA,QAAAA;AAAM,KAAA;AAEzD,IAAA,MAAMgB,aAAoB,EAAE;AAC5B,IAAA,MAAMC,iBAA2B,EAAE;AACnC,IAAA,MAAMZ,SAA6B,EAAE;AAErC,IAAA,KAAK,MAAM,CAACI,KAAAA,EAAOxE,OAAO,IAAI8E,iBAAAA,CAAkBG,OAAO,EAAA,CAAI;QACzD,IAAIjF,MAAAA,CAAOyB,OAAO,EAAE;YAClB,MAAMnC,IAAAA,GAAO0E,UAAU,CAACQ,KAAAA,CAAM;;YAE9B,IAAIxE,MAAAA,CAAOkC,YAAY,EAAE;gBACvB5C,IAAAA,CAAK4F,gBAAgB,GAAGlF,MAAAA,CAAOkC,YAAY;AAC7C,YAAA;AACA6C,YAAAA,UAAAA,CAAWI,IAAI,CAAC7F,IAAAA,CAAAA;AAChB0F,YAAAA,cAAAA,CAAeG,IAAI,CAAC7F,IAAAA,CAAKiD,gBAAgB,IAAIjD,KAAKkD,IAAI,CAAA;QACxD,CAAA,MAAO,IAAIxC,MAAAA,CAAON,KAAK,EAAE;AACvB0E,YAAAA,MAAAA,CAAOe,IAAI,CAAC;gBACV7F,IAAAA,EAAM0E,UAAU,CAACQ,KAAAA,CAAM;gBACvBY,aAAAA,EAAeZ,KAAAA;AACf9E,gBAAAA,KAAAA,EAAOM,OAAON;AAChB,aAAA,CAAA;QACF,CAAA,MAAO;;AAEL0E,YAAAA,MAAAA,CAAOe,IAAI,CAAC;gBACV7F,IAAAA,EAAM0E,UAAU,CAACQ,KAAAA,CAAM;gBACvBY,aAAAA,EAAeZ,KAAAA;gBACf9E,KAAAA,EAAO;oBACLgC,IAAAA,EAAM,eAAA;oBACN9B,OAAAA,EAAS,2CAAA;oBACT+B,OAAAA,EAAS;AACP6C,wBAAAA,KAAAA;wBACAnD,QAAAA,EAAU2C,UAAU,CAACQ,KAAAA,CAAM,EAAEhC,QAAQwB,UAAU,CAACQ,MAAM,EAAE7B;AAC1D;AACF;AACF,aAAA,CAAA;AACF,QAAA;AACF,IAAA;IAEA,OAAO;AAAEoC,QAAAA,UAAAA;AAAYC,QAAAA,cAAAA;AAAgBZ,QAAAA;AAAO,KAAA;AAC9C;AAaA;;AAEC,IACM,eAAeiB,oBAAAA,CACpBC,UAAe,EACfC,IAAS,EACTzC,MAAmB,EAAA;IAEnB,MAAM0C,eAAAA,GAAkB,MAAMX,qBAAAA,CAAsBS,UAAAA,EAAYxC,MAAAA,CAAAA;AAEhE,IAAA,IAAI2C,YAAAA,GAAeF,IAAAA;AACnB,IAAA,IAAIA,MAAMG,QAAAA,EAAU;;QAElB,IAAIC,cAAAA,GAAiBJ,KAAKG,QAAQ;AAClC,QAAA,IAAI1D,KAAAA,CAAMC,OAAO,CAACsD,IAAAA,CAAKG,QAAQ,CAAA,EAAG;AAChCC,YAAAA,cAAAA,GAAiBJ,IAAAA,CAAKG,QAAQ,CAACnB,GAAG,CAAC,CAACqB,EAAAA,GAClC,OAAOA,EAAAA,KAAO,QAAA,GAAWC,IAAAA,CAAKC,KAAK,CAACF,EAAAA,CAAAA,GAAMA,EAAAA,CAAAA;AAE9C,QAAA,CAAA,MAAO,IAAI,OAAOL,IAAAA,CAAKG,QAAQ,KAAK,QAAA,EAAU;AAC5CC,YAAAA,cAAAA,GAAiBE,IAAAA,CAAKC,KAAK,CAACP,IAAAA,CAAKG,QAAQ,CAAA;AAC3C,QAAA;;QAGA,IAAI1D,KAAAA,CAAMC,OAAO,CAAC0D,cAAAA,CAAAA,EAAiB;YACjC,MAAMI,cAAAA,GAAiB,IAAIC,GAAAA,CAAIR,eAAAA,CAAgBpB,MAAM,CAACG,GAAG,CAAC,CAAC0B,CAAAA,GAAMA,CAAAA,CAAEb,aAAa,CAAA,CAAA;YAChF,MAAMc,gBAAAA,GAAmBP,cAAAA,CAAeQ,MAAM,CAC5C,CAACC,GAAQ5B,KAAAA,GAAkB,CAACuB,cAAAA,CAAeM,GAAG,CAAC7B,KAAAA,CAAAA,CAAAA;YAGjD,IAAI0B,gBAAAA,CAAiB/G,MAAM,KAAK,CAAA,EAAG;gBACjCsG,YAAAA,GAAe;AACb,oBAAA,GAAGF,IAAI;oBACPG,QAAAA,EAAUQ,gBAAgB,CAAC,CAAA;AAC7B,iBAAA;YACF,CAAA,MAAO;gBACLT,YAAAA,GAAe;AACb,oBAAA,GAAGF,IAAI;oBACPG,QAAAA,EAAUQ;AACZ,iBAAA;AACF,YAAA;QACF,CAAA,MAAO;YACLT,YAAAA,GAAe;AACb,gBAAA,GAAGF,IAAI;gBACPG,QAAAA,EAAUC;AACZ,aAAA;AACF,QAAA;AACF,IAAA;;IAGA,MAAMW,YAAAA,GAAkCd,gBAAgBpB,MAAM,CAACG,GAAG,CAAC,CAAC0B,KAAO;AACzEzD,YAAAA,IAAAA,EAAMyD,EAAE3G,IAAI,EAAEiD,oBAAoB0D,CAAAA,CAAE3G,IAAI,EAAEkD,IAAAA,IAAQ,SAAA;YAClD5C,OAAAA,EAASqG,CAAAA,CAAEvG,KAAK,CAACE;SACnB,CAAA,CAAA;IAEA,OAAO;AACLmF,QAAAA,UAAAA,EAAYS,gBAAgBT,UAAU;AACtCU,QAAAA,YAAAA;QACArB,MAAAA,EAAQkC;AACV,KAAA;AACF;;;;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/upload",
|
|
3
|
-
"version": "5.38.
|
|
3
|
+
"version": "5.38.1",
|
|
4
4
|
"description": "Makes it easy to upload images and files to your Strapi Application.",
|
|
5
|
+
"homepage": "https://strapi.io",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/strapi/strapi/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git://github.com/strapi/strapi.git",
|
|
12
|
+
"directory": "packages/core/upload"
|
|
13
|
+
},
|
|
5
14
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
15
|
"author": {
|
|
7
16
|
"name": "Strapi Solutions SAS",
|
|
@@ -64,11 +73,11 @@
|
|
|
64
73
|
"@radix-ui/react-dialog": "1.0.5",
|
|
65
74
|
"@radix-ui/react-toggle-group": "1.1.11",
|
|
66
75
|
"@reduxjs/toolkit": "1.9.7",
|
|
67
|
-
"@strapi/database": "5.38.
|
|
76
|
+
"@strapi/database": "5.38.1",
|
|
68
77
|
"@strapi/design-system": "2.2.0",
|
|
69
78
|
"@strapi/icons": "2.2.0",
|
|
70
|
-
"@strapi/provider-upload-local": "5.38.
|
|
71
|
-
"@strapi/utils": "5.38.
|
|
79
|
+
"@strapi/provider-upload-local": "5.38.1",
|
|
80
|
+
"@strapi/utils": "5.38.1",
|
|
72
81
|
"byte-size": "8.1.1",
|
|
73
82
|
"cropperjs": "1.6.1",
|
|
74
83
|
"date-fns": "2.30.0",
|
|
@@ -81,7 +90,7 @@
|
|
|
81
90
|
"lodash": "4.17.23",
|
|
82
91
|
"mime-types": "2.1.35",
|
|
83
92
|
"prop-types": "^15.8.1",
|
|
84
|
-
"qs": "6.
|
|
93
|
+
"qs": "6.15.0",
|
|
85
94
|
"react-dnd": "16.0.1",
|
|
86
95
|
"react-intl": "6.6.2",
|
|
87
96
|
"react-query": "3.39.3",
|
|
@@ -92,8 +101,8 @@
|
|
|
92
101
|
"zod": "3.25.67"
|
|
93
102
|
},
|
|
94
103
|
"devDependencies": {
|
|
95
|
-
"@strapi/admin": "5.38.
|
|
96
|
-
"@strapi/types": "5.38.
|
|
104
|
+
"@strapi/admin": "5.38.1",
|
|
105
|
+
"@strapi/types": "5.38.1",
|
|
97
106
|
"@testing-library/dom": "10.4.1",
|
|
98
107
|
"@testing-library/react": "16.3.0",
|
|
99
108
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -103,7 +112,7 @@
|
|
|
103
112
|
"@types/koa-range": "0.3.5",
|
|
104
113
|
"@types/koa-static": "4.0.2",
|
|
105
114
|
"formidable": "3.5.4",
|
|
106
|
-
"koa": "2.16.
|
|
115
|
+
"koa": "2.16.4",
|
|
107
116
|
"koa-body": "6.0.1",
|
|
108
117
|
"msw": "1.3.0",
|
|
109
118
|
"react": "18.3.1",
|