T09 · Insecure Skill Coding Practices
Error
- Location
- src/lib/open.ts:5
- Finding
- Shell Command Injection Through a Server-Provided Authentication URL<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/auth.ts:101-104`, `src/lib/auth.ts:169-172`, and `src/lib/open.ts:5-20` **Vulnerability Type**: OS command injection through shell interpolation **Risk Level**: High ### Vulnerable Code ```ts // src/lib/auth.ts:101-104 export async function getAuthUrl(): Promise<AuthUrlResponse> { const { data } = await apiClient().get<{ data: AuthUrlResponse }>( "/api/auth/lite/auth-url" ); return data.data; } ``` ```ts // src/lib/auth.ts:169-172 output.log(` Opening browser...`); openUrl(authUrl); output.log(` Login link: ${authUrl}\n`); output.log(" Complete login in your browser, then press ENTER.\n"); ``` ```ts // src/lib/open.ts:5-20 import { exec } from "child_process"; export function openUrl(url: string): void { const platform = process.platform; let cmd: string; if (platform === "darwin") { cmd = `open "${url}"`; } else if (platform === "win32") { cmd = `start "" "${url}"`; } else { // Linux / others cmd = `xdg-open "${url}"`; } exec(cmd, (err) => { if (err) { // Silently fail — the URL is always printed as fallback } }); } ``` ### Technical Analysis The authentication URL is supplied by the remote ACP service and passed directly to `openUrl`. That function interpolates the URL into a shell command and invokes it using `child_process.exec`. Placing attacker-controlled data inside double quotes does not make shell execution safe. On Unix-like platforms, constructs such as command substitution can still be evaluated inside double quotes. Platform-specific shell metacharacters can create equivalent risks on Windows. There is no validation that the returned value: - Uses HTTPS. - Belongs to an expected Virtuals authentication host. - Is free of shell metacharacters. - Represents an ordinary HTTP or HTTPS URL. This makes the remote authentication response part of a local command-execution boundary. ### Attack Path 1. An attacker gains cont ...[truncated 971 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not use `exec` or any shell command string to open URLs. - Use argument-based process execution with shell processing disabled: ```ts import { spawn } from "child_process"; export function openUrl(rawUrl: string): void { const parsed = new URL(rawUrl); if (parsed.protocol !== "https:") { throw new Error("Only HTTPS authentication URLs are allowed"); } const allowedHosts = new Set([ "app.virtuals.io", "acpx.virtuals.io", ]); if (!allowedHosts.has(parsed.hostname.toLowerCase())) { throw new Error("Untrusted authentication URL host"); } const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open"; const args = process.platform === "win32" ? ["/c", "start", "", parsed.href] : [parsed.href]; spawn(command, args, { shell: false, detached: true, stdio: "ignore", }).unref(); } ``` - On Windows, prefer a maintained URL-opening library rather than invoking `cmd`. - Apply an explicit host allowlist for login URLs. - Reject embedded credentials, nonstandard schemes, control characters, and unexpected ports. - Treat authentication API responses as untrusted even when received over TLS. ]]>
