diff --git a/lib/routes/index.js b/lib/routes/index.js index cb62ba51..6fdc9ff3 100644 --- a/lib/routes/index.js +++ b/lib/routes/index.js @@ -1,25 +1,24 @@ -var sorter = require('./sorter'); -var canonical = require('./canonical'); -var plan = require('./plan'); -var serviceWorker = require('./service-worker'); -var headers = require('./headers'); +var sorter = require("./sorter"); +var canonical = require("./canonical"); +var plan = require("./plan"); +var serviceWorker = require("./service-worker"); +var headers = require("./headers"); module.exports = routes; function title(suffix) { - var t = 'Liftie'; - if(suffix && suffix.length) { - t += ' | ' + suffix; + var t = "Liftie"; + if (suffix && suffix.length) { + t += " | " + suffix; } return t; } - function renderResorts(req, res, next) { var opts = req.opts || {}; - req.data.get(req.requested, function(err, items) { - var template = opts.template || 'index'; + req.data.get(req.requested, function (err, items) { + var template = opts.template || "index"; if (err) { return next(err); @@ -27,7 +26,7 @@ function renderResorts(req, res, next) { if (opts.openSingle && items.length === 1) { items[0].open = true; // if single resort requested, always mark it as open res.locals.title = items[0].name; - if (template !== 'widget') { + if (template !== "widget") { res.locals.single = true; } } else { @@ -53,7 +52,7 @@ function renderResorts(req, res, next) { function index(req, res, next) { req.requested = req.params.resort || req.query.resorts; req.opts = { - openSingle: true + openSingle: true, }; next(); } @@ -63,14 +62,14 @@ function index(req, res, next) { */ function widget(req, res, next) { var requested = req.params.resort; - if (req.query.style === 'naked') { - res.locals.widgetStyle = 'naked'; + if (req.query.style === "naked") { + res.locals.widgetStyle = "naked"; } req.opts = { openSingle: true, - template: 'widget' + template: "widget", }; - req.requested = requested.split(',')[0]; // only one resort allowed + req.requested = requested.split(",")[0]; // only one resort allowed next(); } @@ -79,8 +78,8 @@ function widget(req, res, next) { * /stars - display starred subset of resorts */ function stars(req, res, next) { - req.requested = req.cookies['resorts-starred']; - res.locals.title = 'Stars'; + req.requested = req.cookies["resorts-starred"]; + res.locals.title = "Stars"; next(); } @@ -93,95 +92,97 @@ function tag(req, res, next) { } function about(req, res) { - res.render('about', { - title: title('About') + res.render("about", { + title: title("About"), }); } function sitemap(req, res) { - res.contentType('application/xml'); - res.render('sitemap', { - resorts: req.data.all(true) + res.contentType("application/xml"); + res.render("sitemap", { + resorts: req.data.all(true), }); } function absent(req, res, next) { - req.requested = req.data.filtered(function(resort) { - var hasLists = resort.lifts && resort.lifts.status && Object.keys(resort.lifts.status).length; + req.requested = req.data.filtered(function (resort) { + var hasLists = + resort.lifts && + resort.lifts.status && + Object.keys(resort.lifts.status).length; return !hasLists; }); - res.locals.title = 'Absent'; + res.locals.title = "Absent"; next(); } function confused(req, res, next) { - req.requested = req.data.filtered(function(resort) { + req.requested = req.data.filtered(function (resort) { return resort.opening && resort.lifts.stats && resort.lifts.stats.open; }); - res.locals.title = 'Confused'; + res.locals.title = "Confused"; next(); } function closed(req, res, next) { - req.requested = req.data.filtered(function(resort) { + req.requested = req.data.filtered(function (resort) { return resort.opening; }); - res.locals.title = 'Closed'; + res.locals.title = "Closed"; next(); } function stats(req, res) { var t = req.tag; - res.render('stats', { - title: title('Statistics'), - sectionLink: t ? '/tag/' + t.slug : '/', - sectionTitle: t ? t.label : 'All Lifts', - stats: req.data.stats(req.requested) + res.render("stats", { + title: title("Statistics"), + sectionLink: t ? "/tag/" + t.slug : "/", + sectionTitle: t ? t.label : "All Lifts", + stats: req.data.stats(req.requested), }); } function api(req, res, next) { - req.data.get(req.params.resort, function(err, resorts) { - if(err) { + req.data.get(req.params.resort, function (err, resorts) { + if (err) { return next(err); } - if(resorts.length !== 1) { - return res.send(404, 'Invalid resort name: ' + req.params.resort); + if (resorts.length !== 1) { + return res.send(404, "Invalid resort name: " + req.params.resort); } // do not cache API responses - res.header('Cache-Control', 'no-cache, max-age=0, must-revalidate'); + res.header("Cache-Control", "no-cache, max-age=0, must-revalidate"); res.send(resorts[0]); }); } function meta(req, res, next) { - req.data.meta(function(err, resorts) { - if(err) { + req.data.meta(function (err, resorts) { + if (err) { return next(err); } - res.header('Cache-Control', 'public, max-age=86400'); // good for 24hours + res.header("Cache-Control", "public, max-age=86400"); // good for 24hours res.send(resorts); }); } function routes(app) { - function reqData(req, res, next) { req.data = app.data; next(); } - app.param('tag', function(req, res, next, t) { + app.param("tag", function (req, res, next, t) { var data = app.data, tags = data.tags(), requested = tags[t] && tags[t].members; - if(!requested) { + if (!requested) { t = canonical(t); - if(tags[t]) { + if (tags[t]) { // permanent redirect to canonical form of the tag - return res.redirect(301, '/tag/' + t); + return res.redirect(301, "/tag/" + t); } - return res.send(404, 'Invalid tag name: ' + req.params.tag); + return res.send(404, "Invalid tag name: " + req.params.tag); } req.requested = requested; req.tag = tags[t]; @@ -189,19 +190,28 @@ function routes(app) { next(); }); - app.get('/', reqData, headers, index, renderResorts); - app.get('/resort/:resort', reqData, headers, index, renderResorts); - app.get('/widget/resort/:resort', reqData, headers, widget, renderResorts); - app.get('/tag/:tag', reqData, headers, tag, renderResorts); - app.get('/stars', reqData, headers, stars, renderResorts); - app.get('/api/resort/:resort', reqData, api); - app.get('/api/meta', reqData, meta); - app.get('/sitemap.xml', reqData, sitemap); - app.get('/about', headers, about); - app.get('/absent', reqData, headers, absent, renderResorts); - app.get('/confused', reqData, headers, confused, renderResorts); - app.get('/closed', reqData, headers, closed, renderResorts); - app.get('/stats/:tag', reqData, headers, stats); - app.get('/stats', reqData, headers, stats); - app.get('/sw.js', serviceWorker); + app.get("/api/resort/:resort", reqData, api); + app.get("/api/meta", reqData, meta); + + // We (FATMAP) don't need the html version of this site, since we only use + // the API as part of our live status service. There's a theoretical XSS + // injection attack inherent in rendering html that has been scraped from + // external sources, so we're disabling the html pages entirely. + // See https://strava.atlassian.net/browse/SC-598 + // + // app.get('/', reqData, headers, index, renderResorts); + // app.get('/resort/:resort', reqData, headers, index, renderResorts); + // app.get('/widget/resort/:resort', reqData, headers, widget, renderResorts); + // app.get('/tag/:tag', reqData, headers, tag, renderResorts); + // app.get('/stars', reqData, headers, stars, renderResorts); + // app.get('/absent', reqData, headers, absent, renderResorts); + // app.get('/confused', reqData, headers, confused, renderResorts); + // app.get('/closed', reqData, headers, closed, renderResorts); + // + // These are probably less risky but also unnecessary so might as well turn them off too: + // app.get("/sitemap.xml", reqData, sitemap); + // app.get("/about", headers, about); + // app.get("/stats/:tag", reqData, headers, stats); + // app.get("/stats", reqData, headers, stats); + // app.get("/sw.js", serviceWorker); }