Add scoped human-unlocked vault agent autofill
This commit is contained in:
1 parent
61fcd8336f
commit
e8a654fb38
23 files changed
+383
-29
No files matched your search
+11
-3
@@ -49,11 +49,19 @@ function generatePassword(length = 32) {
|
||||
}
|
||||
function createVault({ filePath, protect, unprotect, idleSeconds = 300 }) {
|
||||
if (typeof filePath !== 'string' || !path.isAbsolute(filePath) || typeof protect !== 'function' || typeof unprotect !== 'function' || !Number.isInteger(idleSeconds) || idleSeconds < 60 || idleSeconds > 900) fail();
|
||||
let key = null, entries = [], envelope = null, timer = null;
|
||||
let key = null, entries = [], envelope = null, timer = null, accessGeneration = 0;
|
||||
// Process-local capability epoch. Never persisted or included in public metadata.
|
||||
// create/unlock begin with lock, so even reopening the same vault invalidates refs.
|
||||
function invalidateAccess() {
|
||||
if (accessGeneration >= Number.MAX_SAFE_INTEGER) { lock(); fail(); }
|
||||
accessGeneration += 1;
|
||||
}
|
||||
function lock() {
|
||||
clearTimeout(timer); timer = null; wipe(key); key = null;
|
||||
for (const entry of entries) { entry.password = ''; entry.username = ''; entry.label = ''; entry.origin = ''; }
|
||||
entries = []; envelope = null;
|
||||
if (accessGeneration >= Number.MAX_SAFE_INTEGER) fail();
|
||||
accessGeneration += 1;
|
||||
}
|
||||
function touch() { clearTimeout(timer); timer = setTimeout(lock, idleSeconds * 1000); timer.unref(); }
|
||||
function requireUnlocked(refreshIdle = true) { if (!key) fail(); if (refreshIdle) touch(); }
|
||||
@@ -136,7 +144,7 @@ function createVault({ filePath, protect, unprotect, idleSeconds = 300 }) {
|
||||
function status() { return { exists: fs.existsSync(filePath), locked: !key, idleSeconds, algorithm: 'AES-256-GCM / ML-KEM-1024 / scrypt / Windows DPAPI' }; }
|
||||
function list() { requireUnlocked(false); return entries.map(({ id, origin, username, label }) => ({ id, origin, username, label })); }
|
||||
function mutate(action) {
|
||||
requireUnlocked(); const previous = entries.map(entry => ({ ...entry }));
|
||||
requireUnlocked(); invalidateAccess(); const previous = entries.map(entry => ({ ...entry }));
|
||||
try { const result = action(); persist(); return result; } catch { entries = previous; fail(); }
|
||||
}
|
||||
function upsert(entry) {
|
||||
@@ -156,6 +164,6 @@ function createVault({ filePath, protect, unprotect, idleSeconds = 300 }) {
|
||||
if (!entry || entry.origin !== expected) fail();
|
||||
return { username: entry.username, password: entry.password };
|
||||
}
|
||||
return { create, unlock, lock, status, list, upsert, delete: remove, credential, generatePassword };
|
||||
return { create, unlock, lock, status, list, upsert, delete: remove, credential, generatePassword, accessGeneration: () => accessGeneration };
|
||||
}
|
||||
module.exports = { createVault, generatePassword };
|
||||
Reference in new issue
Block a user