Oh My Posh: Arbitrary Command Execution via Template Injection in Path Segment
Oh My Posh re-renders the current working directory path through Go's text/template engine, which exposes a cmd() function, so navigating into a directory with a crafted name executes arbitrary OS…

The problem
The path segment in Oh My Posh builds a display string by inserting raw filesystem folder names into a format string, then passes that whole string back through Go's text/template engine via template.Render().
The template engine's function map includes a cmd() helper that calls env.RunCommand() directly. Because every folder name in the current working directory ends up inside that re-rendered string, any folder whose name contains Go template syntax is executed unconditionally on every prompt render.
All path styles (full, folder, agnoster, mixed, letter) are affected because the second render happens after the style switch. The default config ships a path segment, so no custom configuration is required.
Proof of concept
A working proof-of-concept for this issue in github.com/jandedobbeleer/oh-my-posh, with the exact payload below.
# Create the malicious directory and navigate into it
mkdir -p '/home/user/{{ cmd `whoami` }}'
cd '/home/user/{{ cmd `whoami` }}'
# Trigger prompt render (the username is printed in place of the template expression)
oh-my-posh print primary --config p.json --shell fish \
--pwd '/home/user/{{ cmd `whoami` }}'
# Output: /home/user/<username> <- whoami executed
# File-write variant (no slash in payload, works on Windows too)
oh-my-posh print primary --config p.json --shell fish \
--pwd '/home/user/{{ cmd `sh` `-c` `id>proof` }}'
cat proof
# Output: uid=1000(user) gid=1000(user) ...
# Minimal p.json config needed (uses only the default path segment)
# { "version":3, "blocks":[{"type":"prompt","alignment":"left","segments":[{"type":"path","style":"plain","foreground":"#ffffff","template":"{{ .Path }}","properties":{"style":"full"}}]}]}The root cause is a double-evaluation pattern: folder names from the filesystem are inserted verbatim into a format string, and that string is then parsed and executed as a Go text/template. CWE-94 (Code Injection) and CWE-1336 (Template Engine Injection) both apply.
The cmd() function in src/template/func_map.go wraps env.RunCommand() with no restrictions, so any expression reachable through the template engine can spawn arbitrary processes. The path separator constraint (slashes cannot appear in a folder name on most systems) is trivially bypassed with shell IFS tricks or by running a script staged in the same directory.
The fix in commit 88ddbe0b removes the template.Render() call that re-processes the already-assembled path string, breaking the injection path. Folder names are now treated as inert data rather than template source.
The fix
Upgrade to oh-my-posh v29.35.1 or later. The patch (commit 88ddbe0b) removes the second template.Render() pass over the composed path string in src/segments/path.go so raw folder names are never parsed as template expressions.
Related research