Skip to content

Commit d4265cb

Browse files
ralyodioclaude
andcommitted
web settings: mask vault passphrase prompt with a native <dialog>
The tronbrowser.dev/settings decrypt path used window.prompt() (always plaintext). Replace with a native <dialog> + real type=password input (Enter=Unlock, Esc=cancel), styled to match the settings page. Mirrors the extension options-page fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 18c80ab commit d4265cb

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

apps/web/public/settings.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
.row{display:flex;gap:10px;align-items:center;margin-top:18px}
2424
button{background:var(--cyan);color:#04060c;border:0;border-radius:8px;padding:10px 18px;font:inherit;font-weight:700;cursor:pointer}
2525
.saved{color:var(--cyan)} .err{color:#ff8a9b}
26+
/* Native <dialog> for the masked vault passphrase prompt. */
27+
.pw-dialog{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:12px;padding:20px;max-width:420px;width:92%;font:inherit}
28+
.pw-dialog::backdrop{background:rgba(0,0,0,.6)}
29+
.pw-dialog .pw-msg{margin:0 0 12px}
30+
.pw-dialog input{width:100%;padding:10px;background:#04060c;color:var(--fg);border:1px solid var(--line);border-radius:8px;font:inherit}
31+
.pw-dialog menu{display:flex;gap:10px;justify-content:flex-end;margin:14px 0 0;padding:0}
32+
.pw-dialog button{border-radius:8px;padding:8px 14px;font:inherit;cursor:pointer;background:transparent;color:var(--fg);border:1px solid var(--line)}
33+
.pw-dialog button.primary{background:var(--cyan);color:#04060c;border:0;font-weight:700}
2634
a{color:var(--cyan)}
2735
</style></head>
2836
<body>

apps/web/public/settings.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,40 @@ async function fetchModels() {
143143
$('default').addEventListener('change', () => { $('model').value = ''; fetchModels(); });
144144
$('providers').addEventListener('input', (e) => { if (e.target.matches(`[data-key="${$('default').value}"]`)) fetchModels(); });
145145

146+
// Masked passphrase prompt — native <dialog> (prompt() always shows plaintext).
147+
function promptPassword(message) {
148+
return new Promise((resolve) => {
149+
const dlg = document.createElement('dialog');
150+
dlg.className = 'pw-dialog';
151+
dlg.innerHTML =
152+
`<form method="dialog">
153+
<p class="pw-msg"></p>
154+
<input type="password" autocomplete="current-password" spellcheck="false" />
155+
<menu>
156+
<button type="button" data-cancel>Cancel</button>
157+
<button value="ok" class="primary">Unlock</button>
158+
</menu>
159+
</form>`;
160+
dlg.querySelector('.pw-msg').textContent = message; // text node — no HTML injection
161+
const input = dlg.querySelector('input');
162+
dlg.querySelector('[data-cancel]').addEventListener('click', () => dlg.close(''));
163+
dlg.addEventListener('close', () => {
164+
const v = dlg.returnValue === 'ok' ? input.value : '';
165+
dlg.remove();
166+
resolve(v);
167+
});
168+
document.body.appendChild(dlg);
169+
dlg.showModal();
170+
input.focus();
171+
});
172+
}
173+
146174
async function load() {
147175
if (!(await requireAuth())) return;
148176
settings = await api('/api/settings').then((r) => (r.ok ? r.json() : {})).catch(() => ({}));
149177
// Decrypt the E2E vault into editable keys if present.
150178
if (settings.aiVault && !settings.aiProviders) {
151-
let pass = sessionStorage.getItem('tb_vault_pass') || prompt('Enter your vault passphrase to decrypt your AI keys:') || '';
179+
let pass = sessionStorage.getItem('tb_vault_pass') || await promptPassword('Enter your vault passphrase to decrypt your AI keys:') || '';
152180
if (pass) {
153181
try { settings.aiProviders = await decryptVault(pass, settings.aiVault); sessionStorage.setItem('tb_vault_pass', pass); }
154182
catch { $('msg').textContent = 'wrong vault passphrase'; $('msg').className = 'err'; }

0 commit comments

Comments
 (0)