deleted KeeWebHttp

This commit is contained in:
antelle 2021-05-08 22:38:53 +02:00
parent 7935b91ac3
commit aa7fee653a
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
4 changed files with 2 additions and 612 deletions

View File

@ -1,6 +1,6 @@
{
"date": "2021-05-08T08:54:44.596Z",
"signature": "KS+Q2i/t1bSEviqNVO0e8Xp3+NQl12O6b5hBAeFWM3OEJjieH4OIf/WT644YAeJmIyDTpMGXcAQcrUdmpTNPMpuy1y4aFc8vfyAzehVmComAzxUuNmv/XsZ83jZJJ3F9WYag49SzsOPhg4mrfqCWaE1cGRomCv9kQPzThmuFAAFwkeqWF/8mGL96cyz+ZABv2tmQFUu6h1nTP4fYPxVZC+ZXKa6O5mRWvv7AWH6xdlLSejD5i7xaLlVJdYbTs7LHmHOay14+8V0yiAkg7HKSyJBMgpaUKJujevahpjVHB84YaBAp9cR2PjtVefimfSk6KkX2obWudbPU2TIcxeKFbQ==",
"date": "2021-05-08T20:38:37.719Z",
"signature": "Nisjd1Y//AdjogoVI1D6PKJ+VZsz6s5Mk5JB8Om+Lu9eK2TKDmfLK0XWmEjDQKxnV5ZkSdxcPkOluUnzkuAlS7MDZfWo/CEDvdoUpsOUHZW69Dj/L5TYON+xuhLlop0+t6Me7UaOAcljfZyWL+Uq0CSxkfwQiCK25Ekp0SNg/nL9Q+3965rviBtBw43TIHeWI/bxO5HaZHdLRuNQ2YugwslI/LUUHxcdrKoSABi0M/BIVSzNP/eV1Io+77e5oVsyUmzbEwuEsDdL1bmAQZaJMjdQqIEyVWavz1TAQD7XT3/F4A28D0is1g0Sapdobxy7h2tCXab6V1NtwG27zEAVvA==",
"plugins": [
{
"url": "https://plugins.keeweb.info/translations/zh-CN",
@ -340,29 +340,6 @@
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRq2k3TTx0ewTe6wDr6QVeB5diwiIWzsJD+ApfZu1KNPedcAgslAfjpNsYF1if6cYsPMJH70xJ2np6RQBl1VPdwShOuxkD7m0BD5Hw/Aar8Hdp5cvAdOOMdBO+0DbGeUMy+z66s+oUCJmqVp19T6PkkxbhN08rgtT7v+aFvrbqbO/vlsskbJpH2K2io+e1XmRGPnSr9q4KSqfGbTfe5gLwDIOFd66Z4mb5Utb5wWpsy6Gjh06Yf257AccGD3A1bkTNOyeeX0tqciYBePWMk0icP/aZ6hnErfhnUKf3tOgPLppSHiGcaSKekhChZ2xLUs3U64JwrXSmwHj+TzdO3S0QIDAQAB"
}
},
{
"url": "https://plugins.keeweb.info/plugins/keewebhttp",
"official": true,
"manifest": {
"version": "1.0.2",
"manifestVersion": "0.1.0",
"versionMin": "1.11.0",
"name": "keewebhttp",
"description": "KeeWebHttp allows to use browser extensions with KeeWeb",
"author": {
"name": "KeeWeb",
"email": "antelle.net@gmail.com",
"url": "https://keeweb.info"
},
"license": "MIT",
"url": "https://plugins.keeweb.info/plugins/keewebhttp",
"resources": {
"js": "LlPkehKQKbE9xZHoatfNHSx4TA4nZlzuu1ul1YVSJXW9FOmpY7FTVlU50Kl1sXBDwNNnj4sRmBpZ/SZbo7ALOx258iFZVUy+c2O2CSsQnxTmHBH0zoG3Pdse+ByxMszj/guH2Xeiq6q9wtjJszP3RwBNcxOxirjd3FFzNA4jS877Llu29MaadApOBZr+MCgHTzuFoNNU5DYnT5qCbxnW9U9FisiT5JIFsGagy+rEg1MMPtUnVdjFp27Fn8TtrrJr+BsVrQS+FjzV5EGHLqC+piBz5ljysl1yJDU3oFBytADDZjXQAgIjk/OvQDOIJ9/peOIm4NY43f6m8qYg9zVFqg=="
},
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRq2k3TTx0ewTe6wDr6QVeB5diwiIWzsJD+ApfZu1KNPedcAgslAfjpNsYF1if6cYsPMJH70xJ2np6RQBl1VPdwShOuxkD7m0BD5Hw/Aar8Hdp5cvAdOOMdBO+0DbGeUMy+z66s+oUCJmqVp19T6PkkxbhN08rgtT7v+aFvrbqbO/vlsskbJpH2K2io+e1XmRGPnSr9q4KSqfGbTfe5gLwDIOFd66Z4mb5Utb5wWpsy6Gjh06Yf257AccGD3A1bkTNOyeeX0tqciYBePWMk0icP/aZ6hnErfhnUKf3tOgPLppSHiGcaSKekhChZ2xLUs3U64JwrXSmwHj+TzdO3S0QIDAQAB",
"desktop": true
}
},
{
"url": "https://plugins.keeweb.info/plugins/theme-darker",
"official": true,

View File

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>KeeWeb Plugin: KeeWebHttp</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: KeeWebHttp</h1>
<a href="https://plugins.keeweb.info/plugins/keewebhttp">https://plugins.keeweb.info/plugins/keewebhttp</a>
<p>This plugin enables integrations with browser extensions, like
<a href="https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae?utm_source=chrome-ntp-icon">chromeIPass</a>
or
<a href="https://addons.mozilla.org/en-US/firefox/addon/passifox/">PassIFox</a>.
</p>
<p>It's the same as KeePassHttp, but for KeeWeb.</p>
</body>
</html>

View File

@ -1,19 +0,0 @@
{
"version": "1.0.2",
"manifestVersion": "0.1.0",
"versionMin": "1.11.0",
"name": "keewebhttp",
"description": "KeeWebHttp allows to use browser extensions with KeeWeb",
"author": {
"name": "KeeWeb",
"email": "antelle.net@gmail.com",
"url": "https://keeweb.info"
},
"license": "MIT",
"url": "https://plugins.keeweb.info/plugins/keewebhttp",
"resources": {
"js": "LlPkehKQKbE9xZHoatfNHSx4TA4nZlzuu1ul1YVSJXW9FOmpY7FTVlU50Kl1sXBDwNNnj4sRmBpZ/SZbo7ALOx258iFZVUy+c2O2CSsQnxTmHBH0zoG3Pdse+ByxMszj/guH2Xeiq6q9wtjJszP3RwBNcxOxirjd3FFzNA4jS877Llu29MaadApOBZr+MCgHTzuFoNNU5DYnT5qCbxnW9U9FisiT5JIFsGagy+rEg1MMPtUnVdjFp27Fn8TtrrJr+BsVrQS+FjzV5EGHLqC+piBz5ljysl1yJDU3oFBytADDZjXQAgIjk/OvQDOIJ9/peOIm4NY43f6m8qYg9zVFqg=="
},
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnRq2k3TTx0ewTe6wDr6QVeB5diwiIWzsJD+ApfZu1KNPedcAgslAfjpNsYF1if6cYsPMJH70xJ2np6RQBl1VPdwShOuxkD7m0BD5Hw/Aar8Hdp5cvAdOOMdBO+0DbGeUMy+z66s+oUCJmqVp19T6PkkxbhN08rgtT7v+aFvrbqbO/vlsskbJpH2K2io+e1XmRGPnSr9q4KSqfGbTfe5gLwDIOFd66Z4mb5Utb5wWpsy6Gjh06Yf257AccGD3A1bkTNOyeeX0tqciYBePWMk0icP/aZ6hnErfhnUKf3tOgPLppSHiGcaSKekhChZ2xLUs3U64JwrXSmwHj+TzdO3S0QIDAQAB",
"desktop": true
}

View File

@ -1,544 +0,0 @@
let uninstall;
let restart;
let serverPort = 19455;
const timeout = setTimeout(run, 500);
function run() {
const nodeRequire = window.require;
const http = nodeRequire('http');
const crypto = nodeRequire('crypto');
const electron = nodeRequire('electron');
const kdbxweb = require('kdbxweb');
const Events = require('framework/events').Events;
const AppModel = require('models/app-model').AppModel;
const EntryModel = require('models/entry-model').EntryModel;
const GroupModel = require('models/group-model').GroupModel;
const AutoTypeFilter = require('auto-type/auto-type-filter').AutoTypeFilter;
const Logger = require('util/logger').Logger;
const Alerts = require('comp/ui/alerts').Alerts;
const PasswordGenerator = require('util/generators/password-generator').PasswordGenerator;
const GeneratorPresets = require('comp/app/generator-presets').GeneratorPresets;
const Version = '1.8.4.2';
const DebugMode = localStorage.keewebhttpDebug;
const FileReadTimeout = 500;
const EntryTitle = 'KeePassHttp Settings';
const EntryFieldPrefix = 'AES Key: ';
const EntryUuid = 'NGl6QIpbQcCfNol9Yj7LMQ==';
const CreatePasswordsGroupTitle = 'KeePassHttp Passwords';
const keys = {};
const addedKeys = {};
const logger = new Logger('keewebhttp');
let uninstalled;
let server;
uninstall = function () {
uninstalled = true;
removeEventListeners();
stopServer();
};
restart = function () {
stopServer();
startServer();
};
addEventListeners();
startServer();
readAllKeys();
function addEventListeners() {
AppModel.instance.files.on('add', fileOpened);
}
function removeEventListeners() {
AppModel.instance.files.off('add', fileOpened);
}
function startServer() {
if (uninstalled) {
return;
}
server = http.createServer((req, res) => {
const origin = req.headers.origin;
const userAgent = req.headers['user-agent'] || '';
const referer = req.headers.referrer || req.headers.referer;
let originIsValid;
if (origin) {
if (
origin.startsWith('chrome-extension://') ||
origin.startsWith('safari-extension://') ||
// Firefox sends 'null' here, see https://github.com/keeweb/keeweb-plugins/pull/26
(userAgent.includes('Firefox') && origin === 'null')
) {
originIsValid = true;
}
} else {
originIsValid = true;
}
const isAllowed = req.method === 'POST' && !referer && originIsValid;
if (!isAllowed) {
if (DebugMode) {
logger.debug('Request dropped', req.method, req.url, req.headers);
}
req.client.destroy();
res.end();
return;
}
if (req.method === 'POST') {
const body = [];
req.on('data', (data) => body.push(data));
req.on('end', () => {
const postData = Buffer.concat(body).toString();
if (DebugMode) {
logger.debug('< ' + postData);
}
new RequestContext(postData).handle().then((response) => {
if (DebugMode) {
logger.debug('> ' + response);
}
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(response);
});
});
}
});
const port = serverPort;
const hostname = '127.0.0.1';
server.listen(port, hostname, () => {
if (uninstalled) {
server.close();
return;
}
logger.debug(`Server running at http://${hostname}:${port}/`);
});
server.on('connection', (conn) => {
const key = conn.remoteAddress + ':' + conn.remotePort;
server.conn[key] = conn;
conn.on('close', () => {
if (server) {
delete server.conn[key];
}
});
});
server.conn = {};
}
function stopServer() {
if (server) {
server.close();
for (const key of Object.keys(server.conn)) {
server.conn[key].destroy();
}
server = null;
}
}
function fileOpened(file) {
setTimeout(() => {
readKeys(file);
writeAddedKeys(file);
}, FileReadTimeout);
}
function readAllKeys() {
AppModel.instance.files.forEach((file) => readKeys(file));
}
function readKeys(file) {
if (uninstalled) {
return;
}
const entry = getSettingsEntry(file);
if (!entry) {
return;
}
for (const field of Object.keys(entry.fields)) {
if (field.startsWith(EntryFieldPrefix)) {
const key = field.replace(EntryFieldPrefix, '');
let value = entry.fields[field];
if (value && value.isProtected) {
value = value.getText();
}
if (key && value && !keys[key]) {
keys[key] = value;
}
}
}
}
function writeAddedKeysToAllFiles() {
AppModel.instance.files.forEach((file) => {
writeAddedKeys(file);
});
}
function writeAddedKeys(file) {
if (uninstalled || !Object.keys(addedKeys).length) {
return;
}
let settingsEntry = getSettingsEntry(file);
if (!settingsEntry) {
settingsEntry = EntryModel.newEntry(file.groups[0], file);
settingsEntry.entry.uuid = new kdbxweb.KdbxUuid(EntryUuid);
settingsEntry.setField('Title', EntryTitle);
}
for (const key of Object.keys(addedKeys)) {
const keyFieldName = EntryFieldPrefix + key;
const value = addedKeys[key];
let oldValue = settingsEntry.fields[keyFieldName];
if (oldValue && oldValue.isProtected) {
oldValue = oldValue.getText();
}
if (oldValue !== value) {
settingsEntry.setField(keyFieldName, kdbxweb.ProtectedValue.fromString(value));
}
}
file.reload();
Events.emit('refresh');
}
function getSettingsEntry(file) {
return file.getEntry(file.subId(EntryUuid));
}
class RequestContext {
constructor(postData) {
this.postData = postData;
}
handle() {
let result;
try {
this.req = JSON.parse(this.postData);
const response = this.execute() || this.resp;
if (response instanceof Promise) {
result = response.catch((e) => {
return this.makeError(e);
});
} else {
result = Promise.resolve(response);
}
} catch (e) {
result = Promise.resolve(this.makeError(e));
}
return result.then((res) => JSON.stringify(res));
}
execute() {
switch (this.req.RequestType) {
case 'test-associate':
return this.testAssociate();
case 'associate':
return this.associate();
case 'get-logins':
return this.getLogins({});
case 'get-logins-count':
return this.getLogins({ onlyCount: true });
case 'get-all-logins':
return this.getLogins({ all: true });
case 'set-login':
return this.setLogin();
case 'generate-password':
return this.generatePassword();
default:
throw 'Not implemented';
}
}
makeError(e, skipLog) {
const requestType = (this.req && this.req.RequestType) || '';
if (!skipLog) {
logger.error('handleRequest error', requestType, e);
}
return {
Error: e ? e.toString() : '',
Success: false,
RequestType: requestType,
Version: Version
};
}
decrypt(value) {
if (!this.aesKey) {
throw 'No key';
}
if (!this.req.Nonce) {
throw 'No nonce';
}
const key = Buffer.from(this.aesKey, 'base64');
const nonce = Buffer.from(this.req.Nonce, 'base64');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, nonce);
return Buffer.concat([decipher.update(value, 'base64'), decipher.final()]).toString();
}
encrypt(value) {
if (!this.aesKey) {
throw 'No aes key';
}
if (!this.resp || !this.resp.Nonce) {
throw 'No nonce';
}
const key = Buffer.from(this.aesKey, 'base64');
const nonce = Buffer.from(this.resp.Nonce, 'base64');
const cipher = crypto.createCipheriv('aes-256-cbc', key, nonce);
let data;
if (value.isProtected) {
const binaryData = value.getBinary();
data = Buffer.from(binaryData);
binaryData.fill(0);
} else {
data = Buffer.from(value, 'utf8');
}
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]).toString(
'base64'
);
data.fill(0);
return encrypted;
}
getKeyById() {
return keys[this.req.Id];
}
saveKeyWithId() {
keys[this.req.Id] = this.req.Key;
addedKeys[this.req.Id] = this.req.Key;
writeAddedKeysToAllFiles();
}
verifyRequest() {
if (!this.req.Verifier) {
throw 'No verifier';
}
if (!this.aesKey) {
this.aesKey = this.getKeyById();
}
const decrypted = this.decrypt(this.req.Verifier);
if (decrypted !== this.req.Nonce) {
throw 'Bad signature';
}
}
createResponse() {
const resp = {
Success: true,
Nonce: '',
Verifier: '',
Version: Version,
RequestType: this.req.RequestType
};
if (this.req.Id && keys[this.req.Id]) {
const key = Buffer.from(keys[this.req.Id], 'base64');
const nonce = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, nonce);
const encrypted = Buffer.concat([
cipher.update(nonce.toString('base64'), 'utf8'),
cipher.final()
]).toString('base64');
resp.Id = this.req.Id;
resp.Nonce = nonce.toString('base64');
resp.Verifier = encrypted;
}
this.resp = resp;
}
testAssociate() {
if (!this.req.Id) {
return this.makeError('', true);
}
if (!this.getKeyById(this.req.Id)) {
return this.makeError('Unknown Id', true);
}
this.verifyRequest();
this.createResponse();
}
associate() {
if (this.req.Id) {
throw 'Id not expected';
}
if (!this.req.Key) {
throw 'No request key';
}
this.aesKey = this.req.Key;
this.verifyRequest();
electron.remote.app.getMainWindow().focus();
return new Promise((resolve, reject) => {
Alerts.yesno({
icon: 'plug',
header: 'External Connection',
body:
'Some app is trying to manage passwords in KeeWeb. If you are setting up your plugin, please allow the connection. Otherwise, click No.',
success: () => {
resolve();
},
cancel: () => {
reject('Rejected by user');
}
});
}).then(() => {
this.req.Id = 'KeeWeb ' + new Date().toISOString();
logger.info(`associate: ${this.req.Id}`);
this.saveKeyWithId();
this.createResponse();
return this.resp;
});
}
getLogins(config) {
this.verifyRequest();
if (!this.req.Url && !config.all) {
throw 'No url';
}
if (!AppModel.instance.files.hasOpenFiles()) {
if (this.req.TriggerUnlock === true || this.req.TriggerUnlock === 'true') {
electron.remote.app.getMainWindow().focus();
}
return this.makeError('Locked', true);
}
const url = this.req.Url ? this.decrypt(this.req.Url) : '';
this.createResponse();
const filter = new AutoTypeFilter({ url }, AppModel.instance);
if (config.all) {
filter.ignoreWindowInfo = true;
}
const entries = filter.getEntries();
this.resp.Count = entries.length;
logger.info(`getLogins(${url}): ${this.resp.Count}`);
if (!config.onlyCount) {
this.resp.Entries = entries.map((entry) => {
let customFields = null;
for (const field of Object.keys(entry.fields)) {
if (!customFields) {
customFields = [];
}
const fieldKey = this.encrypt(field);
const fieldValue = this.encrypt(entry.fields[field]);
customFields.push({ Key: fieldKey, Value: fieldValue });
}
return {
Login: entry.user ? this.encrypt(entry.user) : '',
Name: entry.title ? this.encrypt(entry.title) : '',
Password: entry.password ? this.encrypt(entry.password) : '',
StringFields: customFields,
Uuid: this.encrypt(entry.entry.uuid.id)
};
});
}
}
setLogin() {
this.verifyRequest();
if (!this.req.Url || !this.req.Login || !this.req.Password) {
throw 'Invalid request';
}
const uuid = this.req.Uuid ? this.decrypt(this.req.Uuid) : null;
const url = this.decrypt(this.req.Url);
const login = this.decrypt(this.req.Login);
const password = this.decrypt(this.req.Password);
if (uuid) {
let result = 'not found';
AppModel.instance.files.forEach((file) => {
const entry = file.getEntry(file.subId(uuid));
if (entry) {
if (entry.user !== login) {
entry.setField('UserName', login);
}
if (!entry.password.equals(password)) {
entry.setField('Password', kdbxweb.ProtectedValue.fromString(password));
}
}
result = 'updated';
});
logger.info(`setLogin(${url}, ${login}, ${password.length}): ${result}`);
} else {
logger.info(`setLogin(${url}, ${login}, ${password.length}): inserted`);
let group, file;
AppModel.instance.files.forEach((f) => {
f.forEachGroup((g) => {
if (g.title === CreatePasswordsGroupTitle) {
group = g;
file = f;
}
});
});
if (!group) {
file = AppModel.instance.files[0];
group = GroupModel.newGroup(file.groups[0], file);
group.setName(CreatePasswordsGroupTitle);
}
const entry = EntryModel.newEntry(group, file);
const domain = url.match(
/^(?:\w+:\/\/)?(?:(?:www|wwws|secure)\.)?([^\/]+)\/?(?:.*)/
);
const title = (domain && domain[1]) || 'Saved Password';
entry.setField('Title', title);
entry.setField('URL', url);
entry.setField('UserName', login);
entry.setField('Password', kdbxweb.ProtectedValue.fromString(password));
}
Events.emit('refresh');
this.createResponse();
}
generatePassword() {
this.verifyRequest();
this.createResponse();
const preset =
GeneratorPresets.all.filter((p) => p.default)[0] || GeneratorPresets.defaultPreset;
const password = PasswordGenerator.generate(preset);
const bits = Buffer.from(password, 'utf8').byteLength * 8;
this.resp.Count = 1;
this.resp.Entries = [
{
Login: this.encrypt(bits.toString()),
Name: '',
Password: this.encrypt(password),
StringFields: null,
Uuid: ''
}
];
}
}
}
module.exports.getSettings = function () {
return [
{
name: 'ServerPort',
label: 'Port to listen to (do not change this setting without a special need to do so)',
type: 'text',
maxlength: 5,
placeholder: '19455',
value: '19455'
}
];
};
module.exports.setSettings = function (changes) {
if (changes.ServerPort) {
const port = +changes.ServerPort;
if (port > 1024 && port < 65535) {
serverPort = port;
if (restart) {
restart();
}
}
}
};
module.exports.uninstall = function () {
if (uninstall) {
uninstall();
} else {
clearTimeout(timeout);
}
};