diff --git a/src/web/httpstatus.js b/src/web/httpstatus.js index eaba24d3..b2e2430d 100644 --- a/src/web/httpstatus.js +++ b/src/web/httpstatus.js @@ -1,3 +1,4 @@ export const BAD_REQUEST = 400; export const FORBIDDEN = 403; +export const NOT_FOUND = 404; export const INTERNAL_SERVER_ERROR = 500; diff --git a/src/web/routes/channel.js b/src/web/routes/channel.js index 4a185f3e..8fcf1dea 100644 --- a/src/web/routes/channel.js +++ b/src/web/routes/channel.js @@ -7,7 +7,7 @@ import { HTTPError } from '../../errors'; export default function initialize(app, ioConfig) { app.get('/r/:channel', (req, res) => { if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) { - throw new HTTPError(`"${sanitizeText(req.params.channel)} is not a valid ` + + throw new HTTPError(`"${sanitizeText(req.params.channel)}" is not a valid ` + 'channel name.', { status: HTTPStatus.BAD_REQUEST }); } diff --git a/src/web/webserver.js b/src/web/webserver.js index 0b4bdc0a..c0c4b586 100644 --- a/src/web/webserver.js +++ b/src/web/webserver.js @@ -17,7 +17,7 @@ var session = require("../session"); var csrf = require("./csrf"); var XSS = require("../xss"); import * as HTTPStatus from './httpstatus'; -import { CSRFError } from '../errors'; +import { CSRFError, HTTPError } from '../errors'; const LOG_FORMAT = ':real-address - :remote-user [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'; morgan.token('real-address', function (req) { return req._ip; }); @@ -211,11 +211,19 @@ module.exports = { app.use(serveStatic(path.join(__dirname, "..", "..", "www"), { maxAge: Config.get("http.max-age") || Config.get("http.cache-ttl") })); + app.use((req, res, next) => { + return next(new HTTPError(`No route for ${req.path}`, { + status: HTTPStatus.NOT_FOUND + })); + }); app.use(function (err, req, res, next) { if (err) { if (err instanceof CSRFError) { res.status(HTTPStatus.FORBIDDEN); - return sendJade(res, 'csrferror', { path: req.path }); + return sendJade(res, 'csrferror', { + path: req.path, + referer: req.header('referer') + }); } let { message, status } = err; @@ -226,11 +234,17 @@ module.exports = { message = 'An unknown error occurred.'; } + // Log 5xx (server) errors if (Math.floor(status / 100) === 5) { Logger.errlog.log(err.stack); } - return res.status(status).send(message); + res.status(status); + return sendJade(res, 'httperror', { + path: req.path, + status: status, + message: message + }); } else { next(); } diff --git a/templates/csrferror.jade b/templates/csrferror.jade index b67bd428..a3a8add3 100644 --- a/templates/csrferror.jade +++ b/templates/csrferror.jade @@ -24,7 +24,8 @@ html(lang="en") li A malicious user has attempted to tamper with your session li Your browser does not support cookies, or they are not enabled | If the problem persists, please contact an administrator. - a(href=path) Return to previous page + if referer + a(href=referer) Return to previous page include footer mixin footer() diff --git a/templates/httperror.jade b/templates/httperror.jade new file mode 100644 index 00000000..5ac23c71 --- /dev/null +++ b/templates/httperror.jade @@ -0,0 +1,36 @@ +mixin notfound() + h1 Not Found + p The page you were looking for doesn't seem to exist. Please check that you typed the URL correctly. +mixin forbidden() + h1 Forbidden + p You don't have permission to access #{path} +mixin genericerror() + h1 Oops + p Your request could not be processed. Status code: #{status}, message: #{message} +doctype html +html(lang="en") + head + include head + mixin head() + body + #wrap + nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") + include nav + mixin navheader() + #nav-collapsible.collapse.navbar-collapse + ul.nav.navbar-nav + mixin navdefaultlinks(path) + mixin navloginlogout(path) + + section#mainpage.container + .col-md-12 + .alert.alert-danger + if status == 404 + mixin notfound() + else if status == 403 + mixin forbidden() + else + mixin genericerror() + + include footer + mixin footer()