From d6f44ef4461c38e4be3205e539d64c6ca868f867 Mon Sep 17 00:00:00 2001 From: ArshVermaGit Date: Thu, 28 May 2026 18:18:35 +0530 Subject: [PATCH] Fix (Security): Secure credential transmission and storage via frontend hashing --- locks.json | 1 - ui/app.js | 51 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 15 deletions(-) delete mode 100644 locks.json diff --git a/locks.json b/locks.json deleted file mode 100644 index 9e26dfe..0000000 --- a/locks.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/ui/app.js b/ui/app.js index c4fb694..d51f7a1 100644 --- a/ui/app.js +++ b/ui/app.js @@ -75,24 +75,43 @@ let state = { reliabilityDiagnostics: null, }; -const unlockCredentials = new Map(); +// ─── Cryptography & Credentials ───────────────────────────── + +async function hashPassword(plainText) { + if (!plainText) return ''; + const encoder = new TextEncoder(); + const data = encoder.encode(plainText); + const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); +} + +const CredentialStore = (() => { + const store = new Map(); + return { + set: (path, passHash) => store.set(path, passHash), + get: (path) => store.get(path) || '', + delete: (path) => store.delete(path), + clear: () => store.clear() + }; +})(); function isScriptUnlocked(relPath) { return !!state.unlockedScripts[relPath]; } function getUnlockPassword(relPath) { - return unlockCredentials.get(relPath) || ''; + return CredentialStore.get(relPath); } -function markScriptUnlocked(relPath, password) { +function markScriptUnlocked(relPath, passHash) { state.unlockedScripts[relPath] = true; - if (password) unlockCredentials.set(relPath, password); + if (passHash) CredentialStore.set(relPath, passHash); } function clearScriptUnlock(relPath) { delete state.unlockedScripts[relPath]; - unlockCredentials.delete(relPath); + CredentialStore.delete(relPath); } function serializeUnlockedScripts() { @@ -108,7 +127,7 @@ function restoreUnlockedScripts(raw = {}) { for (const [path, val] of Object.entries(raw)) { if (val) state.unlockedScripts[path] = true; } - unlockCredentials.clear(); + CredentialStore.clear(); } const RUN_BUTTON_IDLE_HTML = `Run`; @@ -2960,11 +2979,12 @@ async function selectScript(relPath) { unlockBtn.parentNode.replaceChild(newUnlockBtn, unlockBtn); const unlockAction = async () => { - const content = await fetchScriptContent(relPath, passInput.value); + const passHash = await hashPassword(passInput.value); + const content = await fetchScriptContent(relPath, passHash); if (content.locked) { notify('Incorrect password.', 'error'); } else { - markScriptUnlocked(relPath, passInput.value); + markScriptUnlocked(relPath, passHash); passInput.value = ''; selectScript(relPath); } @@ -3607,18 +3627,21 @@ function bindEvents() { if (sc && sc.locked) isLocked = true; } - let oldPass = '', newPass = ''; + let oldPassPlain = '', newPassPlain = ''; if (isLocked) { - oldPass = document.getElementById('lock-current-pass').value; - newPass = ''; // meaning remove lock + oldPassPlain = document.getElementById('lock-current-pass').value; + newPassPlain = ''; // meaning remove lock } else { - oldPass = ''; - newPass = document.getElementById('lock-new-pass').value; - if (!newPass) { + oldPassPlain = ''; + newPassPlain = document.getElementById('lock-new-pass').value; + if (!newPassPlain) { return notify('Password cannot be empty when setting a lock.', 'warning'); } } + const oldPass = await hashPassword(oldPassPlain); + const newPass = await hashPassword(newPassPlain); + const success = await manageLock(state.activeScript, oldPass, newPass); if (success) { notify(