T09 · Insecure Skill Coding Practices
- Location
- auto-capture.js:73
- Finding
- Remote Command Injection Through Attacker-Controlled Email Sender Data<![CDATA[ ## Vulnerability Details **File Location**: `auto-capture.js:73-110` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```js async function searchOkkiCustomer(email, companyName) { const domain = extractDomain(email); try { const { execSync } = require('child_process'); if (domain) { try { const result = execSync( `python3 "${CONFIG.vectorSearchPath}" search "${domain}"`, { encoding: 'utf8', timeout: 10000 } ); if (result && result.trim()) { return { source: 'domain', data: result }; } } catch (e) { // Ignore errors and continue } } if (companyName) { try { const result = execSync( `python3 "${CONFIG.vectorSearchPath}" search "${companyName}"`, { encoding: 'utf8', timeout: 10000 } ); if (result && result.trim()) { return { source: 'company', data: result }; } } catch (e) { // Ignore errors } } } catch (err) { return null; } } ``` The values are populated from a received email at `auto-capture.js:263-266`: ```js const email = parsed.from?.value?.[0]?.address || ''; const companyName = parsed.from?.text?.replace(/<.*>/, '').trim() || ''; const okkiMatch = await searchOkkiCustomer(email, companyName); ``` ### Technical Analysis `execSync()` receives a command string that is interpreted by a shell. Both `domain` and `companyName` originate from an untrusted email `From` header and are interpolated directly inside double quotes. Double quotes do not neutralize shell command substitution, and an attacker can also inject a closing quote followed by shell operators. The filename of the vector-search script is configurable, but neither that path nor the untrusted query is passed as a discrete process argument. Because automatic capture can process remotely supplied messages, this flaw creates a remote-to- ...[truncated 1166 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Replace shell command construction with `execFile()` or `spawn()` and a fixed argument array: ```js const { execFile } = require('child_process'); execFile( 'python3', [CONFIG.vectorSearchPath, 'search', domain], { encoding: 'utf8', timeout: 10000, env: { PATH: process.env.PATH, PYTHONIOENCODING: 'utf-8' } }, callback ); ``` - Resolve the configured script path and require it to fall under an administrator-approved directory. - Validate sender addresses with a proper email parser instead of the permissive `@(.+)>?` expression. - Apply strict length and character limits to search terms. - Run automatic mail processing in a sandbox with no access to unrelated secrets or files. - Add regression tests containing quotes, semicolons, command substitutions, newlines, and shell operators. ]]>
