const config = require('./config'); const express = require('express'); const fileUpload = require('express-fileupload'); const path = require('path'); const fs = require('fs'); const mime = require('mime-types') const cors = require('cors'); const app = express(); app.use(fileUpload()); app.set('view engine', 'ejs') app.use(express.urlencoded({ extended: false })); app.use(express.json()); const isPathValid = (filename, filePath) => { if (!filename) { return false; } if (filename.indexOf('\0') !== -1 || filename.indexOf('%') !== -1 || filename.indexOf('/') !== -1 || filename.indexOf('..') !== -1) { return false; } if (!/[A-Za-z1-9.]+/g.test(filename)) { return false; } if (filePath.indexOf(config.storagePath) !== 0) { return false; } return true; }; const isFilenameValid = (filename) => { if (!filename) { return false; } if (filename.indexOf('\0') !== -1 || filename.indexOf('%') !== -1 || filename.indexOf('..') !== -1 || filename.indexOf('&') !== -1) { return false; } if (!/[A-Za-z1-9.]+/g.test(filename)) { return false; } return true; }; const getFileType = (filename) => { const extension = path.extname(filename).substring(1); let type = config.files.embed[extension]; if (!type) { type = config.files.other; } return type; }; const allowlist = ['https://hippoz.xyz/', 'https://hippoz.xyz'] const corsOptions = { origin: function (origin, callback) { if (allowlist.indexOf(origin) !== -1) { callback(null, true) } else { callback(new Error('Not allowed by CORS')) } } } app.get('/', (req, res) => { if (!config.rootRedirect) { res.render('upload'); } else { res.redirect(301, config.rootRedirect); } }); app.get('/file/:filename', (req, res) => { const filename = req.params.filename; const filePath = path.join(config.storagePath, filename); const isValid = isPathValid(filename, filePath); if (!isValid) { res.status(400).send('invalid input.'); return; } fs.access(filePath, fs.F_OK, (err) => { if (err) { res.status(404).send('file not found or is invalid.'); return; } const type = getFileType(filePath); const mimeType = mime.lookup(filePath); if (type === config.files.other) { res.contentType('text/plain'); } else { res.contentType(mimeType); } res.sendFile(filePath); }); }); // TODO: add cors for all endpoints app.post('/api/upload', [ cors(corsOptions) ], (req, res) => { const password = req.body.password; const chosenFileName = req.body.filename; if (config.passwords.indexOf(password) === -1) { return res.status(401).render('uploadfailed', { message: 'the password you entered is not correct.' }); } if (!req.files || Object.keys(req.files).length === 0) { return res.status(400).render('uploadfailed', { message: 'you forgot to upload a file.' }); } const file = req.files.file; if (!isFilenameValid(chosenFileName)) { return res.status(400).render('uploadfailed', { message: 'invalid name.' }); } const filepath = path.join(config.storagePath, chosenFileName); if (!isFilenameValid(file.name) || !isPathValid(chosenFileName, filepath)) { return res.status(400).render('uploadfailed', { message: 'invalid name.' }); } fs.stat(filepath, (err) => { if(err == null) { return res.status(400).render('uploadfailed', { message: 'a file with that name already exists.' }); } else if(err.code === 'ENOENT') { file.mv(filepath, (err) => { if (err) return res.status(500).render('uploadfailed', { message: 'something went wrong while uploading the file.' }); res.render('uploaded', { baseurl: config.url, file: { name: chosenFileName } }); }); } else { return res.status(500).render('uploadfailed', { message: 'something went wrong.' });; } }); }); app.use(function (err, req, res, next) { console.error(err.stack); res.status(500).send('internal server error: something went wrong'); }); app.listen(config.server.port, () => { console.log(`started server on port ${config.server.port}`); });