T09 · Insecure Skill Coding Practices
Error
- Location
- references/secure-storage.md:145
- Finding
- Shell Command Injection in 1Password Secret Retrieval<![CDATA[ ## Vulnerability Details **File Location**: `references/secure-storage.md`, lines 145–163 **Vulnerability Type**: OS command injection through shell interpolation **Risk Level**: High ### Complete Vulnerable Code ```typescript import { execSync } from 'child_process'; interface SessionKey { sessionKey: string; smartAccount: string; chainId: number; expires: string; spendingLimit: string; allowedContracts: string[]; allowedMethods: string[]; } function getSessionKey(itemName: string): SessionKey { const vault = "Agent-Credentials"; const output = execSync( `op item get "${itemName}" --vault "${vault}" --format json`, { encoding: 'utf-8', timeout: 30000 } ); ``` ### Technical Analysis The function interpolates `itemName` directly into a command passed to `execSync`. By default, `execSync` executes the string through a command shell. Placing the value inside double quotes does not make it safe: a crafted value can terminate the quoted argument and introduce shell metacharacters or additional commands. This shell invocation is unnecessary because the `op` executable supports discrete command-line arguments. The flaw is particularly sensitive because the affected function retrieves wallet session keys from 1Password. If `itemName` can be influenced by a user, prompt-derived content, configuration, or another untrusted source, command execution occurs with the privileges and environment of the agent process. ### Attack Path 1. An attacker gains influence over the `itemName` supplied to `getSessionKey`, such as through an agent request, configuration field, or upstream application parameter. 2. The attacker supplies a value containing a closing quotation mark followed by shell syntax and an additional command. 3. The template literal embeds that value into the command string. 4. `execSync` passes the resulting string to a shell. 5. The injected command executes with the operating-system privileges of the agent. 6. Depe ...[truncated 1058 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Avoid shell interpretation entirely. Invoke the executable with a fixed argument array: ```typescript import { execFileSync } from "child_process"; function getSessionKey(itemName: string): SessionKey { const vault = "Agent-Credentials"; if (!/^[A-Za-z0-9._ -]{1,128}$/.test(itemName)) { throw new Error("Invalid 1Password item name"); } const output = execFileSync( "op", ["item", "get", itemName, "--vault", vault, "--format", "json"], { encoding: "utf-8", timeout: 30000, shell: false, } ); // Parse and validate the response. } ``` Additional hardening measures: 1. Use an allowlist of known item identifiers instead of accepting arbitrary names where possible. 2. Run the agent under a dedicated, unprivileged operating-system account. 3. Restrict the 1Password service account to read-only access to the smallest necessary vault and items. 4. Avoid placing unrelated credentials in the same vault. 5. Restrict outbound network access for the credential-handling process. 6. Ensure errors do not include command output or secret values. 7. Add tests using item names containing quotation marks, command separators, substitutions, whitespace, and newlines to verify they are rejected or passed only as literal arguments. ]]>
