@tiledesk/tiledesk-server 2.14.22 → 2.14.24
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/CHANGELOG.md +8 -1
- package/middleware/file-type.js +50 -3
- package/package.json +2 -2
- package/routes/filesp.js +36 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
🚀 IN PRODUCTION 🚀
|
|
6
6
|
(https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
|
|
7
7
|
|
|
8
|
-
# 2.14.
|
|
8
|
+
# 2.14.24
|
|
9
|
+
- Improved extension management on file uploading to support .wav and .svg
|
|
10
|
+
|
|
11
|
+
# 2.14.23
|
|
12
|
+
- Updated whatsapp-connector to 1.0.22
|
|
13
|
+
- Added new endpoint for files uploading
|
|
14
|
+
|
|
15
|
+
# 2.14.22 - aborted
|
|
9
16
|
- Updated whatsapp-connector to 1.0.20
|
|
10
17
|
- Added new endpoint for files uploading
|
|
11
18
|
|
package/middleware/file-type.js
CHANGED
|
@@ -4,9 +4,46 @@ const fs = require('fs');
|
|
|
4
4
|
// List of text-based MIME types that FileType cannot detect (they don't have binary signatures)
|
|
5
5
|
const TEXT_MIME_TYPES = [
|
|
6
6
|
'text/plain',
|
|
7
|
-
'text/csv'
|
|
7
|
+
'text/csv',
|
|
8
|
+
'image/svg+xml',
|
|
9
|
+
'application/xml',
|
|
10
|
+
'text/xml'
|
|
8
11
|
];
|
|
9
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Checks if two MIME types are equivalent, accepting common aliases
|
|
15
|
+
* Examples:
|
|
16
|
+
* - audio/wav === audio/wave === audio/vnd.wave
|
|
17
|
+
* - image/jpeg === image/jpg
|
|
18
|
+
*/
|
|
19
|
+
function areMimeTypesEquivalent(mimeType1, mimeType2) {
|
|
20
|
+
if (!mimeType1 || !mimeType2) return false;
|
|
21
|
+
if (mimeType1 === mimeType2) return true;
|
|
22
|
+
|
|
23
|
+
// Normalize to lowercase for comparison
|
|
24
|
+
const m1 = mimeType1.toLowerCase();
|
|
25
|
+
const m2 = mimeType2.toLowerCase();
|
|
26
|
+
if (m1 === m2) return true;
|
|
27
|
+
|
|
28
|
+
// Common MIME type aliases
|
|
29
|
+
const aliases = {
|
|
30
|
+
'audio/wav': ['audio/wave', 'audio/x-wav', 'audio/vnd.wave'],
|
|
31
|
+
'audio/wave': ['audio/wav', 'audio/x-wav', 'audio/vnd.wave'],
|
|
32
|
+
'audio/x-wav': ['audio/wav', 'audio/wave', 'audio/vnd.wave'],
|
|
33
|
+
'audio/vnd.wave': ['audio/wav', 'audio/wave', 'audio/x-wav'],
|
|
34
|
+
'image/jpeg': ['image/jpg'],
|
|
35
|
+
'image/jpg': ['image/jpeg'],
|
|
36
|
+
'application/x-zip-compressed': ['application/zip'],
|
|
37
|
+
'application/zip': ['application/x-zip-compressed'],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Check if m1 is an alias of m2 or vice versa
|
|
41
|
+
if (aliases[m1] && aliases[m1].includes(m2)) return true;
|
|
42
|
+
if (aliases[m2] && aliases[m2].includes(m1)) return true;
|
|
43
|
+
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
10
47
|
async function verifyFileContent(buffer, mimetype) {
|
|
11
48
|
if (!buffer) throw new Error("No file provided");
|
|
12
49
|
|
|
@@ -26,6 +63,16 @@ async function verifyFileContent(buffer, mimetype) {
|
|
|
26
63
|
err.source = "FileContentVerification";
|
|
27
64
|
throw err;
|
|
28
65
|
}
|
|
66
|
+
} else if (mimetype && mimetype.startsWith('image/svg')) {
|
|
67
|
+
// Handle SVG files (can be image/svg+xml or variants)
|
|
68
|
+
try {
|
|
69
|
+
buffer.toString('utf8');
|
|
70
|
+
return true;
|
|
71
|
+
} catch (e) {
|
|
72
|
+
const err = new Error(`File content is not valid text for mimetype: ${mimetype}`);
|
|
73
|
+
err.source = "FileContentVerification";
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
29
76
|
} else {
|
|
30
77
|
// For non-text files, FileType should be able to detect them
|
|
31
78
|
const err = new Error(`File content does not match mimetype. Detected: unknown, provided: ${mimetype}`);
|
|
@@ -34,8 +81,8 @@ async function verifyFileContent(buffer, mimetype) {
|
|
|
34
81
|
}
|
|
35
82
|
}
|
|
36
83
|
|
|
37
|
-
// If FileType detected a type, it must match the declared mimetype
|
|
38
|
-
if (mimetype && fileType.mime
|
|
84
|
+
// If FileType detected a type, it must match the declared mimetype (or be equivalent)
|
|
85
|
+
if (mimetype && !areMimeTypesEquivalent(fileType.mime, mimetype)) {
|
|
39
86
|
const err = new Error(`File content does not match mimetype. Detected: ${fileType.mime}, provided: ${mimetype}`);
|
|
40
87
|
err.source = "FileContentVerification";
|
|
41
88
|
throw err;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiledesk/tiledesk-server",
|
|
3
3
|
"description": "The Tiledesk server module",
|
|
4
|
-
"version": "2.14.
|
|
4
|
+
"version": "2.14.24",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "node ./bin/www",
|
|
7
7
|
"pretest": "mongodb-runner start",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@tiledesk/tiledesk-tybot-connector": "^2.0.43",
|
|
53
53
|
"@tiledesk/tiledesk-voice-twilio-connector": "^0.1.28",
|
|
54
54
|
"@tiledesk/tiledesk-vxml-connector": "^0.1.89",
|
|
55
|
-
"@tiledesk/tiledesk-whatsapp-connector": "1.0.
|
|
55
|
+
"@tiledesk/tiledesk-whatsapp-connector": "1.0.22",
|
|
56
56
|
"@tiledesk/tiledesk-whatsapp-jobworker": "^0.0.13",
|
|
57
57
|
"amqplib": "^0.5.5",
|
|
58
58
|
"app-root-path": "^3.0.0",
|
package/routes/filesp.js
CHANGED
|
@@ -83,7 +83,7 @@ const fileFilter = (extensionsSource = 'chat') => {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
const expectedMimeType = mime.lookup(ext);
|
|
86
|
-
if (expectedMimeType && file.mimetype
|
|
86
|
+
if (expectedMimeType && !areMimeTypesEquivalent(file.mimetype, expectedMimeType)) {
|
|
87
87
|
const error = new Error(`File content does not match mimetype. Detected: ${file.mimetype}, provided: ${expectedMimeType}`);
|
|
88
88
|
error.status = 403;
|
|
89
89
|
return cb(error);
|
|
@@ -102,6 +102,41 @@ function getMimeTypes(allowed_extension) {
|
|
|
102
102
|
return allowedMimeTypes;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Checks if two MIME types are equivalent, accepting common aliases
|
|
107
|
+
* Examples:
|
|
108
|
+
* - audio/wav === audio/wave
|
|
109
|
+
* - audio/x-wav === audio/wave
|
|
110
|
+
* - image/jpeg === image/jpg
|
|
111
|
+
*/
|
|
112
|
+
function areMimeTypesEquivalent(mimeType1, mimeType2) {
|
|
113
|
+
if (!mimeType1 || !mimeType2) return false;
|
|
114
|
+
if (mimeType1 === mimeType2) return true;
|
|
115
|
+
|
|
116
|
+
// Normalize to lowercase for comparison
|
|
117
|
+
const m1 = mimeType1.toLowerCase();
|
|
118
|
+
const m2 = mimeType2.toLowerCase();
|
|
119
|
+
if (m1 === m2) return true;
|
|
120
|
+
|
|
121
|
+
// Common MIME type aliases
|
|
122
|
+
const aliases = {
|
|
123
|
+
'audio/wav': ['audio/wave', 'audio/x-wav', 'audio/vnd.wave'],
|
|
124
|
+
'audio/wave': ['audio/wav', 'audio/x-wav', 'audio/vnd.wave'],
|
|
125
|
+
'audio/x-wav': ['audio/wav', 'audio/wave', 'audio/vnd.wave'],
|
|
126
|
+
'audio/vnd.wave': ['audio/wav', 'audio/wave', 'audio/x-wav'],
|
|
127
|
+
'image/jpeg': ['image/jpg'],
|
|
128
|
+
'image/jpg': ['image/jpeg'],
|
|
129
|
+
'application/x-zip-compressed': ['application/zip'],
|
|
130
|
+
'application/zip': ['application/x-zip-compressed'],
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Check if m1 is an alias of m2 or vice versa
|
|
134
|
+
if (aliases[m1] && aliases[m1].includes(m2)) return true;
|
|
135
|
+
if (aliases[m2] && aliases[m2].includes(m1)) return true;
|
|
136
|
+
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
105
140
|
const uploadChat = multer({
|
|
106
141
|
storage: fileService.getStorage("files"),
|
|
107
142
|
fileFilter: fileFilter('chat'),
|