Repository navigation
Expand file tree
/
Copy pathaddKey.js
More file actions
203 lines (165 loc) · 5.9 KB
/
Copy pathaddKey.js
File metadata and controls
203 lines (165 loc) · 5.9 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* global Notify storePathComponents,copyStringToClipboard */
const notify = new Notify(document.querySelector('#notify'));
const addKeyForm = document.getElementById('addKeyForm');
const urlRegexInput = document.getElementById('urlRegex');
const generatePasswordButton = document.getElementById(
'generatePasswordButton'
);
const cancelButton = document.getElementById('cancelButton');
const secretPathSelect = document.getElementById('secretPath');
async function mainLoaded() {
// Load available secret paths
await loadSecretPaths();
// Pre-fill URL regex input with current tab's hostname
await preFillUrlRegexInput();
// Set up event listeners
addKeyForm.addEventListener('submit', handleFormSubmit);
generatePasswordButton.addEventListener('click', handleGeneratePassword);
cancelButton.addEventListener('click', handleCancel);
}
async function loadSecretPaths() {
try {
const secrets = (await browser.storage.sync.get('secrets')).secrets;
if (!secrets || secrets.length === 0) {
notify.error(
'No secret paths configured. Please go to Options and configure at least one secret path first.'
);
return;
}
// Populate the dropdown
secrets.forEach((secret) => {
const option = document.createElement('option');
option.value = secret;
option.textContent = secret;
secretPathSelect.appendChild(option);
});
// If there's only one path, auto-select it
if (secrets.length === 1) {
secretPathSelect.value = secrets[0];
}
} catch (err) {
notify.error(`Error loading secret paths: ${err.message}`);
}
}
async function preFillUrlRegexInput() {
let currentUrl;
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
for (let tabIndex = 0; tabIndex < tabs.length; tabIndex++) {
const tab = tabs[tabIndex];
if (tab.url) {
currentUrl = tab.url;
break;
}
}
if (currentUrl) {
// extract hostname from URL and remove "www." prefix if present
const hostnameMatch = currentUrl.match(/^https?:\/\/(www\.)?([^/]+)/i);
if (hostnameMatch) {
urlRegexInput.value = hostnameMatch[2];
}
}
}
async function handleFormSubmit(event) {
event.preventDefault();
// Clear any existing messages
notify.clear();
// Scroll to top to show messages
window.scrollTo({ top: 0, behavior: 'smooth' });
const secretPath = document.getElementById('secretPath').value.trim();
const urlRegex = document.getElementById('urlRegex').value.trim();
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value.trim();
const title = document.getElementById('title').value.trim();
// Validate inputs
if (!secretPath) {
notify.error('Please select a secret path.');
return;
}
if (!urlRegex || !username || !password) {
notify.error('URL pattern, username, and password are required.');
return;
}
// Check if URL pattern starts with http:// or https://
if (urlRegex.startsWith('http://') || urlRegex.startsWith('https://')) {
notify.error(
'URL pattern cannot start with http:// or https://. Please use a regex pattern instead (e.g., ^https://example\\.com.*).'
);
return;
}
// Validate regex pattern
try {
new RegExp(urlRegex);
} catch {
notify.error('Invalid URL regex pattern. Please check your pattern.');
return;
}
// Double-encode the URL regex to use as the key name in Vault
// First encoding: a/b -> a%2Fb
// Second encoding: a%2Fb -> a%252Fb
// Vault will decode once: a%252Fb -> a%2Fb (stored in Vault)
const encodedKeyName = encodeURIComponent(encodeURIComponent(urlRegex));
try {
// Get Vault credentials
const vaultToken = (await browser.storage.local.get('vaultToken'))
.vaultToken;
const vaultServerAddress = (await browser.storage.sync.get('vaultAddress'))
.vaultAddress;
const storePath = (await browser.storage.sync.get('storePath')).storePath;
if (!vaultToken || !vaultServerAddress) {
notify.error(
'Not authenticated with Vault. Please go to Options and login first.'
);
return;
}
const storeComponents = storePathComponents(storePath);
// Create the data object to save
const keyData = {
username: username,
password: password,
};
if (title) {
keyData.title = title;
}
// Save to Vault using the encoded key name
// The key name is already encoded earlier in the function
const vaultUrl = `${vaultServerAddress}/v1/${storeComponents.root}/data/${storeComponents.subPath}/${secretPath}${encodedKeyName}`;
const response = await fetch(vaultUrl, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({ data: keyData }),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to save to Vault: ${errorText}`);
}
notify.success('Key saved successfully to Vault!', { time: 2000 });
// Clear form
addKeyForm.reset();
// Redirect back to popup after a short delay
setTimeout(() => {
window.location.href = '/popup.html';
}, 1500);
} catch (err) {
notify.error(`Error saving key: ${err.message}`);
}
}
function handleGeneratePassword() {
const length = 20;
const charset =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-=';
let password = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
password += charset[randomIndex];
}
document.getElementById('password').value = password;
copyStringToClipboard(password);
notify.success('Generated password copied to clipboard!', { time: 5000 });
}
function handleCancel() {
window.location.href = '/popup.html';
}
document.addEventListener('DOMContentLoaded', mainLoaded, false);