feat: check for real mimetype using file-type

For now, if file-type returns undefined, we take the value from the browser. In the future this should be removed to ensure people can't bypass the real mime checking using a special file that can't be recognized by file-type.
This commit is contained in:
Zephyrrus 2021-01-05 00:25:53 +02:00
parent 46ec3f7168
commit 53f5015c99
6 changed files with 110 additions and 25 deletions

84
package-lock.json generated
View File

@ -2850,6 +2850,11 @@
"@sinonjs/commons": "^1.7.0"
}
},
"@tokenizer/token": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.1.1.tgz",
"integrity": "sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w=="
},
"@types/babel__core": {
"version": "7.1.12",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz",
@ -2901,6 +2906,11 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
"integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
},
"@types/debug": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
},
"@types/graceful-fs": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz",
@ -2942,8 +2952,7 @@
"@types/node": {
"version": "14.14.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz",
"integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==",
"dev": true
"integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ=="
},
"@types/normalize-package-data": {
"version": "2.4.0",
@ -2962,6 +2971,15 @@
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"@types/readable-stream": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz",
"integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==",
"requires": {
"@types/node": "*",
"safe-buffer": "*"
}
},
"@types/stack-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz",
@ -7439,6 +7457,17 @@
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
"integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
},
"file-type": {
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.1.0.tgz",
"integrity": "sha512-G4Klqf6tuprtG0pC4r9kni4Wv8XhAAsfHphVqsQGA+YiOlPAO40BZduDqKfv0RFsu9q9ZbFObWfwszY/NqhEZw==",
"requires": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.0.3",
"token-types": "^2.0.0",
"typedarray-to-buffer": "^3.1.5"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@ -13154,6 +13183,11 @@
"sha.js": "^2.4.8"
}
},
"peek-readable": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.1.tgz",
"integrity": "sha512-QHJag0oYYPVkx6rVPEgCLEUMo6VRYbV3GUrqy00lxXJBEIw9LhPCP5MQI6mEfahJO9KYUP8W8qD8kC0V9RyZFQ=="
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@ -14929,6 +14963,27 @@
}
}
},
"readable-web-to-node-stream": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.1.tgz",
"integrity": "sha512-4zDC6CvjUyusN7V0QLsXVB7pJCD9+vtrM9bYDRv6uBQ+SKfx36rp5AFNPRgh9auKRul/a1iFZJYXcCbwRL+SaA==",
"requires": {
"@types/readable-stream": "^2.3.9",
"readable-stream": "^3.6.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"readdirp": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
@ -16359,6 +16414,16 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"strtok3": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.6.tgz",
"integrity": "sha512-fVxvAEKDwHFfbQO1yKxKBPfkWZyBr0Zf20UQ/mblbkAQe5h0Xdd2jDb3Mh7yRZd7LSItJ9JWgQWelpEmVoBe2g==",
"requires": {
"@tokenizer/token": "^0.1.1",
"@types/debug": "^4.1.5",
"peek-readable": "^3.1.1"
}
},
"style-resources-loader": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/style-resources-loader/-/style-resources-loader-1.3.3.tgz",
@ -16470,6 +16535,11 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true
},
"systeminformation": {
"version": "4.33.8",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-4.33.8.tgz",
"integrity": "sha512-2kTDsCO/NBQArncwJ3ZA0qQOMTCdqixcggVxlPJrtinfQ42f3x4BAmIwmzkOlAsYYVnT63vOYHlunC2dNyZzTA=="
},
"table": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.6.tgz",
@ -16913,6 +16983,15 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"token-types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-2.0.0.tgz",
"integrity": "sha512-WWvu8sGK8/ZmGusekZJJ5NM6rRVTTDO7/bahz4NGiSDb/XsmdYBn6a1N/bymUHuWYTWeuLUg98wUzvE4jPdCZw==",
"requires": {
"@tokenizer/token": "^0.1.0",
"ieee754": "^1.1.13"
}
},
"toposort": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
@ -17057,7 +17136,6 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"dev": true,
"requires": {
"is-typedarray": "^1.0.0"
}

View File

@ -53,6 +53,7 @@
"express-rate-limit": "^3.4.0",
"ffmpeg-probe": "^1.0.6",
"file-saver": "^2.0.1",
"file-type": "^16.1.0",
"fluent-ffmpeg": "^2.1.2",
"fs-jetpack": "^2.2.2",
"helmet": "^3.15.1",
@ -74,6 +75,7 @@
"serve-static": "^1.13.2",
"sharp": "^0.27.0",
"sqlite3": "^5.0.0",
"systeminformation": "^4.33.8",
"uuid": "^3.3.2",
"v-clipboard": "^2.2.1",
"vue-axios": "^2.1.4",

View File

@ -28,6 +28,6 @@ exports.up = async knex => {
table.unique(['fileId', 'tagId']);
});
};
exports.down = async knex => {
exports.down = async () => {
// Nothing
};

View File

@ -1,6 +1,7 @@
const path = require('path');
const jetpack = require('fs-jetpack');
const multer = require('multer');
const Util = require('../../utils/Util');
const Route = require('../../structures/Route');
const multerStorage = require('../../utils/multerStorage');
@ -10,6 +11,22 @@ const chunkedUploadsTimeout = 1800000;
const chunksDir = path.join(__dirname, '../../../../', process.env.UPLOAD_FOLDER, 'chunks');
const uploadDir = path.join(__dirname, '../../../../', process.env.UPLOAD_FOLDER);
const cleanUpChunks = async (uuid, onTimeout) => {
// Remove tmp file
await jetpack.removeAsync(path.join(chunksData[uuid].root, chunksData[uuid].filename))
.catch(error => {
if (error.code !== 'ENOENT') console.error(error);
});
// Remove UUID dir
await jetpack.removeAsync(chunksData[uuid].root);
// Delete cached chunks data
if (!onTimeout) chunksData[uuid].clearTimeout();
delete chunksData[uuid];
};
class ChunksData {
constructor(uuid, root) {
this.uuid = uuid;
@ -134,7 +151,7 @@ const uploadFile = async (req, res) => {
// If the uploaded file is a chunk then just say that it was a success
const uuid = req.body.uuid;
if (chunksData[uuid] !== undefined) {
req.files.forEach(file => {
req.files.forEach(() => {
chunksData[uuid].chunks++;
});
res.json({ success: true });
@ -149,7 +166,7 @@ const uploadFile = async (req, res) => {
return infoMap[0];
};
const finishChunks = async (req, res) => {
const finishChunks = async req => {
const check = file => typeof file.uuid !== 'string' ||
!chunksData[file.uuid] ||
chunksData[file.uuid].chunks < 2;
@ -228,21 +245,6 @@ const finishChunks = async (req, res) => {
}
};
const cleanUpChunks = async (uuid, onTimeout) => {
// Remove tmp file
await jetpack.removeAsync(path.join(chunksData[uuid].root, chunksData[uuid].filename))
.catch(error => {
if (error.code !== 'ENOENT') console.error(error);
});
// Remove UUID dir
await jetpack.removeAsync(chunksData[uuid].root);
// Delete cached chunks data
if (!onTimeout) chunksData[uuid].clearTimeout();
delete chunksData[uuid];
};
class uploadPOST extends Route {
constructor() {
super('/upload', 'post', {

View File

@ -35,6 +35,10 @@ class Util {
return blockedExtensions.includes(extension);
}
static getMimeFromType(fileTypeMimeObj) {
return fileTypeMimeObj ? fileTypeMimeObj.mime : undefined;
}
static constructFilePublicLink(file) {
/*
TODO: This wont work without a reverse proxy serving both
@ -225,6 +229,7 @@ class Util {
static async storeFileToDb(req, res, user, file, db) {
const dbFile = await db.table('files')
// eslint-disable-next-line func-names
.where(function() {
if (user === undefined) {
this.whereNull('userId');

View File

@ -8,7 +8,7 @@ function DiskStorage(opts) {
if (typeof opts.destination === 'string') {
jetpack.dir(opts.destination);
this.getDestination = function($0, $1, cb) { cb(null, opts.destination); };
this.getDestination = ($0, $1, cb) => { cb(null, opts.destination); };
} else {
this.getDestination = opts.destination;
}
@ -86,6 +86,4 @@ DiskStorage.prototype._removeFile = function _removeFile(req, file, cb) {
fs.unlink(path, cb);
};
module.exports = function(opts) {
return new DiskStorage(opts);
};
module.exports = opts => new DiskStorage(opts);