T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/subaccount.js:14
- Finding
- OS Command Injection Through Unvalidated NEAR CLI Arguments## Vulnerability Details **File Location**: `scripts/subaccount.js:14-17`, `scripts/subaccount.js:27-30`, `scripts/subaccount.js:43-46`, and `scripts/subaccount.js:68-69` **Vulnerability Type**: OS command injection through `child_process.exec` **Risk Level**: High ### Vulnerable Code ```js async function createSubaccount(subaccountName, masterAccount) { const subaccountId = `${subaccountName}.${masterAccount}`; const cmd = `near create-account ${subaccountId} --masterAccount ${masterAccount} --initialBalance 0.1 ${networkFlag}`; try { await execAsync(cmd); ``` ```js async function listSubaccounts(accountId) { const cmd = `near view ${accountId} list_subaccounts ${networkFlag}`; try { const { stdout } = await execAsync(cmd); ``` ```js async function deleteSubaccount(subaccountId, masterAccount) { const cmd = `near delete-account ${subaccountId} --beneficiaryId ${masterAccount} ${networkFlag}`; try { await execAsync(cmd); ``` ```js for (const subaccountId of subaccounts) { try { const cmd = `near send ${masterAccount} ${subaccountId} ${amount} ${networkFlag}`; await execAsync(cmd); ``` ### Technical Analysis The application builds shell command strings by directly interpolating values from command-line arguments, the `NEAR_ACCOUNT` environment variable, and an attacker-controllable JSON file. These strings are passed to the promisified form of `child_process.exec`. Unlike an API that executes a program with a separate argument array, `exec` sends the command string through a system shell. Consequently, shell metacharacters and constructs contained in values such as `subaccountName`, `masterAccount`, `accountId`, `subaccountId`, or `amount` are interpreted by the shell rather than treated solely as NEAR CLI arguments. The application does not validate these values against the NEAR account-ID syntax, constrain the amount to a positive decimal number, or ...[truncated 1837 chars]
- Remediation
- ## Remediation Suggestions 1. Replace `child_process.exec` with `execFile` or `spawn`, passing every argument as a distinct array element and keeping shell execution disabled. ```js const { execFile } = require('child_process'); const { promisify } = require('util'); const execFileAsync = promisify(execFile); await execFileAsync('near', [ 'create-account', subaccountId, '--masterAccount', masterAccount, '--initialBalance', '0.1', '--networkId', 'testnet' ]); ``` 2. Apply the same argument-array approach to the `view`, `delete-account`, and `send` operations. Do not construct a command by concatenating or interpolating user-controlled strings. 3. Strictly validate every account identifier, including values from JSON and `NEAR_ACCOUNT`, against the exact NEAR account-ID rules. Reject whitespace, shell metacharacters, malformed labels, invalid lengths, and unexpected network suffixes. 4. Parse distribution amounts as numeric decimal values and enforce explicit minimum and maximum limits. Convert the validated value back to a canonical decimal string before passing it to the NEAR CLI. 5. Validate the entire distribution document before initiating any transfer. Reject unexpected properties, non-string entries, duplicate accounts, oversized arrays, and invalid destination accounts. 6. Do not treat shell escaping as the primary fix. Correct process invocation with `shell: false`, separate arguments, and strict domain validation provides stronger protection. 7. Add automated tests using inputs containing spaces, semicolons, quotes, command substitution, redirection operators, and newlines. The tests should verify that malicious input is rejected and never interpreted by a shell.
