Skip to content

Commit 2f897c9

Browse files
authored
add autofill command (#831)
* add autofill command * address comment
1 parent 393a6b2 commit 2f897c9

File tree

6 files changed

+204
-136
lines changed

6 files changed

+204
-136
lines changed

manifests/manifest-chrome.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"_execute_browser_action": {},
2323
"scan-qr": {
2424
"description": "Scan a QR code"
25+
},
26+
"autofill": {
27+
"description": "Autofill the matched code"
2528
}
2629
},
2730
"options_ui": {

manifests/manifest-edge.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"_execute_browser_action": {},
2323
"scan-qr": {
2424
"description": "Scan a QR code"
25+
},
26+
"autofill": {
27+
"description": "Autofill the matched code"
2528
}
2629
},
2730
"options_ui": {

manifests/manifest-firefox.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
"_execute_browser_action": {},
3535
"scan-qr": {
3636
"description": "Scan a QR code"
37+
},
38+
"autofill": {
39+
"description": "Autofill the matched code"
3740
}
3841
},
3942
"options_ui": {

src/background.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { Encryption } from "./models/encryption";
88
import { EntryStorage, ManagedStorage } from "./models/storage";
99
import { Dropbox, Drive, OneDrive } from "./models/backup";
1010
import * as uuid from "uuid/v4";
11+
import { getSiteName, getMatchedEntries } from "./utils";
12+
import { CodeState } from "./models/otp";
1113

1214
import { getOTPAuthPerLineFromOPTAuthMigration } from "./models/migration";
1315

@@ -560,6 +562,55 @@ chrome.commands.onCommand.addListener(async (command: string) => {
560562
});
561563
break;
562564

565+
case "autofill":
566+
await new Promise(
567+
(resolve: () => void, reject: (reason: Error) => void) => {
568+
try {
569+
return chrome.tabs.executeScript(
570+
{ file: "/dist/content.js" },
571+
() => {
572+
chrome.tabs.insertCSS({ file: "/css/content.css" }, resolve);
573+
}
574+
);
575+
} catch (error) {
576+
console.error(error);
577+
return reject(error);
578+
}
579+
}
580+
);
581+
582+
chrome.tabs.query(
583+
{ active: true, lastFocusedWindow: true },
584+
async (tabs) => {
585+
const tab = tabs[0];
586+
if (!tab || !tab.id) {
587+
return;
588+
}
589+
contentTab = tab;
590+
591+
const siteName = await getSiteName();
592+
const entries = await EntryStorage.get();
593+
const matchedEntries = getMatchedEntries(siteName, entries);
594+
595+
if (matchedEntries && matchedEntries.length === 1) {
596+
const entry = matchedEntries[0];
597+
const encryption = new Encryption(cachedPassphrase);
598+
entry.applyEncryption(encryption);
599+
600+
if (
601+
entry.code !== CodeState.Encrypted &&
602+
entry.code !== CodeState.Invalid
603+
) {
604+
chrome.tabs.sendMessage(tab.id, {
605+
action: "pastecode",
606+
code: matchedEntries[0].code,
607+
});
608+
}
609+
}
610+
}
611+
);
612+
break;
613+
563614
default:
564615
break;
565616
}

src/store/Accounts.ts

Lines changed: 3 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Encryption } from "../models/encryption";
33
import * as CryptoJS from "crypto-js";
44
import { OTPType, OTPAlgorithm } from "../models/otp";
55
import { ActionContext } from "vuex";
6+
import { getSiteName, getMatchedEntriesHash } from "../utils";
67

78
export class Accounts implements Module {
89
async getModule() {
@@ -22,7 +23,7 @@ export class Accounts implements Module {
2223
sectorOffset: 0, // Offset in seconds for animations
2324
second: 0, // Offset in seconds for math
2425
filter: true,
25-
siteName: await this.getSiteName(),
26+
siteName: await getSiteName(),
2627
showSearch: false,
2728
exportData: await EntryStorage.getExport(entries),
2829
exportEncData: await EntryStorage.getExport(entries, true),
@@ -41,7 +42,7 @@ export class Accounts implements Module {
4142
);
4243
},
4344
matchedEntries: (state: AccountsState) => {
44-
return this.matchedEntries(state.siteName, state.entries);
45+
return getMatchedEntriesHash(state.siteName, state.entries);
4546
},
4647
currentlyEncrypted(state: AccountsState) {
4748
for (const entry of state.entries) {
@@ -513,82 +514,6 @@ export class Accounts implements Module {
513514
};
514515
}
515516

516-
private async getSiteName() {
517-
return new Promise((resolve: (value: Array<string | null>) => void) => {
518-
chrome.tabs.query({ active: true, lastFocusedWindow: true }, (tabs) => {
519-
const tab = tabs[0];
520-
const query = new URLSearchParams(
521-
document.location.search.substring(1)
522-
);
523-
524-
let title: string | null;
525-
let url: string | null;
526-
const titleFromQuery = query.get("title");
527-
const urlFromQuery = query.get("url");
528-
529-
if (titleFromQuery && urlFromQuery) {
530-
title = decodeURIComponent(titleFromQuery);
531-
url = decodeURIComponent(urlFromQuery);
532-
} else {
533-
if (!tab) {
534-
return resolve([null, null]);
535-
}
536-
537-
title = tab.title?.replace(/[^a-z0-9]/gi, "").toLowerCase() ?? null;
538-
url = tab.url ?? null;
539-
}
540-
541-
if (!url) {
542-
return resolve([title, null]);
543-
}
544-
545-
const urlParser = new URL(url);
546-
const hostname = urlParser.hostname; // it's always lower case
547-
548-
// try to parse name from hostname
549-
// i.e. hostname is www.example.com
550-
// name should be example
551-
let nameFromDomain = "";
552-
553-
// ip address
554-
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
555-
nameFromDomain = hostname;
556-
}
557-
558-
// local network
559-
if (hostname.indexOf(".") === -1) {
560-
nameFromDomain = hostname;
561-
}
562-
563-
const hostLevelUnits = hostname.split(".");
564-
565-
if (hostLevelUnits.length === 2) {
566-
nameFromDomain = hostLevelUnits[0];
567-
}
568-
569-
// www.example.com
570-
// example.com.cn
571-
if (hostLevelUnits.length > 2) {
572-
// example.com.cn
573-
if (
574-
["com", "net", "org", "edu", "gov", "co"].indexOf(
575-
hostLevelUnits[hostLevelUnits.length - 2]
576-
) !== -1
577-
) {
578-
nameFromDomain = hostLevelUnits[hostLevelUnits.length - 3];
579-
} else {
580-
// www.example.com
581-
nameFromDomain = hostLevelUnits[hostLevelUnits.length - 2];
582-
}
583-
}
584-
585-
nameFromDomain = nameFromDomain.replace(/-/g, "").toLowerCase();
586-
587-
return resolve([title, nameFromDomain, hostname]);
588-
});
589-
});
590-
}
591-
592517
private getCachedPassphrase() {
593518
return new Promise((resolve: (value: string) => void) => {
594519
chrome.runtime.sendMessage(
@@ -604,62 +529,4 @@ export class Accounts implements Module {
604529
const otpEntries = await EntryStorage.get();
605530
return otpEntries;
606531
}
607-
608-
private matchedEntries(
609-
siteName: Array<string | null>,
610-
entries: OTPEntryInterface[]
611-
) {
612-
if (siteName.length < 2) {
613-
return false;
614-
}
615-
616-
const matched = [];
617-
618-
for (const entry of entries) {
619-
if (this.isMatchedEntry(siteName, entry)) {
620-
matched.push(entry.hash);
621-
}
622-
}
623-
624-
return matched;
625-
}
626-
627-
private isMatchedEntry(
628-
siteName: Array<string | null>,
629-
entry: OTPEntryInterface
630-
) {
631-
if (!entry.issuer) {
632-
return false;
633-
}
634-
635-
const issuerHostMatches = entry.issuer.split("::");
636-
const issuer = issuerHostMatches[0]
637-
.replace(/[^0-9a-z]/gi, "")
638-
.toLowerCase();
639-
640-
if (!issuer) {
641-
return false;
642-
}
643-
644-
const siteTitle = siteName[0] || "";
645-
const siteNameFromHost = siteName[1] || "";
646-
const siteHost = siteName[2] || "";
647-
648-
if (issuerHostMatches.length > 1) {
649-
if (siteHost && siteHost.indexOf(issuerHostMatches[1]) !== -1) {
650-
return true;
651-
}
652-
}
653-
// site title should be more detailed
654-
// so we use siteTitle.indexOf(issuer)
655-
if (siteTitle && siteTitle.indexOf(issuer) !== -1) {
656-
return true;
657-
}
658-
659-
if (siteNameFromHost && issuer.indexOf(siteNameFromHost) !== -1) {
660-
return true;
661-
}
662-
663-
return false;
664-
}
665532
}

0 commit comments

Comments
 (0)