T09 · Insecure Skill Coding Practices
- Location
ucloud.mjs:158- Finding
Shell Command Injection Through Unquoted CLI Arguments
- Content
View full analysis
Vulnerability Details
File Location:
ucloud.mjs, lines 158–167
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
js const fullArgs = [...baseArgs, ...args]; const command = "ucloud " + fullArgs.join(" "); try { const { stdout, stderr } = await execAsync(command); if (stderr) { return { success: false, error: stderr }; }User-controlled arguments reach this function through handlers such as the UHost creation handler:
js const createArgs = ["uhost", "create", "--cpu", values.cpu, "--memory-gb", values["memory-gb"], "--password", values.password, "--image-id", values["image-id"]]; if (values.name) createArgs.push("--name", values.name); if (values.count) createArgs.push("--count", values.count); result = await runUcloudCommand(createArgs);Technical Analysis
runUcloudCommand()constructs a single command string by joining argument values with spaces and executes it throughchild_process.exec(). Theexec()API invokes a command shell, so shell metacharacters in any argument are interpreted as command syntax rather than preserved as literal argument data.Many values incorporated into the command are externally controlled, including
--name,--password, resource identifiers, image identifiers, numeric fields, region, zone, project ID, and credentials obtained from environment variables. The implementation performs no shell escaping, quoting, allowlist validation, or character restrictions before execution.For example, a value containing a semicolon, command substitution, redirection, or shell pipeline can terminate or alter the intended UCloud command and introduce an additional command.
Attack Path
- An attacker supplies a crafted value through an accepted command-line option, such as a
--namevalue containing shell metacharacters. parseArgs()accepts the value as a string.- The relevant resource handler ...[truncated 1193 chars]
- An attacker supplies a crafted value through an accepted command-line option, such as a
- Remediation
View remediation
Remediation Suggestions
Replace shell-string execution with an API that invokes the executable directly and accepts an argument array, such as
execFile()orspawn():js import { execFile } from "node:child_process"; import { promisify } from "node:util"; const execFileAsync = promisify(execFile); async function runUcloudCommand(args, options = {}) { const fullArgs = [...baseArgs, ...args]; const { stdout, stderr } = await execFileAsync( "ucloud", fullArgs, { shell: false, maxBuffer: 1024 * 1024 } ); }Additional hardening should include:
- Explicitly set
shell: false. - Validate numeric options using strict numeric parsing and acceptable ranges.
- Restrict regions, zones, modes, traffic modes, and resource types to allowlisted values.
- Validate resource identifiers against documented UCloud identifier formats.
- Reject control characters and unexpected newline characters.
- Resolve the expected UCloud executable through a trusted absolute path where feasible.
- Run the skill under a minimally privileged operating-system account.
- Add security tests containing semicolons, command substitutions, redirects, pipelines, spaces, and newlines to ensure they remain literal arguments.
- Explicitly set
