removed haveibeenpwned
This commit is contained in:
parent
c044091a1c
commit
31abf7f97b
|
@ -668,27 +668,6 @@
|
|||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRq2k3TTx0ewTe6wDr6QVeB5diwiIWzsJD+ApfZu1KNPedcAgslAfjpNsYF1if6cYsPMJH70xJ2np6RQBl1VPdwShOuxkD7m0BD5Hw/Aar8Hdp5cvAdOOMdBO+0DbGeUMy+z66s+oUCJmqVp19T6PkkxbhN08rgtT7v+aFvrbqbO/vlsskbJpH2K2io+e1XmRGPnSr9q4KSqfGbTfe5gLwDIOFd66Z4mb5Utb5wWpsy6Gjh06Yf257AccGD3A1bkTNOyeeX0tqciYBePWMk0icP/aZ6hnErfhnUKf3tOgPLppSHiGcaSKekhChZ2xLUs3U64JwrXSmwHj+TzdO3S0QIDAQAB"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "https://plugins.keeweb.info/plugins/haveibeenpwned",
|
||||
"manifest": {
|
||||
"version": "0.1.11",
|
||||
"manifestVersion": "0.1.0",
|
||||
"name": "haveibeenpwned",
|
||||
"description": "Check HaveIBeenPwned password database",
|
||||
"author": {
|
||||
"name": "Olivier LEVILLAIN",
|
||||
"email": "olivier.levillain@free.fr",
|
||||
"url": "https://github.com/leolivier"
|
||||
},
|
||||
"resources": {
|
||||
"js": "LjFZ6E0K4JagS4VQKjsTNynxv/0hmr7gfJkn9hrtyCjXiibiCJ/cAcxlvS5mia/T1X79NoMt/57+6mklr+BwakVZSVCPVpQ1XQWaN3JNpu3OrWQ8wc2dAui9mdPN91wSrSiOks6ypU6xFHArxUk5M63Md2H1nShBiHpi6McTVv/6/QoYQhhTnztoZJA5CZcZXPVdby9aPzJTwoi24okBggI7fIIfMwE1Hocobq8annyuafGm+/pRMMmVHHG34T3T9+3GSTZ8cRr0q3hOgAN88Eovf3xKQ3/21HUvQtcHPT+YgeWZWWZjM2UvfVMzPU6kF8ks/MfZBZFmLGUc/Zlr5Q==",
|
||||
"css": "K8gtQONyUe5AMIEa2ozxHvVC4umblP5r6FOdKgX+288Il3m928SquewF9fIqDeeMONtGEQnFpMG60jgP1edEZvBZdjhT3qJySdTUYf7etmjQInY/4/0K18HenixwhW4C6XQm/+M90voHsiKOcZMSY4/uSCmltnI7qaRr3Ag+2+QQXdpMDNQthRCZhgrh3KtO0hHAcGYURRQ2YEfHcfT5JZrl4kpOhJYTTt3Dy0XNnRQaa30qedVY2rCQtAnE870D+MSUpri4BGAGdxz2Wi3BdWUtBPT6wAdoJQWhsIcuRv0f0LIFbjsw1sI52do2oj/dmfJ1lJ3RnEB/ALHDIvx+ew=="
|
||||
},
|
||||
"url": "https://plugins.keeweb.info/plugins/haveibeenpwned",
|
||||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRq2k3TTx0ewTe6wDr6QVeB5diwiIWzsJD+ApfZu1KNPedcAgslAfjpNsYF1if6cYsPMJH70xJ2np6RQBl1VPdwShOuxkD7m0BD5Hw/Aar8Hdp5cvAdOOMdBO+0DbGeUMy+z66s+oUCJmqVp19T6PkkxbhN08rgtT7v+aFvrbqbO/vlsskbJpH2K2io+e1XmRGPnSr9q4KSqfGbTfe5gLwDIOFd66Z4mb5Utb5wWpsy6Gjh06Yf257AccGD3A1bkTNOyeeX0tqciYBePWMk0icP/aZ6hnErfhnUKf3tOgPLppSHiGcaSKekhChZ2xLUs3U64JwrXSmwHj+TzdO3S0QIDAQAB",
|
||||
"license": "MIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "https://plugins.keeweb.info/translations/tr",
|
||||
"official": true,
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>KeeWeb Plugin: HaveIBeenPwned</title>
|
||||
<link rel="shortcut icon" href="/favicon.png" />
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, "BlinkMacSystemFont", "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>KeeWeb Plugin: HaveIBeenPwned</h1>
|
||||
<a href="https://plugins.keeweb.info/plugins/haveibeenpwned">https://plugins.keeweb.info/plugins/haveibeenpwned</a>
|
||||
<p>This plugin checks the <a href="https://haveibeenpwned.com">HaveIBeenPwned</a> site each time you enter a password to look if it has been pawned in a breach. <br/>
|
||||
If pawned, it displays a dancing icon looking like this: <strong>';--</strong> near the pawned password<br/>
|
||||
<i>Note: Passwords is safely checked (only a hash is sent over the network).</i></p>
|
||||
<p>Since the V3 version of the <a href="https://haveibeenpwned.com">HaveIBeenPwned</a> API (mid 2019), checking user names is not free anymore
|
||||
(3.5$/month, see <a href="https://haveibeenpwned.com/API/Key">API V3 Key</a>)...<br/>
|
||||
So user name checking has been disabled in the latest version of this plugin.</p>
|
||||
<p>Freely inspired from the equivalent keepass plugin</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"version": "0.1.11",
|
||||
"manifestVersion": "0.1.0",
|
||||
"name": "haveibeenpwned",
|
||||
"description": "Check HaveIBeenPwned password database",
|
||||
"author": {
|
||||
"name": "Olivier LEVILLAIN",
|
||||
"email": "olivier.levillain@free.fr",
|
||||
"url": "https://github.com/leolivier"
|
||||
},
|
||||
"resources": {
|
||||
"js": "LjFZ6E0K4JagS4VQKjsTNynxv/0hmr7gfJkn9hrtyCjXiibiCJ/cAcxlvS5mia/T1X79NoMt/57+6mklr+BwakVZSVCPVpQ1XQWaN3JNpu3OrWQ8wc2dAui9mdPN91wSrSiOks6ypU6xFHArxUk5M63Md2H1nShBiHpi6McTVv/6/QoYQhhTnztoZJA5CZcZXPVdby9aPzJTwoi24okBggI7fIIfMwE1Hocobq8annyuafGm+/pRMMmVHHG34T3T9+3GSTZ8cRr0q3hOgAN88Eovf3xKQ3/21HUvQtcHPT+YgeWZWWZjM2UvfVMzPU6kF8ks/MfZBZFmLGUc/Zlr5Q==",
|
||||
"css": "K8gtQONyUe5AMIEa2ozxHvVC4umblP5r6FOdKgX+288Il3m928SquewF9fIqDeeMONtGEQnFpMG60jgP1edEZvBZdjhT3qJySdTUYf7etmjQInY/4/0K18HenixwhW4C6XQm/+M90voHsiKOcZMSY4/uSCmltnI7qaRr3Ag+2+QQXdpMDNQthRCZhgrh3KtO0hHAcGYURRQ2YEfHcfT5JZrl4kpOhJYTTt3Dy0XNnRQaa30qedVY2rCQtAnE870D+MSUpri4BGAGdxz2Wi3BdWUtBPT6wAdoJQWhsIcuRv0f0LIFbjsw1sI52do2oj/dmfJ1lJ3RnEB/ALHDIvx+ew=="
|
||||
},
|
||||
"url": "https://plugins.keeweb.info/plugins/haveibeenpwned",
|
||||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRq2k3TTx0ewTe6wDr6QVeB5diwiIWzsJD+ApfZu1KNPedcAgslAfjpNsYF1if6cYsPMJH70xJ2np6RQBl1VPdwShOuxkD7m0BD5Hw/Aar8Hdp5cvAdOOMdBO+0DbGeUMy+z66s+oUCJmqVp19T6PkkxbhN08rgtT7v+aFvrbqbO/vlsskbJpH2K2io+e1XmRGPnSr9q4KSqfGbTfe5gLwDIOFd66Z4mb5Utb5wWpsy6Gjh06Yf257AccGD3A1bkTNOyeeX0tqciYBePWMk0icP/aZ6hnErfhnUKf3tOgPLppSHiGcaSKekhChZ2xLUs3U64JwrXSmwHj+TzdO3S0QIDAQAB",
|
||||
"license": "MIT"
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
.hibp-pwned:after{
|
||||
content: "';--";
|
||||
font: bold 1.2em "Courrier New";
|
||||
text-align: center;
|
||||
background-color: var(--text-color);
|
||||
background-size:cover;
|
||||
position: relative;
|
||||
width:1.5em;
|
||||
height:1.5em;
|
||||
animation: hibp-spin 1s linear 0s infinite;
|
||||
transform-origin: center center;
|
||||
}
|
||||
@keyframes hibp-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
25% { transform: rotate(10deg); color: var(--error-color); }
|
||||
50% { transform: rotate(0deg); }
|
||||
75% { transform: rotate(-10deg); color: var(--error-color); }
|
||||
100% { transform: rotate(0deg); }
|
||||
}
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
/**
|
||||
* KeeWeb plugin: haveibeenpwned
|
||||
* @author Olivier LEVILLAIN
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const Logger = require('util/logger').Logger;
|
||||
// change log level here.
|
||||
const LogLevel = Logger.Level.Info;
|
||||
|
||||
const DetailsView = require('views/details/details-view').DetailsView;
|
||||
const InputFx = require('util/ui/input-fx').InputFx;
|
||||
const Kdbxweb = require('kdbxweb');
|
||||
const utilFn = require('util/fn');
|
||||
const Tip = require('util/ui/tip').Tip;
|
||||
const detailsViewFieldChanged = DetailsView.prototype.fieldChanged;
|
||||
|
||||
let _seen = [];
|
||||
class HIBPUtils {
|
||||
constructor() {
|
||||
_seen = [];
|
||||
this.checkPwnedPwd = true;
|
||||
this.checkPwnedName = true;
|
||||
this.blockPwnedPwd = false;
|
||||
this.blockPwnedName = false;
|
||||
this.logger = new Logger('HaveIBeenPwned');
|
||||
this.logger.setLevel(LogLevel);
|
||||
}
|
||||
|
||||
replacer(key, value) {
|
||||
if (value != null && typeof value === 'object') {
|
||||
if (_seen.indexOf(value) >= 0) {
|
||||
return;
|
||||
}
|
||||
_seen.push(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
stringify(obj) {
|
||||
const ret = JSON.stringify(obj, this.replacer);
|
||||
_seen = [];
|
||||
return ret;
|
||||
}
|
||||
|
||||
xhrcall(config) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
if (config.responseType) {
|
||||
xhr.responseType = config.responseType;
|
||||
}
|
||||
const statuses = config.statuses || [200];
|
||||
xhr.addEventListener('load', () => {
|
||||
if (statuses.indexOf(xhr.status) >= 0) {
|
||||
return config.success && config.success(xhr.response, xhr);
|
||||
} else {
|
||||
return config.error && config.error('http status ' + xhr.status, xhr);
|
||||
}
|
||||
});
|
||||
xhr.addEventListener('error', () => {
|
||||
const err = (xhr.response && xhr.response.error) || new Error('Network error');
|
||||
this.logger.error('HaveIBeenPwned API error', 'GET', xhr.status, err);
|
||||
err.status = xhr.status;
|
||||
return err;
|
||||
});
|
||||
xhr.addEventListener('timeout', () => {
|
||||
return config.error && config.error('timeout', xhr);
|
||||
});
|
||||
xhr.open(config.method || 'GET', config.url);
|
||||
if (config.headers) {
|
||||
for (const key in config.headers) {
|
||||
xhr.setRequestHeader(key, config.headers[key]);
|
||||
}
|
||||
}
|
||||
xhr.send(config.data);
|
||||
}
|
||||
|
||||
hex(buffer) {
|
||||
const hexCodes = [];
|
||||
const view = new DataView(buffer);
|
||||
for (let i = 0; i < view.byteLength; i += 4) {
|
||||
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
|
||||
const value = view.getUint32(i);
|
||||
// toString(16) will give the hex representation of the number without padding
|
||||
const stringValue = value.toString(16);
|
||||
// We use concatenation and slice for padding
|
||||
const padding = '00000000';
|
||||
const paddedValue = (padding + stringValue).slice(-padding.length);
|
||||
hexCodes.push(paddedValue);
|
||||
}
|
||||
// Join all the hex strings into one
|
||||
return hexCodes.join('');
|
||||
}
|
||||
|
||||
digest(algo, str) {
|
||||
const buffer = Kdbxweb.ByteUtils.stringToBytes(str);
|
||||
const subtle = window.crypto.subtle || window.crypto.webkitSubtle;
|
||||
const _self = this;
|
||||
return subtle.digest(algo, buffer).then((hash) => {
|
||||
return _self.hex(hash);
|
||||
});
|
||||
}
|
||||
|
||||
sha1(str) {
|
||||
return this.digest('SHA-1', str);
|
||||
}
|
||||
|
||||
sha256(str) {
|
||||
return this.digest('SHA-256', str);
|
||||
}
|
||||
|
||||
alert(el, msg) {
|
||||
// Alerts.info({ body: msg, title: 'HaveIBeenPwned' });
|
||||
el.focus();
|
||||
el.addClass('input--error');
|
||||
el.addClass('hibp-pwned');
|
||||
Tip.createTip(el, { title: msg, placement: 'bottom' });
|
||||
InputFx.shake(el);
|
||||
}
|
||||
|
||||
passed(el, msg) {
|
||||
hibp.logger.info(msg);
|
||||
el.removeClass('input--error');
|
||||
el.removeClass('hibp-pwned');
|
||||
}
|
||||
}
|
||||
|
||||
const hibp = new HIBPUtils();
|
||||
|
||||
DetailsView.prototype.checkNamePwned = function (name) {
|
||||
hibp.logger.info('check hibp name ' + name);
|
||||
name = encodeURIComponent(name);
|
||||
const url = `https://haveibeenpwned.com/api/v2/breachedaccount/${name}?truncateResponse=true`;
|
||||
hibp.logger.debug('url ' + url);
|
||||
hibp.xhrcall({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
responseType: 'json',
|
||||
data: null,
|
||||
statuses: [200, 404],
|
||||
success: (data, xhr) => {
|
||||
if (data && data.length > 0) {
|
||||
hibp.logger.debug('found breaches ' + JSON.stringify(data));
|
||||
let breaches = '';
|
||||
data.forEach((breach) => {
|
||||
breaches += '<li>' + utilFn.escape(breach.Name) + '</li>\n';
|
||||
});
|
||||
hibp.alert(
|
||||
this.userEditView.$el,
|
||||
`WARNING! This account has been pawned in the following breaches<br/>\n<ul>\n${breaches}\n</ul>\n<p>Please check on <a href='https://haveibeenpwned.com'>https://haveibeenpwned.com</a>\n`
|
||||
);
|
||||
} else {
|
||||
hibp.passed(this.userEditView.$el, 'check pwned user name passed...');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DetailsView.prototype.checkPwdPwned = function (passwordHash) {
|
||||
hibp.logger.info('check hibp pwd (hash) ' + passwordHash);
|
||||
const prefix = passwordHash.substring(0, 5);
|
||||
hibp.xhrcall({
|
||||
url: `https://api.pwnedpasswords.com/range/${prefix}`,
|
||||
method: 'GET',
|
||||
responseType: 'text',
|
||||
data: null,
|
||||
statuses: [200, 404],
|
||||
success: (data) => {
|
||||
if (data) {
|
||||
hibp.logger.debug('found breaches ' + JSON.stringify(data));
|
||||
data.split('\r\n').forEach((line) => {
|
||||
const h = line.split(':');
|
||||
const suffix = h[0];
|
||||
if (prefix + suffix === passwordHash) {
|
||||
const nb = utilFn.escape(h[1]);
|
||||
hibp.alert(
|
||||
this.getFieldView('$Password').$el,
|
||||
`WARNING: This password is referenced as pawned ${nb} times on https://haveibeenpwned.com!\n`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
hibp.passed(this.userEditView.$el, 'check pwned password passed...');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DetailsView.prototype.fieldChanged = function (e) {
|
||||
detailsViewFieldChanged.apply(this, arguments);
|
||||
if (e.field) {
|
||||
hibp.logger.debug('field changed ' + hibp.stringify(e));
|
||||
if (e.field === '$Password' && hibp.checkPwnedPwd) {
|
||||
if (this.getFieldView('$Password').value) {
|
||||
const pwd = this.getFieldView('$Password').value.getText();
|
||||
if (pwd.replace(/\s/, '') !== '' && !pwd.startsWith('{REF:')) {
|
||||
hibp.sha1(pwd).then((hash) => {
|
||||
this.checkPwdPwned(hash.toUpperCase());
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (e.field === '$UserName' && hibp.checkPwnedName) {
|
||||
this.checkNamePwned(e.val);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.getSettings = function () {
|
||||
return [
|
||||
{
|
||||
name: 'checkPwnedPwd',
|
||||
label: 'Check passwords against HaveIBeenPwned list',
|
||||
type: 'checkbox',
|
||||
value: hibp.checkPwnedPwd
|
||||
// disabled since API V3 of HaveIbeenPwned is not free anymore for checking accounts
|
||||
// }, {
|
||||
// name: 'checkPwnedName',
|
||||
// label: 'Check user ids against HaveIBeenPwned list',
|
||||
// type: 'checkbox',
|
||||
// value: hibp.checkPwnedName
|
||||
},
|
||||
{
|
||||
name: 'blockPwnedPwd',
|
||||
label: 'Block pwned passwords if they are in HaveIBeenPwned list',
|
||||
type: 'checkbox',
|
||||
value: hibp.blockPwnedPwd
|
||||
// }, {
|
||||
// name: 'blockPwnedName',
|
||||
// label: 'Block pwned names if they are in HaveIBeenPwned list',
|
||||
// type: 'checkbox',
|
||||
// value: hibp.blockPwnedName
|
||||
}
|
||||
];
|
||||
};
|
||||
// disabled since API V3 of HaveIbeenPwned is not free anymore for checking accounts
|
||||
hibp.checkPwnedName = false;
|
||||
hibp.blockPwnedName = false;
|
||||
|
||||
module.exports.setSettings = function (changes) {
|
||||
for (const field in changes) {
|
||||
const ccfield = field.substr(0, 1).toLowerCase() + field.substring(1);
|
||||
hibp[ccfield] = changes[field];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.uninstall = function () {
|
||||
DetailsView.prototype.fieldChanged = detailsViewFieldChanged;
|
||||
};
|
Loading…
Reference in New Issue