Why
The visidata DAP helper embeds a temp-file path directly into an os.system shell string without quoting, so a temp path containing shell metacharacters (or a race-condition replacement) executes arbitrary shell commands; separately, subprocess is imported unconditionally before the user-triggered block, widening the attack surface available to any injection that follows.
Current state
In lua/config/plugins/specs/nvim-dap.lua line 14, dap.repl.execute('import subprocess') runs every time the helper is invoked, regardless of whether subprocess is ever used.
At lines 22–23, the tmux command is constructed as:
os.system(f"tmux new-window 'sh -c \"vd {tmpfile_path}; rm {tmpfile_path}\"'")
tmpfile_path comes from tempfile.NamedTemporaryFile but is embedded bare — no quoting, no escaping — into the shell string passed to os.system. A path that contains a space, backtick, $(), or other shell metacharacter would alter the shell command structure. The subprocess import on line 14 makes subprocess.run, subprocess.Popen, etc. available to any Python injected after it.
Ideal state
subprocess is imported only inside the branch that actually uses it, not at the top of every REPL execution.
- The tmux command uses
subprocess.run with an argument list instead of os.system with a shell-interpolated string, eliminating the shell-injection surface entirely.
- If
os.system is retained, tmpfile_path is shell-quoted (e.g. via Python's shlex.quote) before being embedded in the command string.
Out of scope
Starting points
lua/config/plugins/specs/nvim-dap.lua — lines 14 and 22–23, the import subprocess call and the os.system invocation.
- Python
shlex.quote docs — for safe shell-string escaping if os.system is kept.
- Python
subprocess.run docs — for the array-form alternative that avoids shell interpolation entirely.
QA plan
- Trigger the visidata CSV keymap (
<leader>dvc) with a normal Python variable selected.
- Expect: visidata opens in a new tmux window displaying the CSV data; no shell errors.
- Trigger the keymap again with a variable selected while the temp directory is set to a path containing a space (e.g. rename
/tmp symlink in a test env).
- Expect: the tmux command still executes correctly without breaking on the space.
- Confirm
subprocess does not appear in REPL state before the keymap is triggered (i.e. a fresh REPL does not have subprocess pre-imported from a previous unrelated invocation).
Done when
The tmux command in the visidata DAP helper cannot be altered by shell metacharacters in tmpfile_path, and subprocess is not imported unless the code path that uses it is actually reached.
Why
The visidata DAP helper embeds a temp-file path directly into an
os.systemshell string without quoting, so a temp path containing shell metacharacters (or a race-condition replacement) executes arbitrary shell commands; separately,subprocessis imported unconditionally before the user-triggered block, widening the attack surface available to any injection that follows.Current state
In
lua/config/plugins/specs/nvim-dap.lualine 14,dap.repl.execute('import subprocess')runs every time the helper is invoked, regardless of whethersubprocessis ever used.At lines 22–23, the tmux command is constructed as:
tmpfile_pathcomes fromtempfile.NamedTemporaryFilebut is embedded bare — no quoting, no escaping — into the shell string passed toos.system. A path that contains a space, backtick,$(), or other shell metacharacter would alter the shell command structure. Thesubprocessimport on line 14 makessubprocess.run,subprocess.Popen, etc. available to any Python injected after it.Ideal state
subprocessis imported only inside the branch that actually uses it, not at the top of every REPL execution.subprocess.runwith an argument list instead ofos.systemwith a shell-interpolated string, eliminating the shell-injection surface entirely.os.systemis retained,tmpfile_pathis shell-quoted (e.g. via Python'sshlex.quote) before being embedded in the command string.Out of scope
selectioninjection issue tracked in Security: DAP REPL executes raw buffer selection as Python code #31.Starting points
lua/config/plugins/specs/nvim-dap.lua— lines 14 and 22–23, theimport subprocesscall and theos.systeminvocation.shlex.quotedocs — for safe shell-string escaping ifos.systemis kept.subprocess.rundocs — for the array-form alternative that avoids shell interpolation entirely.QA plan
<leader>dvc) with a normal Python variable selected./tmpsymlink in a test env).subprocessdoes not appear in REPL state before the keymap is triggered (i.e. a fresh REPL does not havesubprocesspre-imported from a previous unrelated invocation).Done when
The tmux command in the visidata DAP helper cannot be altered by shell metacharacters in
tmpfile_path, andsubprocessis not imported unless the code path that uses it is actually reached.