05/18 Enhanced codes and functionalities 12
This commit is contained in:
+76
-11
@@ -51,6 +51,45 @@ const Vault = (() => {
|
||||
document.addEventListener(e, handler, { passive: true }),
|
||||
);
|
||||
_resetWebIdleTimer();
|
||||
|
||||
// Lock when the tab is hidden for longer than the idle timeout.
|
||||
// This catches screen-lock, minimize, and long tab switches without
|
||||
// requiring the user to wait for the inactivity timer to fire after
|
||||
// returning to the tab.
|
||||
let _hiddenAt = null;
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "hidden") {
|
||||
_hiddenAt = Date.now();
|
||||
// Pause the inactivity timer while hidden — user activity is impossible.
|
||||
if (_webIdleTimer) {
|
||||
clearTimeout(_webIdleTimer);
|
||||
_webIdleTimer = null;
|
||||
}
|
||||
} else {
|
||||
// Tab became visible again.
|
||||
const mins = _getWebIdleMinutes();
|
||||
if (mins > 0 && _hiddenAt !== null) {
|
||||
const hiddenMs = Date.now() - _hiddenAt;
|
||||
if (hiddenMs >= mins * 60_000 && VaultSession.getKey()) {
|
||||
console.log(
|
||||
"[PassKeeper] Locking — tab was hidden for",
|
||||
Math.round(hiddenMs / 1000),
|
||||
"s (idle timeout:",
|
||||
mins,
|
||||
"min)",
|
||||
);
|
||||
VaultSession.clear();
|
||||
showUnlockOverlay();
|
||||
showToast("Vault locked due to inactivity.", "info");
|
||||
_hiddenAt = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_hiddenAt = null;
|
||||
// Resume the inactivity timer now that the user is back.
|
||||
_resetWebIdleTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
let _activeFilter = null;
|
||||
let _sortOrder = "name-asc";
|
||||
@@ -863,16 +902,18 @@ const Vault = (() => {
|
||||
|
||||
const cutoff = Date.now() - 180 * 86400000;
|
||||
// "Old" only penalises passwords that are ALSO weak or reused.
|
||||
// A strong, unique password that hasn't changed in 200 days is fine —
|
||||
// penalising it discourages good password hygiene.
|
||||
// Uses plain.password_changed_at when available (set on create/edit),
|
||||
// falling back to created_at for items saved before this feature.
|
||||
const weakOrReusedIds = new Set([
|
||||
...weak.map((i) => i.id),
|
||||
...reused.map((i) => i.id),
|
||||
]);
|
||||
const old = pwItems.filter(
|
||||
(i) =>
|
||||
new Date(i.created_at).getTime() < cutoff && weakOrReusedIds.has(i.id),
|
||||
);
|
||||
const old = pwItems.filter((i) => {
|
||||
const ageRef = i.plain?.password_changed_at
|
||||
? new Date(i.plain.password_changed_at).getTime()
|
||||
: new Date(i.created_at).getTime();
|
||||
return ageRef < cutoff && weakOrReusedIds.has(i.id);
|
||||
});
|
||||
|
||||
const total = pwItems.length;
|
||||
const score = Math.max(
|
||||
@@ -3495,10 +3536,34 @@ const Vault = (() => {
|
||||
mode === "add"
|
||||
? getVal("field-type")
|
||||
: form.dataset.itemType || "password";
|
||||
const { enc_data, iv } = await Crypto.encryptItem(
|
||||
vaultKey,
|
||||
buildPlainData(itemType),
|
||||
);
|
||||
|
||||
const plainData = buildPlainData(itemType);
|
||||
|
||||
// Track when the password was last changed — stored inside the encrypted
|
||||
// blob so the server never sees it. Used by the security dashboard to
|
||||
// accurately flag old passwords vs. old items that had their password changed.
|
||||
if (itemType === "password") {
|
||||
if (mode === "add") {
|
||||
// New item — set password_changed_at to now.
|
||||
plainData.password_changed_at = new Date().toISOString();
|
||||
} else {
|
||||
// Edit — only update if the password field actually changed.
|
||||
const existingItem = _items.find((i) => i.id === parseInt(itemId));
|
||||
const existingPassword = existingItem?.plain?.password ?? null;
|
||||
const existingChangedAt = existingItem?.plain?.password_changed_at ?? null;
|
||||
if (plainData.password !== existingPassword) {
|
||||
// Password changed — record now.
|
||||
plainData.password_changed_at = new Date().toISOString();
|
||||
} else if (existingChangedAt) {
|
||||
// Password unchanged — preserve the existing timestamp.
|
||||
plainData.password_changed_at = existingChangedAt;
|
||||
}
|
||||
// If no existing timestamp and password unchanged, leave it absent
|
||||
// — the security dashboard will fall back to created_at.
|
||||
}
|
||||
}
|
||||
|
||||
const { enc_data, iv } = await Crypto.encryptItem(vaultKey, plainData);
|
||||
// Encrypt the item name client-side so it is never stored in plaintext.
|
||||
const { enc_name, iv_name } = await Crypto.encryptName(vaultKey, name);
|
||||
const folderVal = getVal("field-folder");
|
||||
@@ -4347,4 +4412,4 @@ const Vault = (() => {
|
||||
}
|
||||
})();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", Vault.init);
|
||||
document.addEventListener("DOMContentLoaded", Vault.init);
|
||||
Reference in New Issue
Block a user