diff -r de56132c008d -r bdac73ed481e includes/clientside/static/pwstrength.js --- a/includes/clientside/static/pwstrength.js Sun Mar 28 21:49:26 2010 -0400 +++ b/includes/clientside/static/pwstrength.js Sun Mar 28 23:10:46 2010 -0400 @@ -12,281 +12,281 @@ function password_score_len(password) { - if ( typeof(password) != "string" ) - { - return -10; - } - var len = password.length; - var score = len - 7; - return score; + if ( typeof(password) != "string" ) + { + return -10; + } + var len = password.length; + var score = len - 7; + return score; } function password_score(password) { - if ( typeof(password) != "string" ) - { - return -10; - } - var score = 0; - var debug = []; - // length check - var lenscore = password_score_len(password); - - debug.push(''+lenscore+' points for length'); - - score += lenscore; - - var has_upper_lower = false; - var has_symbols = false; - var has_numbers = false; - - // contains uppercase and lowercase - if ( password.match(/[A-z]+/) && password.toLowerCase() != password ) - { - score += 1; - has_upper_lower = true; - debug.push('1 point for having uppercase and lowercase'); - } - - // contains symbols - if ( password.match(/[^A-z0-9]+/) ) - { - score += 1; - has_symbols = true; - debug.push('1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)'); - } - - // contains numbers - if ( password.match(/[0-9]+/) ) - { - score += 1; - has_numbers = true; - debug.push('1 point for having numbers'); - } - - if ( has_upper_lower && has_symbols && has_numbers && password.length >= 9 ) - { - // if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points - score += 4; - debug.push('4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters'); - } - else if ( has_upper_lower && has_symbols && has_numbers && password.length >= 6 ) - { - // still give some points for passing complexity check - score += 2; - debug.push('2 points for having uppercase and lowercase, numbers, and nonalphanumeric'); - } - else if(( ( has_upper_lower && has_symbols ) || - ( has_upper_lower && has_numbers ) || - ( has_symbols && has_numbers ) ) && password.length >= 6 ) - { - // if 2 of the three main complexity checks passed, add a point - score += 1; - debug.push('1 point for having 2 of 3 complexity checks'); - } - else if ( ( !has_upper_lower && !has_numbers && has_symbols ) || - ( !has_upper_lower && !has_symbols && has_numbers ) || - ( !has_numbers && !has_symbols && has_upper_lower ) ) - { - score += -2; - debug.push('-2 points for only meeting 1 complexity check'); - } - else if ( password.match(/^[0-9]*?([a-z]+)[0-9]?$/) ) - { - // password is something like magnum1 which will be cracked in seconds - score += -4; - debug.push('-4 points for being of the form [number][word][number], which is easily cracked'); - } - else if ( !has_upper_lower && !has_numbers && !has_symbols ) - { - // this is if somehow the user inputs a password that doesn't match the rule above, but still doesn't contain upper and lowercase, numbers, or symbols - debug.push('-3 points for not meeting any complexity checks'); - score += -3; - } - - // - // Repetition - // Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points - // None of the positive ones kick in unless the length is at least 8 - // - - if ( password.match(/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/) ) - { - debug.push('-2 points for having more than 4 letters of the same case in a row'); - score += -2; - } - else if ( password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) ) - { - debug.push('-1 points for having more than 3 letters of the same case in a row'); - score += -1; - } - else if ( password.match(/[A-z]/) && !password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) && password.length >= 8 ) - { - debug.push('1 point for never having more than 2 letters of the same case in a row'); - score += 1; - } - - if ( password.match(/[0-9][0-9][0-9][0-9]/) ) - { - debug.push('-2 points for having 4 or more numbers in a row'); - score += -2; - } - else if ( password.match(/[0-9][0-9][0-9]/) ) - { - debug.push('-1 points for having 3 or more numbers in a row'); - score += -1; - } - else if ( has_numbers && !password.match(/[0-9][0-9][0-9]/) && password.length >= 8 ) - { - debug.push('1 point for never more than 2 numbers in a row'); - score += -1; - } - - // make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row - var prev_char = ''; - var warn = false; - var loss = 0; - for ( var i = 0; i < password.length; i++ ) - { - var chr = password.substr(i, 1); - if ( chr == prev_char && warn ) - { - loss += -1; - } - else if ( chr == prev_char && !warn ) - { - warn = true; - } - else if ( chr != prev_char && warn ) - { - warn = false; - } - prev_char = chr; - } - if ( loss < 0 ) - { - debug.push(''+loss+' points for immediate character repetition'); - score += loss; - // this can bring the score below -10 sometimes - if ( score < -10 ) - { - debug.push('Score set to -10 because it went below that floor'); - score = -10; - } - } - - var debug_txt = "How this score was calculated\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n\n"; - for ( var i = 0; i < debug.length; i++ ) - { - debug_txt += debug[i] + "\n"; - } - - // For users that really want to know why their password sucks. - // Not localized because the feature is really only used for debugging the algorithm. - if ( document.getElementById('passdebug') ) - document.getElementById('passdebug').innerHTML = debug_txt; - - return score; + if ( typeof(password) != "string" ) + { + return -10; + } + var score = 0; + var debug = []; + // length check + var lenscore = password_score_len(password); + + debug.push(''+lenscore+' points for length'); + + score += lenscore; + + var has_upper_lower = false; + var has_symbols = false; + var has_numbers = false; + + // contains uppercase and lowercase + if ( password.match(/[A-z]+/) && password.toLowerCase() != password ) + { + score += 1; + has_upper_lower = true; + debug.push('1 point for having uppercase and lowercase'); + } + + // contains symbols + if ( password.match(/[^A-z0-9]+/) ) + { + score += 1; + has_symbols = true; + debug.push('1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)'); + } + + // contains numbers + if ( password.match(/[0-9]+/) ) + { + score += 1; + has_numbers = true; + debug.push('1 point for having numbers'); + } + + if ( has_upper_lower && has_symbols && has_numbers && password.length >= 9 ) + { + // if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points + score += 4; + debug.push('4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters'); + } + else if ( has_upper_lower && has_symbols && has_numbers && password.length >= 6 ) + { + // still give some points for passing complexity check + score += 2; + debug.push('2 points for having uppercase and lowercase, numbers, and nonalphanumeric'); + } + else if(( ( has_upper_lower && has_symbols ) || + ( has_upper_lower && has_numbers ) || + ( has_symbols && has_numbers ) ) && password.length >= 6 ) + { + // if 2 of the three main complexity checks passed, add a point + score += 1; + debug.push('1 point for having 2 of 3 complexity checks'); + } + else if ( ( !has_upper_lower && !has_numbers && has_symbols ) || + ( !has_upper_lower && !has_symbols && has_numbers ) || + ( !has_numbers && !has_symbols && has_upper_lower ) ) + { + score += -2; + debug.push('-2 points for only meeting 1 complexity check'); + } + else if ( password.match(/^[0-9]*?([a-z]+)[0-9]?$/) ) + { + // password is something like magnum1 which will be cracked in seconds + score += -4; + debug.push('-4 points for being of the form [number][word][number], which is easily cracked'); + } + else if ( !has_upper_lower && !has_numbers && !has_symbols ) + { + // this is if somehow the user inputs a password that doesn't match the rule above, but still doesn't contain upper and lowercase, numbers, or symbols + debug.push('-3 points for not meeting any complexity checks'); + score += -3; + } + + // + // Repetition + // Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points + // None of the positive ones kick in unless the length is at least 8 + // + + if ( password.match(/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/) ) + { + debug.push('-2 points for having more than 4 letters of the same case in a row'); + score += -2; + } + else if ( password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) ) + { + debug.push('-1 points for having more than 3 letters of the same case in a row'); + score += -1; + } + else if ( password.match(/[A-z]/) && !password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) && password.length >= 8 ) + { + debug.push('1 point for never having more than 2 letters of the same case in a row'); + score += 1; + } + + if ( password.match(/[0-9][0-9][0-9][0-9]/) ) + { + debug.push('-2 points for having 4 or more numbers in a row'); + score += -2; + } + else if ( password.match(/[0-9][0-9][0-9]/) ) + { + debug.push('-1 points for having 3 or more numbers in a row'); + score += -1; + } + else if ( has_numbers && !password.match(/[0-9][0-9][0-9]/) && password.length >= 8 ) + { + debug.push('1 point for never more than 2 numbers in a row'); + score += -1; + } + + // make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row + var prev_char = ''; + var warn = false; + var loss = 0; + for ( var i = 0; i < password.length; i++ ) + { + var chr = password.substr(i, 1); + if ( chr == prev_char && warn ) + { + loss += -1; + } + else if ( chr == prev_char && !warn ) + { + warn = true; + } + else if ( chr != prev_char && warn ) + { + warn = false; + } + prev_char = chr; + } + if ( loss < 0 ) + { + debug.push(''+loss+' points for immediate character repetition'); + score += loss; + // this can bring the score below -10 sometimes + if ( score < -10 ) + { + debug.push('Score set to -10 because it went below that floor'); + score = -10; + } + } + + var debug_txt = "How this score was calculated\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n\n"; + for ( var i = 0; i < debug.length; i++ ) + { + debug_txt += debug[i] + "\n"; + } + + // For users that really want to know why their password sucks. + // Not localized because the feature is really only used for debugging the algorithm. + if ( document.getElementById('passdebug') ) + document.getElementById('passdebug').innerHTML = debug_txt; + + return score; } function password_score_draw(score) { - if ( !$lang ) - { - // $lang isn't initted yet, this happens sometimes on the usercp/emailpassword form. - // Try to init it if we have ENANO_LANG_ID and enano_lang; if not, report an error. - load_component('l10n'); - if ( typeof(enano_lang) == 'object' && typeof(ENANO_LANG_ID) == 'number' ) - { - language_onload(); - } - else - { - return { - 'color' : '#000000', - 'fgcolor' : '#666666', - 'str' : 'Language init failed' - }; - } - } - // some colors are from the Gmail sign-up form - if ( score >= 10 ) - { - var color = '#010101'; - var fgcolor = '#666666'; - var str = $lang.get('usercp_pwstrength_score_verystrong', { score: score }); - } - else if ( score > 3 ) - { - var color = '#008000'; - var fgcolor = '#004000'; - var str = $lang.get('usercp_pwstrength_score_strong', { score: score }); - } - else if ( score >= 1 ) - { - var color = '#6699cc'; - var fgcolor = '#4477aa'; - var str = $lang.get('usercp_pwstrength_score_good', { score: score }); - } - else if ( score >= -3 ) - { - var color = '#f5ac00'; - var fgcolor = '#ffcc33'; - var str = $lang.get('usercp_pwstrength_score_fair', { score: score }); - } - else - { - var color = '#aa0033'; - var fgcolor = '#FF6060'; - var str = $lang.get('usercp_pwstrength_score_weak', { score: score }); - } - var ret = { - color: color, - fgcolor: fgcolor, - str: str - }; - return ret; + if ( !$lang ) + { + // $lang isn't initted yet, this happens sometimes on the usercp/emailpassword form. + // Try to init it if we have ENANO_LANG_ID and enano_lang; if not, report an error. + load_component('l10n'); + if ( typeof(enano_lang) == 'object' && typeof(ENANO_LANG_ID) == 'number' ) + { + language_onload(); + } + else + { + return { + 'color' : '#000000', + 'fgcolor' : '#666666', + 'str' : 'Language init failed' + }; + } + } + // some colors are from the Gmail sign-up form + if ( score >= 10 ) + { + var color = '#010101'; + var fgcolor = '#666666'; + var str = $lang.get('usercp_pwstrength_score_verystrong', { score: score }); + } + else if ( score > 3 ) + { + var color = '#008000'; + var fgcolor = '#004000'; + var str = $lang.get('usercp_pwstrength_score_strong', { score: score }); + } + else if ( score >= 1 ) + { + var color = '#6699cc'; + var fgcolor = '#4477aa'; + var str = $lang.get('usercp_pwstrength_score_good', { score: score }); + } + else if ( score >= -3 ) + { + var color = '#f5ac00'; + var fgcolor = '#ffcc33'; + var str = $lang.get('usercp_pwstrength_score_fair', { score: score }); + } + else + { + var color = '#aa0033'; + var fgcolor = '#FF6060'; + var str = $lang.get('usercp_pwstrength_score_weak', { score: score }); + } + var ret = { + color: color, + fgcolor: fgcolor, + str: str + }; + return ret; } function password_score_field(field) { - var indicator = false; - if ( field.nextSibling ) - { - if ( field.nextSibling.className == 'password-checker' ) - { - indicator = field.nextSibling; - } - } - if ( !indicator ) - { - var indicator = document.createElement('span'); - indicator.className = 'password-checker'; - if ( field.nextSibling ) - { - field.parentNode.insertBefore(indicator, field.nextSibling); - } - else - { - field.parentNode.appendChild(indicator); - } - } - var score = password_score(field.value); - var data = password_score_draw(score); - indicator.style.color = data.color; - indicator.style.fontWeight = 'bold'; - indicator.innerHTML = ' ' + data.str; - - if ( document.getElementById('pwmeter') ) - { - var div = document.getElementById('pwmeter'); - div.style.width = '250px'; - score += 10; - if ( score > 25 ) - score = 25; - div.style.backgroundColor = data.color; - var width = Math.round( score * (250 / 25) ); - div.innerHTML = '
'; - } + var indicator = false; + if ( field.nextSibling ) + { + if ( field.nextSibling.className == 'password-checker' ) + { + indicator = field.nextSibling; + } + } + if ( !indicator ) + { + var indicator = document.createElement('span'); + indicator.className = 'password-checker'; + if ( field.nextSibling ) + { + field.parentNode.insertBefore(indicator, field.nextSibling); + } + else + { + field.parentNode.appendChild(indicator); + } + } + var score = password_score(field.value); + var data = password_score_draw(score); + indicator.style.color = data.color; + indicator.style.fontWeight = 'bold'; + indicator.innerHTML = ' ' + data.str; + + if ( document.getElementById('pwmeter') ) + { + var div = document.getElementById('pwmeter'); + div.style.width = '250px'; + score += 10; + if ( score > 25 ) + score = 25; + div.style.backgroundColor = data.color; + var width = Math.round( score * (250 / 25) ); + div.innerHTML = '
'; + } }