limit max imported file size to 250 MiB, #3108

This commit is contained in:
zadam 2022-09-03 15:11:03 +02:00
parent 7f566178d3
commit b2a63afc28
5 changed files with 578 additions and 254 deletions

761
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@
"@electron/remote": "2.0.8",
"@excalidraw/excalidraw": "0.12.0",
"archiver": "5.3.1",
"async-mutex": "0.3.2",
"async-mutex": "0.4.0",
"axios": "0.27.2",
"better-sqlite3": "7.4.5",
"chokidar": "3.5.3",
@ -37,7 +37,7 @@
"compression": "^1.7.4",
"cookie-parser": "1.4.6",
"csurf": "1.11.0",
"dayjs": "1.11.4",
"dayjs": "1.11.5",
"dayjs-plugin-utc": "^0.1.2",
"ejs": "3.1.8",
"electron-debug": "3.2.0",
@ -45,16 +45,16 @@
"electron-window-state": "5.0.3",
"express": "4.18.1",
"express-partial-content": "1.0.2",
"express-rate-limit": "6.5.1",
"express-rate-limit": "6.5.2",
"express-session": "1.17.3",
"fs-extra": "10.1.0",
"helmet": "5.1.1",
"helmet": "6.0.0",
"html": "1.0.0",
"html2plaintext": "2.1.4",
"http-proxy-agent": "5.0.0",
"https-proxy-agent": "5.0.1",
"image-type": "4.1.0",
"ini": "3.0.0",
"ini": "3.0.1",
"is-animated": "2.0.2",
"is-svg": "4.3.2",
"jimp": "0.16.1",
@ -89,15 +89,15 @@
"devDependencies": {
"cross-env": "7.0.3",
"electron": "16.2.8",
"electron-builder": "23.1.0",
"electron-packager": "15.5.1",
"electron-rebuild": "3.2.8",
"electron-builder": "23.3.3",
"electron-packager": "16.0.0",
"electron-rebuild": "3.2.9",
"esm": "3.2.25",
"jasmine": "4.3.0",
"jsdoc": "3.6.11",
"lorem-ipsum": "2.0.8",
"rcedit": "3.0.1",
"webpack": "5.73.0",
"webpack": "5.74.0",
"webpack-cli": "4.10.0"
},
"optionalDependencies": {

View file

@ -32,6 +32,9 @@ export async function uploadFiles(parentNoteId, files, options) {
dataType: 'json',
type: 'POST',
timeout: 60 * 60 * 1000,
error: function(xhr) {
toastService.showError("Import failed: " + xhr.responseText);
},
contentType: false, // NEEDED, DON'T REMOVE THIS
processData: false, // NEEDED, DON'T REMOVE THIS
}));

View file

@ -215,7 +215,7 @@ function update(req) {
setInterval(() => {
for (const key in partialRequests) {
if (Date.now() - partialRequests[key].createdAt > 5 * 60 * 1000) {
if (Date.now() - partialRequests[key].createdAt > 20 * 60 * 1000) {
log.info(`Cleaning up unfinished partial requests for ${key}`);
delete partialRequests[key];

View file

@ -4,14 +4,7 @@ const setupRoute = require('./setup');
const loginRoute = require('./login');
const indexRoute = require('./index');
const utils = require('../services/utils');
const multer = require('multer')({
fileFilter: (req, file, cb) => {
// UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side.
// See https://github.com/expressjs/multer/pull/1102.
file.originalname = Buffer.from(file.originalname, "latin1").toString("utf-8");
cb(null, true);
}
});
const multer = require('multer');
// API routes
const treeApiRoute = require('./api/tree');
@ -201,8 +194,33 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
});
}
const MAX_ALLOWED_FILE_SIZE_MB = 250;
const GET = 'get', POST = 'post', PUT = 'put', PATCH = 'patch', DELETE = 'delete';
const uploadMiddleware = multer.single('upload');
const uploadMiddleware = multer({
fileFilter: (req, file, cb) => {
// UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side.
// See https://github.com/expressjs/multer/pull/1102.
file.originalname = Buffer.from(file.originalname, "latin1").toString("utf-8");
cb(null, true);
},
limits: {
fileSize: MAX_ALLOWED_FILE_SIZE_MB * 1024 * 1024
}
}).single('upload');
const uploadMiddlewareWithErrorHandling = function (req, res, next) {
uploadMiddleware(req, res, function (err) {
if (err?.code === 'LIMIT_FILE_SIZE') {
res.setHeader("Content-Type", "text/plain")
.status(400)
.send(`Cannot upload file because it excceeded max allowed file size of ${MAX_ALLOWED_FILE_SIZE_MB} MiB`);
}
else {
next();
}
});
};
function register(app) {
route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index);
@ -260,9 +278,9 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
route(POST, '/api/notes/:parentNoteId/import', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], importRoute.importToBranch, apiResultHandler);
route(POST, '/api/notes/:parentNoteId/import', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], importRoute.importToBranch, apiResultHandler);
route(PUT, '/api/notes/:noteId/file', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware],
route(PUT, '/api/notes/:noteId/file', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware],
filesRoute.updateFile, apiResultHandler);
route(GET, '/api/notes/:noteId/open', [auth.checkApiAuthOrElectron], filesRoute.openFile);
@ -301,10 +319,10 @@ function register(app) {
apiRoute(POST, '/api/special-notes/search-note', specialNotesRoute.createSearchNote);
apiRoute(POST, '/api/special-notes/save-search-note', specialNotesRoute.saveSearchNote);
// :filename is not used by trilium, but instead used for "save as" to assign a human readable filename
// :filename is not used by trilium, but instead used for "save as" to assign a human-readable filename
route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage);
route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.uploadImage, apiResultHandler);
route(PUT, '/api/images/:noteId', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.updateImage, apiResultHandler);
route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], imageRoute.uploadImage, apiResultHandler);
route(PUT, '/api/images/:noteId', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware], imageRoute.updateImage, apiResultHandler);
apiRoute(GET, '/api/recent-changes/:ancestorNoteId', recentChangesApiRoute.getRecentChanges);
@ -366,7 +384,7 @@ function register(app) {
// no CSRF since this is called from android app
route(POST, '/api/sender/login', [], loginApiRoute.token, apiResultHandler);
route(POST, '/api/sender/image', [auth.checkEtapiToken, uploadMiddleware], senderRoute.uploadImage, apiResultHandler);
route(POST, '/api/sender/image', [auth.checkEtapiToken, uploadMiddlewareWithErrorHandling], senderRoute.uploadImage, apiResultHandler);
route(POST, '/api/sender/note', [auth.checkEtapiToken], senderRoute.saveNote, apiResultHandler);
apiRoute(GET, '/api/quick-search/:searchString', searchRoute.quickSearch);