Enforce HTTPS for new profile images

This commit is contained in:
Calvin Montgomery 2016-12-13 22:44:23 -08:00
parent 53d385f53e
commit 8719527a31
2 changed files with 78 additions and 15 deletions

View file

@ -13,6 +13,7 @@ var Config = require("../config");
var Server = require("../server"); var Server = require("../server");
var session = require("../session"); var session = require("../session");
var csrf = require("./csrf"); var csrf = require("./csrf");
const url = require("url");
/** /**
* Handles a GET request for /account/edit * 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 * Handles a POST request to edit a profile
*/ */
@ -410,23 +430,37 @@ function handleAccountProfile(req, res) {
}); });
} }
var image = req.body.image; var rawImage = String(req.body.image).substring(0, 255);
var text = req.body.text; var text = String(req.body.text).substring(0, 255);
db.users.setProfile(req.user.name, { image: image, text: text }, function (err) { validateProfileImage(rawImage, (error, image) => {
if (err) { if (error) {
sendPug(res, "account-profile", { db.users.getProfile(req.user.name, function (err, profile) {
profileImage: "", var errorMessage = err || error.message;
profileText: "", sendPug(res, "account-profile", {
profileError: err profileImage: profile ? profile.image : "",
profileText: profile ? profile.text : "",
profileError: errorMessage
});
}); });
return; return;
} }
sendPug(res, "account-profile", { db.users.setProfile(req.user.name, { image: image, text: text }, function (err) {
profileImage: image, if (err) {
profileText: text, sendPug(res, "account-profile", {
profileError: false profileImage: "",
profileText: "",
profileError: err
});
return;
}
sendPug(res, "account-profile", {
profileImage: image,
profileText: text,
profileError: false
});
}); });
}); });
} }

View file

@ -35,13 +35,42 @@ html(lang="en")
input(type="hidden", name="_csrf", value=csrfToken) input(type="hidden", name="_csrf", value=csrfToken)
.form-group .form-group
label.control-label(for="profileimage") Image 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 .form-group
label.control-label(for="profiletext") Text 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 button.btn.btn-primary.btn-block(type="submit") Save
include footer include footer
+footer() +footer()
script(type="text/javascript"). 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 = $("<p/>")
.attr({ id: "profileimage-error" })
.addClass("text-danger")
.html("Profile image must be a URL beginning with <code>https://</code>")
.insertAfter($profileImage);
}
} else {
hasError = false;
$profileImage.parent().removeClass("has-error");
$("#profileimage-error").remove();
}
}
$("form").submit(function (event) {
validateImage();
if (hasError) {
event.preventDefault();
}
});