diff --git a/bin/browserid b/bin/browserid index 253f0a481873bf842ed175261d5c710186b15681..dc6c99cadcae41015424d6b2c4ba3e43388091c6 100755 --- a/bin/browserid +++ b/bin/browserid @@ -91,6 +91,10 @@ app.use(express.logger({ } })); +// limit all content bodies to 10kb, at which point we'll forcefully +// close down the connection. +app.use(express.limit("10kb")); + var statsd_config = config.get('statsd'); if (statsd_config && statsd_config.enabled) { logger_statsd = require("connect-logger-statsd"); diff --git a/resources/static/pages/manage_account.js b/resources/static/pages/manage_account.js index 0aa3a655f9ec8f5614dbf16a19b0d263cc30347d..896099c622d8dbe95e66712f6e64ad8089522125 100644 --- a/resources/static/pages/manage_account.js +++ b/resources/static/pages/manage_account.js @@ -239,6 +239,10 @@ BrowserID.manageAccount = (function() { tooltip.showTooltip("#tooltipNewRequired"); complete(false); } + else if(newPassword.length < 8 || 80 < newPassword.length) { + tooltip.showTooltip("tooltipPasswordLength"); + complete(false); + } else { user.changePassword(oldPassword, newPassword, function(status) { if(status) { diff --git a/resources/static/shared/validation.js b/resources/static/shared/validation.js index 515fba9f04fd396d71093af432289f8f113b72be..b31ba379580992b93a97e7a3ff98fb24e1c4be00 100644 --- a/resources/static/shared/validation.js +++ b/resources/static/shared/validation.js @@ -90,10 +90,10 @@ BrowserID.Validation = (function() { } function passwordLength(password) { - var valid = password && (password.length >= 8); + var valid = password && (password.length >= 8 && password.length <= 80); if(!valid) { - tooltip.showTooltip("#password_too_short"); + tooltip.showTooltip("#password_length"); } return valid; diff --git a/resources/static/test/qunit/pages/manage_account_unit_test.js b/resources/static/test/qunit/pages/manage_account_unit_test.js index b28d68a765841751dfd53b9b540b850ea54d7c00..50c679253ce5452f2a9778ea726c946c79f54f01 100644 --- a/resources/static/test/qunit/pages/manage_account_unit_test.js +++ b/resources/static/test/qunit/pages/manage_account_unit_test.js @@ -192,6 +192,37 @@ }); }); + asyncTest("changePassword with too short of a password, expect tooltip", function() { + bid.manageAccount(mocks, function() { + $("#old_password").val("oldpassword"); + $("#new_password").val("pass"); + + bid.manageAccount.changePassword(function(status) { + equal(status, false, "on too short of a password, status is false"); + equal(tooltip.shown, true, "tooltip is visible"); + start(); + }); + }); + }); + + asyncTest("changePassword with too long of a password, expect tooltip", function() { + bid.manageAccount(mocks, function() { + $("#old_password").val("oldpassword"); + var tooLong = ""; + for(var i = 0; i < 81; i++) { + tooLong += (i % 10); + } + $("#new_password").val(tooLong); + + bid.manageAccount.changePassword(function(status) { + equal(status, false, "on too short of a password, status is false"); + equal(tooltip.shown, true, "tooltip is visible"); + start(); + }); + }); + }); + + asyncTest("changePassword with incorrect old password, expect tooltip", function() { bid.manageAccount(mocks, function() { xhr.useResult("incorrectPassword"); diff --git a/resources/static/test/qunit/shared/validation_unit_test.js b/resources/static/test/qunit/shared/validation_unit_test.js index ac2e4fe1fc95ccc7aeff9f8e01f615cefaef4f00..971473410b94e77bbea120dc8b996550d796afb7 100644 --- a/resources/static/test/qunit/shared/validation_unit_test.js +++ b/resources/static/test/qunit/shared/validation_unit_test.js @@ -242,6 +242,17 @@ equal(tooltipShown, true, "too short password shows tooltip"); }); + test("passwordAndValidationPassword with too long password", function() { + var tooLong = ""; + for(var i = 0; i < 81; i++) { + tooLong += (i % 10); + } + var valid = validation.passwordAndValidationPassword(tooLong, tooLong); + + equal(valid, false, "too short password is invalid"); + equal(tooltipShown, true, "too short password shows tooltip"); + }); + test("passwordAndValidationPassword with empty validation password", function() { var valid = validation.passwordAndValidationPassword("password", ""); diff --git a/resources/views/index.ejs b/resources/views/index.ejs index 9d9aea739112578bb45d68478ad9551920e336e3..3ff5034233c11fb15dc2be95087f24345a3f5349 100644 --- a/resources/views/index.ejs +++ b/resources/views/index.ejs @@ -32,13 +32,14 @@ </div> <form id="edit_password_form" class="showedit"> - <input type="password" id="old_password" name="old_password" placeholder="old password"/> - <input type="password" id="new_password" name="new_password" placeholder="new password"/> + <input type="password" id="old_password" name="old_password" placeholder="old password" maxlength="80"/> + <input type="password" id="new_password" name="new_password" placeholder="new password" maxlength="80"/> <button id="changePassword">done</button> - <div class="tooltip" for="old_password" id="tooltipOldRequired">Old password is required</div> - <div class="tooltip" for="old_password" id="tooltipInvalidPassword">Incorrect old password, password not updated</div> - <div class="tooltip" for="new_password" id="tooltipNewRequired">New password is required</div> + <div class="tooltip" for="old_password" id="tooltipOldRequired">Old password is required.</div> + <div class="tooltip" for="old_password" id="tooltipInvalidPassword">Incorrect old password, password not updated.</div> + <div class="tooltip" for="new_password" id="tooltipNewRequired">New password is required.</div> + <div class="tooltip" for="new_password" id="tooltipPasswordLength">Password must be between 8 and 80 characters long.</div> </form> </section> diff --git a/resources/views/verify_email_address.ejs b/resources/views/verify_email_address.ejs index 235e7873d27f84a8b40f5f974fa47c4c6a796460..181c6a4f02993ff4d4a38904a916cf49ce5a5922 100644 --- a/resources/views/verify_email_address.ejs +++ b/resources/views/verify_email_address.ejs @@ -23,8 +23,8 @@ Password is required. </div> - <div class="tooltip" id="password_too_short" for="password"> - Password must be at least 8 characters long. + <div class="tooltip" id="password_length" for="password"> + Password must be between 8 and 80 characters long. </div> </li> <li> diff --git a/tests/post-limiting-test.js b/tests/post-limiting-test.js new file mode 100755 index 0000000000000000000000000000000000000000..70b86b71ca4dd34be3ff3e9bdb30e4554f016180 --- /dev/null +++ b/tests/post-limiting-test.js @@ -0,0 +1,116 @@ +#!/usr/bin/env node + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +require('./lib/test_env.js'); + +const assert = +require('assert'), +vows = require('vows'), +start_stop = require('./lib/start-stop.js'), +wsapi = require('./lib/wsapi.js'), +config = require('../lib/configuration.js'), +http = require('http'); +secrets = require('../lib/secrets.js'); + +var suite = vows.describe('post-limiting'); + +// disable vows (often flakey?) async error behavior +suite.options.error = false; + +start_stop.addStartupBatches(suite); + +// test posting more than 10kb +suite.addBatch({ + "posting more than 10kb": { + topic: function(assertion) { + var cb = this.callback; + var req = http.request({ + host: '127.0.0.1', + port: 10002, + path: '/wsapi/authenticate_user', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + method: "POST" + }, function (res) { + cb(res); + }).on('error', function (e) { + cb(undefined, e); + }); + req.write(secrets.weakGenerate(1024 * 10 + 1)); + req.end(); + }, + "fails": function (r, err) { + assert.ok(/socket hang up/.test(err.toString())); + } + } +}); + +// test posting more than 10kb with content-length header +suite.addBatch({ + "posting more than 10kb with content-length": { + topic: function(assertion) { + var cb = this.callback; + var req = http.request({ + host: '127.0.0.1', + port: 10002, + path: '/wsapi/authenticate_user', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': 1024 * 10 + 1 + }, + method: "POST" + }, function (res) { + cb(res); + }).on('error', function (e) { + cb(undefined, e); + }); + req.write(secrets.weakGenerate(1024 * 10 + 1)); + req.end(); + }, + "fails": function (r, err) { + assert.strictEqual(413, r.statusCode); + } + } +}); + + +start_stop.addShutdownBatches(suite); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module);