diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..7247f35b --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,7 @@ +## 2024-08-29 - Command Injection in Native Directory Picker + +**Vulnerability:** A command injection vulnerability existed in `pysus/web/pages/1_client.py` within the `_native_dir_picker` function. When opening a directory picker on Windows or macOS (Darwin), user-provided strings (`title` and `initialdir`) were directly formatted into PowerShell and AppleScript strings without any escaping. + +**Learning:** When using Python's `subprocess.run` to execute scripts that dynamically construct logic via format strings (f-strings) inside platforms like PowerShell or `osascript`, quoting strings within those formats is insufficient if the external platform is processing the final string payload. Even though `shell=True` was not used, the script engines evaluated the unescaped inputs as code. + +**Prevention:** Always escape data embedded inside dynamically generated scripts being passed to an interpreter like PowerShell (`'` to `''`) or AppleScript (`\` to `\\`, `"` to `\"`), or pass the data as parameters/arguments to the script rather than embedding them directly in the script source. diff --git a/pysus/web/pages/1_client.py b/pysus/web/pages/1_client.py index fd5cfdb5..454bb283 100644 --- a/pysus/web/pages/1_client.py +++ b/pysus/web/pages/1_client.py @@ -726,11 +726,14 @@ def _native_dir_picker(title: str, initialdir: str) -> str: continue elif system == "Windows": + # Escape single quotes for PowerShell string literals + title_esc = title.replace("'", "''") + initialdir_esc = initialdir.replace("'", "''") ps = f""" Add-Type -AssemblyName System.Windows.Forms $f = New-Object System.Windows.Forms.FolderBrowserDialog -$f.Description = '{title}' -$f.SelectedPath = '{initialdir}' +$f.Description = '{title_esc}' +$f.SelectedPath = '{initialdir_esc}' $f.ShowDialog() | Out-Null $f.SelectedPath """ @@ -742,10 +745,13 @@ def _native_dir_picker(title: str, initialdir: str) -> str: return r.stdout.strip() elif system == "Darwin": + # Escape backslashes and double quotes for AppleScript string literals + title_esc = title.replace("\\", "\\\\").replace('"', '\\"') + initialdir_esc = initialdir.replace("\\", "\\\\").replace('"', '\\"') prompt_line = ( 'set f to choose folder with prompt "{}"' ' default location POSIX file "{}"' - ).format(title, initialdir) + ).format(title_esc, initialdir_esc) applescript = ( f'tell application "System Events"\n' f" activate\n"