diff --git a/client/src/components/Upload/Initial/index.jsx b/client/src/components/Upload/Initial/index.jsx
index a446589..14887d9 100644
--- a/client/src/components/Upload/Initial/index.jsx
+++ b/client/src/components/Upload/Initial/index.jsx
@@ -40,14 +40,15 @@ export default function Initial({ onError, onUpload, templateId }) {
};
const upload = (files) => {
- const allowedMineTypes = [
- "application/vnd.ms-excel",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
- "text/csv",
- ];
- if (files && files[0] && allowedMineTypes.indexOf(files[0].type) !== -1)
- onUpload(files[0]);
- else setInvalidFileExtension(true);
+ const allowedExtensions = ["xls", "xlsx", "csv"];
+ if (files && files[0]) {
+ const ext = files[0].name.split(".").pop();
+ if (allowedExtensions.includes(ext.toLowerCase())) {
+ onUpload(files[0]);
+ } else {
+ setInvalidFileExtension(true);
+ }
+ }
};
let contentStyle = style.content;
@@ -85,6 +86,7 @@ export default function Initial({ onError, onUpload, templateId }) {
onChange={(e) => upload(e.target.files)}
ref={fileInputRef}
type="file"
+ accept=".xls,.xlsx,.csv"
/>
@@ -99,7 +101,11 @@ export default function Initial({ onError, onUpload, templateId }) {
+
Download Import Template (.XLSX)
>
diff --git a/package-lock.json b/package-lock.json
index ad9b8d7..15f9307 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -70,6 +70,11 @@
"@hapi/hoek": "^8.3.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/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
@@ -93,6 +98,11 @@
"@types/node": "*"
}
},
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
+ },
"@types/express": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
@@ -1577,6 +1587,17 @@
"flat-cache": "^2.0.1"
}
},
+ "file-type": {
+ "version": "14.6.2",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-14.6.2.tgz",
+ "integrity": "sha512-kSZTAJxPXBdBgJyoC7TexkBWoMI/D1Gas6aTtAn9VIRFwCehwiluGV5O8O2GwqO5zIqeEvXxEKl/xfcaAKB0Yg==",
+ "requires": {
+ "readable-web-to-node-stream": "^2.0.0",
+ "strtok3": "^6.0.3",
+ "token-types": "^2.0.0",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -2801,6 +2822,11 @@
"pify": "^2.0.0"
}
},
+ "peek-readable": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.0.tgz",
+ "integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA=="
+ },
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -3072,6 +3098,11 @@
"util-deprecate": "^1.0.1"
}
},
+ "readable-web-to-node-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-2.0.0.tgz",
+ "integrity": "sha512-+oZJurc4hXpaaqsN68GoZGQAQIA3qr09Or4fqEsargABnbe5Aau8hFn6ISVleT3cpY/0n/8drn7huyyEvTbghA=="
+ },
"reconnect-core": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz",
@@ -3548,6 +3579,32 @@
"integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
"dev": true
},
+ "strtok3": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.3.tgz",
+ "integrity": "sha512-/3RaYN9rW5WEYNHSvn081CgL4HziT027hfi5tsksbPfeWxi3BSLb8tolZDzpYU3I78/0ZqRiFpMDAqN2t4YShA==",
+ "requires": {
+ "@tokenizer/token": "^0.1.1",
+ "@types/debug": "^4.1.5",
+ "debug": "^4.1.1",
+ "peek-readable": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
"superagent": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz",
@@ -3750,6 +3807,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"
+ }
+ },
"topo": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz",
@@ -3840,6 +3906,14 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
+ "typedarray-to-buffer": {
+ "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==",
+ "requires": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
"unfetch": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz",
diff --git a/package.json b/package.json
index ccc5fba..22ba665 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"dynamoose": "^1.8.0",
"express": "^4.17.1",
"express-interceptor": "^1.2.0",
+ "file-type": "^14.6.2",
"get-parameter-names": "^0.3.0",
"http-status-codes": "^1.3.0",
"js-yaml": "^3.14.0",
diff --git a/src/services/UploadService.js b/src/services/UploadService.js
index 05e52a4..d9dc270 100644
--- a/src/services/UploadService.js
+++ b/src/services/UploadService.js
@@ -5,9 +5,33 @@ const _ = require('lodash')
const Joi = require('joi')
const config = require('config')
const { v4: uuid } = require('uuid')
+const FileType = require('file-type')
+const errors = require('../common/errors')
const helper = require('../common/helper')
const logger = require('../common/logger')
+/**
+ * Checks the type of uploaded file and ensures it's allowed.
+ * @param {Object} upload The uploaded file
+ */
+async function ensureFileTypeIsValid(upload) {
+ const allowedExtensions = ['xls', 'xlsx', 'csv']
+ const allowedMimeTypes = [
+ 'application/vnd.ms-excel',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'text/csv',
+ ]
+ const fileType = await FileType.fromBuffer(upload.buffer)
+ const fileExt = upload.originalname.split('.').pop().toLowerCase()
+
+ const isValidMimeType = fileType && _.includes(allowedMimeTypes, fileType.mime)
+ const isValidExt = _.includes(allowedExtensions, fileExt)
+ const isAllowed = fileType !== undefined ? isValidMimeType : isValidExt
+ if (isAllowed === false) {
+ throw new errors.ForbiddenError(`You are allowed to upload only ${_.join(allowedExtensions, ',')} types.`)
+ }
+}
+
/**
* Get upload entity by id.
* @param {String} id the upload id
@@ -31,6 +55,7 @@ getEntity.schema = {
* @returns {Object} the created upload
*/
async function create (authUser, upload, data) {
+ await ensureFileTypeIsValid(upload)
const id = uuid()
// upload file to s3 under uploads folder
const objectKey = await helper.uploadToS3(config.UPLOAD_S3_BUCKET, upload, `uploads/${id}`)