Skip to content

Commit 562e1e0

Browse files
authored
Merge pull request #1636 from hackmdio/bugfix/check-image-mime
Check upload image mime type
2 parents 381b3ff + de0f458 commit 562e1e0

File tree

5 files changed

+79
-21
lines changed

5 files changed

+79
-21
lines changed

lib/config/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ switch (config.imageUploadType) {
189189
'image/png',
190190
'image/jpg',
191191
'image/gif',
192-
'image/svg+xml'
192+
'image/svg+xml',
193+
'image/bmp',
194+
'image/tiff'
193195
]
194196
}
195197

lib/imageRouter/index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
11
'use strict'
22

33
const fs = require('fs')
4+
const path = require('path')
45
const Router = require('express').Router
56
const formidable = require('formidable')
67

8+
const readChunk = require('read-chunk')
9+
const imageType = require('image-type')
10+
const mime = require('mime-types')
11+
712
const config = require('../config')
813
const logger = require('../logger')
914
const response = require('../response')
1015

1116
const imageRouter = module.exports = Router()
1217

18+
function checkImageValid (filepath) {
19+
const buffer = readChunk.sync(filepath, 0, 12)
20+
/** @type {{ ext: string, mime: string } | null} */
21+
const mimetypeFromBuf = imageType(buffer)
22+
const mimeTypeFromExt = mime.lookup(path.extname(filepath))
23+
24+
return mimetypeFromBuf && config.allowedUploadMimeTypes.includes(mimetypeFromBuf.mime) &&
25+
mimeTypeFromExt && config.allowedUploadMimeTypes.includes(mimeTypeFromExt)
26+
}
27+
1328
// upload image
1429
imageRouter.post('/uploadimage', function (req, res) {
1530
var form = new formidable.IncomingForm()
@@ -24,6 +39,10 @@ imageRouter.post('/uploadimage', function (req, res) {
2439
logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image))
2540
}
2641

42+
if (!checkImageValid(files.image.path)) {
43+
return response.errorForbidden(req, res)
44+
}
45+
2746
const uploadProvider = require('./' + config.imageUploadType)
2847
uploadProvider.uploadImage(files.image.path, function (err, url) {
2948
// remove temporary upload file, and ignore any error

lib/utils.js

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,14 @@
22
const fs = require('fs')
33
const path = require('path')
44
const bodyParser = require('body-parser')
5+
const mime = require('mime-types')
56

67
exports.isSQLite = function isSQLite (sequelize) {
78
return sequelize.options.dialect === 'sqlite'
89
}
910

1011
exports.getImageMimeType = function getImageMimeType (imagePath) {
11-
const fileExtension = /[^.]+$/.exec(imagePath)
12-
13-
switch (fileExtension[0]) {
14-
case 'bmp':
15-
return 'image/bmp'
16-
case 'gif':
17-
return 'image/gif'
18-
case 'jpg':
19-
case 'jpeg':
20-
return 'image/jpeg'
21-
case 'png':
22-
return 'image/png'
23-
case 'tiff':
24-
return 'image/tiff'
25-
default:
26-
return undefined
27-
}
12+
return mime.lookup(path.extname(imagePath))
2813
}
2914

3015
exports.isRevealTheme = function isRevealTheme (theme) {

package-lock.json

Lines changed: 53 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"helmet": "~3.20.0",
5858
"https-proxy-agent": "^3.0.1",
5959
"i18n": "~0.8.3",
60+
"image-type": "^4.1.0",
6061
"isomorphic-fetch": "~2.2.1",
6162
"jsdom-nogyp": "~0.8.3",
6263
"lodash": "~4.17.15",
@@ -89,6 +90,7 @@
8990
"prom-client": "^12.0.0",
9091
"prometheus-api-metrics": "^2.2.5",
9192
"randomcolor": "~0.5.4",
93+
"read-chunk": "^3.2.0",
9294
"readline-sync": "~1.4.7",
9395
"request": "~2.88.0",
9496
"scrypt-kdf": "^2.0.1",

0 commit comments

Comments
 (0)