Hi Zed team,
Thank you for your work on Zed. I found and verified a command injection issue in the remote terminal command-building path and wanted to share a clear, reproducible report to help with triage and fixing.
I tested this on:
- Zed version:
v0.225.9 (release tag)
- Client: macOS Zed desktop
- Remote mode: SSH remote project
- Remote host: Linux (
/bin/bash)
Summary
Zed v0.225.9 builds SSH/WSL remote commands as a shell command string that starts with exec env ..., but environment variable keys are inserted without shell quoting or validation.
If an attacker can control an environment variable key (for example via project terminal settings), shell expansions in the key (such as $(...)) are evaluated by the remote shell when a terminal is opened. This can lead to arbitrary command execution on the remote host under the victim user's account.
Severity (my estimate): High (CWE-78, OS Command Injection)
Details
Root cause
In the SSH and WSL remote command builders, Zed constructs a shell command string like:
exec env KEY=VALUE program args...
The value is quoted for the target shell, but the key is written directly into the shell command string.
This means a malicious key such as ZED$(touch /tmp/pwned) is parsed by the shell, and the command substitution executes before exec env runs.
Incriminated source code (v0.225.9)
SSH (POSIX path):
WSL:
The relevant logic (simplified) is:
write!(exec, "exec env ")?;
for (k, v) in input_env.iter() {
write!(exec, "{}={} ", k, shell_kind.try_quote(v)?)?;
}
Reachability from project settings (real user workflow)
Project terminal settings are merged into the terminal environment:
The remote terminal path then passes this env map into build_command(...):
Scope notes
- Affects SSH remote (POSIX shell path) and WSL command-building paths.
- I verified the issue through the SSH remote terminal flow.
- The Windows-over-SSH path in
v0.225.9 appears to skip setting env vars in that code path, so this specific bug may not apply there.
PoC
Below are complete reproduction steps on a real Zed instance (no file uploads required).
Reproduction prerequisites
- Zed
v0.225.9
- Open a remote SSH project in Zed (not a local folder)
- Remote host uses a POSIX shell (for example
/bin/bash)
Step 1: Create project settings in the correct path
Important: the file must be inside .zed/settings.json in the project root.
Example path on the remote host:
/home/<user>/<project>/.zed/settings.json
Create the file with this content:
{
"terminal": {
"env": {
"ZED_POC_NORMAL": "works",
"ZED$(touch /tmp/zed_remote_env_key_injection)": "1"
}
}
}
ZED_POC_NORMAL is included as a simple sanity check to confirm the project terminal settings are being applied.
Step 2: Open a fresh terminal in the same remote project
- Close existing terminal tabs in that remote workspace
- Open a new integrated terminal in the remote project
The terminal should be newly opened because the environment is prepared during terminal launch.
Step 3: Verify settings were applied and payload executed
Run these commands in the remote terminal:
echo "$ZED_POC_NORMAL"
ls -l /tmp/zed_remote_env_key_injection
Expected result
ZED_POC_NORMAL should prove the project settings env was loaded:
The injected file should exist on the remote host, proving command execution:
-rw-r--r-- 1 <user> <group> 0 <date> /tmp/zed_remote_env_key_injection
Optional cleanup:
rm -f /tmp/zed_remote_env_key_injection
Please check my test here:
Impact
What kind of vulnerability is it?
- OS Command Injection (CWE-78)
Who is impacted?
- Users opening a remote SSH/WSL project in Zed where attacker-controlled environment keys can flow into the remote terminal/command environment.
- A realistic vector is a malicious repository with a project settings file (
.zed/settings.json) that defines terminal.env.
What can an attacker do?
- Execute arbitrary shell commands on the remote host as the user running the remote terminal/session
- Modify files, exfiltrate data accessible to that user, install persistence in that user account, etc.
Recommended fix
I think the safest fix is to validate environment variable names before formatting them into shell command strings.
Suggested approach
Reject env keys that do not match a strict identifier pattern, for example:
This should be applied before interpolation in:
- SSH POSIX command builder
- WSL command builder
Why validation is preferable here
- Quoting env keys correctly is shell-specific and easy to get wrong
- Environment variable names have a narrow valid character set anyway
- Validation is simpler and less error-prone than escaping arbitrary key strings
Additional hardening recommendations
- Add regression tests for invalid env keys containing:
- $(...)
- backticks (`...`)
- ;
- spaces
- =
- Consider validating env keys earlier when loading project terminal settings, so invalid keys are rejected consistently
References
Patches
Hi Zed team,
Thank you for your work on Zed. I found and verified a command injection issue in the remote terminal command-building path and wanted to share a clear, reproducible report to help with triage and fixing.
I tested this on:
v0.225.9(release tag)/bin/bash)Summary
Zed
v0.225.9builds SSH/WSL remote commands as a shell command string that starts withexec env ..., but environment variable keys are inserted without shell quoting or validation.If an attacker can control an environment variable key (for example via project terminal settings), shell expansions in the key (such as
$(...)) are evaluated by the remote shell when a terminal is opened. This can lead to arbitrary command execution on the remote host under the victim user's account.Severity (my estimate): High (CWE-78, OS Command Injection)
Details
Root cause
In the SSH and WSL remote command builders, Zed constructs a shell command string like:
The value is quoted for the target shell, but the key is written directly into the shell command string.
This means a malicious key such as
ZED$(touch /tmp/pwned)is parsed by the shell, and the command substitution executes beforeexec envruns.Incriminated source code (v0.225.9)
SSH (POSIX path):
crates/remote/src/transport/ssh.rslines 1612-1620 (v0.225.9)WSL:
crates/remote/src/transport/wsl.rslines 445-453 (v0.225.9)The relevant logic (simplified) is:
Reachability from project settings (real user workflow)
Project terminal settings are merged into the terminal environment:
crates/terminal/src/terminal_settings.rsline 34 (env field)crates/terminal/src/terminal_settings.rslines 79-85 (project settings merge)crates/terminal/src/terminal_settings.rsline 101 (project env applied)The remote terminal path then passes this env map into
build_command(...):crates/project/src/terminals.rslines 541-554 (remote terminal env -> build_command)crates/project/src/terminals.rslines 621-626 (remote shell path)Scope notes
v0.225.9appears to skip setting env vars in that code path, so this specific bug may not apply there.PoC
Below are complete reproduction steps on a real Zed instance (no file uploads required).
Reproduction prerequisites
v0.225.9/bin/bash)Step 1: Create project settings in the correct path
Important: the file must be inside
.zed/settings.jsonin the project root.Example path on the remote host:
Create the file with this content:
{ "terminal": { "env": { "ZED_POC_NORMAL": "works", "ZED$(touch /tmp/zed_remote_env_key_injection)": "1" } } }ZED_POC_NORMALis included as a simple sanity check to confirm the project terminal settings are being applied.Step 2: Open a fresh terminal in the same remote project
The terminal should be newly opened because the environment is prepared during terminal launch.
Step 3: Verify settings were applied and payload executed
Run these commands in the remote terminal:
Expected result
ZED_POC_NORMALshould prove the project settings env was loaded:The injected file should exist on the remote host, proving command execution:
Optional cleanup:
Please check my test here:
Impact
What kind of vulnerability is it?
Who is impacted?
.zed/settings.json) that definesterminal.env.What can an attacker do?
Recommended fix
I think the safest fix is to validate environment variable names before formatting them into shell command strings.
Suggested approach
Reject env keys that do not match a strict identifier pattern, for example:
This should be applied before interpolation in:
Why validation is preferable here
Additional hardening recommendations
-
$(...)- backticks (
`...`)-
;- spaces
-
=References
Patches