├── .travis.yml ├── .gitattributes ├── .prettierrc ├── .jshintrc ├── dist ├── pass-meter.js └── pass-meter.min.js ├── bower.json ├── test └── pass-meter.js ├── package.json ├── LICENSE ├── .gitignore ├── Gruntfile.js ├── example ├── jquery.html └── oop.html ├── README.md └── src └── pass-meter.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | example/* linguist-documentation 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "globals": { 4 | "describe": true, 5 | "it": true 6 | }, 7 | "asi": true 8 | } -------------------------------------------------------------------------------- /dist/pass-meter.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * pass-meter v1.0.5 3 | * https://github.com/syntaqx/pass-meter 4 | * 5 | * Copyright (c) 2021 Chase Pierce 6 | * Released under the MIT license 7 | */ 8 | -------------------------------------------------------------------------------- /dist/pass-meter.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * pass-meter v1.0.5 3 | * https://github.com/syntaqx/pass-meter 4 | * 5 | * Copyright (c) 2021 Chase Pierce 6 | * Released under the MIT license 7 | */ 8 | 9 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pass-meter", 3 | "description": "Simple password strength testing.", 4 | "main": "dist/pass-meter.js", 5 | "authors": [ 6 | "Chase Pierce " 7 | ], 8 | "license": "MIT", 9 | "keywords": [ 10 | "password", 11 | "strength", 12 | "meter", 13 | "micro", 14 | "microjs" 15 | ], 16 | "homepage": "https://github.com/syntaqx/pass-meter", 17 | "ignore": [ 18 | "**/.*", 19 | "node_modules", 20 | "bower_components", 21 | "test", 22 | "tests" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /test/pass-meter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Module dependencies 4 | var assert = require('assert'); 5 | var PassMeter = require('../src/pass-meter.js'); 6 | 7 | // Simple Tests 8 | 9 | describe('PassMeter', function () { 10 | var meter = new PassMeter(); 11 | 12 | it('#test() should return a number between 0 and 100', function () { 13 | assert.ok(typeof meter.test('random') === 'number'); 14 | }); 15 | 16 | it('#test() should return a score between 0 and 100', function () { 17 | var strings = [ 18 | 'something', 19 | 'somethingElse', 20 | 'anotherRandom', 21 | 'andThisGoodies', 22 | ], 23 | result; 24 | 25 | strings.forEach(function (value) { 26 | result = meter.test(value); 27 | assert.ok(result >= 0 && result <= 100); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_name": "pass-meter", 3 | "name": "@syntaqx/pass-meter", 4 | "description": "Simple password strength testing.", 5 | "version": "1.0.5", 6 | "author": "Chase Pierce ", 7 | "repository": "syntaqx/pass-meter", 8 | "homepage": "https://github.com/syntaqx/pass-meter", 9 | "license": "MIT", 10 | "main": "src/pass-meter.js", 11 | "scripts": { 12 | "build": "grunt build", 13 | "pretest": "grunt lint", 14 | "test": "mocha", 15 | "preversion": "npm test", 16 | "version": "npm run build && git add -A dist", 17 | "postversion": "git push && git push --tags && rm -rf build/temp" 18 | }, 19 | "devDependencies": { 20 | "grunt": "^1.4.0", 21 | "grunt-contrib-concat": "^1.0.1", 22 | "grunt-contrib-jshint": "^3.0.0", 23 | "grunt-contrib-uglify": "^5.0.1", 24 | "grunt-contrib-watch": "^1.1.0", 25 | "mocha": "^8.3.2" 26 | }, 27 | "keywords": [ 28 | "password", 29 | "strength", 30 | "meter", 31 | "micro", 32 | "microjs" 33 | ], 34 | "files": [ 35 | "src" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Chase Pierce 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Microbundle cache 51 | .rpt2_cache/ 52 | .rts2_cache_cjs/ 53 | .rts2_cache_es/ 54 | .rts2_cache_umd/ 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | .parcel-cache 72 | 73 | # Next.js build output 74 | .next 75 | out 76 | 77 | # Stores VSCode versions used for testing VSCode extensions 78 | .vscode-test 79 | 80 | # yarn v2 81 | .yarn/cache 82 | .yarn/unplugged 83 | .yarn/build-state.yml 84 | .yarn/install-state.gz 85 | .pnp.* 86 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (grunt) { 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | jshint: { 7 | files: ['Gruntfile.js', 'src/**/*.js', 'test/**.*.js'], 8 | options: { 9 | jshintrc: '.jshintrc', 10 | }, 11 | }, 12 | concat: { 13 | options: { 14 | banner: [ 15 | '/*!', 16 | ' * <%= pkg._name %> v<%= pkg.version %>', 17 | ' * <%= pkg.homepage %>', 18 | ' *', 19 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>', 20 | ' * Released under the <%= pkg.license %> license', 21 | ' */\n', 22 | ].join('\n'), 23 | }, 24 | dist: { 25 | src: ['src/<%= pkg.main %>.js'], 26 | dest: 'dist/<%= pkg._name %>.js', 27 | }, 28 | }, 29 | uglify: { 30 | options: { 31 | banner: [ 32 | '/*!', 33 | ' * <%= pkg._name %> v<%= pkg.version %>', 34 | ' * <%= pkg.homepage %>', 35 | ' *', 36 | ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>', 37 | ' * Released under the <%= pkg.license %> license', 38 | ' */\n', 39 | ].join('\n'), 40 | }, 41 | dist: { 42 | files: { 43 | 'dist/<%= pkg._name %>.min.js': ['dist/<%= pkg._name %>.js'], 44 | }, 45 | }, 46 | }, 47 | watch: { 48 | files: ['<%= jshint.files %>'], 49 | tasks: ['jshint'], 50 | }, 51 | }); 52 | 53 | grunt.loadNpmTasks('grunt-contrib-jshint'); 54 | grunt.loadNpmTasks('grunt-contrib-uglify'); 55 | grunt.loadNpmTasks('grunt-contrib-watch'); 56 | grunt.loadNpmTasks('grunt-contrib-concat'); 57 | 58 | grunt.registerTask('build', ['concat', 'uglify']); 59 | grunt.registerTask('lint', ['jshint']); 60 | 61 | grunt.registerTask('default', ['test', 'lint']); 62 | }; 63 | -------------------------------------------------------------------------------- /example/jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pass-Meter Tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 |
15 | 16 |
17 |

Pass Meter Tests

18 |

Example using jQuery

19 |
20 | 21 |

22 |
23 |
24 |
25 | 26 |

27 |
28 |
29 |
30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /example/oop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pass-Meter Tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 |
15 | 16 |
17 |

Pass Meter Tests

18 |

Example for OOP

19 |
20 | 21 |

22 |
23 |
24 |
25 | 26 |

27 |
28 |
29 |
30 | 31 |

32 |
33 |
34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pass-meter 2 | 3 | [![Build Status](https://travis-ci.com/syntaqx/pass-meter.svg)](https://travis-ci.com/syntaqx/pass-meter) 4 | [![microjs](https://img.shields.io/badge/microjs-0.7kb-blueviolet)](http://microjs.com/#pass-meter) 5 | [![npm](https://img.shields.io/npm/v/@syntaqx/pass-meter)](https://www.npmjs.com/package/@syntaqx/pass-meter) 6 | 7 | Simple password strength testing. 8 | 9 | ## Optional Dependencies 10 | 11 | - [jQuery](https://jquery.com/) 1.7 or higher _(needed for plugin usage)_ 12 | 13 | ## Install 14 | 15 | ```sh 16 | npm install @syntaqx/pass-meter 17 | ``` 18 | 19 | ## Usage 20 | 21 | When used as either a jQuery plugin or Module, Pass-Meter only expects a single 22 | argument. If the argument is a function, it is treated as the `afterTest` 23 | callback option. If you need to specify additional options, you will need to 24 | provide a standard options object. 25 | 26 | ### jQuery 27 | 28 | Simple call the `$.passMeter` plugin on any elements you'd like. Create your own 29 | styling in the callback: 30 | 31 | ```js 32 | $('input[type="password"]').passMeter(function (score) { 33 | alert('Your password is ' + score + '% strong.'); 34 | }); 35 | ``` 36 | 37 | Or, with additional options: 38 | 39 | ```js 40 | $('input[type="password"]').passMeter({ 41 | event: 'change', 42 | afterTest: function (score) { 43 | alert('Your password is ' + score + '% strong.'); 44 | }, 45 | }); 46 | ``` 47 | 48 | ### Module 49 | 50 | ```js 51 | var PassMeter = require('pass-meter'); 52 | 53 | var meter = new PassMeter(); 54 | var pass = 'apasswordtotest'; 55 | 56 | console.log('The password "' + pass + '" is ' + meter.test(pass) + '% strong'); 57 | ``` 58 | 59 | Or, with additional options: 60 | 61 | ```js 62 | var PassMeter = require('pass-meter'); 63 | 64 | var meter = new PassMeter({ 65 | afterTest: function (score, value) { 66 | console.log('The password ' + value + ' is ' + score + '% strong'); 67 | }, 68 | }); 69 | 70 | meter.test('apasswordtotest'); 71 | ``` 72 | 73 | ## Options 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
VariableDefault ValueDescription
eventskeyupEvents to bind when using the module as a jQuery plugin
afterTestnullA callback for when a test has been completed.
commonPasswords['password', '123456', '12345678', '1234', 'qwerty']An array of common passwords to instantly fail.
101 | 102 | ## License 103 | 104 | This project is open source available under the [MIT license](./LICENSE). 105 | -------------------------------------------------------------------------------- /src/pass-meter.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | // AMD. Register as a named module. 3 | if (typeof define === 'function' && define.amd) { 4 | define('pass-meter', factory); 5 | } 6 | // Node. Does not work with strict CommonJS, but only CommonJS-like 7 | // environments that supports module.exports like Node. 8 | else if (typeof module === 'object' && module.exports) { 9 | module.exports = factory(); 10 | } 11 | // Browser globals (root is window) 12 | else { 13 | root.PassMeter = factory(); 14 | } 15 | })(typeof window !== 'undefined' ? window : this, function () { 16 | 'use strict'; 17 | 18 | var defaultOptions = { 19 | events: 'keyup', 20 | afterTest: null, 21 | // http://xato.net/passwords/more-top-worst-passwords 22 | commonPasswords: ['password', '123456', '12345678', '1234', 'qwerty'], 23 | }; 24 | 25 | var extend; 26 | 27 | if (typeof jQuery !== 'function') { 28 | // A simpler version of jQuery's extend function to combine objets. 29 | extend = function () { 30 | var target = arguments[0] || {}, 31 | i = 1, 32 | length = arguments.length, 33 | options, 34 | name, 35 | copy, 36 | src, 37 | clone; 38 | 39 | for (; i < length; i++) { 40 | if ((options = arguments[i]) !== null) { 41 | for (name in options) { 42 | src = target[name]; 43 | copy = options[name]; 44 | 45 | if (target === copy) { 46 | continue; 47 | } 48 | 49 | if (copy !== undefined) { 50 | target[name] = copy; 51 | } 52 | } 53 | } 54 | } 55 | 56 | return target; 57 | }; 58 | } else { 59 | extend = jQuery.extend; 60 | } 61 | 62 | var PassMeter = function (options) { 63 | if (typeof options === 'undefined') { 64 | options = {}; 65 | } else if (typeof options === 'function') { 66 | options = { 67 | afterTest: options, 68 | }; 69 | } 70 | 71 | this.options = extend({}, defaultOptions, options); 72 | }; 73 | 74 | PassMeter.prototype = { 75 | constructor: PassMeter, 76 | 77 | // Password checks 78 | checks: [ 79 | { 80 | score: 35, 81 | callback: function (value) { 82 | return value.length >= 8; 83 | }, 84 | }, 85 | { score: 20, regex: new RegExp('[A-Z]') }, // uppercase 86 | { score: 10, regex: new RegExp('[a-z]') }, // lowercase 87 | { score: 10, regex: new RegExp('[0-9]') }, // numbers 88 | { score: 25, regex: new RegExp('\\W') }, // symbols 89 | { 90 | score: -100, 91 | callback: function (value) { 92 | if ( 93 | this.options.commonPasswords.indexOf( 94 | String(value).toLowerCase() 95 | ) !== -1 96 | ) { 97 | return true; 98 | } 99 | 100 | return false; 101 | }, 102 | }, 103 | ], 104 | 105 | // Test the strength of a given value 106 | // @TODO: This needs to be a decent algorithm. It's current pretty 107 | // simplistic and could benefit greatly. 108 | test: function (value) { 109 | var self = this, 110 | total = 0; 111 | 112 | // Iterate each check and return the sum of all scores 113 | this.checks.forEach(function (check) { 114 | if (check.hasOwnProperty('regex')) { 115 | if (value.match(check.regex)) { 116 | total += check.score; 117 | } 118 | } else if (check.hasOwnProperty('callback')) { 119 | if (check.callback.call(self, value)) { 120 | total += check.score; 121 | } 122 | } 123 | }); 124 | 125 | // Make sure we're still dealing with 0-100% (just incase) 126 | if (total < 0) total = 0; 127 | if (total > 100) total = 100; 128 | 129 | // Run an afterTest callback if defined 130 | if (typeof this.options.afterTest === 'function') { 131 | this.options.afterTest(total, value); 132 | } 133 | 134 | return total; 135 | }, 136 | }; 137 | 138 | // A really lightweight jQuery plugin wrapper around the constructor, 139 | // preventing against multiple instantiations. 140 | if (typeof jQuery === 'function' && jQuery.fn) { 141 | jQuery.fn.passMeter = function (options) { 142 | return this.each(function () { 143 | var $el = jQuery(this); 144 | 145 | if (!$el.data('pass-meter')) { 146 | var obj = new PassMeter(options); 147 | 148 | // Bind to the events specified in the options 149 | $el.on(obj.options.events, function () { 150 | obj.test(this.value); 151 | }); 152 | 153 | $el.data('pass-meter', obj); 154 | } 155 | }); 156 | }; 157 | } 158 | 159 | // Export to UMD 160 | return PassMeter; 161 | }); 162 | --------------------------------------------------------------------------------