forked from Dmitry1987/vault-chrome-extension
-
Notifications
You must be signed in to change notification settings - Fork 38
/
background.js
128 lines (107 loc) · 3.39 KB
/
background.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* eslint-disable no-console */
/* global chrome */
function storageGetterProvider(storageType) {
return function (key, defaultValue) {
return new Promise(function (resolve, reject) {
try {
chrome.storage[storageType].get([key], function (result) {
const value = result[key] || defaultValue || null;
resolve(value);
});
} catch (error) {
reject(error);
}
});
};
}
const storage = {
local: {
get: storageGetterProvider('local'),
},
sync: {
get: storageGetterProvider('sync'),
},
};
class Vault {
constructor(token, address) {
this.token = token;
this.address = address;
this.base = `${this.address}/v1`;
}
async request(method, endpoint) {
const res = await fetch(this.base + endpoint, {
method: method.toUpperCase(),
headers: {
'X-Vault-Token': this.token,
'Content-Type': 'application/json',
},
});
if (!res.ok) throw new Error(`Error calling: ${method.toUpperCase()} ${this.base}${endpoint} -> HTTP ${res.status} - ${res.statusText}`);
const json = await res.json();
return json;
}
list(endpoint) {
return this.request('LIST', endpoint);
}
get(endpoint) {
return this.request('GET', endpoint);
}
}
function storePathComponents(storePath) {
let path = 'secret/vaultPass';
if (storePath && storePath.length > 0) {
path = storePath;
}
const pathComponents = path.split('/');
const storeRoot = pathComponents[0];
const storeSubPath = pathComponents.length > 0 ? pathComponents.slice(1).join('/') : '';
return {
root: storeRoot,
subPath: storeSubPath
};
}
function clearHostname(hostname) {
const match = hostname.match(/^(www\.)?(.*)$/);
return match[2] ? match[2] : match[1];
}
async function autoFillSecrets(message, sender) {
const vaultToken = await storage.local.get('vaultToken');
const vaultAddress = await storage.sync.get('vaultAddress');
const secretList = await storage.sync.get('secrets', []);
const storePath = await storage.sync.get('storePath');
const storeComponents = storePathComponents(storePath);
if (!vaultAddress || !vaultAddress) return;
const url = new URL(sender.tab.url);
const hostname = clearHostname(url.hostname);
const vault = new Vault(vaultToken, vaultAddress);
let loginCount = 0;
for (const secret of secretList) {
const secretKeys = await vault.list(`/${storeComponents.root}/metadata/${storeComponents.subPath}/${secret}`);
for (const key of secretKeys.data.keys) {
const pattern = new RegExp(key);
const patternMatches = pattern.test(hostname);
// If the key is an exact match to the current hostname --> autofill
if (hostname === clearHostname(key)) {
const credentials = await vault.get(
`/${storeComponents.root}/data/${storeComponents.subPath}/${secret}${key}`
);
chrome.tabs.sendMessage(sender.tab.id, {
message: 'fill_creds',
username: credentials.data.data.username,
password: credentials.data.data.password,
});
}
if (patternMatches) {
loginCount++;
}
}
}
if (loginCount > 0) {
chrome.action.setBadgeText({ text: '*', tabId: sender.tab.id });
}
}
chrome.runtime.onMessage.addListener(function (message, sender) {
if (message.type === 'auto_fill_secrets') {
autoFillSecrets(message, sender).catch(console.error);
}
});