From 7a88f9d95ac304c95f12441549425c7b930a4356 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Tue, 22 Dec 2020 16:48:13 +0800 Subject: [PATCH 1/5] Check upload image mime type Signed-off-by: Yukai Huang --- lib/imageRouter/index.js | 14 ++++++++++ package-lock.json | 56 +++++++++++++++++++++++++++++++++++++--- package.json | 2 ++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/imageRouter/index.js b/lib/imageRouter/index.js index 5af005bc..5a4eb8d8 100644 --- a/lib/imageRouter/index.js +++ b/lib/imageRouter/index.js @@ -4,12 +4,22 @@ const fs = require('fs') const Router = require('express').Router const formidable = require('formidable') +const readChunk = require('read-chunk') +const imageType = require('image-type') + const config = require('../config') const logger = require('../logger') const response = require('../response') const imageRouter = module.exports = Router() +function checkImageValid (filepath) { + const supported = ['png', 'jpg', 'jpeg', 'bmp', 'tif', 'tiff', 'gif'] + const buffer = readChunk.sync(filepath, 0, 12) + const type = imageType(buffer) + return type && supported.some(e => e === type.ext) +} + // upload image imageRouter.post('/uploadimage', function (req, res) { var form = new formidable.IncomingForm() @@ -24,6 +34,10 @@ imageRouter.post('/uploadimage', function (req, res) { logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)) } + if (!checkImageValid(files.image.path)) { + return response.errorForbidden(req, res) + } + const uploadProvider = require('./' + config.imageUploadType) uploadProvider.uploadImage(files.image.path, function (err, url) { // remove temporary upload file, and ignore any error diff --git a/package-lock.json b/package-lock.json index c9b2e409..43856670 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5404,7 +5404,7 @@ "integrity": "sha512-+eqpz5j8WONSzxmc4avCN4XX/6q5+J6JfWz2AaluZIOVNgXPxUjXBhKS73+nRhM3nE1pGeRMqkyZevTQWgYTTw==", "dev": true }, - "dictionary-en-gb": { + "dictionary-en-gb": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/dictionary-en-gb/-/dictionary-en-gb-2.2.2.tgz", "integrity": "sha512-36Pz/2BGmJfXtAo5+IGOi+U6gwtxFsFXFJMOX0FC1z2YeLd1IXkxsfAhieC06OrdGie3SqCZmUOYeYgct5Hzzw==", @@ -7149,6 +7149,11 @@ "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==", "dev": true }, + "file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==" + }, "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", @@ -8837,6 +8842,14 @@ "dev": true, "optional": true }, + "image-type": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/image-type/-/image-type-4.1.0.tgz", + "integrity": "sha512-CFJMJ8QK8lJvRlTCEgarL4ro6hfDQKif2HjSvYCdQZESaIPV4v9imrf7BQHK+sQeTeNeMpWciR9hyC/g8ybXEg==", + "requires": { + "file-type": "^10.10.0" + } + }, "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", @@ -11960,8 +11973,7 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-is-promise": { "version": "2.1.0", @@ -13979,6 +13991,22 @@ "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", "dev": true }, + "read-chunk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", + "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", + "requires": { + "pify": "^4.0.1", + "with-open-file": "^0.1.6" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -18046,6 +18074,28 @@ "triple-beam": "^1.2.0" } }, + "with-open-file": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", + "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", + "requires": { + "p-finally": "^1.0.0", + "p-try": "^2.1.0", + "pify": "^4.0.1" + }, + "dependencies": { + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, "wkx": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.8.tgz", diff --git a/package.json b/package.json index 74134c14..22d18181 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "helmet": "~3.20.0", "https-proxy-agent": "^3.0.1", "i18n": "~0.8.3", + "image-type": "^4.1.0", "isomorphic-fetch": "~2.2.1", "jsdom-nogyp": "~0.8.3", "lodash": "~4.17.15", @@ -89,6 +90,7 @@ "prom-client": "^12.0.0", "prometheus-api-metrics": "^2.2.5", "randomcolor": "~0.5.4", + "read-chunk": "^3.2.0", "readline-sync": "~1.4.7", "request": "~2.88.0", "scrypt-kdf": "^2.0.1", From c1a22a53185dad2bb7f63fbec20f24048ae0659b Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Wed, 23 Dec 2020 15:43:14 +0800 Subject: [PATCH 2/5] Replace hard coded impl in getImageMimeType Signed-off-by: Yukai Huang --- lib/utils.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 4180e202..b0c71fc6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,29 +2,14 @@ const fs = require('fs') const path = require('path') const bodyParser = require('body-parser') +const mime = require('mime-types') exports.isSQLite = function isSQLite (sequelize) { return sequelize.options.dialect === 'sqlite' } exports.getImageMimeType = function getImageMimeType (imagePath) { - const fileExtension = /[^.]+$/.exec(imagePath) - - switch (fileExtension[0]) { - case 'bmp': - return 'image/bmp' - case 'gif': - return 'image/gif' - case 'jpg': - case 'jpeg': - return 'image/jpeg' - case 'png': - return 'image/png' - case 'tiff': - return 'image/tiff' - default: - return undefined - } + return mime.contentType(path.extname(imagePath)) } exports.isRevealTheme = function isRevealTheme (theme) { From e19e6642fbaecc9fabac93fcbd8232d52fe1ac36 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Wed, 23 Dec 2020 15:45:49 +0800 Subject: [PATCH 3/5] Allow bmp/tiff image to be uploaded Signed-off-by: Yukai Huang --- lib/config/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/config/index.js b/lib/config/index.js index 150116f6..467ab123 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -189,7 +189,9 @@ switch (config.imageUploadType) { 'image/png', 'image/jpg', 'image/gif', - 'image/svg+xml' + 'image/svg+xml', + 'image/bmp', + 'image/tiff' ] } From c9e23985d346ffda7ce433c388d318b962a8aa8d Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Wed, 23 Dec 2020 15:46:19 +0800 Subject: [PATCH 4/5] Check image type from file extension Signed-off-by: Yukai Huang --- lib/imageRouter/index.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/imageRouter/index.js b/lib/imageRouter/index.js index 5a4eb8d8..5e2d3dfb 100644 --- a/lib/imageRouter/index.js +++ b/lib/imageRouter/index.js @@ -1,11 +1,13 @@ 'use strict' const fs = require('fs') +const path = require('path') const Router = require('express').Router const formidable = require('formidable') const readChunk = require('read-chunk') const imageType = require('image-type') +const mime = require('mime-types') const config = require('../config') const logger = require('../logger') @@ -14,10 +16,13 @@ const response = require('../response') const imageRouter = module.exports = Router() function checkImageValid (filepath) { - const supported = ['png', 'jpg', 'jpeg', 'bmp', 'tif', 'tiff', 'gif'] const buffer = readChunk.sync(filepath, 0, 12) - const type = imageType(buffer) - return type && supported.some(e => e === type.ext) + /** @type {{ ext: string, mime: string } | null} */ + const mimetypeFromBuf = imageType(buffer) + const mimeTypeFromExt = mime.lookup(path.extname(filepath)) + + return mimetypeFromBuf && config.allowedUploadMimeTypes.includes(mimetypeFromBuf.mime) && + mimeTypeFromExt && config.allowedUploadMimeTypes.includes(mimeTypeFromExt) } // upload image From de0f4588ac6f634e8c471da6a6bb49f94b950133 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Wed, 23 Dec 2020 22:16:28 +0800 Subject: [PATCH 5/5] Fix getImageMimeType mime usage Signed-off-by: Yukai Huang --- lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index b0c71fc6..eb10ca45 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -9,7 +9,7 @@ exports.isSQLite = function isSQLite (sequelize) { } exports.getImageMimeType = function getImageMimeType (imagePath) { - return mime.contentType(path.extname(imagePath)) + return mime.lookup(path.extname(imagePath)) } exports.isRevealTheme = function isRevealTheme (theme) {