-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Summary
The runServer() and related functions in packages/cli/src/gcp/gcp.ts use shellQuote() to wrap user-controlled commands before passing them to SSH. While single-quote wrapping prevents most issues, the shell escaping pattern '\'' for embedded single quotes could potentially be bypassed with carefully crafted input.
Impact
If an attacker can control the cmd parameter passed to runServer(), runServerCapture(), or interactiveSession(), they could potentially execute arbitrary commands on the remote GCP instance.
Vulnerable Code
gcp.ts:827-856 (runServer)
export async function runServer(cmd: string, timeoutSecs?: number): Promise<void> {
const username = resolveUsername();
const fullCmd = `export PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && ${cmd}`;
const keyOpts = getSshKeyOpts(await ensureSshKeys());
const proc = Bun.spawn(
[
"ssh",
...SSH_BASE_OPTS,
...keyOpts,
`${username}@${gcpServerIp}`,
`bash -c ${shellQuote(fullCmd)}`, // Potential injection point
],gcp.ts:997-999 (shellQuote function)
function shellQuote(s: string): string {
return "'" + s.replace(/'/g, "'\\''") + "'";
}Attack Vector
The shellQuote function escapes single quotes using the pattern '\'' which closes the current quote, adds an escaped quote, and reopens. This is generally safe BUT:
- If the input contains sequences like
' ; malicious_cmd ; ', the escaping might not work as expected - The
cmdparameter comes from agent launch commands stored in manifest.json, which could be tampered with if manifest caching is compromised - No additional validation is performed on
cmdbefore passing to SSH
Recommendation
Option 1: Input Validation (Preferred)
Add strict validation on cmd parameter before passing to SSH:
function validateRemoteCommand(cmd: string): void {
if (!cmd || cmd.trim() === "") {
throw new Error("Command is required");
}
// Reject dangerous shell metacharacters in unexpected contexts
const dangerous = /[;&|<>$(){}[\]]/;
if (dangerous.test(cmd)) {
logWarn("Command contains shell metacharacters - ensure it's from trusted source");
}
}
export async function runServer(cmd: string, timeoutSecs?: number): Promise<void> {
validateRemoteCommand(cmd); // Validate before use
const username = resolveUsername();
const fullCmd = `export PATH="..." && ${cmd}`;
// ... rest of function
}Option 2: Use SSH Argument Separation (Defense-in-Depth)
Instead of passing the command as a single string to bash -c, pass it as a separate SSH argument.
Severity
HIGH - Potential for arbitrary command execution on remote GCP instances if manifest.json is compromised.
References
- File:
packages/cli/src/gcp/gcp.tslines 827-856, 858-893, 932-962, 997-999 - Similar issue previously fixed in other cloud drivers (see closed security issues)
- OWASP: Command Injection (A03:2021)