Skip to content

Commit 689e5ae

Browse files
committed
Clear credentials from clipboard after 60 seconds
The clipboard is cleared 60 seconds after a username or password is copied, if the content of the clipboard is still equal to the copied string.
1 parent 064fe57 commit 689e5ae

File tree

5 files changed

+76
-14
lines changed

5 files changed

+76
-14
lines changed

README.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,17 @@ If you want to intentionally disable phishing attack protection and search the e
139139
140140
Note: If the cursor is located in the search input field, every shortcut that works on the selected entry will be applied on the first entry in the popup list.
141141
142-
| Shortcut | Action |
143-
| ---------------------------------------------------- | ------------------------------------------------ |
144-
| <kbd>Ctrl+Shift+L</kbd> | Open Browserpass popup |
145-
| <kbd>Ctrl+Shift+F</kbd> | Fill the form with the best matching credentials |
146-
| <kbd>Enter</kbd> | Submit form with currently selected credentials |
147-
| Arrow keys and <kbd>Tab</kbd> / <kbd>Shift+Tab</kbd> | Navigate popup list |
148-
| <kbd>Ctrl+C</kbd> | Copy password to clipboard |
149-
| <kbd>Ctrl+Shift+C</kbd> | Copy username to clipboard |
150-
| <kbd>Ctrl+G</kbd> | Open URL in the current tab |
151-
| <kbd>Ctrl+Shift+G</kbd> | Open URL in the new tab |
152-
| <kbd>Backspace</kbd> (with no search text entered) | Search passwords in the entire password store |
142+
| Shortcut | Action |
143+
| ---------------------------------------------------- | ----------------------------------------------------- |
144+
| <kbd>Ctrl+Shift+L</kbd> | Open Browserpass popup |
145+
| <kbd>Ctrl+Shift+F</kbd> | Fill the form with the best matching credentials |
146+
| <kbd>Enter</kbd> | Submit form with currently selected credentials |
147+
| Arrow keys and <kbd>Tab</kbd> / <kbd>Shift+Tab</kbd> | Navigate popup list |
148+
| <kbd>Ctrl+C</kbd> | Copy password to clipboard (will clear in 60 seconds) |
149+
| <kbd>Ctrl+Shift+C</kbd> | Copy username to clipboard (will clear in 60 seconds) |
150+
| <kbd>Ctrl+G</kbd> | Open URL in the current tab |
151+
| <kbd>Ctrl+Shift+G</kbd> | Open URL in the new tab |
152+
| <kbd>Backspace</kbd> (with no search text entered) | Search passwords in the entire password store |
153153
154154
### Password matching and sorting
155155
@@ -271,7 +271,9 @@ Browserpass extension requests the following permissions:
271271
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
272272
| `debugger` | Only used for "auto-submit" functionality: if all attepts to locate a "Submit" button failed, Browserpass will put focus inside the login form and issue an <kbd>Enter</kbd> keypress. This is only available in Chromium-based browsers, and sadly this permission [cannot be specified as optional](https://developer.chrome.com/apps/permissions) |
273273
| `activeTab` | To get URL of the current tab, used for example to determine which passwords to show you by default in the popup |
274+
| `alarms` | To set a timer for clearing the clipboard 60 seconds after credentials are copied |
274275
| `tabs` | To get URL of a given tab, used for example to set count of the matching passwords for a given tab |
276+
| `clipboardRead` | To ensure only copied credentials and not other content is cleared from the clipboard after 60 seconds |
275277
| `clipboardWrite` | For "Copy password" and "Copy username" functionality |
276278
| `nativeMessaging` | To allow communication with the native app |
277279
| `notifications` | To show browser notifications on install or update |

src/background.js

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ var defaultSettings = {
2828

2929
var authListeners = {};
3030

31+
// the last text copied to the clipboard is stored here in order to be cleared after 60 seconds
32+
let lastCopiedText = null;
33+
3134
chrome.browserAction.setBadgeBackgroundColor({
3235
color: "#666"
3336
});
@@ -80,6 +83,16 @@ chrome.commands.onCommand.addListener(async command => {
8083
}
8184
});
8285

86+
// handle fired alarms
87+
chrome.alarms.onAlarm.addListener(alarm => {
88+
if (alarm.name === "clearClipboard") {
89+
if (readFromClipboard() === lastCopiedText) {
90+
copyToClipboard("", false);
91+
}
92+
lastCopiedText = null;
93+
}
94+
});
95+
8396
chrome.runtime.onInstalled.addListener(onExtensionInstalled);
8497

8598
//----------------------------------- Function definitions ----------------------------------//
@@ -127,14 +140,15 @@ async function updateMatchingPasswordsCount(tabId) {
127140
}
128141

129142
/**
130-
* Copy text to clipboard
143+
* Copy text to clipboard and optionally clear it from the clipboard after one minute
131144
*
132-
* @since 3.0.0
145+
* @since 3.2.0
133146
*
134147
* @param string text Text to copy
148+
* @param boolean clear Whether to clear the clipboard after one minute
135149
* @return void
136150
*/
137-
function copyToClipboard(text) {
151+
function copyToClipboard(text, clear = true) {
138152
document.addEventListener(
139153
"copy",
140154
function(e) {
@@ -144,6 +158,42 @@ function copyToClipboard(text) {
144158
{ once: true }
145159
);
146160
document.execCommand("copy");
161+
162+
if (clear) {
163+
// the clipboard is cleared only when its contents are still the same, hence we need the
164+
// clipboardRead permission
165+
chrome.permissions.contains(
166+
{
167+
permissions: ["clipboardRead"]
168+
},
169+
granted => {
170+
if (granted) {
171+
lastCopiedText = text;
172+
chrome.alarms.create("clearClipboard", { delayInMinutes: 1 });
173+
}
174+
}
175+
);
176+
}
177+
}
178+
179+
/**
180+
* Read plain text from clipboard
181+
*
182+
* @since 3.2.0
183+
*
184+
* @return string The current plaintext content of the clipboard
185+
*/
186+
function readFromClipboard() {
187+
const ta = document.createElement("textarea");
188+
// these lines are carefully crafted to make paste work in both Chrome and Firefox
189+
ta.contentEditable = true;
190+
ta.textContent = "";
191+
document.body.appendChild(ta);
192+
ta.select();
193+
document.execCommand("paste");
194+
const content = ta.value;
195+
document.body.removeChild(ta);
196+
return content;
147197
}
148198

149199
/**

src/manifest-chromium.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"permissions": [
3030
"debugger",
3131
"activeTab",
32+
"alarms",
3233
"tabs",
3334
"clipboardWrite",
3435
"nativeMessaging",
@@ -38,6 +39,7 @@
3839
"http://*/*",
3940
"https://*/*"
4041
],
42+
"optional_permissions": ["clipboardRead"],
4143
"commands": {
4244
"_execute_browser_action": {
4345
"suggested_key": {

src/manifest-firefox.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
},
2727
"permissions": [
2828
"activeTab",
29+
"alarms",
2930
"tabs",
3031
"clipboardWrite",
3132
"nativeMessaging",
@@ -35,6 +36,7 @@
3536
"http://*/*",
3637
"https://*/*"
3738
],
39+
"optional_permissions": ["clipboardRead"],
3840
"applications": {
3941
"gecko": {
4042

src/popup/popup.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,15 @@ async function withLogin(action) {
9191
handleError("Launching URL in a new tab...", "notice");
9292
break;
9393
case "copyPassword":
94+
await chrome.permissions.request({
95+
permissions: ["clipboardRead"]
96+
});
9497
handleError("Copying password to clipboard...", "notice");
9598
break;
9699
case "copyUsername":
100+
await chrome.permissions.request({
101+
permissions: ["clipboardRead"]
102+
});
97103
handleError("Copying username to clipboard...", "notice");
98104
break;
99105
default:

0 commit comments

Comments
 (0)