fix(security): add path traversal defense-in-depth to uploadFile (#1988)

Add `|| remotePath.includes("..")` check to hetzner, digitalocean,
and aws uploadFile functions. The regex `/^[a-zA-Z0-9/_.~-]+$/`
allows `.` characters, so paths like `../../etc/passwd` pass the
regex but are path traversal attempts. gcp, daytona, and sprite
already include this explicit check — this makes all providers
consistent.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
A 2026-02-27 03:20:32 -08:00 committed by GitHub
parent 2eb623e386
commit e13d809f37
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 3 additions and 3 deletions

View file

@ -1086,7 +1086,7 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number): Promi
}
export async function uploadFile(localPath: string, remotePath: string): Promise<void> {
if (!/^[a-zA-Z0-9/_.~-]+$/.test(remotePath)) {
if (!/^[a-zA-Z0-9/_.~-]+$/.test(remotePath) || remotePath.includes("..")) {
throw new Error(`Invalid remote path: ${remotePath}`);
}
const keyOpts = getSshKeyOpts(await ensureSshKeys());

View file

@ -1040,7 +1040,7 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number, ip?: s
export async function uploadFile(localPath: string, remotePath: string, ip?: string): Promise<void> {
const serverIp = ip || doServerIp;
if (!/^[a-zA-Z0-9/_.~-]+$/.test(remotePath)) {
if (!/^[a-zA-Z0-9/_.~-]+$/.test(remotePath) || remotePath.includes("..")) {
logError(`Invalid remote path: ${remotePath}`);
throw new Error("Invalid remote path");
}

View file

@ -579,7 +579,7 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number, ip?: s
export async function uploadFile(localPath: string, remotePath: string, ip?: string): Promise<void> {
const serverIp = ip || hetznerServerIp;
if (!/^[a-zA-Z0-9/_.~-]+$/.test(remotePath)) {
if (!/^[a-zA-Z0-9/_.~-]+$/.test(remotePath) || remotePath.includes("..")) {
logError(`Invalid remote path: ${remotePath}`);
throw new Error("Invalid remote path");
}