05/18 Enhanced codes and functionalities 9 (extension)
This commit is contained in:
+107
-1
@@ -617,6 +617,11 @@ async function fetchAndDecryptVault() {
|
||||
vault_items: itemsForContentScript,
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
// Run health checks in the background — sync metrics first, then HIBP.
|
||||
// Results are sent to the background SW via HEALTH_UPDATE so the toolbar
|
||||
// badge reflects the vault health even when the popup is closed.
|
||||
_runPopupHealthCheck();
|
||||
} catch (err) {
|
||||
console.error("fetchAndDecryptVault:", err);
|
||||
} finally {
|
||||
@@ -624,6 +629,107 @@ async function fetchAndDecryptVault() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Vault health check ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Compute weak/reused counts synchronously, then run HIBP in parallel.
|
||||
* Sends HEALTH_UPDATE to the background service worker so the toolbar
|
||||
* badge is updated even after the popup closes.
|
||||
* Called once per fetchAndDecryptVault() cycle.
|
||||
*/
|
||||
async function _runPopupHealthCheck() {
|
||||
try {
|
||||
const pwItems = _items.filter(
|
||||
(i) => i.item_type === "password" && i.plain?.password,
|
||||
);
|
||||
if (!pwItems.length) {
|
||||
chrome.runtime
|
||||
.sendMessage({ type: "HEALTH_UPDATE", breached: 0, weak: 0, reused: 0 })
|
||||
.catch(() => {});
|
||||
return;
|
||||
}
|
||||
|
||||
// Synchronous metrics.
|
||||
const weak = pwItems.filter((i) => {
|
||||
const p = i.plain.password;
|
||||
if (p.length < 10) return true;
|
||||
return (
|
||||
[/[A-Z]/, /[a-z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((r) => r.test(p))
|
||||
.length < 2
|
||||
);
|
||||
});
|
||||
|
||||
const passCounts = {};
|
||||
pwItems.forEach((i) => {
|
||||
const p = i.plain.password;
|
||||
(passCounts[p] = passCounts[p] || []).push(i);
|
||||
});
|
||||
const reused = Object.values(passCounts)
|
||||
.filter((a) => a.length > 1)
|
||||
.flat();
|
||||
|
||||
// Send sync results immediately so background badge updates quickly.
|
||||
chrome.runtime
|
||||
.sendMessage({
|
||||
type: "HEALTH_UPDATE",
|
||||
breached: 0,
|
||||
weak: weak.length,
|
||||
reused: reused.length,
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
// HIBP — k-anonymity, parallel.
|
||||
const hibpResults = await Promise.all(
|
||||
pwItems.map(async (item) => ({
|
||||
item,
|
||||
count: await _popupCheckHibp(item.plain.password),
|
||||
})),
|
||||
);
|
||||
const breachedCount = hibpResults.filter((r) => r.count > 0).length;
|
||||
|
||||
// Send final results with breach count.
|
||||
chrome.runtime
|
||||
.sendMessage({
|
||||
type: "HEALTH_UPDATE",
|
||||
breached: breachedCount,
|
||||
weak: weak.length,
|
||||
reused: reused.length,
|
||||
})
|
||||
.catch(() => {});
|
||||
} catch (err) {
|
||||
console.error("[PassKeeper] Popup health check failed:", err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HIBP k-anonymity check — identical to the web app's checkHibp().
|
||||
* Only the first 5 hex chars of SHA-1(password) are sent; the full
|
||||
* hash never leaves the browser.
|
||||
*/
|
||||
async function _popupCheckHibp(password) {
|
||||
try {
|
||||
const enc = new TextEncoder().encode(password);
|
||||
const hashBuf = await crypto.subtle.digest("SHA-1", enc);
|
||||
const hashHex = Array.from(new Uint8Array(hashBuf))
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("")
|
||||
.toUpperCase();
|
||||
const prefix = hashHex.slice(0, 5);
|
||||
const suffix = hashHex.slice(5);
|
||||
const res = await fetch(
|
||||
`https://api.pwnedpasswords.com/range/${prefix}`,
|
||||
{ headers: { "Add-Padding": "true" } },
|
||||
);
|
||||
if (!res.ok) return 0;
|
||||
const text = await res.text();
|
||||
const line = text.split("\n").find((l) => l.startsWith(suffix));
|
||||
if (!line) return 0;
|
||||
return parseInt(line.split(":")[1], 10) || 0;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ── TOTP engine (RFC 6238) — pure Web Crypto, no library ─────────────────────
|
||||
|
||||
function _extractTotpSecret(uri) {
|
||||
@@ -1655,4 +1761,4 @@ async function init() {
|
||||
initTabs();
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
Reference in New Issue
Block a user