Skip to content

Security: DAP visidata helper passes tmpfile_path into os.system shell string and unconditionally imports subprocess #90

@ooloth

Description

@ooloth

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

  1. Trigger the visidata CSV keymap (<leader>dvc) with a normal Python variable selected.
  2. Expect: visidata opens in a new tmux window displaying the CSV data; no shell errors.
  3. 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).
  4. Expect: the tmux command still executes correctly without breaking on the space.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions