UNPKG

namastejs

Version:

A spiritual greeting from your JavaScript code. Because every function deserves a 'Namaste ๐Ÿ™'

239 lines (202 loc) โ€ข 7.24 kB
const readline = require("readline"); const crypto = require("crypto"); const { encrypt, decrypt, hashPassword } = require("./vaultCrypto"); const { readVault, writeVault } = require("./vaultStorage"); let sessionKey = null; let sessionExpiry = 0; const SESSION_DURATION = 60 * 1000; // 60 sec function prompt(question, hide = false) { return new Promise((resolve) => { const rl = readline.createInterface({ input: process.stdin, output: hide ? undefined : process.stdout, terminal: true, }); if (hide) { process.stdout.write(question); process.stdin.setRawMode(true); let input = ""; process.stdin.on("data", (char) => { char = char.toString(); if (char === "\n" || char === "\r" || char === "\u0004") { process.stdin.setRawMode(false); process.stdout.write("\n"); rl.close(); resolve(input); } else if (char === "\u0003") { process.exit(); } else { input += char; } }); } else { rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }); } }); } // Get or set master password async function getMasterKey(vault) { const now = Date.now(); if (sessionKey && now < sessionExpiry) return sessionKey; // If no master password yet, set it if (!vault.__master) { console.log("๐Ÿ” No master password set. Please create one."); const password = await prompt("Enter new master password: ", true); const confirm = await prompt("Confirm master password: ", true); if (password !== confirm) { console.log("โŒ Passwords do not match. Aborting."); process.exit(1); } vault.__master = hashPassword(password); writeVault(vault); const salt = crypto.createHash("sha256").update("namastejs-vault").digest(); sessionKey = crypto.pbkdf2Sync(password, salt, 100000, 32, "sha256"); sessionExpiry = now + SESSION_DURATION; console.log("โœ… Master password set!"); return sessionKey; } // Ask for master password const password = await prompt("๐Ÿ” Enter master password: ", true); if (hashPassword(password) !== vault.__master) { console.log("โŒ Wrong master password."); process.exit(1); } // Derive session key const salt = crypto.createHash("sha256").update("namastejs-vault").digest(); sessionKey = crypto.pbkdf2Sync(password, salt, 100000, 32, "sha256"); sessionExpiry = now + SESSION_DURATION; return sessionKey; } async function vaultCLI(command, keyArg) { const vault = readVault(); switch (command) { case "add": { const key = await prompt("๐Ÿ”‘ Service name (e.g., github): "); const username = await prompt("๐Ÿ‘ค Username: "); const password = await prompt( "๐Ÿ” Password (leave blank to auto-generate): " ); const pwd = password || Math.random().toString(36).slice(-12) + Date.now().toString(36).slice(-4); const masterKey = await getMasterKey(vault); if (vault[key]) { const confirm = await prompt( `โš ๏ธ '${key}' exists. Overwrite? (yes/no): ` ); if (confirm.toLowerCase() !== "yes") { console.log("โŽ Cancelled."); return; } } vault[key] = { username, password: encrypt(pwd, masterKey), }; writeVault(vault); console.log(`โœ… Saved: ${key}`); break; } case "get": { const key = keyArg || (await prompt("๐Ÿ” Service name: ")); if (!vault[key]) return console.log("โŒ Not found."); const masterKey = await getMasterKey(vault); const { username, password } = vault[key]; console.log(`\n๐Ÿ‘ค Username: ${username}`); console.log(`๐Ÿ” Password: ${decrypt(password, masterKey)}\n`); break; } case "list": { const keys = Object.keys(vault).filter((k) => k !== "__master"); if (!keys.length) return console.log("๐Ÿ“ญ Vault is empty."); console.log("๐Ÿ“š Stored services:\n"); keys.forEach((key) => console.log("โ€ข", key)); console.log(); break; } case "delete": { const key = keyArg || (await prompt("๐Ÿ—‘ Service to delete: ")); if (!vault[key]) return console.log("โŒ Not found."); await getMasterKey(vault); const confirm = await prompt( `โš ๏ธ Are you sure you want to delete '${key}'? (yes/no): ` ); if (confirm.toLowerCase() === "yes") { delete vault[key]; writeVault(vault); console.log(`โœ… '${key}' deleted.`); } else { console.log("โŽ Cancelled."); } break; } case "clear": { const confirm1 = await prompt( "โš ๏ธ This will erase everything. Type 'confirm': " ); const confirm2 = await prompt("โš ๏ธ Are you really sure? Type 'yes': "); if (confirm1 === "confirm" && confirm2 === "yes") { await getMasterKey(vault); // Ensure password check writeVault({ __master: vault.__master }); console.log("๐Ÿงจ Vault cleared."); } else { console.log("โŽ Cancelled."); } break; } case "reset-master": { const masterKeyOld = await getMasterKey(vault); // validate current password console.log("๐Ÿ” Set your new master password ๐Ÿ”"); const newPassword = await prompt("Enter new master password: ", true); const confirm = await prompt("Confirm new master password: ", true); if (newPassword !== confirm) { console.log("โŒ Passwords do not match. Aborting."); return; } // Derive new session key const salt = crypto .createHash("sha256") .update("namastejs-vault") .digest(); const masterKeyNew = crypto.pbkdf2Sync( newPassword, salt, 100000, 32, "sha256" ); // Re-encrypt all entries for (const key of Object.keys(vault)) { if (key === "__master") continue; const entry = vault[key]; const plain = decrypt(entry.password, masterKeyOld); entry.password = encrypt(plain, masterKeyNew); } // Update master password hash vault.__master = hashPassword(newPassword); writeVault(vault); // Update session sessionKey = masterKeyNew; sessionExpiry = Date.now() + SESSION_DURATION; console.log("โœ… Master password has been reset successfully!"); break; } default: console.log("๐Ÿ“– Commands:"); console.log("add โ†’ Add new credential"); console.log("get KEY โ†’ Show credential for a service"); console.log("list โ†’ List all saved services"); console.log("delete KEY โ†’ Remove a service"); console.log("clear โ†’ Wipe entire vault\n"); break; } } process.on("SIGINT", () => { sessionKey = null; process.exit(); }); module.exports = { vaultCLI };