T09 · Insecure Skill Coding Practices
- Location
src/lib/open.ts:5- Finding
Shell Command Injection Through Server-Provided Authentication URL
- Content
View full analysis
{ const { data } = await apiClient().get<{ data: AuthUrlResponse }>( "/api/auth/lite/auth-url" ); return data.data; } ``` ```ts // src/lib/auth.ts:170-172 output.log(` Opening browser...`); openUrl(authUrl); output.log(` Login link: ${authUrl}\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 returned by `https://acpx.virtuals.io/api/auth/lite/auth-url` is passed directly to `openUrl()`. That function embeds the value into a command string and invokes it through `child_process.exec()`, which executes the string using a system shell. Surrounding the URL with double quotes does not make this safe. On Unix-like systems, shell substitutions such as `$(...)` and backticks are still evaluated inside double quotes. Other platforms have their own shell metacharacters and quoting behavior. The expected authentication request is consistent with the Skill's declared functionality. However, opening the returned URL through a shell exceeds the minimum privilege required: launching a browser only requires passing the URL as a process argument, not granting it shell interpretation. ### Attack Path 1. An attacker compromises, impers ...[truncated 1096 chars]- Remediation
View remediation
