From 8719527a313a406b56ca3c6d2e03adfe4d55de41 Mon Sep 17 00:00:00 2001 From: Calvin Montgomery Date: Tue, 13 Dec 2016 22:44:23 -0800 Subject: [PATCH] Enforce HTTPS for new profile images --- src/web/account.js | 58 +++++++++++++++++++++++++++-------- templates/account-profile.pug | 35 +++++++++++++++++++-- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/web/account.js b/src/web/account.js index 2073fbe6..2b0e0479 100644 --- a/src/web/account.js +++ b/src/web/account.js @@ -13,6 +13,7 @@ var Config = require("../config"); var Server = require("../server"); var session = require("../session"); var csrf = require("./csrf"); +const url = require("url"); /** * Handles a GET request for /account/edit @@ -396,6 +397,25 @@ function handleAccountProfilePage(req, res) { }); } +function validateProfileImage(image, callback) { + var prefix = "Invalid URL for profile image: "; + var link = image.trim(); + if (!link) { + process.nextTick(callback, null, link); + } else { + var data = url.parse(link); + if (!data.protocol || data.protocol !== 'https:') { + process.nextTick(callback, + new Error(prefix + " URL must begin with 'https://'")); + } else if (!data.host) { + process.nextTick(callback, + new Error(prefix + "missing hostname")); + } else { + process.nextTick(callback, null, link); + } + } +} + /** * Handles a POST request to edit a profile */ @@ -410,23 +430,37 @@ function handleAccountProfile(req, res) { }); } - var image = req.body.image; - var text = req.body.text; + var rawImage = String(req.body.image).substring(0, 255); + var text = String(req.body.text).substring(0, 255); - db.users.setProfile(req.user.name, { image: image, text: text }, function (err) { - if (err) { - sendPug(res, "account-profile", { - profileImage: "", - profileText: "", - profileError: err + validateProfileImage(rawImage, (error, image) => { + if (error) { + db.users.getProfile(req.user.name, function (err, profile) { + var errorMessage = err || error.message; + sendPug(res, "account-profile", { + profileImage: profile ? profile.image : "", + profileText: profile ? profile.text : "", + profileError: errorMessage + }); }); return; } - sendPug(res, "account-profile", { - profileImage: image, - profileText: text, - profileError: false + db.users.setProfile(req.user.name, { image: image, text: text }, function (err) { + if (err) { + sendPug(res, "account-profile", { + profileImage: "", + profileText: "", + profileError: err + }); + return; + } + + sendPug(res, "account-profile", { + profileImage: image, + profileText: text, + profileError: false + }); }); }); } diff --git a/templates/account-profile.pug b/templates/account-profile.pug index 25ca1574..56faf95d 100644 --- a/templates/account-profile.pug +++ b/templates/account-profile.pug @@ -35,13 +35,42 @@ html(lang="en") input(type="hidden", name="_csrf", value=csrfToken) .form-group label.control-label(for="profileimage") Image - input#profileimage.form-control(type="text", name="image") + input#profileimage.form-control(type="text", name="image", maxlength="255") .form-group label.control-label(for="profiletext") Text - textarea#profiletext.form-control(cols="10", name="text")= profileText + textarea#profiletext.form-control(cols="10", name="text", maxlength="255")= profileText button.btn.btn-primary.btn-block(type="submit") Save include footer +footer() script(type="text/javascript"). - $("#profileimage").val("#{profileImage}"); + var $profileImage = $("#profileimage"); + $profileImage.val("#{profileImage}"); + var hasError = false; + function validateImage() { + var value = $profileImage.val().trim(); + $profileImage.val(value); + if (!/^$|^https:/.test(value)) { + hasError = true; + $profileImage.parent().addClass("has-error"); + var $error = $("#profileimage-error"); + if ($error.length === 0) { + $error = $("

") + .attr({ id: "profileimage-error" }) + .addClass("text-danger") + .html("Profile image must be a URL beginning with https://") + .insertAfter($profileImage); + } + } else { + hasError = false; + $profileImage.parent().removeClass("has-error"); + $("#profileimage-error").remove(); + } + } + + $("form").submit(function (event) { + validateImage(); + if (hasError) { + event.preventDefault(); + } + });