Summary
The built-in Debugger Console in ui/app.js evaluates user-typed expressions using a bare eval() call with no sandboxing, scope restriction, or input validation. This gives any expression typed into the console unrestricted read/write access to the entire application's global scope, including state, fetch, DOM APIs, and all runtime variables.
Location
File: ui/app.js
Function: DebuggerConsole.evaluate()
function evaluate(expr) {
if (!expr.trim()) return;
// ...
try {
const result = eval(expr); // ← unrestricted global eval
// ...
} catch (e) {
addEntry('error', e.message, 'eval');
}
}
Risk
- Any expression can mutate
state.unlockedScripts, bypassing the password lock system entirely.
- A user could call
fetch() to exfiltrate data or trigger unintended API calls to the Flask backend.
- Pasting untrusted code snippets (from Stack Overflow, etc.) into the console runs with full app privileges.
- There is no warning shown to the user about the console's capabilities.
Proposed Fix
- Add a one-time visible warning entry in the debugger body on first open, clearly stating the console has full JS access.
- Replace the bare
eval() with a Function() constructor scoped to a whitelist object, restricting access to only safe read-only references like state:
const sandboxed = new Function('state', `"use strict"; return (${expr})`);
const result = sandboxed(state);
- Block expressions containing dangerous patterns (
fetch, XMLHttpRequest, document.cookie, localStorage) with a warning message rather than executing them.
Steps to Reproduce
- Open DevShell and click the Debugger Console toggle.
- Type
state.unlockedScripts['docker/list_containers.sh'] = 'anypassword' and press Enter.
- Observe that a locked script is now unlocked in the session without entering the correct password.
Environment
- File:
ui/app.js
- Affects: All platforms (Electron desktop + web mode)
- Labels suggested:
type:bug, security, level:advanced
Summary
The built-in Debugger Console in
ui/app.jsevaluates user-typed expressions using a bareeval()call with no sandboxing, scope restriction, or input validation. This gives any expression typed into the console unrestricted read/write access to the entire application's global scope, including state, fetch, DOM APIs, and all runtime variables.Location
File:
ui/app.jsFunction:
DebuggerConsole.evaluate()Risk
state.unlockedScripts, bypassing the password lock system entirely.fetch()to exfiltrate data or trigger unintended API calls to the Flask backend.Proposed Fix
eval()with aFunction()constructor scoped to a whitelist object, restricting access to only safe read-only references like state:fetch,XMLHttpRequest,document.cookie,localStorage) with a warning message rather than executing them.Steps to Reproduce
state.unlockedScripts['docker/list_containers.sh'] = 'anypassword'and press Enter.Environment
ui/app.jstype:bug,security,level:advanced