diff --git a/ass.js b/ass.js index ae17f66..c2552b0 100755 --- a/ass.js +++ b/ass.js @@ -7,11 +7,12 @@ try { } // Load the config -const { host, port, resourceIdSize, diskFilePath, gfyIdSize, resourceIdType, isProxied, s3enabled, saveAsOriginal } = require('./config.json'); +const { host, port, useSsl, resourceIdSize, diskFilePath, gfyIdSize, resourceIdType, isProxied, s3enabled, saveAsOriginal } = require('./config.json'); //#region Imports const fs = require('fs-extra'); const express = require('express'); +const helmet = require("helmet"); const escape = require('escape-html'); const useragent = require('express-useragent'); const rateLimit = require("express-rate-limit"); @@ -74,26 +75,45 @@ function preStartup() { * ///todo: make this separate */ function startup() { + // Enable/disable Express features app.enable('case sensitive routing'); + app.disable("x-powered-by"); + + // Set Express variables app.set('trust proxy', isProxied); app.set('view engine', 'pug'); + + // Express middleware app.use(useragent.express()); - // Rate limit + // Helmet security middleware + app.use(helmet.noSniff()); + app.use(helmet.ieNoOpen()); + app.use(helmet.xssFilter()); + app.use(helmet.referrerPolicy()); + app.use(helmet.dnsPrefetchControl()); + useSsl && app.use(helmet.hsts({ includeSubDomains: false, preload: true })); + + // Rate limit middleware app.use(rateLimit({ windowMs: 1000 * 60, // 60 seconds // skipcq: JS-0074 max: 90 // Limit each IP to 30 requests per windowMs // skipcq: JS-0074 })); - // Don't process favicon requests + // Don't process favicon requests (custom middleware) app.use((req, res, next) => (req.url.includes('favicon.ico') ? res.sendStatus(CODE_NO_CONTENT) : next())); // Index - app.get('/', (_req, res) => fs.readFile(path('README.md')).then((bytes) => bytes.toString()).then(marked).then((d) => res.render('index', { data: d }))); + app.get('/', (_req, res, next) => + fs.readFile(path('README.md')) + .then((bytes) => bytes.toString()) + .then(marked) + .then((d) => res.render('index', { data: d })) + .catch(next)); // Block unauthorized requests and attempt token sanitization app.post('/', (req, res, next) => { - req.token = req.headers.authorization.replace(/[^\da-z]/gi, ''); + req.token = req.headers.authorization.replace(/[^\da-z]/gi, ''); // Strip anything that isn't a digit or ASCII letter !verify(req, users) ? res.sendStatus(CODE_UNAUTHORIZED) : next(); // skipcq: JS-0093 }); diff --git a/package-lock.json b/package-lock.json index e6ac920..4e777c0 100755 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "express-useragent": "^1.0.15", "ffmpeg-static": "^4.3.0", "fs-extra": "^9.1.0", + "helmet": "^4.6.0", "jimp": "^0.16.1", "luxon": "^1.26.0", "marked": "^2.0.7", @@ -1462,6 +1463,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -3994,6 +4003,11 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, + "helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", diff --git a/package.json b/package.json index 204d121..79e3f1f 100755 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "express-useragent": "^1.0.15", "ffmpeg-static": "^4.3.0", "fs-extra": "^9.1.0", + "helmet": "^4.6.0", "jimp": "^0.16.1", "luxon": "^1.26.0", "marked": "^2.0.7",