├── .travis.yml ├── .editorconfig ├── example.js ├── .gitignore ├── package.json ├── LICENSE ├── .eslintrc ├── README.md ├── test.js ├── index.js └── CHANGELOG.md /.travis.yml: -------------------------------------------------------------------------------- 1 | os: "linux" 2 | dist: "bionic" 3 | language: "node_js" 4 | node_js: 5 | - "16" 6 | - "14" 7 | - "12" 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.json] 12 | insert_final_newline = false 13 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | // Load module 2 | const pwned = require ('haveibeenpwned') (); 3 | 4 | // Fancy console.log 5 | function output (err, data) { 6 | console.dir (err || data, { 7 | depth: null, 8 | colors: true 9 | }); 10 | } 11 | 12 | // Get all Linkedin breaches 13 | pwned.breaches ({ domain: 'linkedin.com' }, output); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | .*_history 5 | npm-debug.log* 6 | 7 | # Runtime 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Test 13 | lib-cov 14 | coverage 15 | .grunt 16 | .lock-wscript 17 | 18 | # Compile 19 | build/Release 20 | 21 | # Dependencies 22 | node_modules 23 | .npm 24 | .node_repl_history 25 | package-lock.json 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Franklin", 4 | "email": "info@fvdm.com", 5 | "url": "https://fvdm.com" 6 | }, 7 | "name": "haveibeenpwned", 8 | "description": "API methods for HaveIBeenPwned (unofficial)", 9 | "version": "1.1.0", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/fvdm/nodejs-haveibeenpwned.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/fvdm/nodejs-haveibeenpwned/issues" 16 | }, 17 | "main": "index.js", 18 | "files": [ 19 | "example.js" 20 | ], 21 | "dependencies": { 22 | "httpreq": "^0.5.1" 23 | }, 24 | "devDependencies": { 25 | "dotest": "^2.7.1" 26 | }, 27 | "engines": { 28 | "node": ">=12.0.0" 29 | }, 30 | "scripts": { 31 | "test": "dotest" 32 | }, 33 | "tonicExampleFilename": "example.js", 34 | "keywords": [ 35 | "api", 36 | "breach", 37 | "infosec", 38 | "hacking", 39 | "haveibeenpwned", 40 | "security", 41 | "password", 42 | "passwords" 43 | ], 44 | "license": "Unlicense" 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 2017, 4 | "sourceType": "module" 5 | }, 6 | 7 | "env": { 8 | "node": true, 9 | "es6": true 10 | }, 11 | 12 | "rules": { 13 | "array-bracket-spacing": [2, "never"], 14 | "brace-style": [2, "1tbs", { 15 | "allowSingleLine": true 16 | }], 17 | "camelcase": [2, { 18 | "properties": "never" 19 | }], 20 | "comma-spacing": [2, { 21 | "before": false, 22 | "after": true 23 | }], 24 | "comma-style": [2, "last"], 25 | "comma-dangle": [2, "never"], 26 | "complexity": [1, 8], 27 | "computed-property-spacing": [2, "never"], 28 | "consistent-return": 1, 29 | "curly": [2, "all"], 30 | "default-case": 2, 31 | "dot-notation": [1, { 32 | "allowKeywords": true 33 | }], 34 | "dot-location": [2, "property"], 35 | "eol-last": 2, 36 | "eqeqeq": 2, 37 | "func-style": 0, 38 | "guard-for-in": 0, 39 | "handle-callback-err": [2, "^(e|er|err|error)[0-9]{1,2}?$"], 40 | "indent": [2, 2, { 41 | "SwitchCase": 1 42 | }], 43 | "keyword-spacing": 2, 44 | "key-spacing": [2, { 45 | "beforeColon": false, 46 | "afterColon": true 47 | }], 48 | "lines-around-comment": [2, { 49 | "beforeBlockComment": true, 50 | "afterBlockComment": true, 51 | "beforeLineComment": true, 52 | "afterLineComment": false, 53 | "allowBlockStart": true, 54 | "allowBlockEnd": false 55 | }], 56 | "linebreak-style": [2, "unix"], 57 | "max-nested-callbacks": [1, 3], 58 | "new-cap": 0, 59 | "newline-after-var": [2, "always"], 60 | "no-alert": 2, 61 | "no-caller": 2, 62 | "no-catch-shadow": 2, 63 | "no-delete-var": 2, 64 | "no-div-regex": 2, 65 | "no-duplicate-case": 2, 66 | "no-else-return": 2, 67 | "no-empty": 2, 68 | "no-empty-character-class": 2, 69 | "no-eval": 2, 70 | "no-extend-native": 2, 71 | "no-extra-semi": 2, 72 | "no-fallthrough": 2, 73 | "no-floating-decimal": 2, 74 | "no-func-assign": 2, 75 | "no-implied-eval": 2, 76 | "no-inline-comments": 2, 77 | "no-invalid-regexp": 2, 78 | "no-label-var": 2, 79 | "no-labels": 2, 80 | "no-lone-blocks": 2, 81 | "no-lonely-if": 2, 82 | "no-mixed-requires": 0, 83 | "no-mixed-spaces-and-tabs": 2, 84 | "no-multi-spaces": 2, 85 | "no-multi-str": 2, 86 | "no-multiple-empty-lines": [2, { 87 | "max": 2 88 | }], 89 | "no-native-reassign": 2, 90 | "no-nested-ternary": 2, 91 | "no-new-func": 2, 92 | "no-new-object": 2, 93 | "no-new-wrappers": 2, 94 | "no-octal-escape": 2, 95 | "no-octal": 2, 96 | "no-path-concat": 2, 97 | "no-param-reassign": 0, 98 | "no-process-env": 0, 99 | "no-proto": 2, 100 | "no-redeclare": 2, 101 | "no-reserved-keys": 0, 102 | "no-return-assign": [2, "always"], 103 | "no-self-compare": 2, 104 | "no-sequences": 2, 105 | "no-shadow": 2, 106 | "no-shadow-restricted-names": 2, 107 | "no-spaced-func": 0, 108 | "no-sparse-arrays": 1, 109 | "no-sync": 1, 110 | "no-ternary": 0, 111 | "no-throw-literal": 2, 112 | "no-trailing-spaces": 2, 113 | "no-undef": 2, 114 | "no-undef-init": 2, 115 | "no-undefined": 2, 116 | "no-underscore-dangle": 2, 117 | "no-unexpected-multiline": 2, 118 | "no-unneeded-ternary": 2, 119 | "no-unreachable": 2, 120 | "no-unused-vars": 1, 121 | "no-use-before-define": 2, 122 | "no-useless-concat": 2, 123 | "no-warning-comments": 1, 124 | "no-with": 2, 125 | "no-wrap-func": 0, 126 | "object-curly-spacing": [2, "always", { 127 | "objectsInObjects": false, 128 | "arraysInObjects": false 129 | }], 130 | "one-var": [2, "never"], 131 | "operator-assignment": [2, "always"], 132 | "operator-linebreak": [2, "none", { 133 | "overrides": { 134 | "+": "after" 135 | } 136 | }], 137 | "padded-blocks": [2, "never"], 138 | "quote-props": [2, "consistent"], 139 | "quotes": [2, "single", "avoid-escape"], 140 | "radix": 2, 141 | "semi": 2, 142 | "semi-spacing": [2, { 143 | "before": false, 144 | "after": true 145 | }], 146 | "space-before-blocks": [2, "always"], 147 | "space-before-function-paren": [2, "always"], 148 | "space-in-parens": [2, "never"], 149 | "space-infix-ops": 2, 150 | "space-unary-ops": [2, { 151 | "words": true, 152 | "nonwords": false 153 | }], 154 | "spaced-comment": [2, "always"], 155 | "use-isnan": 2, 156 | "valid-typeof": 2, 157 | "vars-on-top": 2, 158 | "wrap-regex": 0, 159 | "yoda": [2, "never"] 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # haveibeenpwned 2 | 3 | API methods for Have I been pwned (unofficial) 4 | 5 | [![npm](https://img.shields.io/npm/v/haveibeenpwned.svg?maxAge=3600)](https://github.com/fvdm/nodejs-haveibeenpwned/blob/master/CHANGELOG.md) 6 | [![Build Status](https://api.travis-ci.com/fvdm/nodejs-haveibeenpwned.svg?branch=master)](https://travis-ci.com/github/fvdm/nodejs-haveibeenpwned) 7 | [![Coverage Status](https://coveralls.io/repos/github/fvdm/nodejs-haveibeenpwned/badge.svg?branch=master)](https://coveralls.io/github/fvdm/nodejs-haveibeenpwned?branch=master) 8 | 9 | 10 | * [Node.js](https://nodejs.org) 11 | * [Have I been pwned](https://haveibeenpwned.com) 12 | * [API documentation](https://haveibeenpwned.com/API/v2) 13 | 14 | 15 | # Example 16 | 17 | ```js 18 | const hibp = require ('haveibeenpwned') (); 19 | 20 | hibp.breachedAccount ('foo@bar.com', (err, data) => { 21 | if (err) { 22 | return console.log (err); 23 | } 24 | 25 | console.log (data); 26 | }); 27 | ``` 28 | 29 | 30 | # Installation 31 | 32 | `npm i haveibeenpwned` 33 | 34 | 35 | # Configuration 36 | 37 | name | type | required | default | description 38 | :-------|:-------|:---------|:--------|:-------------------------- 39 | timeout | number | no | 5000 | Request wait timeout in ms 40 | 41 | 42 | ```js 43 | const pwned = require ('haveibeenpwned') ({ 44 | timeout: 10000 45 | }); 46 | ``` 47 | 48 | 49 | # Have I Been Pwned 50 | 51 | Below are the methods for the main Have I Been Pwned API. 52 | 53 | For simplicity no error handling is included in the callback examples. 54 | It's straightforward: when there is a problem `err` is an instance of 55 | _Error_ and `data` is _undefined_, otherwise `err` is _null_ and 56 | `data` is set to the result. 57 | 58 | 59 | ## .breachedAccount 60 | 61 | **( account, [params], callback )** 62 | 63 | All breaches for an account. 64 | 65 | 66 | argument | type | required | description 67 | :--------|:---------|:---------|:-------------------------------- 68 | account | string | yes | Email or username to check 69 | params | object | no | Additional parameters to include 70 | callback | function | yes | `function (err, data) {}` 71 | 72 | 73 | ```js 74 | hibp.breachedAccount ('foo@bar.com', yourCallback); 75 | ``` 76 | 77 | [Example output](https://haveibeenpwned.com/api/v2/breachedaccount/foo@bar.com) 78 | 79 | 80 | ## .breaches 81 | 82 | **( [params], callback )** 83 | 84 | All breaches in the system. 85 | 86 | 87 | argument | type | required | description 88 | :--------|:---------|:---------|:-------------------------------- 89 | params | object | no | Additional parameters to include 90 | callback | function | yes | `function (err, data) {}` 91 | 92 | 93 | ```js 94 | hibp.breaches (yourCallback); 95 | ``` 96 | 97 | [Example output](https://haveibeenpwned.com/api/v2/breaches) 98 | 99 | 100 | ## .breach 101 | 102 | **( name, [params], callback )** 103 | 104 | A single breached site. 105 | 106 | 107 | argument | type | required | description 108 | :--------|:---------|:---------|:----------------------------------- 109 | name | string | yes | Site name to check, i.e. `linkedin` 110 | params | object | no | Additional parameters to include 111 | callback | function | yes | `function (err, data) {}` 112 | 113 | 114 | ```js 115 | hibp.breach ('Adobe', yourCallback); 116 | ``` 117 | 118 | [Example output](https://haveibeenpwned.com/api/v2/breach/Adobe) 119 | 120 | 121 | ## .pasteAccount 122 | 123 | **( account, callback )** 124 | 125 | All pastes for an account. 126 | 127 | 128 | argument | type | required | description 129 | :--------|:---------|:---------|:-------------------------- 130 | account | string | yes | Email or username to check 131 | callback | function | yes | `function (err, data) {}` 132 | 133 | 134 | ```js 135 | hibp.pasteAccount ('foo@bar.com', yourCallback); 136 | ``` 137 | 138 | [Example output](https://haveibeenpwned.com/api/v2/pasteaccount/foo@bar.com) 139 | 140 | 141 | ## .dataclasses 142 | 143 | **( callback )** 144 | 145 | All pastes for an account. 146 | 147 | 148 | argument | type | required | description 149 | :--------|:---------|:---------|:------------------------- 150 | callback | function | yes | `function (err, data) {}` 151 | 152 | 153 | ```js 154 | hibp.dataclasses (yourCallback); 155 | ``` 156 | 157 | [Example output](https://haveibeenpwned.com/api/v2/dataclasses) 158 | 159 | 160 | # Pwned Passwords 161 | 162 | Below are the methods for the Pwned Passwords API. 163 | 164 | 165 | ## .pwnedpasswords.byPassword 166 | 167 | **( password, [hashed], callback )** 168 | 169 | Check if a password was leaked. 170 | The `data` is `0` (int) when none were found. 171 | 172 | 173 | argument | type | default | description 174 | :--------|:---------|:--------|:--------------------- 175 | password | string | | The password to check 176 | hashed | bool | false | Is the password already SHA-1 hashed 177 | callback | function | | `(err, data)` 178 | 179 | 180 | 181 | ```js 182 | hibp.pwnedpasswords.byPassword ('secret', (err, count) => { 183 | if (!count) { 184 | console.log ('Great! Password is not found.'); 185 | } else { 186 | console.log ('Oops! Password was found ' + count + ' times!'); 187 | } 188 | }); 189 | 190 | // -> Oops! Password was found 195263 times! 191 | ``` 192 | 193 | 194 | ## .pwnedpasswords.byRange 195 | 196 | **( hash, [sort], callback )** 197 | 198 | Get password hashes similar to the first 5 characters of the SHA-1 hash 199 | provided. The callback `data` is an *object* where the keys are the 200 | lowercase hashes and the values are the number of times they were used. 201 | When nothing is found `data` is `0` (int). 202 | 203 | A hash longer then 5 characters is truncated before being sent to the API 204 | and can be in uppercase or lowercase. 205 | 206 | 207 | argument | type | default | description 208 | :--------|:---------|:--------|:----------------------- 209 | hash | string | | The SHA-1 hash to check 210 | sort | bool | false | Sort by counts in descending order 211 | callback | function | | `(err, data)` 212 | 213 | 214 | ### Sorted by key (default) 215 | 216 | ```js 217 | hibp.pwnedpasswords.byRange ('abcdef', (err, data) => { 218 | for (sha in data) { 219 | console.log ('%s %i x', sha, data[sha]); 220 | } 221 | }); 222 | ``` 223 | 224 | 225 | ### Sorted by use counts 226 | 227 | ```js 228 | hibp.pwnedpasswords.byRange ('abcdef', true, (err, data) => { 229 | const count = Object.keys (data).length; 230 | let i = 1; 231 | 232 | console.log ('Found %i matches', count); 233 | console.log ('Top 3:'); 234 | 235 | for (sha in data) { 236 | console.log ('%i %s %i x', i, sha, data[sha]); 237 | 238 | // stop after 3 239 | if (i === 3) { break; } 240 | i++; 241 | } 242 | }); 243 | 244 | // -> 245 | // Found 511 matches 246 | // Top 3: 247 | // 1 3b8a55c2b3bf42b83e41f0f95a4149043f6 336 x 248 | // 2 6a7fc410810db77855d9dfe5b94b95196f7 304 x 249 | // 3 73a1a21a7b13b50536df19fd586abdf3145 193 x 250 | ``` 251 | 252 | 253 | # Unicense 254 | 255 | This is free and unencumbered software released into the public domain. 256 | 257 | Anyone is free to copy, modify, publish, use, compile, sell, or 258 | distribute this software, either in source code form or as a compiled 259 | binary, for any purpose, commercial or non-commercial, and by any 260 | means. 261 | 262 | In jurisdictions that recognize copyright laws, the author or authors 263 | of this software dedicate any and all copyright interest in the 264 | software to the public domain. We make this dedication for the benefit 265 | of the public at large and to the detriment of our heirs and 266 | successors. We intend this dedication to be an overt act of 267 | relinquishment in perpetuity of all present and future rights to this 268 | software under copyright law. 269 | 270 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 271 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 272 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 273 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 274 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 275 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 276 | OTHER DEALINGS IN THE SOFTWARE. 277 | 278 | For more information, please refer to 279 | 280 | 281 | # Author 282 | 283 | [Franklin](https://fvdm.com) 284 | | [Buy me a coffee](https://fvdm.com/donating/) 285 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Name: haveibeenpwned - test.js 3 | Description: Unit tests for index.js 4 | Author: Franklin van de Meent (https://frankl.in) 5 | Source code: https://github.com/fvdm/nodejs-haveibeenpwned 6 | Feedback: https://github.com/fvdm/nodejs-haveibeenpwned/issues 7 | License: Unlicense (public domain, see LICENSE file) 8 | */ 9 | 10 | 11 | const dotest = require ('dotest'); 12 | const crypto = require ('crypto'); 13 | const app = require ('./'); 14 | 15 | const config = { 16 | timeout: process.env.Timeout || null 17 | }; 18 | 19 | const pwned = app (config); 20 | 21 | const testPass = 'doesnt-exist,' + Date.now(); 22 | 23 | 24 | /** 25 | * Generate SHA-1 hash from a string 26 | * 27 | * @return {string} SHA-1 hash as hex string 28 | * @param {mixed} str Data to hash 29 | */ 30 | 31 | function hashSha1 (str) { 32 | return crypto 33 | .createHash ('sha1') 34 | .update (str) 35 | .digest ('hex'); 36 | } 37 | 38 | 39 | dotest.add ('Interface', test => { 40 | const pp = pwned && pwned.pwnedpasswords; 41 | 42 | test() 43 | .isFunction ('fail', 'exports', app) 44 | .isObject ('fail', 'interface', pwned) 45 | .isObject ('fail', '.pwnedpasswords', pp); 46 | 47 | if (pwned) { 48 | test() 49 | .isFunction ('fail', '.breachedAccount', pwned.breachedAccount) 50 | .isFunction ('fail', '.breaches', pwned.breaches) 51 | .isFunction ('fail', '.breach', pwned.breach) 52 | .isFunction ('fail', '.pasteAccount', pwned.pasteAccount) 53 | .isFunction ('fail', '.dataclasses', pwned.dataclasses); 54 | } 55 | 56 | if (pp) { 57 | test() 58 | .isFunction ('fail', '.pwnedpasswords.byPassword', pp && pp.byPassword) 59 | .isFunction ('fail', '.pwnedpasswords.byRange', pp && pp.byRange); 60 | } 61 | 62 | 63 | test().done(); 64 | }); 65 | 66 | 67 | dotest.add ('Method .breachedAccount - without params', test => { 68 | pwned.breachedAccount ('foo@bar.com', (err, data) => { 69 | const item = data && data [0]; 70 | 71 | test (err) 72 | .isArray ('fail', 'data', data) 73 | .isNotEmpty ('fail', 'data', data) 74 | .isObject ('fail', 'data[0]', item) 75 | .isNotEmpty ('fail', 'data[0]', item) 76 | .done(); 77 | }); 78 | }); 79 | 80 | 81 | dotest.add ('Method .breachedAccount - with params', test => { 82 | const params = { 83 | domain: 'acne.org' 84 | }; 85 | 86 | pwned.breachedAccount ('foo@bar.com', params, (err, data) => { 87 | const item = data && data [0]; 88 | 89 | test (err) 90 | .isArray ('fail', 'data', data) 91 | .isNotEmpty ('fail', 'data', data) 92 | .isObject ('fail', 'data[0]', item) 93 | .isNotEmpty ('fail', 'data[0]', item) 94 | .done(); 95 | }); 96 | }); 97 | 98 | 99 | dotest.add ('Method .breaches - without params', test => { 100 | pwned.breaches ((err, data) => { 101 | const item = data && data [0]; 102 | 103 | test (err) 104 | .isArray ('fail', 'data', data) 105 | .isNotEmpty ('fail', 'data', data) 106 | .isObject ('fail', 'data[0]', item) 107 | .isNotEmpty ('fail', 'data[0]', item) 108 | .done(); 109 | }); 110 | }); 111 | 112 | 113 | dotest.add ('Method .breaches - with params', test => { 114 | const params = { 115 | domain: 'linkedin.com' 116 | }; 117 | 118 | pwned.breaches (params, (err, data) => { 119 | const item = data && data [0]; 120 | 121 | test (err) 122 | .isArray ('fail', 'data', data) 123 | .isNotEmpty ('fail', 'data', data) 124 | .isObject ('fail', 'data[0]', item) 125 | .isNotEmpty ('fail', 'data[0]', item) 126 | .done(); 127 | }); 128 | }); 129 | 130 | 131 | dotest.add ('Method .breach', test => { 132 | pwned.breach ('LinkedIn', (err, data) => { 133 | test (err) 134 | .isObject ('fail', 'data', data) 135 | .isNotEmpty ('fail', 'data', data) 136 | .done(); 137 | }); 138 | }); 139 | 140 | 141 | dotest.add ('Method .pasteAccount', test => { 142 | pwned.pasteAccount ('foo@bar.com', (err, data) => { 143 | const item = data && data [0]; 144 | 145 | test (err) 146 | .isArray ('fail', 'data', data) 147 | .isNotEmpty ('fail', 'data', data) 148 | .isObject ('fail', 'data[0]', item) 149 | .isNotEmpty ('fail', 'data[0]', item) 150 | .done(); 151 | }); 152 | }); 153 | 154 | 155 | dotest.add ('Method .dataclasses', test => { 156 | pwned.dataclasses ((err, data) => { 157 | const item = data && data [0]; 158 | 159 | test (err) 160 | .isArray ('fail', 'data', data) 161 | .isString ('fail', 'data[0]', item) 162 | .isNotEmpty ('fail', 'data[0]', item) 163 | .done(); 164 | }); 165 | }); 166 | 167 | 168 | dotest.add ('Method .pwnedpassword.byPassword - plain, none found', test => { 169 | pwned.pwnedpasswords.byPassword (testPass, (err, data) => { 170 | test (err) 171 | .info ('Password: ' + testPass) 172 | .info (' ') 173 | .isNull ('fail', 'err', err) 174 | .isExactly ('fail', 'data', data, 0) 175 | .done(); 176 | }); 177 | }); 178 | 179 | 180 | dotest.add ('Method .pwnedpasswords.byPassword - hashed, none found', test => { 181 | const hash = hashSha1 (testPass); 182 | 183 | pwned.pwnedpasswords.byPassword (hash, true, (err, data) => { 184 | test (err) 185 | .info ('Password: ' + testPass) 186 | .info ('Hash: ' + hash) 187 | .info (' ') 188 | .isNull ('fail', 'err', err) 189 | .isExactly ('fail', 'data', data, 0) 190 | .done(); 191 | }); 192 | }); 193 | 194 | 195 | dotest.add ('Method .pwnedpasswords.byPassword - plain, results', test => { 196 | pwned.pwnedpasswords.byPassword ('12345', (err, data) => { 197 | test (err) 198 | .info ('Password: 12345') 199 | .info (' ') 200 | .isNull ('fail', 'err', err) 201 | .isNumber ('fail', 'data', data) 202 | .isCondition ('fail', 'data', data, '>', 0) 203 | .done(); 204 | }); 205 | }); 206 | 207 | 208 | dotest.add ('Method .pwnedpasswords.byRange - results', test => { 209 | const hash = hashSha1 ('12345'); 210 | 211 | pwned.pwnedpasswords.byRange (hash, (err, data) => { 212 | const item = data && data['37d0679ca88db6464eac60da96345513964']; 213 | 214 | test (err) 215 | .info ('Password: 12345') 216 | .info ('Hash: ' + hash) 217 | .info ('Item: 37d0679ca88db6464eac60da96345513964') 218 | .info (' ') 219 | .isNull ('fail', 'err', err) 220 | .isObject ('fail', 'data', data) 221 | .isNumber ('fail', 'data[item]', item) 222 | .isCondition ('fail', 'data[item]', item, '>', 0) 223 | .done(); 224 | }); 225 | }); 226 | 227 | 228 | dotest.add ('Method .pwnedpasswords.byRange - sorted', test => { 229 | const hash = hashSha1 ('12345'); 230 | 231 | pwned.pwnedpasswords.byRange (hash, true, (err, data) => { 232 | const item = data && data['37d0679ca88db6464eac60da96345513964']; 233 | const values = Object.keys (data).map (key => { 234 | return data [key]; 235 | }); 236 | const first = values.shift(); 237 | const second = values.shift(); 238 | const third = values.shift(); 239 | 240 | test (err) 241 | .info ('Password: 12345') 242 | .info ('Hash: ' + hash) 243 | .info ('Item: 37d0679ca88db6464eac60da96345513964') 244 | .info (' ') 245 | .isNull ('fail', 'err', err) 246 | .isObject ('fail', 'data', data) 247 | .isNumber ('fail', 'data[item]', item) 248 | .isCondition ('fail', 'data[item]', item, '>', 0) 249 | .isCondition ('fail', 'order 1 vs 2', first, '>', second) 250 | .isCondition ('fail', 'order 2 vs 3', second, '>', third) 251 | .done(); 252 | }); 253 | }); 254 | 255 | 256 | dotest.add ('Error: invalid hash prefix', test => { 257 | pwned.pwnedpasswords.byRange ('test', (err, data) => { 258 | test () 259 | .isError ('fail', 'err', err) 260 | .isExactly ('warn', 'err.message', err && err.message, 'The hash prefix was not in a valid format') 261 | .isUndefined ('fail', 'data', data) 262 | .done(); 263 | }); 264 | }); 265 | 266 | 267 | dotest.add ('Error: not found', test => { 268 | pwned.breachedAccount ('info@invalid--example.net', (err, data) => { 269 | test() 270 | .isError ('fail', 'err', err) 271 | .isExactly ('fail', 'err.message', err && err.message, 'not found') 272 | .isExactly ('fail', 'err.statusCode', err && err.statusCode, 404) 273 | .isUndefined ('fail', 'data', data) 274 | .done(); 275 | }); 276 | }); 277 | 278 | 279 | dotest.add ('Error: API error', test => { 280 | pwned.breachedAccount ('', (err, data) => { 281 | test() 282 | .isError ('fail', 'err', err) 283 | .isNumber ('fail', 'err.statusCode', err && err.statusCode) 284 | .isUndefined ('fail', 'data', data) 285 | .done(); 286 | }); 287 | }); 288 | 289 | 290 | dotest.add ('Error: request timed out - haveibeenpwned API', test => { 291 | const tmp = app ({ 292 | timeout: 1 293 | }); 294 | 295 | tmp.dataclasses ((err, data) => { 296 | test() 297 | .isError ('fail', 'err', err) 298 | .isExactly ('fail', 'err.code', err && err.code, 'TIMEOUT') 299 | .isUndefined ('fail', 'data', data) 300 | .done(); 301 | }); 302 | }); 303 | 304 | 305 | dotest.add ('Error: request timed out - pwnedpasswords API', test => { 306 | const tmp = app ({ 307 | timeout: 1 308 | }); 309 | 310 | tmp.pwnedpasswords.byPassword ('test', (err, data) => { 311 | test() 312 | .isError ('fail', 'err', err) 313 | .isExactly ('fail', 'err.code', err && err.code, 'TIMEOUT') 314 | .isUndefined ('fail', 'data', data) 315 | .done(); 316 | }); 317 | }); 318 | 319 | 320 | dotest.run (2000); 321 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Name: haveibeenpwned - index.js 3 | Description: API methods for HaveIBeenPwnd.com (unofficial) 4 | Author: Franklin (https://fvdm.com) 5 | Source code: https://github.com/fvdm/nodejs-haveibeenpwned 6 | Feedback: https://github.com/fvdm/nodejs-haveibeenpwned/issues 7 | License: Unlicense (public domain, see LICENSE file) 8 | */ 9 | 10 | 11 | const httpreq = require ('httpreq'); 12 | 13 | let config = { 14 | timeout: 5000, 15 | userAgent: 'nodejs-haveibeenpwned (https://github.com/fvdm/nodejs-haveibeenpwned)' 16 | }; 17 | 18 | 19 | /** 20 | * Sort an object by its values 21 | * in descending order 22 | * 23 | * @return {object} The sorted object 24 | * @param {object} obj The object to sort 25 | */ 26 | 27 | function sortObjectByValues (obj) { 28 | let sorting = []; 29 | let result = {}; 30 | let key; 31 | 32 | for (key in obj) { 33 | sorting.push ({ key, count: obj [key] }); 34 | } 35 | 36 | sorting.sort ((a, b) => { 37 | if (a.count > b.count) { 38 | return -1; 39 | } 40 | 41 | if (a.count < b.count) { 42 | return 1; 43 | } 44 | 45 | return 0; 46 | }); 47 | 48 | sorting.forEach (itm => { 49 | result [itm.key] = itm.count; 50 | }); 51 | 52 | return result; 53 | } 54 | 55 | 56 | /** 57 | * Process API error 58 | * 59 | * @callback callback 60 | * @return {void} 61 | * 62 | * @param {object} res httpreq response details 63 | * @param {Error} err Additional Error to include 64 | * @param {function} callback `(err, data)` 65 | */ 66 | 67 | function processApiError (res, err, callback) { 68 | let error = new Error ('API error'); 69 | 70 | error.statusCode = res.statusCode; 71 | error.body = res.body; 72 | error.error = err; 73 | callback (error); 74 | } 75 | 76 | 77 | /** 78 | * Process HTTP response 79 | * 80 | * @callback callback 81 | * @return {void} 82 | * 83 | * @param {Error|null} err httpreq error 84 | * @param {object} res httpreq response details 85 | * @param {function} callback `(err, data)` 86 | */ 87 | 88 | function processResponse (err, res, callback) { 89 | let data; 90 | let error; 91 | 92 | if (err) { 93 | return callback (err); 94 | } 95 | 96 | if (res.statusCode === 404) { 97 | error = new Error ('not found'); 98 | error.statusCode = res.statusCode; 99 | return callback (error); 100 | } 101 | 102 | try { 103 | data = JSON.parse (res.body); 104 | } catch (e) { 105 | return processApiError (res, e, callback); 106 | } 107 | 108 | return callback (null, data); 109 | } 110 | 111 | 112 | /** 113 | * Send HTTP request to API 114 | * 115 | * @callback callback 116 | * @return {void} 117 | * 118 | * @param {string} service API service name 119 | * @param {string} method API service method name 120 | * @param {object} [params] Additional parameters to include 121 | * @param {function} callback `(err, data)` 122 | */ 123 | 124 | function httpRequestHIBP (service, method, params, callback) { 125 | let options = { 126 | url: 'https://haveibeenpwned.com/api/v2/' + service + '/' + method, 127 | method: 'GET', 128 | timeout: config.timeout, 129 | headers: { 130 | 'User-Agent': config.userAgent 131 | } 132 | }; 133 | 134 | if (typeof params === 'function') { 135 | callback = params; 136 | params = {}; 137 | } 138 | 139 | options.parameters = params; 140 | 141 | httpreq.doRequest (options, (err, res) => { 142 | processResponse (err, res, callback); 143 | }); 144 | } 145 | 146 | 147 | /** 148 | * All breaches for an account 149 | * 150 | * @callback callback 151 | * @return {void} 152 | * 153 | * @param {string} account Email or username 154 | * @param {object} [params] Additional parameters to include 155 | * @param {function} callback `(err, data)` 156 | */ 157 | 158 | function methodBreachedAccount (account, params, callback) { 159 | httpRequestHIBP ('breachedaccount', account, params, callback); 160 | } 161 | 162 | 163 | /** 164 | * All breached sites in the system 165 | * 166 | * @callback callback 167 | ^ @return {void} 168 | * 169 | * @param {object} [params] Additional parameters to include 170 | * @param {function} callback `(err, data)` 171 | */ 172 | 173 | function methodBreaches (params, callback) { 174 | httpRequestHIBP ('breaches', '', params, callback); 175 | } 176 | 177 | 178 | /** 179 | * A single breached site 180 | * 181 | * @callback callback 182 | * @return {void} 183 | * 184 | * @param {string} name Email or username 185 | * @param {function} callback `(err, data)` 186 | */ 187 | 188 | function methodBreach (name, callback) { 189 | httpRequestHIBP ('breach', name, callback); 190 | } 191 | 192 | 193 | /** 194 | * All pastes for an account 195 | * 196 | * @callback callback 197 | * @return {void} 198 | * 199 | * @param {string} amount Email or username 200 | * @param {function} callback `(err, data)` 201 | */ 202 | 203 | function methodPasteAccount (account, callback) { 204 | httpRequestHIBP ('pasteaccount', account, callback); 205 | } 206 | 207 | 208 | /** 209 | * All data classes in the system 210 | * 211 | * @callback callback 212 | * @return {void} 213 | * 214 | * @param {function} callback `(err, data)` 215 | */ 216 | 217 | function methodDataclasses (callback) { 218 | httpRequestHIBP ('dataclasses', '', callback); 219 | } 220 | 221 | 222 | /** 223 | * Send HTTP request to API 224 | * 225 | * @callback callback 226 | * @return {void} 227 | * 228 | * @param {string} path API method path 229 | * @param {object} [params] Additional parameters to include 230 | * @param {function} callback `(err, data)` 231 | */ 232 | 233 | function httpRequestPP (path, params, callback) { 234 | let options = { 235 | url: 'https://api.pwnedpasswords.com/' + path, 236 | method: 'GET', 237 | timeout: config.timeout, 238 | headers: { 239 | 'User-Agent': config.userAgent 240 | } 241 | }; 242 | 243 | if (typeof params === 'function') { 244 | callback = params; 245 | params = {}; 246 | } 247 | 248 | options.parameters = params; 249 | 250 | httpreq.doRequest (options, (err, res) => { 251 | let error; 252 | let msg; 253 | 254 | if (err) { 255 | callback (err); 256 | } else if (res.statusCode === 404) { 257 | callback (null, 0); 258 | } else if (res.statusCode >= 300) { 259 | msg = res.body 260 | .replace (/.+

(.+)<\/p>.+/, '$') 261 | .trim(); 262 | error = new Error (msg); 263 | error.statusCode = res.statusCode; 264 | error.body = res.body; 265 | 266 | callback (error); 267 | } else { 268 | callback (null, res.body); 269 | } 270 | }); 271 | } 272 | 273 | 274 | /** 275 | * Search the Pwned Passwords database for a password 276 | * 277 | * Password not found: callback `data` is {int} `0` 278 | * Password found: callback `data` is {int} amount 279 | * 280 | * @callback callback 281 | * @return {void} 282 | * 283 | * @param {string} password Password to check 284 | * @param {bool} [hashed] Is the password already SHA-1 hashed 285 | * @param {function} callback `(err, data)` 286 | */ 287 | 288 | function methodPwnedPasswordsByPassword (password, hashed, callback) { 289 | let params = {}; 290 | 291 | if (typeof hashed === 'function') { 292 | callback = hashed; 293 | hashed = false; 294 | } 295 | 296 | if (hashed) { 297 | params.originalPasswordIsAHash = 'true'; 298 | } 299 | 300 | httpRequestPP ('pwnedpassword/' + password, params, (err, data) => { 301 | if (err) { 302 | callback (err); 303 | } else { 304 | callback (null, parseInt (data, 10)); 305 | } 306 | }); 307 | } 308 | 309 | 310 | /** 311 | * Search the Pwned Passwords database for the first 5 chars of a hash 312 | * and callback the matched hashed with their counts. 313 | * 314 | * Not found: callback `data` is {int} `0` 315 | * Found: callback `data` is {object} 316 | * 317 | * @callback callback 318 | * @return {void} 319 | * 320 | * @param {string} hash SHA-1 hash to check 321 | * @param {bool} [sort] Sort by counts in descending order 322 | * @param {function} callback `(err, data)` 323 | */ 324 | 325 | function methodPwnedPasswordsByRange (hash, sort, callback) { 326 | hash = hash.substr (0, 5); 327 | 328 | if (typeof sort === 'function') { 329 | callback = sort; 330 | sort = false; 331 | } 332 | 333 | httpRequestPP ('range/' + hash, (err, data) => { 334 | let result = {}; 335 | let i; 336 | let str; 337 | let sha; 338 | 339 | if (err) { 340 | callback (err); 341 | return; 342 | } 343 | 344 | data = data.trim().split ('\n'); 345 | 346 | if (data.length) { 347 | for (i = 0; i < data.length; i++) { 348 | str = data[i].split (':'); 349 | sha = str[0].toLowerCase(); 350 | result[sha] = parseInt (str[1], 10); 351 | } 352 | 353 | if (sort) { 354 | result = sortObjectByValues (result); 355 | } 356 | 357 | callback (null, result); 358 | return; 359 | } 360 | 361 | callback (null, 0); 362 | }); 363 | } 364 | 365 | 366 | /** 367 | * Module interface 368 | * 369 | * @return {object} Methods 370 | * 371 | * @param {object} set Configuration params 372 | * @param {int} [set.timeout=5000] Wait timeout in ms 373 | */ 374 | 375 | module.exports = set => { 376 | if (set && set.timeout) { 377 | config.timeout = set.timeout; 378 | } 379 | 380 | return { 381 | breachedAccount: methodBreachedAccount, 382 | breaches: methodBreaches, 383 | breach: methodBreach, 384 | pasteAccount: methodPasteAccount, 385 | dataclasses: methodDataclasses, 386 | pwnedpasswords: { 387 | byPassword: methodPwnedPasswordsByPassword, 388 | byRange: methodPwnedPasswordsByRange 389 | } 390 | }; 391 | }; 392 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.1.0 (2018-02-23) 2 | 3 | ##### Chores 4 | 5 | * **package:** update dotest to version 2.3.0 ([c098f8bc](https://github.com/fvdm/nodejs-haveibeenpwned/commit/c098f8bc4ec4d948a1f48ac0d465cc62f283a310)) 6 | 7 | ##### Documentation Changes 8 | 9 | * **readme:** 10 | * Add Pwned Passwords methods ([793d2f71](https://github.com/fvdm/nodejs-haveibeenpwned/commit/793d2f71a306b8a9ec1a6b499fb82937d98807a6)) 11 | * Minor clean up ([260a7819](https://github.com/fvdm/nodejs-haveibeenpwned/commit/260a78190a5cf01cd983e1c44af0aa5fc19656eb)) 12 | 13 | ##### New Features 14 | 15 | * **byRange:** Add optional sorting ([49ee72d7](https://github.com/fvdm/nodejs-haveibeenpwned/commit/49ee72d70e3cfd4b1764caef192d0e7e4de73225)) 16 | * **httpRequestPP:** Pwned Passwords methods ([ec1020c0](https://github.com/fvdm/nodejs-haveibeenpwned/commit/ec1020c0c7cd3be590b0a65673aac04612f22c7b)) 17 | 18 | ##### Refactors 19 | 20 | * **export:** Cleaner timeout setting ([58f17aa9](https://github.com/fvdm/nodejs-haveibeenpwned/commit/58f17aa9397bce12fb33220c212f9a39544d7cf8)) 21 | * **httpRequest:** Renamed to httpRequestHIBP ([418f5eb5](https://github.com/fvdm/nodejs-haveibeenpwned/commit/418f5eb56d1c523899bea970ba5f4ea65507f4f0)) 22 | 23 | ##### Code Style Changes 24 | 25 | * **syntax:** Clean up and modernization ([d70ef7f0](https://github.com/fvdm/nodejs-haveibeenpwned/commit/d70ef7f04cb10e038446382696de15ccdf5269f6)) 26 | * **readme:** Better parsable syntax ([5803e5d6](https://github.com/fvdm/nodejs-haveibeenpwned/commit/5803e5d6358c7fdf79db5d17e80de3588efec5be)) 27 | 28 | ##### Tests 29 | 30 | * **main:** 31 | * Bypass Node v6.13.0 missing Object.values ([ef3f7766](https://github.com/fvdm/nodejs-haveibeenpwned/commit/ef3f776660b1a328c1d66160caea719c65541fdd)) 32 | * Method tests for pwnedpasswords ([f05ccc4c](https://github.com/fvdm/nodejs-haveibeenpwned/commit/f05ccc4c72ca5ad8bdbc052b0593fad87c9f3d39)) 33 | * Interface checks for pwnedpasswords ([44273e53](https://github.com/fvdm/nodejs-haveibeenpwned/commit/44273e534053ecd59ab4f250d7362fe643ee292c)) 34 | * Add SHA-1 hash function ([f85570c3](https://github.com/fvdm/nodejs-haveibeenpwned/commit/f85570c384df5762b9d43b99c11531d1587ef365)) 35 | 36 | #### 1.0.3 (2017-12-12) 37 | 38 | ##### Chores 39 | 40 | * **package:** 41 | * Reduce dev deps to dotest ([fa9e1b13](https://github.com/fvdm/nodejs-haveibeenpwned/commit/fa9e1b1343c7d40d20975fada3ba09139bd9a918)) 42 | * Update dependencies ([e6bd733e](https://github.com/fvdm/nodejs-haveibeenpwned/commit/e6bd733eed52b014a9f4d43bb334b077ea3c47d9)) 43 | * Update dotest dev dep ([935f36a8](https://github.com/fvdm/nodejs-haveibeenpwned/commit/935f36a882e03f17036720188755b31ba3839872)) 44 | * Update dev deps ([69875d00](https://github.com/fvdm/nodejs-haveibeenpwned/commit/69875d00d42e8d1a0a726c381d6dfe8701817bf0)) 45 | * Replaced test runner and dev deps by dotest ([e14f8679](https://github.com/fvdm/nodejs-haveibeenpwned/commit/e14f86795d4986ee428989cb1a8b84b66ec58bd5)) 46 | * update eslint to version 3.0.0 ([36e2933a](https://github.com/fvdm/nodejs-haveibeenpwned/commit/36e2933a0e3b392ca612ebb03cb677f780c25dc9)) 47 | * **develop:** 48 | * Cleanup .gitignore ([70ca7760](https://github.com/fvdm/nodejs-haveibeenpwned/commit/70ca7760545d51c8f12a47ce84386b45fdc42b6a)) 49 | * Added bitHound config ([6f95c3e2](https://github.com/fvdm/nodejs-haveibeenpwned/commit/6f95c3e23a9e15cd3efc3f6b4c2902b6c418fbea)) 50 | 51 | ##### Documentation Changes 52 | 53 | * **readme:** 54 | * Minor edits ([a995fec4](https://github.com/fvdm/nodejs-haveibeenpwned/commit/a995fec4ebe8ed81da6dfaa9cfb97ca07ec52f26)) 55 | * add Greenkeeper badge ([4bedee12](https://github.com/fvdm/nodejs-haveibeenpwned/commit/4bedee124a1e236444f1f22df1a5c12e54fac8f3)) 56 | * Add coffee link to Author ([253de0e9](https://github.com/fvdm/nodejs-haveibeenpwned/commit/253de0e9490d26ef7ea7fc2366a05dbc6dca5f43)) 57 | * **badges:** 58 | * Change bitHound to master ([b58a8cc4](https://github.com/fvdm/nodejs-haveibeenpwned/commit/b58a8cc44528bca752f452601e86db7136812307)) 59 | * Move Greenkeeper to badges ([44a4661a](https://github.com/fvdm/nodejs-haveibeenpwned/commit/44a4661a2b7fc42f3a8afb2a27785511c65fefb1)) 60 | * Added bitHound code quality ([d6a4f400](https://github.com/fvdm/nodejs-haveibeenpwned/commit/d6a4f400b682a16742230a3ad2e5147bb4dd4e5b)) 61 | 62 | ##### Refactors 63 | 64 | * **package:** Minimum supported node v4.0 ([6db83d62](https://github.com/fvdm/nodejs-haveibeenpwned/commit/6db83d62dbc831f783719acdc61fc91c5ac4cf36)) 65 | 66 | ##### Code Style Changes 67 | 68 | * **comment:** Fix JSdoc syntax ([64b1d22d](https://github.com/fvdm/nodejs-haveibeenpwned/commit/64b1d22dda27222caf2c6db2405891623713c77d)) 69 | * **example:** ES6 syntax ([a81ba827](https://github.com/fvdm/nodejs-haveibeenpwned/commit/a81ba82715e28d25134c86a7a2770e1a4be9420d)) 70 | 71 | ##### Tests 72 | 73 | * **main:** 74 | * Fix syntax typos ([3f9edd68](https://github.com/fvdm/nodejs-haveibeenpwned/commit/3f9edd68c1fe423ad581becb1b24e69e92c050e1)) 75 | * Pause 2 seconds between tests ([1df40fa6](https://github.com/fvdm/nodejs-haveibeenpwned/commit/1df40fa6fbba5f0231e07e6a423860740f2c5a89)) 76 | * Replaced invalid check example.net ([9619424f](https://github.com/fvdm/nodejs-haveibeenpwned/commit/9619424fdd7fb1a0310fae8c7d5e2d3ad590c992)) 77 | * **config:** 78 | * Allow 500 lines in .bithoundrc ([ac3f7f99](https://github.com/fvdm/nodejs-haveibeenpwned/commit/ac3f7f99e47726705dc935f980ca10b01aeab11d)) 79 | * Remove ecmaVersion from .eslintrc ([72840934](https://github.com/fvdm/nodejs-haveibeenpwned/commit/72840934b807a960e043d3db95a1a18102e34605)) 80 | * Update Travis CI node versions ([770ab612](https://github.com/fvdm/nodejs-haveibeenpwned/commit/770ab612c911e529287f85692378688b393609a7)) 81 | * Use dynamic node versions on Travis CI ([4fce2800](https://github.com/fvdm/nodejs-haveibeenpwned/commit/4fce2800772b6d941d62794f3fd8a5fa8e73390c)) 82 | * **style:** 83 | * Convert to ES6 syntax ([147f862c](https://github.com/fvdm/nodejs-haveibeenpwned/commit/147f862c3199d8c65a39c905b8235ed6b0adacf7)) 84 | * Cleanup intro comment ([06ee70d7](https://github.com/fvdm/nodejs-haveibeenpwned/commit/06ee70d76f723e297dac46c7ca21d00212416f59)) 85 | * **lint:** Update eslint to ES6 ([d102da84](https://github.com/fvdm/nodejs-haveibeenpwned/commit/d102da84500bf55b8952b5b2883fc0ec2fcd92c1)) 86 | 87 | #### 1.0.2 (2016-6-8) 88 | 89 | ##### Documentation Changes 90 | 91 | * **changelog:** Fixed bad commit urls ([798c6874](https://github.com/fvdm/nodejs-haveibeenpwned/commit/798c6874a311f3087fa2b1b46d002485aff357f3)) 92 | 93 | #### 1.0.1 (2016-6-8) 94 | 95 | ##### Documentation Changes 96 | 97 | * **badges:** Deeplink Gemnasium to runtime deps ([0e76b043](https://github.com/fvdm/nodejs-haveibeenpwned/commit/0e76b043fd9e7dd465366a12239edf6d6ef461bd)) 98 | * **changelog:** Fixed missing types ([7f68a376](https://github.com/fvdm/nodejs-haveibeenpwned/commit/7f68a37695ca1f354989092e3bdd572155e2ad1e)) 99 | 100 | ##### Bug Fixes 101 | 102 | * **package:** Fixed bad git url copy ([eb9fb42f](https://github.com/fvdm/nodejs-haveibeenpwned/commit/eb9fb42f6f72f5f7ccd3292dfa137b0eb7f1afd1)) 103 | 104 | ## 1.0.0 (2016-6-7) 105 | 106 | ##### Chores 107 | 108 | * **package:** 109 | * Add example.js for Tonic ([8952d599](https://github.com/fvdm/nodejs-haveibeenpwned/commit/8952d599194e3d35355241165fbb3020b21f41a7)) 110 | * gitignore .*_history files ([708e3e75](https://github.com/fvdm/nodejs-haveibeenpwned/commit/708e3e759283e43d32ebc1b02e02afa72e242a1a)) 111 | * **cleanup:** Changed `Have I been pwned` references ([f0d5eec6](https://github.com/fvdm/nodejs-haveibeenpwned/commit/f0d5eec6e7640e094e5c13e63c71c555fcb6196b)) 112 | 113 | ##### Documentation Changes 114 | 115 | * **readme:** 116 | * Fixed .breach example code ([6dc5acaa](https://github.com/fvdm/nodejs-haveibeenpwned/commit/6dc5acaae87407323bfa4fba5f7f0071ab4ae270)) 117 | * Add important links to intro text ([6f552a94](https://github.com/fvdm/nodejs-haveibeenpwned/commit/6f552a949cc33816cbee5a2f4e737b4ac8ed2d0d)) 118 | * Add links to example outputs ([aeecdeb4](https://github.com/fvdm/nodejs-haveibeenpwned/commit/aeecdeb48536ccaa5e0c7431557336edb1379860)) 119 | * Removed invalid argument ([86f034e9](https://github.com/fvdm/nodejs-haveibeenpwned/commit/86f034e934cdc10a273feaa6421b324ff9139538)) 120 | * Add methods descriptions ([6f23c228](https://github.com/fvdm/nodejs-haveibeenpwned/commit/6f23c228fe07cf257342cc3159c8322fa0abf340)) 121 | * Add readme basics ([8836c2df](https://github.com/fvdm/nodejs-haveibeenpwned/commit/8836c2dfa0e86961d2da9d709738c459b748df04)) 122 | 123 | ##### New Features 124 | 125 | * **methods:** 126 | * Add method .breachedAccount ([34fbd135](https://github.com/fvdm/nodejs-haveibeenpwned/commit/34fbd135e5d0fe025a7f03b436ab214bd69735e2)) 127 | * Add method .breaches ([ca57077c](https://github.com/fvdm/nodejs-haveibeenpwned/commit/ca57077ca322911643464fb183d16c1b4494efa1)) 128 | * Add method .breach ([662ab512](https://github.com/fvdm/nodejs-haveibeenpwned/commit/662ab5120faa3a8630f3f8f2b272bb400d051389)) 129 | * Add method .pasteAccount ([8742da29](https://github.com/fvdm/nodejs-haveibeenpwned/commit/8742da293a2abdcc9b673b4e3d7d5ef705935874)) 130 | * Add method .dataclasses ([4f0e503b](https://github.com/fvdm/nodejs-haveibeenpwned/commit/4f0e503b90e76aee1b5b118df50b26aee6bfcb6d)) 131 | 132 | ##### Bug Fixes 133 | 134 | * **code:** Fixed setup without config ([2638e216](https://github.com/fvdm/nodejs-haveibeenpwned/commit/2638e21684112940b0af38a30e5675c7620a4c48)) 135 | 136 | ##### Other Changes 137 | 138 | * **comments:** Add intro, author, unlicense comment ([3f655f53](https://github.com/fvdm/nodejs-haveibeenpwned/commit/3f655f53f0bc3e16f8b2a2ea500d2bbecc0f3595)) 139 | * **lint:** 140 | * Fixed unchecked data argument ([d2b4b6e6](https://github.com/fvdm/nodejs-haveibeenpwned/commit/d2b4b6e6af106eb60dc282e1c83cda0d98e5edd1)) 141 | * Fixed inconsistent returns ([65f9e357](https://github.com/fvdm/nodejs-haveibeenpwned/commit/65f9e357456b4b1c82b753f96f939564080113a0)) 142 | * **style:** Fixed missing semicolon ([c8c41113](https://github.com/fvdm/nodejs-haveibeenpwned/commit/c8c4111390e2119a72b7d70f3e5e5e2b68e3a538)) 143 | * **code:** Removed invalid JSdoc params ([9e39bb89](https://github.com/fvdm/nodejs-haveibeenpwned/commit/9e39bb890fe853725c72987c42aae73fcdb8b19d)) 144 | 145 | ##### Refactors 146 | 147 | * **package:** 148 | * Add more npm keywords ([dbe0c919](https://github.com/fvdm/nodejs-haveibeenpwned/commit/dbe0c9197f6d86329160a882433554ff3b25d388)) 149 | * Add package.json ([8889601f](https://github.com/fvdm/nodejs-haveibeenpwned/commit/8889601f092bb116feeaf592e6fa3b27ebbf11c7)) 150 | * **code:** Add index.js main code ([0d4c9567](https://github.com/fvdm/nodejs-haveibeenpwned/commit/0d4c9567b630a312d759e7fcf1da9e595cff27cf)) 151 | 152 | ##### Tests 153 | 154 | * **runner:** 155 | * Verbose coverage commands ([850f6782](https://github.com/fvdm/nodejs-haveibeenpwned/commit/850f6782b1ae3d58eace871e7b84fdb0829a94e9)) 156 | * Add test.sh bash script ([039cb6cf](https://github.com/fvdm/nodejs-haveibeenpwned/commit/039cb6cf109c3ab9ced0373b50595cf25f5c24e1)) 157 | * **script:** Add test.js script ([b0a85372](https://github.com/fvdm/nodejs-haveibeenpwned/commit/b0a8537244b239befcd1f421c00a9a44b0f78525)) 158 | * **config:** 159 | * Add Travis CI configuration ([25f070b7](https://github.com/fvdm/nodejs-haveibeenpwned/commit/25f070b7f6cd3a785b3d5d7b58fba52e2b097455)) 160 | * Add eslint configuration ([a4692162](https://github.com/fvdm/nodejs-haveibeenpwned/commit/a4692162c8c9ca38b5cf8d55b4c1c1dcaf5ef2be)) 161 | 162 | --------------------------------------------------------------------------------